final class Compiler extends java.lang.Object implements CompilerConstants, ExpressionFlags, ExpressionParserTokenTypes
compile(com.goldencode.expr.Expression)
method. This method is intended to be
called only from the Expression
class. At a high level, it does
the following:
Compiler
instance and associates it with
a particular Expression
instance.
Expression
object into an abstract syntax tree (AST);
execute
method containing the bytecode assembled in the previous step. The
class generated is a direct subclass of CompiledExpression
.
CompiledExpression
subclass
and returns it.
Modifier and Type | Class and Description |
---|---|
private static class |
Compiler.LogicalData
Helper class which stores information to assist when assembling branch
instructions for logical tests.
|
private static class |
Compiler.PrimitiveInfo
A helper class which stores information about the various primitive
data types supported by the expression compiler.
|
Modifier and Type | Field and Description |
---|---|
private static java.lang.String |
CLASS_NAME_PREFIX
Class name prefix
|
private static int |
classCount
Counter appended to each new expression class' name
|
private ClassFile |
classFile
Class file object which represents compiled class
|
private CompiledExpression |
compiled
Instance of compiled expression custom subclass
|
private ConstantPool |
constantPool
Constant pool of class file
|
private ExtraAst |
currentAst
AST currently being assembled
|
private java.lang.Class |
customClass
Expression class after classloading
|
(package private) static boolean |
debug
Master debug flag; verbose output to stdout generated when true
|
private static double |
EPSILON
Value for floating point epsilon testing
|
private Expression |
expression
Object which holds expression information
|
private java.util.Map |
libraries
Map of library name symbols to library runtime index
|
private static ExpressionClassLoader |
loader
Custom classloader for in-memory compiled expression class loading
|
private int |
maxLocals
Maximum local variables in use within execute() method
|
private int |
maxStackDepth
Maximum stack depth across all instructions
|
private int |
nextAstId
Next AST ID to be assigned during assembly of bytecode instructions
|
(package private) static java.lang.String |
PKG_PREFIX
Package prefix using slash separator; e.g., 'foo/bar'
|
private static java.util.Map |
PRIMITIVES
Primitive signature names by primitive class
|
private java.lang.Class |
resultType
Data type of result of evaluating expression (may be primitive)
|
private java.lang.Class |
returnType
Data type which must be returned from execute() (must be object)
|
private double |
roundingConstant
Constant for rounding floating point calculations to fixed precision
|
private int |
stackDepth
Stack depth at current instruction
|
private ExtraAst |
tree
Parsed infix expression in AST form
|
private java.util.Map |
variables
Map of variable name symbols to variable runtime index
|
private static java.util.Map |
WRAPPERS
Map of primitive object wrapper classes to primitive types
|
_aastore, _aconst_null, _aload_0, _aload_1, _aload_2, _aload_3, _anewarray, _areturn, _astore_1, _astore_2, _astore_3, _checkcast, _d2i, _d2l, _dadd, _dcmpg, _dconst_0, _dconst_1, _ddiv, _dmul, _dneg, _drem, _dreturn, _dsub, _dup, _getfield, _goto, _i2d, _i2l, _iadd, _iand, _iconst_0, _iconst_1, _iconst_2, _iconst_3, _iconst_4, _iconst_5, _idiv, _if_icmpeq, _if_icmpge, _if_icmpgt, _if_icmple, _if_icmplt, _if_icmpne, _ifeq, _ifge, _ifgt, _ifle, _iflt, _ifne, _ifnonnull, _ifnull, _imul, _ineg, _invokeinterface, _invokespecial, _invokestatic, _invokevirtual, _ior, _irem, _ireturn, _ishl, _ishr, _isub, _iushr, _ixor, _l2d, _l2i, _ladd, _land, _lcmp, _lconst_0, _lconst_1, _ldc_w, _ldc2_w, _ldiv, _lmul, _lneg, _lor, _lrem, _lshl, _lshr, _lsub, _lushr, _lxor, _new, _pop, _pop2, _return, _swap, CLASS_COMPILEDEXPRESSION, METHDESC_EXECUTE, METHNAME_EXECUTE, METHOD_INIT
RT_CLONE, RT_DEBUG, RT_ERROR, RT_FP_ROUNDING, RT_FP_WIDENING, RT_INT_NARROWING, RT_NO_ASSIGN, RT_NONE
AND, ASSIGN, BIT_AND, BIT_COMP, BIT_OR, BIT_XOR, BOOL_FALSE, BOOL_TRUE, CAST, COMMA, DEC_LITERAL, DIGIT, DIVIDE, DOT, DSTRING, EOF, EQUALS, GOTO, GT, GTE, HASH, HEX_LITERAL, HEXDIGIT, IDENTITY, IS_NULL, LETTER, LPARENS, LSHIFT, LT, LTE, METH_BOOLEAN, METH_DOUBLE, METH_INT, METH_LONG, METH_OBJ, MINUS, MODULO, MULTIPLY, NOT, NOT_EQ, NOT_NULL, NULL, NULL_TREE_LOOKAHEAD, NUM_LITERAL, OR, PLUS, POSPARM, RPARENS, RSHIFT, SSTRING, STRING, SYM_CHAR, SYMBOL, UN_MINUS, VALID_SYM_CHAR, WS, ZRSHIFT
Modifier | Constructor and Description |
---|---|
private |
Compiler(Expression expression)
Private constructor.
|
Modifier and Type | Method and Description |
---|---|
private void |
addDebug(CodeUnit unit)
Adds instructions to the specified code unit to update diagnostic
information at runtime.
|
private CodeUnit |
assemble(ExtraAst ast)
Primary driver method for the assembly of the compiled expression's
execute method's bytecode
instructions. |
private CodeUnit |
assembleArithmeticNegation(java.lang.Class required,
CodeUnit operand)
Assemble the instructions necessary to perform an arithmetic (sign)
negation of the specified operand.
|
private CodeUnit |
assembleArrayStore(int index,
java.lang.Class required,
CodeUnit argUnit)
Assemble the instructions necessary to store a single data value in an
object array.
|
private CodeUnit |
assembleBinaryArithmeticOp(int type,
java.lang.Class required,
java.util.List units)
Assemble the instructions necessary to perform a binary, arithmetic
operation (multiplication, division, remainder, addition, subtraction).
|
private CodeUnit |
assembleBinaryBitwiseOp(int type,
java.lang.Class required,
java.util.List units)
Assemble the instructions necessary to perform a binary, bitwise
operation.
|
private CodeUnit |
assembleBitwiseNegation(java.lang.Class required,
CodeUnit operand)
Assemble instructions necessary to perform a bitwise negation of the
specified operand.
|
private CodeUnit |
assembleBoolLiteral(int type,
Compiler.LogicalData logData)
Assemble the instructions necessary to load either a boolean constant
onto the runtime stack, or to branch to another instruction, based
upon the value of a boolean literal and the provided
logData information. |
private CodeUnit |
assembleBootstrapMethod(int type,
Compiler.LogicalData logData,
Function function,
java.util.List units)
Assemble the instructions necessary to invoke a "bootstrap" method.
|
private CodeUnit |
assembleBranchOnBooleanResult(int type,
Compiler.LogicalData logData,
CodeUnit core,
java.lang.Class required)
Assemble instructions necessary to branch the flow of control based
upon the result of executing the instructions within the given code
unit.
|
private CodeUnit |
assembleComparatorOp(int type,
Compiler.LogicalData logData,
java.lang.Class opClass,
java.util.List units)
Assemble instructions to perform a logical comparison of two values,
and to proceed (possibly branch) to the proper instruction, based upon
the outcome of that comparison.
|
private CodeUnit |
assembleConjunctionOp(ExtraAst ast)
Assemble the instructions necessary to execute a logical conjunction
operation (
AND or OR ). |
private CodeUnit |
assembleDecLiteral(double num)
Assemble the instructions necessary to load a floating point constant
onto the runtime stack.
|
private CodeUnit |
assembleEpsilonTest()
Assemble instructions necessary to perform an "epsilon test" to
determine the equality of two floating point values.
|
private CodeUnit |
assembleExplicitCast(java.lang.Class required)
Assemble the instruction necessary to cast the object reference at the
top of the runtime stack to the required object type.
|
private CodeUnit |
assembleLogicalNegation(Compiler.LogicalData logData,
java.lang.Class required,
CodeUnit operand)
Assemble the instructions necessary to perform a logical negation of
the specified operand code unit.
|
private CodeUnit |
assembleNewObjectReference(java.lang.Class type)
Assemble instructions to push a new object reference of the specified
type onto the runtime stack.
|
private CodeUnit |
assembleNullTest(int type,
Compiler.LogicalData logData,
CodeUnit testUnit)
Assemble the instructions necessary to perform an explicit test for a
null object reference. |
private CodeUnit |
assembleNumLiteral(java.lang.Class type,
long num)
Assemble the instructions necessary to load an integral constant onto
the runtime stack.
|
private CodeUnit |
assemblePrimitiveConversion(java.lang.Class required,
java.lang.Class provided)
Assemble the instructions necessary to convert the data value at the
top of the runtime stack from primitive type
provided to
primitive type required . |
private CodeUnit |
assemblePrimitiveUnwrap(java.lang.Class primitive)
Assemble the instructions necessary to unwrap a primitive data value
from a containing object wrapper.
|
private CodeUnit |
assembleRegularMethod(int type,
Compiler.LogicalData logData,
Function function,
java.util.List units)
Assemble the instructions necessary to perform a method invocation.
|
private void |
assembleReturn(CodeUnit core,
CodeAttribute code)
Assemble instructions to wrapper (if necessary) the data value on top
of the runtime stack, and to return an object from the current method.
|
private CodeUnit |
assembleStringLiteral(java.lang.String text)
Assemble the instructions necessary to load a string constant onto the
runtime stack.
|
private CodeUnit |
assembleUserCast(Compiler.LogicalData logData,
CodeUnit castTarget,
java.lang.Class required)
Assemble instructions to perform a manual type cast of the runtime
result of executing
castTarget to the required type. |
private CodeUnit |
assembleVarArgArray(int minArgs,
int varSize,
java.lang.Class[] signature,
java.util.List units)
Assemble the instructions necessary to instantiate an array of
Object s which is used as a variable argument list at
runtime. |
private CodeUnit |
assembleWrapperConstructor(java.lang.Class primitive)
Assemble the instructions necessary to wrapper a primitive data value
of the specified type in an object wrapper of the appropriate type.
|
(package private) static CompiledExpression |
compile(Expression expression)
Creates an instance of this class which is associated with the
specified expression, compiles the expression into a custom subclass
of
CompiledExpression , loads it, instantiates it, and
returns the new compiled expression instance. |
private CodeUnit |
convertToRequiredType(Compiler.LogicalData logData,
CodeUnit unit,
java.lang.Class provided,
java.lang.Class required)
Assemble the instructions necessary to convert a data value of one
type into a value of another type, optionally branching to a safe
location if the data value represents a
null object
reference. |
private void |
dumpState()
Produce an expression compilation report which describes the internal
state of the compiler and the expression being compiled.
|
private static java.lang.String |
formatClassName(java.lang.Class cls,
boolean encloseRefs)
Given a class, format the class' name into a form which can be used
within the class file constant pool.
|
(package private) int |
getMaxStackDepth()
Return the maximum number of 32-bit slots which the JVM runtime stack
must be able to accommodate for the method whose bytecode currently is
being assembled.
|
(package private) void |
incrementStackDepth(int increment)
This method tracks changes in the current and maximum stack depths.
|
private void |
instantiate()
Create a new instance of the custom, compiled expression class and
initialize it with:
The class which represents the expected return type from the
compiled expression's
execute method. |
private static boolean |
isMethodInvocation(ExtraAst ast)
Detect whether the given AST represents a method invocation or not.
|
private void |
load()
Load the in-memory byte array which represents the compiled expression
class file, using a
custom class loader . |
private static java.lang.String |
nextClassName()
Provides the next, fully qualified class name available for use as
a custom,
CompiledExpression subclass. |
private CodeUnit |
operandToPrimitive(CodeUnit operand,
java.lang.Class primitive)
Assemble the instructions necessary to convert the specified operand
to a primitive and to cast the result if necessary to the specified
primitive type.
|
private ExtraAst |
parse()
Creates and uses the
ExpressionLexer to scan the infix notation
expression into tokens, and the ExpressionParser to generate an
abstract syntax tree (AST) from these tokens. |
private static double |
parseDouble(ExtraAst ast)
Parse a double value from the text of an AST.
|
private static long |
parseLong(ExtraAst ast,
int radix)
Parse a long value from the text of an AST, using the specified radix.
|
private void |
prepare(CodeUnit core)
Prepare the compiled class file object for class loading.
|
private void |
preprocess(ExtraAst ast)
Preprocess the AST form of the expression to annotate operations which
must produce a boolean result.
|
private void |
preprocessBinaryLogical(ExtraAst ast)
Preprocess an AST which represents a binary, logical (non-conjunctive)
operation, such as a value-based comparison of two operands.
|
private void |
preprocessConjunction(ExtraAst ast,
boolean trigger)
Preprocess an AST which represents a binary, logical, conjunction
operation (logical
AND or OR ). |
private Compiler.LogicalData |
preprocessLogicalTest(ExtraAst ast,
boolean trigger)
Preprocess an AST which represents any logical test.
|
private void |
preprocessMethodInvocation(ExtraAst ast)
Preprocess an AST which represents a method invocation.
|
private CompiledExpression |
process()
The primary driver method for the compilation process.
|
private void |
resetStackVariables()
Reset to zero each of the state variables which track current and
maximum stack depth.
|
static final boolean debug
static final java.lang.String PKG_PREFIX
private static final java.util.Map PRIMITIVES
private static final java.util.Map WRAPPERS
private static final java.lang.String CLASS_NAME_PREFIX
private static final double EPSILON
private static ExpressionClassLoader loader
private static volatile int classCount
private Expression expression
private java.util.Map libraries
private java.util.Map variables
private ExtraAst tree
private java.lang.Class resultType
private java.lang.Class returnType
private ClassFile classFile
private ConstantPool constantPool
private java.lang.Class customClass
private int stackDepth
private int maxStackDepth
private int maxLocals
private CompiledExpression compiled
private double roundingConstant
private ExtraAst currentAst
private int nextAstId
private Compiler(Expression expression)
compile(com.goldencode.expr.Expression)
method. This ensures that a separate instance of the
compiler is created for each expression to be compiled. This is
necessary because the compiler manages state specific to the current
expression, and it is not designed for asynchronous access.expression
- Object containing details about the expression to be compiled,
including the original infix text and runtime flags which
affect the compilation.static CompiledExpression compile(Expression expression) throws CompilerException
CompiledExpression
, loads it, instantiates it, and
returns the new compiled expression instance.expression
- Object which contains information specific to the expression
to be compiled, including the expression text in infix
notation, and runtime flags which impact the behavior of
the compiled expression.CompiledExpression
.CompilerException
- if any error occurs during compilation of the expression.private static java.lang.String nextClassName()
CompiledExpression
subclass. The form of this name
is com.goldencode.expr.CE<n>
, where n
is a sequential number, beginning with 0.private CompiledExpression process() throws CompilerException
If the Expression
object associated with this compiler
instance has the RT_DEBUG
runtime flag set, a report
detailing the compilation is generated to stdout
at the
end of this process. If, additionally, this class' static debug
flag is set to true
, (and the generation of a
class file completed successfully), the custom class file generated
for the current expression is written to the working directory.
CompilerException
- if any error occurs during the compilation process.void incrementStackDepth(int increment)
Instruction
class for each instruction
in a method's code attribute. The maximum stack depth is ultimately
used in the Code
attribute of the method.increment
- An incremental change to the stack depth for the bytecode in
the method currently being assembled. May be zero, positive,
or negative.int getMaxStackDepth()
private void resetStackVariables()
private ExtraAst parse() throws antlr.ANTLRException
ExpressionLexer
to scan the infix notation
expression into tokens, and the ExpressionParser
to generate an
abstract syntax tree (AST) from these tokens. The AST represents the
expression in postfix notation, which is a more appropriate form for
the following steps in the compilation process.antlr.ANTLRException
- if there were any errors lexing or parsing the expression.private void preprocess(ExtraAst ast)
Compiler.LogicalData
object is stored in the ExtraAst
node's
"extra" field. The LogicalData
object contains data used
to determine how the flow of control should branch to other locations
in the bytecode instruction array at runtime, depending upon the
outcome of the logical operation.
When a logical operation takes place, the flow of control moves to the next relevant instruction. Relevance of a set of instructions is determined by the appication of some simple boolean algebra principles:
OR
operation is
relevant only if the left operand evaluates to false
.
AND
operation is
relevant only if the left operand evaluates to true
.
For each logical operator encountered during this stage, a
LogicalData
object is created. This object encapsulates
the notion of a branch trigger. The trigger activates a jump
based upon the rules declared above. Another LogicalData
object is attached to each operand of the operator which cascades the
application of these rules. The jump will always be to an instruction
outside of the logical operation from which it originates and
after the code unit which represents that logical operation.
All irrelevant operations are always skipped as soon as possible. The
number of operations skipped depends upon the structure of the
expression. Thus, jumps may take place through several layers of
nested, logical tests if the expression is so written.
At the top level of a logical expression (or subexpression), the flow
of control must move to an instruction which pushes either a
true
value or a false
value onto the runtime
stack, such that following instructions can use this value. If the top
level represents the highest level operation in the expression, this
value will be wrapped in a Boolean
object and returned
from the compiled expression's execute
method. If the top level represents a subexpression within
the overall expression, this value will simply be an input into the
next higher level operation within the overall expression which does
not represent a logical operation (for example, the value may be used
as a boolean parameter to a method call).
As a result of this method, the AST will contain enough information such that all necessary flow of control branches for logical operations can be assembled into bytecode instructions in the assembly step.
ast
- An AST node in the expression tree. Upon the first call into
this method, this will be the root of the entire tree.
Thereafter, this method will be called recursively, such that
it will be called for each node in the tree.private void preprocessMethodInvocation(ExtraAst ast)
LogicalData
object to the first child of the
AST, the result of which always represents the target object of this
method invocation.ast
- AST which represents a method invocation.private void preprocessBinaryLogical(ExtraAst ast)
LogicalData
object to each
child AST which represents a method invocation.ast
- AST which represents a binary, logical comparison.private void preprocessConjunction(ExtraAst ast, boolean trigger)
AND
or OR
). This
propagates the appropriate LogicalData
object to each
child AST, following the rules of boolean algebra.
Worth noting is that the LogicalData
object attached to
the second (right-hand) operand must enforce a jump trigger on a result
of true
, if ast
represents the top of a chain
of logical operations. This is because at the top level, the flow of
control must move to a manufactured set of instructions that always
has the following structure:
iconst_0 // push false onto stack goto 4 // continue processing after the next instruction iconst_1 // push true onto stack ...Thus, it is always necessary to jump over the first two instructions in this code unit in order to push a
true
value onto the stack. Flow of control naturally proceeds to the
iconst_0
instruction upon evaluation of the top level
operation to false
.ast
- AST which represents a logical AND
or a logical
OR
operator.trigger
- Trigger which causes a flow of control jump: true
for logical OR
, false
for logical
AND
.private Compiler.LogicalData preprocessLogicalTest(ExtraAst ast, boolean trigger)
preprocessXXXX
methods as a precursor
to their work. This method first retrieves the "extra" field from
ast
. If it is an instance of Compiler.LogicalData
,
it is simply returned.
If not, a new LogicalData
object is created and stored in
the "extra" field of ast
(the original value is stored in
LogicalData
's extra field so it is not lost). New ASTs
are created and stored as the true
and false
targets of the new LogicalData
object. Each AST has a code
unit associated with it which pushes the corresponding boolean literal
onto the runtime stack. ast
is marked as the top of a
chain of logical operations. The new LogicalData
object
is returned.
ast
- AST which represents any sort of logical test.trigger
- true
if the flow of control should jump upon
ast
's logical test evaluation to true
at runtime; false
if it should jump upon
evaluation to false
at runtime.LogicalData
object associated with
ast
.private CodeUnit assemble(ExtraAst ast) throws CompilerException
execute
method's bytecode
instructions. This method executes recursively to walk the AST
in a depth-first iteration. At each node encountered, it assembles
bytecode instructions to implement the action represented by that
node.
The key implementation point to understand here is that recursive calls to assemble child nodes take place before any actual assembly is performed. Thus, we first iterate down to the leaf of each AST branch, which represents the most fundamental set of instructions for that branch's operation(s). We then assemble the instructions for that leaf and replace that leaf's "extra" field with the assembled code unit. Next, we visit that leaf's siblings (if any) and recursively assemble them. Then, we return from the recursive call which visited the first leaf, and assemble the instructions which represent that leaf's parent operation, and so on. The last node to be assembled is the root node of the entire tree.
At each return from a recursive call, it can thus be assumed that all child nodes of the current level already have been assembled. This simplifies the assembly of the current node in that assembly at this level becomes largely a matter of combining the code units produced for each child and applying the current operation to these units appropriately.
The code unit returned from the top level invocation of this method
thus represents the core functionality of the compiled expression's
execute
method. The only instructions missing will be
those to wrapper (if necessary) the resulting value into an object,
and those to return that object from the method. These instructions
are later added by the prepare(com.goldencode.expr.CodeUnit)
method.
ast
- AST for which bytecode instructions are to be assembled next.ast
.CompilerException
- if an unrecoverable error occurs while assembling bytecode
instructions.private void addDebug(CodeUnit unit)
unit
's instructions is set in the compiled
expression runtime instance. These are executed just before
instructions which may cause an exception to be generated at runtime,
such that an exception handler around the executing expression can
query this information and provide useful diagnostic messages about
the error.
These special diagnostic instructions are only added if the expression associated with this compilation have one or more of the following runtime flags turned on:
Otherwise, this method returns immediately, without adding any instructions to the code unit.
unit
- Code unit to which diagnostic instructions are added.private void prepare(CodeUnit core)
execute
method
execute
method the code attribute
containing the assembled bytecode instructions for the expression
core
- Code unit which contains the bytecode instructions which
perform the core functionality of the execute
method.private void load() throws java.lang.ClassNotFoundException
custom class loader
.java.lang.ClassNotFoundException
- if any error occurs loading the class file.private void instantiate() throws java.lang.InstantiationException, java.lang.IllegalAccessException
execute
method.
The new instance is stored in the compiled
instance variable.
java.lang.InstantiationException
- if instantiation fails due to a compiler defect.java.lang.IllegalAccessException
- if the class or its default constructor are not accessible.
This should only happen in the case of a compiler defect.private CodeUnit assembleNewObjectReference(java.lang.Class type)
type
represents a
void
return, a null
reference is pushed
onto the stack instead of an object reference. For non-null
values, the reference is duplicated, since it is assumed that the
duplicate reference will be consumed in a later call to the class'
constructor.type
- Class representing the object reference type to create.private void assembleReturn(CodeUnit core, CodeAttribute code)
core
- Code unit which contains the bytecode instructions which
perform the core functionality of the execute
method.code
- Code attribute object to which the return instructions are
appended.private CodeUnit assembleUserCast(Compiler.LogicalData logData, CodeUnit castTarget, java.lang.Class required)
castTarget
to the required type. If
the code unit being cast is a logical code unit, ensure it branches to
the correct destination instruction.logData
- Object which contains information on how flow of control
should proceed upon the execution of castTarget
's
instructions.castTarget
- Unit of code whose result should be typecast.required
- Class type to which targetCast
is to be casted.castTarget
, followed by the
instruction(s) necessary to typecast its result.private CodeUnit assembleBranchOnBooleanResult(int type, Compiler.LogicalData logData, CodeUnit core, java.lang.Class required)
logData
.type
- Type of branch logic; indicates how branching test is
performed and which bytecode instruction is used to jump.logData
- Object which contains information on how flow of control
should proceed upon the execution of core
's
instructions.core
- Underlying code unit whose bytecode instructions are
executed and which must produce a result which determines
whether or not we jump to a new location.required
- Type of result from the execution of core
which
is required to perform the branch.core
, appended with the
instructions necessary to branch based upon the result of
executing core
's instructions.private CodeUnit assembleLogicalNegation(Compiler.LogicalData logData, java.lang.Class required, CodeUnit operand)
null
logData
object is provided:
If logData
is non-null
, the negation amounts
to causing a jump to the instruction indicated by logData
's
appropriate target, if the result of executing operand
's
instructions is false
.
If no logData
object is provided, the negation amounts to
examining the boolean value at the top of the runtime stack and pushing
its opposite back onto the stack.
logData
- Object which contains information on how flow of control
should proceed upon the execution of operand
's
instructions.required
- Data type expected by the operation containing
operand
.operand
- Code unit containing instructions which should evaluate to a
true
or false
result.private CodeUnit assembleConjunctionOp(ExtraAst ast)
AND
or OR
). This consists of
ensuring both operands produce a primitive, boolean result, then
creating a new code unit which simply contains each of the operands'
code units in the order encountered in the AST. This is possible
because each child code unit already contains instructions to proceed
(possibly branch) to the proper instructions, based upon their
respective results.ast
- AST representing the logical conjunction operation. This AST
will have two children: one each for the left and right sides
of the conjunction operation.private CodeUnit assembleComparatorOp(int type, Compiler.LogicalData logData, java.lang.Class opClass, java.util.List units)
preprocess(com.goldencode.expr.ExtraAst)
phase of
compilation, when the appropriate LogicalData
object is
attached to each operand which may be required to branch within a
logical operation.
This method handles numeric, boolean, and Comparable
operands. Numeric and boolean operands may be primitive or object
wrappers. The latter are converted to primitive types for comparison
purposes.
type
- The token type associated with the operation; defines the
nature of the comparison.logData
- Object which contains information on how flow of control
should proceed based upon the result of the comparison.opClass
- Class which represents the least common denominator type among
the operands. This is used to determine the type of comparison
operation to be applied.units
- Code units which contain the assembled instructions of the
two operands of the comparison operation.private CodeUnit assembleEpsilonTest()
private CodeUnit assembleBitwiseNegation(java.lang.Class required, CodeUnit operand)
-(operand + 1)
.required
- Data type expected by the operation containing
operand
.operand
- Code unit containing instructions which generate the value
to be negated.private CodeUnit assembleBinaryBitwiseOp(int type, java.lang.Class required, java.util.List units)
AND
,
OR
, and XOR
. Handles both long and integer
operands (primitive or wrapped). Both operands must be of the same
data type. In the case of the shift operations, the right operand
(the shift magnitude) is always converted to an integer before the
operation is performed.type
- The token type associated with the operation; defines the
nature of the operation.required
- Data type of the result of this operation. If a wrapper class,
the corresponding primitive is used instead.units
- Code units containing the instructions necessary to generate
each of the operands, in the order encountered in the AST.private CodeUnit assembleArithmeticNegation(java.lang.Class required, CodeUnit operand)
required
- Data type expected by the operation containing
operand
.operand
- Code unit containing instructions which generate the value
to be negated.private CodeUnit assembleBinaryArithmeticOp(int type, java.lang.Class required, java.util.List units)
If the operation uses floating point math, and the associated Expression
object has the RT_FP_ROUNDING
runtime flag
set, the result is rounded to the number of decimal places precision
specified in that object. Note that this may generate an exception if
the requested level of precision cannot be supported at runtime.
The resulting stack value will always be a primitive of the same data type as the widest operand.
type
- The token type associated with the operation; defines the
nature of the operation.required
- Data type of the result of this operation. If a wrapper class,
the corresponding primitive is used instead.units
- Code units containing the instructions necessary to generate
each of the operands, in the order encountered in the AST.CompiledExpression.testRound(double)
private CodeUnit assembleBootstrapMethod(int type, Compiler.LogicalData logData, Function function, java.util.List units)
CompiledExpression.libraries
array by the
compiler at expression instantiation time. Likewise, all the user
variables (if any) to which the expression refers are stored within
the CompiledExpression.variables
array by the compiler at the
same time. The indices of these objects in their respective arrays are
known and will not change. They are thus hard-coded as operands to the
bootstrap method invocations generated by this method.
The following methods are considered bootstrap methods:
CompiledExpression.getLib(int)
CompiledExpression.getVar(int)
CompiledExpression.setVar(int, java.lang.Object)
The special handling required for these methods consists of loading
the compiled expression's "this" reference onto the runtime stack,
then loading the proper constant representing the index of the library
or user variable to be accessed onto the stack. After this, the
remaining method invocation assembly is delegated to the assembleRegularMethod(int, com.goldencode.expr.Compiler.LogicalData, com.goldencode.expr.Function, java.util.List)
worker method.
type
- Token type of the AST which represents the method invocation;
passed through to the worker method.logData
- Information required for branching within logical operations;
not used here, but passed through to the worker method.function
- Defines the type of bootstrap method being invoked; also
passed through to the worker method.units
- List of code units which represent any other arguments (i.e.,
in addition to the hard-coded index assembled here) to the
bootstrap method. Currently, the only bootstrap method which
contains an additional argument is the setVar
method. Code units for loading the "this" reference and for
loading the index constant are inserted at the front of this
list before it is passed on to the worker method.private CodeUnit assembleRegularMethod(int type, Compiler.LogicalData logData, Function function, java.util.List units)
units
list, but they may require additional conversion
to match the type of the required target object and the target method's
parameters signature.
The function
parameter contains information about the
invocation target class, the return type expected from this
invocation (which may be different from the return type of the
underlying method itself and require conversion), and a reference to
underlying method.
The units
list contains one or more code units. The first
code unit loads the invocation target object onto the stack. The
remaining units, if any, load each required parameter onto the stack,
in the order required by the method. If function
indicates
that the underlying method supports variable argument lists, all
arguments after the number of required minimum parameters are
marshalled into an Object
array, which is used as the
method's final parameter.
In order to convert the invocation target object and parameters to
the required types, instructions are assembled which perform primitive
wrapping/unwrapping and casting of primitive or reference types. Next,
instructions to invoke the method are assembled. Finally, the return
type is converted to the data type defined by function
as the required return type. If the logData
argument to
this method is non-null
, this conversion includes a check
to determine whether the runtime value returned by the invoked method
is null
. If so, instructions are assembled to branch to
the proper destination, as defined by logData
.
This method handles invocation of static, virtual, and interface
methods. There is one limitation on the invocation of static methods
that should be noted: a static method must always be invoked on an
instance of that method's declaring class. Thus, invocations of the
form Class.staticMethod(parameter list)
are not permitted
at this time. This limitation may be removed in a future version of
the compiler.
type
- Token type of the AST which represents the method invocation.logData
- Information required for branching within logical operations
when a null
result is encountered or when the
return type of the invoked method is boolean.function
- Contains details about the method being invoked.units
- List of code units which represent the invocation target
object and any arguments to the method.private CodeUnit assembleVarArgArray(int minArgs, int varSize, java.lang.Class[] signature, java.util.List units)
Object
s which is used as a variable argument list at
runtime. The array is of the specified size and contains as its
members the data values pushed onto the stack by the instructions
contained in the specified list of code units. If any of these code
units produces a primitive value, it is first wrapped in the proper
object, then added to the array.minArgs
- Minimum number of arguments required by method.varSize
- Number of elements in the object array. If zero, an array of
length zero will be created.signature
- Array of parameter types required for the method (including
required arguments and variable arguments).units
- List of code units which produce the data values to be stored
in the object array. If size
is 0, this argument
may be null
.private CodeUnit assembleArrayStore(int index, java.lang.Class required, CodeUnit argUnit)
index
- Index at which to store the data value in the array.required
- Required data type of the item to be stored in the array.argUnit
- Code unit which produces the data value to be stored.private CodeUnit assembleWrapperConstructor(java.lang.Class primitive)
primitive
- Class which represents the primitive data type to be
encapsulated in a wrapper object.private CodeUnit assemblePrimitiveUnwrap(java.lang.Class primitive)
primitive
- Class which represents the primitive data type encapsulated
within the wrapper object.private CodeUnit assembleNumLiteral(java.lang.Class type, long num)
type
- Type of the integral constant to load; must be a primitive
integer or long.num
- The integral value to load as a constant.private CodeUnit assembleDecLiteral(double num)
num
- The floating point value to load as a constant.private CodeUnit assembleBoolLiteral(int type, Compiler.LogicalData logData)
logData
information. The former is generated if the
logData
parameter is null
; else the latter
is generated.type
- Token type of a boolean literal: either BOOL_TRUE
or BOOL_FALSE
.logData
- Information which indicates to which instruction to branch
based upon the value of type
.private CodeUnit assembleStringLiteral(java.lang.String text)
text
- The string to be loaded at runtime as a constant.private CodeUnit assembleNullTest(int type, Compiler.LogicalData logData, CodeUnit testUnit)
null
object reference. It is assumed that the reference
to be tested is at the top of the runtime stack. The result of the
test is to branch to an instruction defined by the logData
parameter, or to proceed normally to the next instruction in the
bytecode array.type
- Token type of the AST which describes the test to perform:
IS_NULL
or NOT_NULL
. The former
tests positive if the reference is null
, the
latter tests positive if the reference is not
null
.logData
- Information which indicates to which instruction to branch
based upon the value of type
and the outcome of
the runtime check.testUnit
- Code unit which contains the instructions which load the
object reference to be tested.private CodeUnit operandToPrimitive(CodeUnit operand, java.lang.Class primitive)
operand
generates either a primitive value or a reference to a wrapper object
which contains a primitive value which is of type primitive
or which can be safely cast to this type.operand
- Code unit containing instructions to generate the value to be
converted.primitive
- Class which represents the primitive type to which
operand
must be converted.private CodeUnit convertToRequiredType(Compiler.LogicalData logData, CodeUnit unit, java.lang.Class provided, java.lang.Class required)
null
object
reference.
This method will convert:
Where possible given the data provided, multiple conversions will be attempted to achieve the desired, overall conversion.
In certain cases (when the logData
parameter is not
null
), an additional check will be performed during
conversion to test whether the target data value is a null
object reference. If so, instructions will be added to branch
execution to the falseTarget
defined by the
logData
object. This has the effect of "failing" the
innermost logical test containing the null
reference.
The result is effectively the same as if the reference had not been
null
, and the enclosing logical test had evaluated to
false
. In the event of such a branch, instructions may
be inserted to pop obsolete data values off the runtime stack in
order to ensure integrity of the stack at the branch destination
instruction.
If no conversion is necessary, the above null
check will
still take place if a logData
parameter is provided.
logData
- Object which contains information on how flow of control
should proceed based upon the result of the null object
reference check.unit
- Code unit which contains the instructions which will generate
the data value needing conversion.provided
- Class which represents the data type to be converted.required
- Class which represents the data type to which the data value
should be converted.private CodeUnit assemblePrimitiveConversion(java.lang.Class required, java.lang.Class provided)
provided
to
primitive type required
. This conversion deals with
numeric values only, and performs both narrowing and widening
conversions. Currently, only conversions between integers, longs, and
doubles are supported.
At a future date, this method may add instructions to check for
precision loss if certain runtime flags are set in the Expression
object associated with this compilation.
required
- Required primitive type.provided
- Provided primitive type.null
if either argument is
null
or does not represent a primitive type, or
if no conversion is necessary.private CodeUnit assembleExplicitCast(java.lang.Class required)
required
- Class to which the target reference will be cast.private static java.lang.String formatClassName(java.lang.Class cls, boolean encloseRefs)
Optionally, enclose class names within a leading 'L' character and a trailing ';' character, for use within method signature specifications.
cls
- The class whose name is to be formatted.encloseRefs
- true
to wrapper the class name for method
signature use as described above; else false
.private static long parseLong(ExtraAst ast, int radix) throws CompilerException
ast
- AST containing the text to be parsed.radix
- Radix value to use for the parse operation.CompilerException
- if the AST's text could not be parsed into an integral number.private static double parseDouble(ExtraAst ast) throws CompilerException
ast
- AST containing the text to be parsed.CompilerException
- if the AST's text could not be parsed into a floating point
number.private static boolean isMethodInvocation(ExtraAst ast)
ast
- AST to be tested.true
if AST represents a method invocation, else
false
.private void dumpState()
If the debug
flag is set to true (typically only when tracking
down compiler defects), the class file generated for the current
expression (if compilation proceeded that far without error) is written
to the current directory.
Note: this method is only called when the Expression
object
associated with the compiler has the RT_DEBUG
flag set.