public class CodeFlow extends java.lang.Object implements Opcodes
Records intermediate compilation state as the bytecode is generated. Also includes various bytecode generation helper functions.
Modifier and Type | Class and Description |
---|---|
static interface |
CodeFlow.ClinitAdder
Interface used to generate
clinit static initializer blocks. |
static interface |
CodeFlow.FieldAdder
Interface used to generate fields.
|
Modifier and Type | Field and Description |
---|---|
private java.lang.String |
className
Name of the class being generated.
|
private ClassWriter |
classWriter
The current class being generated.
|
private java.util.List<CodeFlow.ClinitAdder> |
clinitAdders
As SpEL ast nodes are called to generate code for the main evaluation method
they can register to add code to a static initializer in the class.
|
private java.util.Deque<java.util.List<java.lang.String>> |
compilationScopes
Record the type of what is on top of the bytecode stack (i.e.
|
private java.util.List<CodeFlow.FieldAdder> |
fieldAdders
As SpEL ast nodes are called to generate code for the main evaluation method
they can register to add a field to this class.
|
private int |
nextFieldId
When code generation requires holding a value in a class level field, this
is used to track the next available field id (used as a name suffix).
|
private int |
nextFreeVariableId
When code generation requires an intermediate variable within a method,
this method records the next available variable (variable 0 is 'this').
|
AALOAD, AASTORE, ACC_ABSTRACT, ACC_ANNOTATION, ACC_BRIDGE, ACC_DEPRECATED, ACC_ENUM, ACC_FINAL, ACC_INTERFACE, ACC_MANDATED, ACC_MODULE, ACC_NATIVE, ACC_OPEN, ACC_PRIVATE, ACC_PROTECTED, ACC_PUBLIC, ACC_STATIC, ACC_STATIC_PHASE, ACC_STRICT, ACC_SUPER, ACC_SYNCHRONIZED, ACC_SYNTHETIC, ACC_TRANSIENT, ACC_TRANSITIVE, ACC_VARARGS, ACC_VOLATILE, ACONST_NULL, ALOAD, ANEWARRAY, ARETURN, ARRAYLENGTH, ASM4, ASM5, ASM6, ASM7, ASTORE, ATHROW, BALOAD, BASTORE, BIPUSH, CALOAD, CASTORE, CHECKCAST, D2F, D2I, D2L, DADD, DALOAD, DASTORE, DCMPG, DCMPL, DCONST_0, DCONST_1, DDIV, DLOAD, DMUL, DNEG, DOUBLE, DREM, DRETURN, DSTORE, DSUB, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, F_APPEND, F_CHOP, F_FULL, F_NEW, F_SAME, F_SAME1, F2D, F2I, F2L, FADD, FALOAD, FASTORE, FCMPG, FCMPL, FCONST_0, FCONST_1, FCONST_2, FDIV, FLOAD, FLOAT, FMUL, FNEG, FREM, FRETURN, FSTORE, FSUB, GETFIELD, GETSTATIC, GOTO, H_GETFIELD, H_GETSTATIC, H_INVOKEINTERFACE, H_INVOKESPECIAL, H_INVOKESTATIC, H_INVOKEVIRTUAL, H_NEWINVOKESPECIAL, H_PUTFIELD, H_PUTSTATIC, I2B, I2C, I2D, I2F, I2L, I2S, IADD, IALOAD, IAND, IASTORE, ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4, ICONST_5, ICONST_M1, IDIV, IF_ACMPEQ, IF_ACMPNE, IF_ICMPEQ, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ICMPLT, IF_ICMPNE, IFEQ, IFGE, IFGT, IFLE, IFLT, IFNE, IFNONNULL, IFNULL, IINC, ILOAD, IMUL, INEG, INSTANCEOF, INTEGER, INVOKEDYNAMIC, INVOKEINTERFACE, INVOKESPECIAL, INVOKESTATIC, INVOKEVIRTUAL, IOR, IREM, IRETURN, ISHL, ISHR, ISTORE, ISUB, IUSHR, IXOR, JSR, L2D, L2F, L2I, LADD, LALOAD, LAND, LASTORE, LCMP, LCONST_0, LCONST_1, LDC, LDIV, LLOAD, LMUL, LNEG, LONG, LOOKUPSWITCH, LOR, LREM, LRETURN, LSHL, LSHR, LSTORE, LSUB, LUSHR, LXOR, MONITORENTER, MONITOREXIT, MULTIANEWARRAY, NEW, NEWARRAY, NOP, NULL, POP, POP2, PUTFIELD, PUTSTATIC, RET, RETURN, SALOAD, SASTORE, SIPUSH, SOURCE_DEPRECATED, SOURCE_MASK, SWAP, T_BOOLEAN, T_BYTE, T_CHAR, T_DOUBLE, T_FLOAT, T_INT, T_LONG, T_SHORT, TABLESWITCH, TOP, UNINITIALIZED_THIS, V_PREVIEW, V1_1, V1_2, V1_3, V1_4, V1_5, V1_6, V1_7, V1_8, V10, V11, V12, V13, V9
Constructor and Description |
---|
CodeFlow(java.lang.String className,
ClassWriter classWriter)
Construct a new
CodeFlow for the given class. |
Modifier and Type | Method and Description |
---|---|
static boolean |
areBoxingCompatible(java.lang.String desc1,
java.lang.String desc2)
Determine whether boxing/unboxing can get from one type to the other.
|
static int |
arrayCodeFor(java.lang.String arraytype)
Determine the appropriate T tag to use for the NEWARRAY bytecode.
|
static java.lang.String |
createSignatureDescriptor(java.lang.reflect.Constructor<?> ctor)
Create the JVM signature descriptor for a constructor.
|
static java.lang.String |
createSignatureDescriptor(java.lang.reflect.Method method)
Create the JVM signature descriptor for a method.
|
void |
enterCompilationScope()
Enter a new compilation scope, usually due to nested expression evaluation.
|
void |
exitCompilationScope()
Exit a compilation scope, usually after a nested expression has been evaluated.
|
void |
finish()
Called after the main expression evaluation method has been generated, this
method will callback any registered FieldAdders or ClinitAdders to add any
extra information to the class representing the compiled expression.
|
java.lang.String |
getClassName() |
static void |
insertAnyNecessaryTypeConversionBytecodes(MethodVisitor mv,
char targetDescriptor,
java.lang.String stackDescriptor)
Insert any necessary numeric conversion bytecodes based upon what is on the stack and the desired target type.
|
static void |
insertArrayStore(MethodVisitor mv,
java.lang.String arrayElementType)
Produce appropriate bytecode to store a stack item in an array.
|
static void |
insertBoxIfNecessary(MethodVisitor mv,
char ch)
Determine the appropriate boxing instruction for a specific type (if it needs
boxing) and insert the instruction into the supplied visitor.
|
static void |
insertBoxIfNecessary(MethodVisitor mv,
java.lang.String descriptor)
Determine the appropriate boxing instruction for a specific type (if it needs
boxing) and insert the instruction into the supplied visitor.
|
static void |
insertCheckCast(MethodVisitor mv,
java.lang.String descriptor)
Insert the appropriate CHECKCAST instruction for the supplied descriptor.
|
static void |
insertNewArrayCode(MethodVisitor mv,
int size,
java.lang.String arraytype)
Produce the correct bytecode to build an array.
|
static void |
insertNumericUnboxOrPrimitiveTypeCoercion(MethodVisitor mv,
java.lang.String stackDescriptor,
char targetDescriptor)
For use in mathematical operators, handles converting from a (possibly boxed)
number on the stack to a primitive numeric type.
|
static void |
insertOptimalLoad(MethodVisitor mv,
int value)
Create the optimal instruction for loading a number on the stack.
|
static void |
insertUnboxInsns(MethodVisitor mv,
char ch,
java.lang.String stackDescriptor)
Insert any necessary cast and value call to convert from a boxed type to a
primitive value.
|
static void |
insertUnboxNumberInsns(MethodVisitor mv,
char targetDescriptor,
java.lang.String stackDescriptor)
For numbers, use the appropriate method on the number to convert it to the primitive type requested.
|
static boolean |
isBooleanCompatible(java.lang.String descriptor)
Determine whether the descriptor is for a boolean primitive or boolean reference type.
|
static boolean |
isIntegerForNumericOp(java.lang.Number number)
Determine whether the given number is to be considered as an integer
for the purposes of a numeric operation at the bytecode level.
|
static boolean |
isPrimitive(java.lang.String descriptor)
Determine whether the descriptor is for a primitive type.
|
static boolean |
isPrimitiveArray(java.lang.String descriptor)
Determine whether the descriptor is for a primitive array (e.g.
|
static boolean |
isPrimitiveOrUnboxableSupportedNumber(java.lang.String descriptor)
Determine if the supplied descriptor is for a supported number.
|
static boolean |
isPrimitiveOrUnboxableSupportedNumberOrBoolean(java.lang.String descriptor)
Determine if the supplied descriptor is for a supported number type or boolean.
|
static boolean |
isReferenceTypeArray(java.lang.String arraytype)
Return if the supplied array type has a core component reference type.
|
java.lang.String |
lastDescriptor()
Return the descriptor for the item currently on top of the stack (in the current scope).
|
void |
loadEvaluationContext(MethodVisitor mv)
Push the bytecode to load the EvaluationContext (the second parameter passed to
the compiled expression method).
|
void |
loadTarget(MethodVisitor mv)
Push the byte code to load the target (i.e.
|
int |
nextFieldId() |
int |
nextFreeVariableId() |
void |
pushDescriptor(java.lang.String descriptor)
Record the descriptor for the most recently evaluated expression element.
|
void |
registerNewClinit(CodeFlow.ClinitAdder clinitAdder)
Register a ClinitAdder which will add code to the static
initializer in the generated class to support the code
produced by an ast nodes primary generateCode() method.
|
void |
registerNewField(CodeFlow.FieldAdder fieldAdder)
Register a FieldAdder which will add a new field to the generated
class to support the code produced by an ast nodes primary
generateCode() method.
|
static java.lang.String |
toBoxedDescriptor(java.lang.String primitiveDescriptor) |
static java.lang.String |
toDescriptor(java.lang.Class<?> type)
Deduce the descriptor for a type.
|
static java.lang.String |
toDescriptorFromObject(java.lang.Object value)
Determine the descriptor for an object instance (or
null ). |
static java.lang.String[] |
toDescriptors(java.lang.Class<?>[] types)
Create an array of descriptors from an array of classes.
|
static java.lang.String |
toJvmDescriptor(java.lang.Class<?> clazz)
Determine the JVM descriptor for a specified class.
|
static java.lang.String[] |
toParamDescriptors(java.lang.reflect.Constructor<?> ctor)
Create an array of descriptors representing the parameter types for the supplied
constructor.
|
static java.lang.String[] |
toParamDescriptors(java.lang.reflect.Method method)
Create an array of descriptors representing the parameter types for the supplied
method.
|
static char |
toPrimitiveTargetDesc(java.lang.String descriptor)
Convert a type descriptor to the single character primitive descriptor.
|
void |
unboxBooleanIfNecessary(MethodVisitor mv)
If the codeflow shows the last expression evaluated to java.lang.Boolean then
insert the necessary instructions to unbox that to a boolean primitive.
|
private final java.lang.String className
private final ClassWriter classWriter
private final java.util.Deque<java.util.List<java.lang.String>> compilationScopes
@Nullable private java.util.List<CodeFlow.FieldAdder> fieldAdders
@Nullable private java.util.List<CodeFlow.ClinitAdder> clinitAdders
private int nextFieldId
private int nextFreeVariableId
public CodeFlow(java.lang.String className, ClassWriter classWriter)
CodeFlow
for the given class.className
- the name of the classclassWriter
- the corresponding ASM ClassWriter
public void loadTarget(MethodVisitor mv)
mv
- the visitor into which the load instruction should be insertedpublic void loadEvaluationContext(MethodVisitor mv)
mv
- the visitor into which the load instruction should be insertedpublic void pushDescriptor(@Nullable java.lang.String descriptor)
descriptor
- type descriptor for most recently evaluated elementpublic void enterCompilationScope()
public void exitCompilationScope()
@Nullable public java.lang.String lastDescriptor()
public void unboxBooleanIfNecessary(MethodVisitor mv)
mv
- the visitor into which new instructions should be insertedpublic void finish()
public void registerNewField(CodeFlow.FieldAdder fieldAdder)
public void registerNewClinit(CodeFlow.ClinitAdder clinitAdder)
public int nextFieldId()
public int nextFreeVariableId()
public java.lang.String getClassName()
public static void insertUnboxInsns(MethodVisitor mv, char ch, @Nullable java.lang.String stackDescriptor)
mv
- the method visitor into which instructions should be insertedch
- the primitive type desired as outputstackDescriptor
- the descriptor of the type on top of the stackpublic static void insertUnboxNumberInsns(MethodVisitor mv, char targetDescriptor, @Nullable java.lang.String stackDescriptor)
mv
- the method visitor into which instructions should be insertedtargetDescriptor
- the primitive type desired as outputstackDescriptor
- the descriptor of the type on top of the stackpublic static void insertAnyNecessaryTypeConversionBytecodes(MethodVisitor mv, char targetDescriptor, java.lang.String stackDescriptor)
mv
- the method visitor into which instructions should be placedtargetDescriptor
- the (primitive) descriptor of the target typestackDescriptor
- the descriptor of the operand on top of the stackpublic static java.lang.String createSignatureDescriptor(java.lang.reflect.Method method)
method
- the methodpublic static java.lang.String createSignatureDescriptor(java.lang.reflect.Constructor<?> ctor)
ctor
- the constructorpublic static java.lang.String toJvmDescriptor(java.lang.Class<?> clazz)
clazz
- a classpublic static java.lang.String toDescriptorFromObject(@Nullable java.lang.Object value)
null
).value
- an object (possibly null
)null
value)public static boolean isBooleanCompatible(@Nullable java.lang.String descriptor)
descriptor
- type descriptortrue
if the descriptor is boolean compatiblepublic static boolean isPrimitive(@Nullable java.lang.String descriptor)
descriptor
- type descriptortrue
if a primitive typepublic static boolean isPrimitiveArray(@Nullable java.lang.String descriptor)
descriptor
- the descriptor for a possible primitive arraytrue
if the descriptor a primitive arraypublic static boolean areBoxingCompatible(java.lang.String desc1, java.lang.String desc2)
true
if it is possible to get (via boxing) from one descriptor to the otherpublic static boolean isPrimitiveOrUnboxableSupportedNumberOrBoolean(@Nullable java.lang.String descriptor)
descriptor
- the descriptor for a typetrue
if the descriptor is for a supported numeric type or booleanpublic static boolean isPrimitiveOrUnboxableSupportedNumber(@Nullable java.lang.String descriptor)
descriptor
- the descriptor for a typetrue
if the descriptor is for a supported numeric typepublic static boolean isIntegerForNumericOp(java.lang.Number number)
number
- the number to checktrue
if it is an Integer
, Short
or Byte
public static char toPrimitiveTargetDesc(java.lang.String descriptor)
descriptor
- a descriptor for a type that should have a primitive representationpublic static void insertCheckCast(MethodVisitor mv, @Nullable java.lang.String descriptor)
mv
- the target visitor into which the instruction should be inserteddescriptor
- the descriptor of the type to cast topublic static void insertBoxIfNecessary(MethodVisitor mv, @Nullable java.lang.String descriptor)
mv
- the target visitor for the new instructionsdescriptor
- the descriptor of a type that may or may not need boxingpublic static void insertBoxIfNecessary(MethodVisitor mv, char ch)
mv
- the target visitor for the new instructionsch
- the descriptor of the type that might need boxingpublic static java.lang.String toDescriptor(java.lang.Class<?> type)
type
- the type (may be primitive) for which to determine the descriptorpublic static java.lang.String[] toParamDescriptors(java.lang.reflect.Method method)
method
- a Methodpublic static java.lang.String[] toParamDescriptors(java.lang.reflect.Constructor<?> ctor)
ctor
- a Constructorpublic static java.lang.String[] toDescriptors(java.lang.Class<?>[] types)
types
- the input array of classespublic static void insertOptimalLoad(MethodVisitor mv, int value)
mv
- where to insert the bytecodevalue
- the value to be loadedpublic static void insertArrayStore(MethodVisitor mv, java.lang.String arrayElementType)
mv
- where to insert the bytecodearrayElementType
- the type of the array elementspublic static int arrayCodeFor(java.lang.String arraytype)
arraytype
- the array primitive component typepublic static boolean isReferenceTypeArray(java.lang.String arraytype)
public static void insertNewArrayCode(MethodVisitor mv, int size, java.lang.String arraytype)
mv
- the methodvisitor into which code should be insertedsize
- the size of the arrayarraytype
- the type of the arraypublic static void insertNumericUnboxOrPrimitiveTypeCoercion(MethodVisitor mv, @Nullable java.lang.String stackDescriptor, char targetDescriptor)
For example, from a Integer to a double, just need to call 'Number.doubleValue()' but from an int to a double, need to use the bytecode 'i2d'.
mv
- the method visitor when instructions should be appendedstackDescriptor
- a descriptor of the operand on the stacktargetDescriptor
- a primitive type descriptorpublic static java.lang.String toBoxedDescriptor(java.lang.String primitiveDescriptor)