gnu.jel
Class ExpressionImage

java.lang.Object
  |
  +--gnu.jel.ExpressionImage

public class ExpressionImage
extends java.lang.Object

This class is responsible for generating valid Java class files based on a sequence of calls to it's methods (asm_XXXX family of methods). This assembler tries to do more things than other java assemblers do. It keeps track of types in Java stack and automatically chooses which java bytecodes should be generated to perform a given operation on types in stack.

Anyway, it is not general purpose Java assembler, it is specifically JEL oriented. Currently this assembler does not provide direct access to Java control transfer instructions.

This class is designed to be the part of a bigger package. This means it will silently generate wrong bytecodes. ;) BUT, if You compile it with debugging ON (see boolean gnu.jel.debug.Debug.enabled) it should warn You about all possible wrong things it does. If there are more assertions to be made, please submit Your patches to metlov@fzu.cz.

Author:
Konstantin L. Metlov (metlov@fzu.cz)
See Also:
Debug.enabled

Field Summary
static int BI_AN
          Denotes the AND binary operation.
static int BI_DI
          Denotes the DIVIDE binary operation.
static int BI_EQ
          Denotes binary comparizon for equality
static int BI_GE
          Denotes binary comparizon for "greater or equal"
static int BI_GT
          Denotes binary comparizon for "greater"
static int BI_LE
          Denotes binary comparizon for "less or equal"
static int BI_LS
          Denotes binary left shift
static int BI_LT
          Denotes binary comparizon for "less"
static int BI_MI
          Denotes the MINUS binary operation.
static int BI_MU
          Denotes the MULTIPLY binary operation.
static int BI_NE
          Denotes binary comparizon for inequality
static int BI_OR
          Denotes the OR binary operation.
static int BI_PL
          Denotes the PLUS binary operation.
static int BI_RE
          Denotes the REMAINDER binary operation.
static int BI_RSS
          Denotes binary right signed shift
static int BI_RUS
          Denotes binary right unsigned shift
static int BI_XO
          Denotes the XOR binary operation.
static java.lang.String[] binaryNames
          Names of binary operations by ID in the readable form.
static java.lang.String[] binarySymbols
          Symbols of binary operations by ID in the readable form.
static int LOG_AN
          Denotes logical conjunction operator
static int LOG_NO
          Denotes logical complement operator
static int LOG_OR
          Denotes logical disjunction operator
static java.lang.String[] logicalNames
          Names of logical operations by ID in the readable form.
static java.lang.String[] logicalSymbols
          Symbols of logical operations by ID in the readable form.
static java.lang.String[] primitiveTypeNames
          Names of the primitive types by ID in readable form.
static java.lang.Class[] primitiveTypes
          Classes of the primitive types by ID
static int UN_NE
          Denotes the unary NEGATION operation.
static int UN_NO
          Denotes the unary bitwise complement operation.
static java.lang.String[] unaryNames
          Names of unary operations by ID in the readable form.
static java.lang.String[] unarySymbols
          Symbols of unary operations by ID in the readable form.
 
Constructor Summary
ExpressionImage()
          Constructs and initializes empty expression image.
 
Method Summary
 void asm_binary_param(int opc)
          Denotes that the first parameter for the given binary OP is now in stack.
 void asm_binary(int o)
          Generates code to perform given binary operation.
 void asm_branch_end()
          Finishes generation of code for conditional.
 void asm_branch_start_false()
          Continues generation of code for conditional.
 void asm_branch_start_true()
          Starts generation of code for conditional.
 boolean asm_convert(java.lang.Class type)
          Converts current top of the java stack to the given class type.
 void asm_func_call()
          Finishes generation of call to a function.
 void asm_func_param()
          Denotes that the next parameter for the current function is now in stack.
 void asm_func_start(java.lang.reflect.Method f, int id)
          Starts generation of code for the method call.
 void asm_get_array_element()
          Generates code for loading an element from the array.
 void asm_load_field(java.lang.reflect.Field f, int id)
          Generates code for loading of a field value to the stack.
 void asm_load_object(java.lang.Object o)
          Generates code to load given object constant into Java stack.
 void asm_load_primitive(java.lang.Object o)
          Generates code to load given constant of a primitive type.
 void asm_logical_binary_param(int opc)
          Denotes the first parameter for the given logical binary OP is now in stack.
 void asm_logical_binary(int opc)
          Generates code to perform given logical binary operation.
 void asm_logical_block()
          This function starts a group of logical subexpressions.
 void asm_logical_unblock_not()
          This function finishes a group of logical subexpressions with inversion.
 void asm_return()
          Finishes construction of expression by generating code to return a value.
 void asm_throw_return()
          Finishes construction of expression by generating code to throw exception.
 void asm_unary(int o)
          Generates code to perform given unary operation on the value in stack.
static boolean canConvert(java.lang.Class t1, java.lang.Class t2)
          Tests is this assembler can generate code to convert from from one type to another even with possible loss of the information.
static boolean canConvertByWidening(java.lang.Class t1, java.lang.Class t2)
          Tests is this assembler can generate code to convert from from one type to another without loss of the information.
static boolean canGenerateBinary(int op, java.lang.Class t1, java.lang.Class t2)
          Used to test if this assembler can generate given binary operation.
static boolean canGenerateUnary(int op, java.lang.Class type)
          Used to test if this assembler can generate given unary operation.
static java.lang.Class getBinaryPromoted(java.lang.Class c1, java.lang.Class c2)
          Performs binary numeric promotion of types.
 ExpressionBits getBits()
          Returns compiled expression represented by ExpressionBits object.
 CompiledExpression getExpression()
          Constructs a new instance of this expression.
 byte[] getImage()
          Used to get the binary image of the class.
static java.lang.String getSignature(java.lang.Class cls)
          Computes the signature of the given class.
static java.lang.String getSignature(java.lang.reflect.Constructor c)
          Computes signature of the given constructor.
static java.lang.String getSignature(java.lang.reflect.Method m)
          Computes signature of the given method.
static java.lang.Class getUnaryPromoted(java.lang.Class c)
          Performs unary numeric promotion of types.
static boolean isPromotionBinary(int binary_op)
          Checks if the binary numeric promotion is required for the operation.
static void main(java.lang.String[] args)
          Performs unitary test of the code generator.
static void test(Tester t)
          Performs unitary test of the code generator.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

BI_PL

public static final int BI_PL
Denotes the PLUS binary operation.

BI_MI

public static final int BI_MI
Denotes the MINUS binary operation.

BI_MU

public static final int BI_MU
Denotes the MULTIPLY binary operation.

BI_DI

public static final int BI_DI
Denotes the DIVIDE binary operation.

BI_RE

public static final int BI_RE
Denotes the REMAINDER binary operation.

BI_AN

public static final int BI_AN
Denotes the AND binary operation.

BI_OR

public static final int BI_OR
Denotes the OR binary operation.

BI_XO

public static final int BI_XO
Denotes the XOR binary operation.

BI_EQ

public static final int BI_EQ
Denotes binary comparizon for equality

BI_NE

public static final int BI_NE
Denotes binary comparizon for inequality

BI_LT

public static final int BI_LT
Denotes binary comparizon for "less"

BI_GE

public static final int BI_GE
Denotes binary comparizon for "greater or equal"

BI_GT

public static final int BI_GT
Denotes binary comparizon for "greater"

BI_LE

public static final int BI_LE
Denotes binary comparizon for "less or equal"

BI_LS

public static final int BI_LS
Denotes binary left shift

BI_RSS

public static final int BI_RSS
Denotes binary right signed shift

BI_RUS

public static final int BI_RUS
Denotes binary right unsigned shift

binaryNames

public static final java.lang.String[] binaryNames
Names of binary operations by ID in the readable form.

binarySymbols

public static final java.lang.String[] binarySymbols
Symbols of binary operations by ID in the readable form.

LOG_AN

public static final int LOG_AN
Denotes logical conjunction operator

LOG_OR

public static final int LOG_OR
Denotes logical disjunction operator

LOG_NO

public static final int LOG_NO
Denotes logical complement operator

logicalNames

public static final java.lang.String[] logicalNames
Names of logical operations by ID in the readable form.

logicalSymbols

public static final java.lang.String[] logicalSymbols
Symbols of logical operations by ID in the readable form.

UN_NE

public static final int UN_NE
Denotes the unary NEGATION operation.

UN_NO

public static final int UN_NO
Denotes the unary bitwise complement operation.

unaryNames

public static final java.lang.String[] unaryNames
Names of unary operations by ID in the readable form.

unarySymbols

public static final java.lang.String[] unarySymbols
Symbols of unary operations by ID in the readable form.

primitiveTypes

public static final java.lang.Class[] primitiveTypes
Classes of the primitive types by ID

primitiveTypeNames

public static final java.lang.String[] primitiveTypeNames
Names of the primitive types by ID in readable form.
Constructor Detail

ExpressionImage

public ExpressionImage()
Constructs and initializes empty expression image.

Immediately after the construction of the expression a number of code generating methods can be issued:

 asm_load_primitive(...)
 asm_binary_param(..)
 ...
 

The code generation should be finished by one of the return methods:

 asm_return();
 asm_throw_return();
 

Before any return method is called, the code generation is assumed to be in progress and methods, attempting to get class representation (getImage) or instantiate the class (getExpression) will fail assertion.

After the code generation is finished and one of return methods is called methods, attempting to modify the class (i.e. asm_load_primitive(...)...) will fail. This is done to ensure integrity of generated classes.

Constants of the type object are allowed in the generated expressions. (If You don't know in java the constants are residing in the constant pool and it is impossible to store constants of general Object type other than java.lang.String there. This assembler overcomes this limitation and allows to use other _NON-MUTABLE_ objects as constants. More information is provided in the description of asm_load_object method.

See Also:
asm_load_object(java.lang.Object), asm_load_primitive(java.lang.Object), asm_binary(int), asm_convert(java.lang.Class), asm_func_call(), asm_logical_binary(int), asm_logical_unblock_not(), asm_unary(int), asm_return(), asm_throw_return()
Method Detail

isPromotionBinary

public static final boolean isPromotionBinary(int binary_op)
Checks if the binary numeric promotion is required for the operation.

Used to check whether given binary operation requires binary or unary numeric promotion (see JLS 5.6.1) of it's operands.

Parameters:
binary_op - is one of BI_XXX constants.
Returns:
true if binary numeric promotion is required false if unary numeric promotion is required.

getSignature

public static java.lang.String getSignature(java.lang.reflect.Method m)
Computes signature of the given method.

The signature of the method(Method descriptor) is the string and it's format is described in the paragraph 4.3.3 of the Java VM specification (ISBN 0-201-63451-1).

This utility method can be used outside of the JEL package it does not involve any JEL specific assumptions and should follow JVM Specification precisely.

Parameters:
m - is the method to compute the sugnature of.
Returns:
the method sugnature.

getSignature

public static java.lang.String getSignature(java.lang.reflect.Constructor c)
Computes signature of the given constructor.
Parameters:
m - is the method to compute the sugnature of.
Returns:
the method sugnature.
See Also:
getSignature(java.lang.reflect.Method)

getSignature

public static java.lang.String getSignature(java.lang.Class cls)
Computes the signature of the given class.

The signature of the class (Field descriptor) is the string and it's format is described in the paragraph 4.3.2 of the Java VM specification (ISBN 0-201-63451-1).

The same can be done using java.lang.Class.getName() by converting it's result into the "historical form".

This utility method can be used outside of the JEL package it does not involve any JEL specific assumptions and should follow JVM Specification precisely.

Parameters:
cls - is the class to compute the sgnature of. Can be primitive or array type.
Returns:
the class signature.

getBits

public ExpressionBits getBits()
Returns compiled expression represented by ExpressionBits object.

This function should be called after the code generation had finished.

The only case You'll want to get gnu.jel.ExpressionBits class instead of instantiated and ready to run gnu.jel.CompiledExpression subclass is when You intend to write expression to a persistent storage and run it in other JVM session.

Returns:
ready to be serialized ExpressionBits object.

getImage

public byte[] getImage()
Used to get the binary image of the class.

This function returns bytecode of generated expression in a Java classfile format. It is for debugging purposes only. Use ExpressionBits if You want to store expression into a stream (file).

Returns:
a binary class representation.

getExpression

public CompiledExpression getExpression()
Constructs a new instance of this expression.

This function returns ready to run instance of gnu.jel.CompiledExpression subclass.

Returns:
a CompiledExpression instance or null if there was a error.

asm_load_object

public void asm_load_object(java.lang.Object o)
Generates code to load given object constant into Java stack.

Constants of type object, other than instances of java.lang.String are not directly supported by Java Virtual Machine. This means there is no way to reinstantiate those object based on the information in the Java class file alone.

To overcome this limitation an array of such objects is created and only integer index into this "object constants" array is compiled into a class file. The drawback is that the generated class file can not be run without proper object constants array supplied. This means bytecode alone does not represent compiled program anymore.

To simplify storage of expressions an ExpressionBits class is introduced which holds both bytecode and object constants, allowing to easily store them together in a stream.

CONCLUSION : if Your class uses this function for objects other than Strings You _must_ use ExpressionBits to store compiled bytecode.

Parameters:
o - is the object to load.
See Also:
ExpressionBits

asm_load_primitive

public void asm_load_primitive(java.lang.Object o)
Generates code to load given constant of a primitive type.

For example, to load double constant 1.0D into the Java stack one has to call :
asm_load_primitive(new Double(1.0)).

Parameters:
o - is the value of a constant of primitive type, wrapped into corresponding reflection object.

canConvert

public static boolean canConvert(java.lang.Class t1,
                                 java.lang.Class t2)
Tests is this assembler can generate code to convert from from one type to another even with possible loss of the information.

Both widening and narrowing conversions are supported by the assembler, this function reports all supported conversions.

There are thoughts about support wrapping/unwrapping conversions (i.e. double <--> java.lang.Double) as described in the Java Reflection Specification. Currently these are not supported.

Parameters:
t1 - is the type You want to convert from.
t2 - is the type You want to convert to.
Returns:
true is the corresponding conversion is supported.

canConvertByWidening

public static boolean canConvertByWidening(java.lang.Class t1,
                                           java.lang.Class t2)
Tests is this assembler can generate code to convert from from one type to another without loss of the information.

Both widening and narrowing conversions are supported by the assembler itself, this function reports widening conversions only.

Parameters:
t1 - is the type You want to convert from.
t2 - is the type You want to convert to.
Returns:
true is the corresponding widening conversion is supported.

asm_convert

public boolean asm_convert(java.lang.Class type)
Converts current top of the java stack to the given class type.
Parameters:
type - is the type to convert to, can be primitive.
Returns:
true if conversion was made, false if types incompatible.

getBinaryPromoted

public static java.lang.Class getBinaryPromoted(java.lang.Class c1,
                                                java.lang.Class c2)
Performs binary numeric promotion of types.

Promotion is done according to the Java Language Specification (paragraph 5.6.2). Both parameters should be Java primitive types.

Parameters:
c1 - first type
c2 - second type
Returns:
Class (primitive type reflection), representing the binary promoted type, null if can't promote.

getUnaryPromoted

public static java.lang.Class getUnaryPromoted(java.lang.Class c)
Performs unary numeric promotion of types.

Promotion is done according to the Java Language Specification (paragraph 5.6.1). Both parameters should be Java primitive numeric types.

Parameters:
c - the type to promote
Returns:
Class (primitive type reflection), representing unary promoted type, null if can't promote.

canGenerateUnary

public static boolean canGenerateUnary(int op,
                                       java.lang.Class type)
Used to test if this assembler can generate given unary operation.
Parameters:
is - the code of operation (UN_NE).
type - is the type of the operand.
Returns:
if the code for such operation can be generated.

asm_unary

public void asm_unary(int o)
Generates code to perform given unary operation on the value in stack.

Unary operations can be performed on the primitive Java types only.

If operation can not be supported for the given type this method returns false and generates nothing.

Parameters:
o - type of operation to perform, one of NE

canGenerateBinary

public static boolean canGenerateBinary(int op,
                                        java.lang.Class t1,
                                        java.lang.Class t2)
Used to test if this assembler can generate given binary operation.
Parameters:
is - the code of operation (BI_PL,BI_MI,BI_MU ...).
t1 - is the type of the first operand.
t2 - is the type of the second operand.
Returns:
if the code for such operation can be generated.

asm_binary

public void asm_binary(int o)
Generates code to perform given binary operation.

Method canGenerateBinary(..) should be used to determine if the binary operation can be generated for particular types of data.

The pattern to generate binary operations with this code generator is following :

 1. Calculate the first operand.
 2. asm_binary_param( BI_XXX )
 3. Calculate the second operand.
 4. asm_binary( BI_XXX )
 

If operation can not be supported due to incompatible types of operands this method returns false and generates nothing.

Parameters:
o - type of operation to perform, one of BI_XXX
See Also:
canGenerateBinary(int, java.lang.Class, java.lang.Class), asm_binary_param(int)

asm_func_start

public void asm_func_start(java.lang.reflect.Method f,
                           int id)
Starts generation of code for the method call.

Method can be static or virtual. See the descrition of asm_func_call for more details.

Parameters:
f - is the method to call.
id - is the number of the function in the array of the instances of objects implementing the function (for virtual methods only).
See Also:
asm_func_call()

asm_func_param

public void asm_func_param()
Denotes that the next parameter for the current function is now in stack.

Actually, this method generates code to convert the value currently on top of Java stack to the next formal parameter type of a current function.

Returns:
false if types are incompatible.
See Also:
asm_func_start(java.lang.reflect.Method, int)

asm_func_call

public void asm_func_call()
Finishes generation of call to a function.

Call to this method should be followed zero or more asm_func_param() calls which would transform parameters on top of the stack to be compatible with the function input (each additioal call will process the next formal parameter).

The pattern to generate the function call is the following

 1. asm_func_start(...);
 2. Calculate the first parameter.
 3. asm_func_param();
 4. Calculate the second parameter.
 5. asm_func_param();
   .... so on ...
 6  Calculate the last parameter.
 7. asm_func_param();
 8. asm_func_call();
 

Correctness of id parameter can not be checked by the code generator !!! Be careful, runtime error will be generated if it's incorrect.

See Also:
asm_func_param(), asm_func_start(java.lang.reflect.Method, int)

asm_load_field

public void asm_load_field(java.lang.reflect.Field f,
                           int id)
Generates code for loading of a field value to the stack.
Parameters:
f - is the method to call.
id - is the number of the object in the array of "this" references, used for non-static fields only.
See Also:
asm_func_call()

asm_get_array_element

public void asm_get_array_element()
Generates code for loading an element from the array.

the array pointer and the in index of element must be already at stack


asm_branch_start_true

public void asm_branch_start_true()
Starts generation of code for conditional.

Starts the branch, corresponding to the case when previous logical was "true". expression.

The pattern to generate an "if" operator or ?: conditional is the following :

 1. Calculate condition 
 2. asm_branch_start_true()
 3. Generate code for the "true" branch
 4. asm_branch_start_false()
 5. Generate code for the "false" branch
 6. asm_branch_end()
 

Note that the code for "true" branch should be generated BEFORE the code for the current branch. This limitation may be removed in a future.


asm_branch_start_false

public void asm_branch_start_false()
Continues generation of code for conditional.
See Also:
asm_branch_start_true()

asm_branch_end

public void asm_branch_end()
Finishes generation of code for conditional.
See Also:
asm_branch_start_true()

asm_binary_param

public void asm_binary_param(int opc)
Denotes that the first parameter for the given binary OP is now in stack.
Parameters:
op - type of operation to perform, one of BI_XXX
See Also:
asm_binary(int)

asm_logical_binary_param

public void asm_logical_binary_param(int opc)
Denotes the first parameter for the given logical binary OP is now in stack.
Parameters:
opc - is the code of the logical binary operation (LOG_AN,LOG_OR)
See Also:
asm_logical_binary(int)

asm_logical_binary

public void asm_logical_binary(int opc)
Generates code to perform given logical binary operation.

The logical binary operations are either logical and (LOG_AN) or logical or (LOG_OR).

The following pattern should be followed in order to evaluate logical expressions :

 1. Calculate the first operand (should be boolean).
 2. asm_logical_binary_param(  |  )
 3. Calculate the second operand (should be boolean).
 4. asm_logical_binary(  |  )
 
Parameters:
opc - is the code of the logical binary operation (LOG_AN,LOG_OR)

asm_logical_block

public void asm_logical_block()
This function starts a group of logical subexpressions.
See Also:
asm_logical_unblock_not()

asm_logical_unblock_not

public void asm_logical_unblock_not()
This function finishes a group of logical subexpressions with inversion.

For example, to calculate "!(..something..)" it is needed to call :

 asm_logical_block()
 ....
 calculate something manipulating with other methods of this code generator
 ....
 asm_logical_unblock_not();
 

After the above block the state of this codegenerator will correspond to the boolean result of the logical not operator on something.


asm_return

public void asm_return()
Finishes construction of expression by generating code to return a value.

Code to return to a caller a quantity on top of the Java stack is generated.


asm_throw_return

public void asm_throw_return()
Finishes construction of expression by generating code to throw exception.

Object on top of the Java stack (which should be instance of java.lang.Throwable) is thrown.


main

public static void main(java.lang.String[] args)
Performs unitary test of the code generator.
Parameters:
args - ignored.

test

public static void test(Tester t)
Performs unitary test of the code generator.

Used if all package is being tested and not just codegen.

Parameters:
t - Tester to report test results.