ArgumentStack.java
package autumn.lang.internals;
import com.mackenziehigh.autumn.resources.Finished;
import java.util.Arrays;
/**
* Instances of this class are used to send arguments into functors and send result back therefrom.
*
* <p>
* <b>Warning:</b> The API of this class shall not be changed, because the methods
* herein are invoked directly from bytecode. As a result, any revision could break
* backwards compatibility. That being said, the internal implementation of this class
* may be enhanced in the future in order to further optimize it.
* </p>
*
* <p>
* The code in this class is among the most frequently accessed code in a running Autumn program.
* </p>
*
* <p>
* There will be one instance of this class for every thread in an Autumn program.
* The instance is usually referred to simply as the "argument-stack".
* The argument-stack should not be confused with the bytecode level operand-stack.
* The two stacks perform similar purposes; however, they are distinct entities.
* </p>
*
* @author Mackenzie High
*/
@Finished("2014/07/12")
public final class ArgumentStack
{
/**
* Instances of this class are used to store arguments on the argument-stack.
*/
private static final class Argument
{
/**
* This is the type of value that is currently stored herein.
*/
public Class type;
public boolean value_Z;
public char value_C;
public byte value_B;
public short value_S;
public int value_I;
public long value_J;
public float value_F;
public double value_D;
public Object value_O;
}
/**
* This thread-local variable stores the argument-stack associated with this thread.
*/
private static final ThreadLocal<ArgumentStack> thread_stack = new ThreadLocal<ArgumentStack>();
/**
* This array stores the arguments that are currently on the argument-stack.
* Note: The size of this array is not the same thing as the size of the argument-stack itself.
*/
private Argument[] stack;
/**
* This is the size of the argument-stack itself.
* In other words, this is the number of arguments that are currently on the argument-stack.
*/
private int size = 0;
/**
* Sole Constructor.
*/
ArgumentStack()
{
/**
* Initially, the stack will have room for sixty-four elements.
* In reality, no thread should need a larger stack than this.
*/
final int INITIAL_SIZE = 64;
// Allocate the stack itself.
stack = new Argument[INITIAL_SIZE];
// Allocate the objects that store the arguemnts.
for (int i = 0; i < INITIAL_SIZE; i++)
{
stack[i] = new Argument();
}
}
/**
* This method allocate space for at least one more argument.
*/
private void increaseSize()
{
if (stack.length == size)
{
stack = Arrays.copyOf(stack, size + 64);
}
stack[size] = new Argument();
}
/**
* This method retrieves the argument-stack that is associated with the current thread.
*
* <p>
* The returned argument-stack will be empty.
* </p>
*
* @return the argument-stack of the current thread.
*/
public static ArgumentStack getThreadStack()
{
final ArgumentStack stk = thread_stack.get();
if (stk != null)
{
stk.clear();
return stk;
}
else
{
final ArgumentStack result = new ArgumentStack();
thread_stack.set(result);
return result;
}
}
/**
* This method removes all arguments from the argument-stack.
*/
public final void clear()
{
// Remove references to objects.
// Otherwise, a memory leak could result.
for (int i = 0; i < size; i++)
{
stack[i].value_O = null;
}
size = 0;
}
/**
* This method retrieves the size of the argument-stack.
*
* @return the number of arguments currently on the argument-stack.
*/
public final int size()
{
return size;
}
/**
* This method determines whether the argument-stack is empty.
*
* @return true, iff the size of this stack is zero.
*/
public final boolean isEmpty()
{
return size == 0;
}
/**
* This method pushes an argument onto the argument-stack.
*
* @param value is the argument to push onto the stack.
*/
public final void push(final boolean value)
{
/**
* Ensure that there is enough space on the argument-stack for one more element.
*/
if (size == stack.length)
{
increaseSize();
}
/**
* Push the value onto the argument-stack.
*/
final Argument x = stack[size++];
x.type = boolean.class;
x.value_Z = value;
}
/**
* This method pushes an argument onto the argument-stack.
*
* @param value is the argument to push onto the stack.
*/
public final void push(final char value)
{
/**
* Ensure that there is enough space on the argument-stack for one more element.
*/
if (size == stack.length)
{
increaseSize();
}
/**
* Push the value onto the argument-stack.
*/
final Argument x = stack[size++];
x.type = char.class;
x.value_C = value;
}
/**
* This method pushes an argument onto the argument-stack.
*
* @param value is the argument to push onto the stack.
*/
public final void push(final byte value)
{
/**
* Ensure that there is enough space on the argument-stack for one more element.
*/
if (size == stack.length)
{
increaseSize();
}
/**
* Push the value onto the argument-stack.
*/
final Argument x = stack[size++];
x.type = byte.class;
x.value_B = value;
}
/**
* This method pushes an argument onto the argument-stack.
*
* @param value is the argument to push onto the stack.
*/
public final void push(final short value)
{
/**
* Ensure that there is enough space on the argument-stack for one more element.
*/
if (size == stack.length)
{
increaseSize();
}
/**
* Push the value onto the argument-stack.
*/
final Argument x = stack[size++];
x.type = short.class;
x.value_S = value;
}
/**
* This method pushes an argument onto the argument-stack.
*
* @param value is the argument to push onto the stack.
*/
public final void push(final int value)
{
/**
* Ensure that there is enough space on the argument-stack for one more element.
*/
if (size == stack.length)
{
increaseSize();
}
/**
* Push the value onto the argument-stack.
*/
final Argument x = stack[size++];
x.type = int.class;
x.value_I = value;
}
/**
* This method pushes an argument onto the argument-stack.
*
* @param value is the argument to push onto the stack.
*/
public final void push(final long value)
{
/**
* Ensure that there is enough space on the argument-stack for one more element.
*/
if (size == stack.length)
{
increaseSize();
}
/**
* Push the value onto the argument-stack.
*/
final Argument x = stack[size++];
x.type = long.class;
x.value_J = value;
}
/**
* This method pushes an argument onto the argument-stack.
*
* @param value is the argument to push onto the stack.
*/
public final void push(final float value)
{
/**
* Ensure that there is enough space on the argument-stack for one more element.
*/
if (size == stack.length)
{
increaseSize();
}
/**
* Push the value onto the argument-stack.
*/
final Argument x = stack[size++];
x.type = float.class;
x.value_F = value;
}
/**
* This method pushes an argument onto the argument-stack.
*
* @param value is the argument to push onto the stack.
*/
public final void push(final double value)
{
/**
* Ensure that there is enough space on the argument-stack for one more element.
*/
if (size == stack.length)
{
increaseSize();
}
/**
* Push the value onto the argument-stack.
*/
final Argument x = stack[size++];
x.type = double.class;
x.value_D = value;
}
/**
* This method pushes an argument onto the argument-stack.
*
* @param value is the argument to push onto the stack.
*/
public final void push(final Object value)
{
/**
* Determine the actual type of the object.
*/
final Class klass = value == null ? null : value.getClass();
/**
* If the object is a boxed type, then treat it as a primitive-type.
*/
if (klass == Boolean.class)
{
push((boolean) (Boolean) value);
}
else if (klass == Character.class)
{
push((char) (Character) value);
}
else if (klass == Byte.class)
{
push((byte) (Byte) value);
}
else if (klass == Short.class)
{
push((short) (Short) value);
}
else if (klass == Integer.class)
{
push((int) (Integer) value);
}
else if (klass == Long.class)
{
push((long) (Long) value);
}
else if (klass == Float.class)
{
push((float) (Float) value);
}
else if (klass == Double.class)
{
push((double) (Double) value);
}
else
{
/**
* Ensure that there is enough space on the argument-stack for one more element.
*/
if (size == stack.length)
{
increaseSize();
}
/**
* Push the value onto the argument-stack.
*/
final Argument x = stack[size++];
x.type = Object.class;
x.value_O = value;
}
}
/**
* This method removes the topmost element from the argument-stack.
*
* @throws IllegalStateException if the argument-stack is already empty.
*/
public final void pop()
{
if (size == 0)
{
throw new IllegalStateException("The argument-stack is empty.");
}
--size;
}
/**
* This method creates a special exception object.
*
* @return the new exception.
*/
private RuntimeException wrongType()
{
return new ClassCastException("The argument cannot be returned due to its type.");
}
/**
* This method retrieves an argument that is at a specific location on the argument-stack.
*
* <p>
* The argument will be unboxed, if necessary.
* </p>
*
* @param index is the zero-based index of the argument as measured from the stack's base.
* @return the argument.
* @throws IndexOutOfBoundsException if the index is invalid.
* @throws ClassCastException if unboxing fails.
* @throws ClassCastException if the value cannot be returned due to its type.
*/
public final boolean getZ(final int index)
{
final Argument x = stack[index];
if (x.type == boolean.class)
{
return x.value_Z;
}
else if (x.type == Object.class)
{
return (boolean) (Boolean) x.value_O;
}
else
{
throw wrongType();
}
}
/**
* This method retrieves an argument that is at a specific location on the argument-stack.
*
* <p>
* The argument will be unboxed, if necessary.
* </p>
*
* @param index is the zero-based index of the argument as measured from the stack's base.
* @return the argument.
* @throws IndexOutOfBoundsException if the index is invalid.
* @throws ClassCastException if unboxing fails.
* @throws ClassCastException if the value cannot be returned due to its type.
*/
public final char getC(final int index)
{
final Argument x = stack[index];
if (x.type == char.class)
{
return x.value_C;
}
else if (x.type == Object.class)
{
return (char) (Character) x.value_O;
}
else
{
throw wrongType();
}
}
/**
* This method retrieves an argument that is at a specific location on the argument-stack.
*
* <p>
* The argument will be unboxed, if necessary.
* </p>
*
* @param index is the zero-based index of the argument as measured from the stack's base.
* @return the argument.
* @throws IndexOutOfBoundsException if the index is invalid.
* @throws ClassCastException if unboxing fails.
* @throws ClassCastException if the value cannot be returned due to its type.
*/
public final byte getB(final int index)
{
final Argument x = stack[index];
if (x.type == byte.class)
{
return x.value_B;
}
else if (x.type == Object.class)
{
return (byte) (Byte) x.value_O;
}
else
{
throw wrongType();
}
}
/**
* This method retrieves an argument that is at a specific location on the argument-stack.
*
* <p>
* The argument will be unboxed or coerced, if necessary.
* </p>
*
* @param index is the zero-based index of the argument as measured from the stack's base.
* @return the argument.
* @throws IndexOutOfBoundsException if the index is invalid.
* @throws ClassCastException if unboxing fails.
* @throws ClassCastException if the value cannot be returned due to its type.
*/
public final short getS(final int index)
{
final Argument x = stack[index];
if (x.type == short.class)
{
return x.value_S;
}
else if (x.type == byte.class)
{
return x.value_B;
}
else if (x.type == Object.class)
{
return (short) (Short) x.value_O;
}
else
{
throw wrongType();
}
}
/**
* This method retrieves an argument that is at a specific location on the argument-stack.
*
* <p>
* The argument will be unboxed or coerced, if necessary.
* </p>
*
* @param index is the zero-based index of the argument as measured from the stack's base.
* @return the argument.
* @throws IndexOutOfBoundsException if the index is invalid.
* @throws ClassCastException if unboxing fails.
* @throws ClassCastException if the value cannot be returned due to its type.
*/
public final int getI(final int index)
{
final Argument x = stack[index];
if (x.type == int.class)
{
return x.value_I;
}
else if (x.type == char.class)
{
return x.value_C;
}
else if (x.type == byte.class)
{
return x.value_B;
}
else if (x.type == short.class)
{
return x.value_S;
}
else if (x.type == Object.class)
{
return (int) (Integer) x.value_O;
}
else
{
throw wrongType();
}
}
/**
* This method retrieves an argument that is at a specific location on the argument-stack.
*
* <p>
* The argument will be unboxed or coerced, if necessary.
* </p>
*
* @param index is the zero-based index of the argument as measured from the stack's base.
* @return the argument.
* @throws IndexOutOfBoundsException if the index is invalid.
* @throws ClassCastException if unboxing fails.
* @throws ClassCastException if the value cannot be returned due to its type.
*/
public final long getJ(final int index)
{
final Argument x = stack[index];
if (x.type == long.class)
{
return x.value_J;
}
else if (x.type == char.class)
{
return x.value_C;
}
else if (x.type == byte.class)
{
return x.value_B;
}
else if (x.type == short.class)
{
return x.value_S;
}
else if (x.type == int.class)
{
return x.value_I;
}
else if (x.type == Object.class)
{
return (long) (Long) x.value_O;
}
else
{
throw wrongType();
}
}
/**
* This method retrieves an argument that is at a specific location on the argument-stack.
*
* <p>
* The argument will be unboxed, if necessary.
* </p>
*
* @param index is the zero-based index of the argument as measured from the stack's base.
* @return the argument.
* @throws IndexOutOfBoundsException if the index is invalid.
* @throws ClassCastException if unboxing fails.
* @throws ClassCastException if the value cannot be returned due to its type.
*/
public final float getF(final int index)
{
final Argument x = stack[index];
if (x.type == float.class)
{
return x.value_F;
}
else if (x.type == Object.class)
{
return (float) (Float) x.value_O;
}
else
{
throw wrongType();
}
}
/**
* This method retrieves an argument that is at a specific location on the argument-stack.
*
* <p>
* The argument will be unboxed or coerced, if necessary.
* </p>
*
* @param index is the zero-based index of the argument as measured from the stack's base.
* @return the argument.
* @throws IndexOutOfBoundsException if the index is invalid.
* @throws ClassCastException if unboxing fails.
* @throws ClassCastException if the value cannot be returned due to its type.
*/
public final double getD(final int index)
{
final Argument x = stack[index];
if (x.type == double.class)
{
return x.value_D;
}
else if (x.type == float.class)
{
return x.value_F;
}
else if (x.type == Object.class)
{
return (double) (Double) x.value_O;
}
else
{
throw wrongType();
}
}
/**
* This method retrieves an argument that is at a specific location on the argument-stack.
*
* <p>
* The argument will be boxed, if necessary.
* </p>
*
* @param index is the zero-based index of the argument as measured from the stack's base.
* @return the argument.
* @throws IndexOutOfBoundsException if the index is invalid.
* @throws ClassCastException if the value cannot be returned due to its type.
*/
public final Object getO(final int index)
{
final Argument x = stack[index];
if (x.type == boolean.class)
{
return x.value_Z;
}
else if (x.type == char.class)
{
return x.value_C;
}
else if (x.type == byte.class)
{
return x.value_B;
}
else if (x.type == short.class)
{
return x.value_S;
}
else if (x.type == int.class)
{
return x.value_I;
}
else if (x.type == long.class)
{
return x.value_J;
}
else if (x.type == float.class)
{
return x.value_F;
}
else if (x.type == double.class)
{
return x.value_D;
}
else if (x.type == Object.class)
{
return x.value_O;
}
else
{
throw wrongType();
}
}
/**
* This method retrieves the argument that is on the top of the argument-stack.
*
* <p>
* The argument will be unboxed, if necessary.
* </p>
*
* @return the argument.
* @throws IndexOutOfBoundsException if the stack is empty.
* @throws ClassCastException if unboxing fails.
* @throws ClassCastException if the value cannot be returned due to its type.
*/
public final boolean peekZ()
{
return getZ(size - 1);
}
/**
* This method retrieves the argument that is on the top of the argument-stack.
*
* <p>
* The argument will be unboxed, if necessary.
* </p>
*
* @return the argument.
* @throws IndexOutOfBoundsException if the stack is empty.
* @throws ClassCastException if unboxing fails.
* @throws ClassCastException if the value cannot be returned due to its type.
*/
public final char peekC()
{
return getC(size - 1);
}
/**
* This method retrieves the argument that is on the top of the argument-stack.
*
* <p>
* The argument will be unboxed, if necessary.
* </p>
*
* @return the argument.
* @throws IndexOutOfBoundsException if the stack is empty.
* @throws ClassCastException if unboxing fails.
* @throws ClassCastException if the value cannot be returned due to its type.
*/
public final byte peekB()
{
return getB(size - 1);
}
/**
* This method retrieves the argument that is on the top of the argument-stack.
*
* <p>
* The argument will be unboxed, if necessary.
* </p>
*
* @return the argument.
* @throws IndexOutOfBoundsException if the stack is empty.
* @throws ClassCastException if unboxing fails.
* @throws ClassCastException if the value cannot be returned due to its type.
*/
public final short peekS()
{
return getS(size - 1);
}
/**
* This method retrieves the argument that is on the top of the argument-stack.
*
* <p>
* The argument will be unboxed, if necessary.
* </p>
*
* @return the argument.
* @throws IndexOutOfBoundsException if the stack is empty.
* @throws ClassCastException if unboxing fails.
* @throws ClassCastException if the value cannot be returned due to its type.
*/
public final int peekI()
{
return getI(size - 1);
}
/**
* This method retrieves the argument that is on the top of the argument-stack.
*
* <p>
* The argument will be unboxed, if necessary.
* </p>
*
* @return the argument.
* @throws IndexOutOfBoundsException if the stack is empty.
* @throws ClassCastException if unboxing fails.
* @throws ClassCastException if the value cannot be returned due to its type.
*/
public final long peekJ()
{
return getJ(size - 1);
}
/**
* This method retrieves the argument that is on the top of the argument-stack.
*
* <p>
* The argument will be unboxed, if necessary.
* </p>
*
* @return the argument.
* @throws IndexOutOfBoundsException if the stack is empty.
* @throws ClassCastException if unboxing fails.
* @throws ClassCastException if the value cannot be returned due to its type.
*/
public final float peekF()
{
return getF(size - 1);
}
/**
* This method retrieves the argument that is on the top of the argument-stack.
*
* <p>
* The argument will be unboxed, if necessary.
* </p>
*
* @return the argument.
* @throws IndexOutOfBoundsException if the stack is empty.
* @throws ClassCastException if unboxing fails.
* @throws ClassCastException if the value cannot be returned due to its type.
*/
public final double peekD()
{
return getD(size - 1);
}
/**
* This method retrieves the argument that is on the top of the argument-stack.
*
* <p>
* The argument will be boxed, if necessary.
* </p>
*
* @return the argument.
* @throws IndexOutOfBoundsException if the stack is empty.
*/
public final Object peekO()
{
return getO(size - 1);
}
/**
* This method retrieves and removes the argument that is on the top of the argument-stack.
*
* <p>
* The argument will be boxed, if necessary.
* </p>
*
* @return the argument.
* @throws IndexOutOfBoundsException if the stack is empty.
*/
public final Object popO()
{
final Object value = peekO();
pop();
return value;
}
/**
* This method retrieves and removes the value that is on top of the argument-stack, if any.
*
* <p>
* Caution: This method will also clear the stack.
* </p>
*
* <p>
* This method will return null, if the stack is empty.
* </p>
*
* <p>
* This method was added in order to simply the retrieval of functor return-values.
* </p>
*
* @return the topmost argument, if one exists; otherwise, return null.
*/
public final Object popResult()
{
final Object result = isEmpty() ? null : popO();
clear();
return result;
}
}