Class CodeFlow
java.lang.Object
org.springframework.expression.spel.CodeFlow
- All Implemented Interfaces:
Opcodes
Manages the class being generated by the compilation process.
Records intermediate compilation state as the bytecode is generated. Also includes various bytecode generation helper functions.
- Since:
- 4.1
- Author:
- Andy Clement, Juergen Hoeller
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic interfaceInterface used to generateclinitstatic initializer blocks.static interfaceInterface used to generate fields. -
Field Summary
Fields inherited from interface org.springframework.asm.Opcodes
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_RECORD, 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, ASM10_EXPERIMENTAL, ASM4, ASM5, ASM6, ASM7, ASM8, ASM9, 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, V14, V15, V16, V17, V18, V19, V20, V21, V9 -
Constructor Summary
ConstructorsConstructorDescriptionCodeFlow(String className, ClassWriter classWriter) Construct a newCodeFlowfor the given class. -
Method Summary
Modifier and TypeMethodDescriptionstatic booleanareBoxingCompatible(String desc1, String desc2) Determine whether boxing/unboxing can get from one type to the other.static intarrayCodeFor(String arrayType) Determine the appropriate T tag to use for the NEWARRAY bytecode.static StringcreateSignatureDescriptor(Constructor<?> ctor) Create the JVM signature descriptor for a constructor.static StringcreateSignatureDescriptor(Method method) Create the JVM signature descriptor for a method.voidEnter a new compilation scope, usually due to nested expression evaluation.voidExit a compilation scope, usually after a nested expression has been evaluated.voidfinish()Called after the main expression evaluation method has been generated, this method will call back any registered FieldAdders or ClinitAdders to add any extra information to the class representing the compiled expression.static voidinsertAnyNecessaryTypeConversionBytecodes(MethodVisitor mv, char targetDescriptor, String stackDescriptor) Insert any necessary numeric conversion bytecodes based upon what is on the stack and the desired target type.static voidinsertArrayStore(MethodVisitor mv, String arrayElementType) Produce appropriate bytecode to store a stack item in an array.static voidinsertBoxIfNecessary(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 voidinsertBoxIfNecessary(MethodVisitor mv, String descriptor) Determine the appropriate boxing instruction for a specific type (if it needs boxing) and insert the instruction into the supplied visitor.static voidinsertCheckCast(MethodVisitor mv, String descriptor) Insert the appropriate CHECKCAST instruction for the supplied descriptor.static voidinsertNewArrayCode(MethodVisitor mv, int size, String arrayType) Produce the correct bytecode to build an array.static voidinsertNumericUnboxOrPrimitiveTypeCoercion(MethodVisitor mv, 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 voidinsertOptimalLoad(MethodVisitor mv, int value) Create the optimal instruction for loading a number on the stack.static voidinsertUnboxInsns(MethodVisitor mv, char ch, String stackDescriptor) Insert any necessary cast and value call to convert from a boxed type to a primitive value.static voidinsertUnboxNumberInsns(MethodVisitor mv, char targetDescriptor, String stackDescriptor) For numbers, use the appropriate method on the number to convert it to the primitive type requested.static booleanisBooleanCompatible(String descriptor) Determine whether the descriptor is for a boolean primitive or boolean reference type.static booleanisIntegerForNumericOp(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 booleanisPrimitive(String descriptor) Determine whether the descriptor is for a primitive type.static booleanisPrimitiveArray(String descriptor) Determine whether the descriptor is for a primitive array (e.g.static booleanisPrimitiveOrUnboxableSupportedNumber(String descriptor) Determine if the supplied descriptor is for a supported number.static booleanisPrimitiveOrUnboxableSupportedNumberOrBoolean(String descriptor) Determine if the supplied descriptor is for a supported number type or boolean.static booleanisReferenceTypeArray(String arrayType) Return if the supplied array type has a core component reference type.Return the descriptor for the item currently on top of the stack (in the current scope).voidPush the bytecode to load the EvaluationContext (the second parameter passed to the compiled expression method).voidPush the byte code to load the target (i.e.intintvoidpushDescriptor(String descriptor) Record the descriptor for the most recently evaluated expression element.voidregisterNewClinit(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 node's primary generateCode() method.voidregisterNewField(CodeFlow.FieldAdder fieldAdder) Register a FieldAdder which will add a new field to the generated class to support the code produced by an AST node's primary generateCode() method.static StringtoBoxedDescriptor(String primitiveDescriptor) static StringtoDescriptor(Class<?> type) Deduce the descriptor for a type.static StringtoDescriptorFromObject(Object value) Determine the descriptor for an object instance (ornull).static String[]toDescriptors(Class<?>[] types) Create an array of descriptors from an array of classes.static StringtoJvmDescriptor(Class<?> clazz) Determine the JVM descriptor for a specified class.static String[]toParamDescriptors(Constructor<?> ctor) Create an array of descriptors representing the parameter types for the supplied constructor.static String[]toParamDescriptors(Method method) Create an array of descriptors representing the parameter types for the supplied method.static chartoPrimitiveTargetDesc(String descriptor) Convert a type descriptor to the single character primitive descriptor.voidIf the codeflow shows the last expression evaluated to java.lang.Boolean then insert the necessary instructions to unbox that to a boolean primitive.
-
Constructor Details
-
CodeFlow
Construct a newCodeFlowfor the given class.- Parameters:
className- the name of the classclassWriter- the corresponding ASMClassWriter
-
-
Method Details
-
loadTarget
Push the byte code to load the target (i.e. what was passed as the first argument to CompiledExpression.getValue(target, context))- Parameters:
mv- the method visitor into which the load instruction should be inserted
-
loadEvaluationContext
Push the bytecode to load the EvaluationContext (the second parameter passed to the compiled expression method).- Parameters:
mv- the method visitor into which the load instruction should be inserted- Since:
- 4.3.4
-
pushDescriptor
Record the descriptor for the most recently evaluated expression element.- Parameters:
descriptor- type descriptor for most recently evaluated element
-
enterCompilationScope
public void enterCompilationScope()Enter a new compilation scope, usually due to nested expression evaluation. For example when the arguments for a method invocation expression are being evaluated, each argument will be evaluated in a new scope. -
exitCompilationScope
public void exitCompilationScope()Exit a compilation scope, usually after a nested expression has been evaluated. For example after an argument for a method invocation has been evaluated this method returns us to the previous (outer) scope. -
lastDescriptor
Return the descriptor for the item currently on top of the stack (in the current scope). -
unboxBooleanIfNecessary
If the codeflow shows the last expression evaluated to java.lang.Boolean then insert the necessary instructions to unbox that to a boolean primitive.- Parameters:
mv- the method visitor into which new instructions should be inserted
-
finish
public void finish()Called after the main expression evaluation method has been generated, this method will call back any registered FieldAdders or ClinitAdders to add any extra information to the class representing the compiled expression. -
registerNewField
Register a FieldAdder which will add a new field to the generated class to support the code produced by an AST node's primary generateCode() method. -
registerNewClinit
Register a ClinitAdder which will add code to the static initializer in the generated class to support the code produced by an AST node's primary generateCode() method. -
nextFieldId
public int nextFieldId() -
nextFreeVariableId
public int nextFreeVariableId() -
getClassName
-
insertUnboxInsns
Insert any necessary cast and value call to convert from a boxed type to a primitive value.- Parameters:
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 stack
-
insertUnboxNumberInsns
public static void insertUnboxNumberInsns(MethodVisitor mv, char targetDescriptor, @Nullable String stackDescriptor) For numbers, use the appropriate method on the number to convert it to the primitive type requested.- Parameters:
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 stack
-
insertAnyNecessaryTypeConversionBytecodes
public static void insertAnyNecessaryTypeConversionBytecodes(MethodVisitor mv, char targetDescriptor, String stackDescriptor) Insert any necessary numeric conversion bytecodes based upon what is on the stack and the desired target type.- Parameters:
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 stack
-
createSignatureDescriptor
Create the JVM signature descriptor for a method. This consists of the descriptors for the method parameters surrounded with parentheses, followed by the descriptor for the return type. Note the descriptors here are JVM descriptors, unlike the other descriptor forms the compiler is using which do not include the trailing semicolon.- Parameters:
method- the method- Returns:
- a String signature descriptor (e.g. "(ILjava/lang/String;)V")
-
createSignatureDescriptor
Create the JVM signature descriptor for a constructor. This consists of the descriptors for the constructor parameters surrounded with parentheses, followed by the descriptor for the return type, which is always "V". Note the descriptors here are JVM descriptors, unlike the other descriptor forms the compiler is using which do not include the trailing semicolon.- Parameters:
ctor- the constructor- Returns:
- a String signature descriptor (e.g. "(ILjava/lang/String;)V")
-
toJvmDescriptor
Determine the JVM descriptor for a specified class. Unlike the other descriptors used in the compilation process, this is the one the JVM wants, so this one includes any necessary trailing semicolon (e.g. Ljava/lang/String; rather than Ljava/lang/String)- Parameters:
clazz- a class- Returns:
- the JVM descriptor for the class
-
toDescriptorFromObject
Determine the descriptor for an object instance (ornull).- Parameters:
value- an object (possiblynull)- Returns:
- the type descriptor for the object
(descriptor is "Ljava/lang/Object" for
nullvalue)
-
isBooleanCompatible
Determine whether the descriptor is for a boolean primitive or boolean reference type.- Parameters:
descriptor- type descriptor- Returns:
trueif the descriptor is boolean compatible
-
isPrimitive
Determine whether the descriptor is for a primitive type.- Parameters:
descriptor- type descriptor- Returns:
trueif a primitive type
-
isPrimitiveArray
Determine whether the descriptor is for a primitive array (e.g. "[[I").- Parameters:
descriptor- the descriptor for a possible primitive array- Returns:
trueif the descriptor a primitive array
-
areBoxingCompatible
Determine whether boxing/unboxing can get from one type to the other.Assumes at least one of the types is in boxed form (i.e. single char descriptor).
- Returns:
trueif it is possible to get (via boxing) from one descriptor to the other
-
isPrimitiveOrUnboxableSupportedNumberOrBoolean
Determine if the supplied descriptor is for a supported number type or boolean. The compilation process only (currently) supports certain number types. These are double, float, long and int.- Parameters:
descriptor- the descriptor for a type- Returns:
trueif the descriptor is for a supported numeric type or boolean
-
isPrimitiveOrUnboxableSupportedNumber
Determine if the supplied descriptor is for a supported number. The compilation process only (currently) supports certain number types. These are double, float, long and int.- Parameters:
descriptor- the descriptor for a type- Returns:
trueif the descriptor is for a supported numeric type
-
isIntegerForNumericOp
Determine whether the given number is to be considered as an integer for the purposes of a numeric operation at the bytecode level. -
toPrimitiveTargetDesc
Convert a type descriptor to the single character primitive descriptor.- Parameters:
descriptor- a descriptor for a type that should have a primitive representation- Returns:
- the single character descriptor for a primitive input descriptor
-
insertCheckCast
Insert the appropriate CHECKCAST instruction for the supplied descriptor.- Parameters:
mv- the method visitor into which the instruction should be inserteddescriptor- the descriptor of the type to cast to
-
insertBoxIfNecessary
Determine the appropriate boxing instruction for a specific type (if it needs boxing) and insert the instruction into the supplied visitor.- Parameters:
mv- the method visitor for the new instructionsdescriptor- the descriptor of a type that may or may not need boxing
-
insertBoxIfNecessary
Determine the appropriate boxing instruction for a specific type (if it needs boxing) and insert the instruction into the supplied visitor.- Parameters:
mv- the method visitor for the new instructionsch- the descriptor of the type that might need boxing
-
toDescriptor
Deduce the descriptor for a type. Descriptors are like JVM type names but missing the trailing ';' so for Object the descriptor is "Ljava/lang/Object" for int it is "I".- Parameters:
type- the type (may be primitive) for which to determine the descriptor- Returns:
- the descriptor
-
toParamDescriptors
Create an array of descriptors representing the parameter types for the supplied method. Returns a zero sized array if there are no parameters.- Parameters:
method- a Method- Returns:
- a String array of descriptors, one entry for each method parameter
-
toParamDescriptors
Create an array of descriptors representing the parameter types for the supplied constructor. Returns a zero sized array if there are no parameters.- Parameters:
ctor- a Constructor- Returns:
- a String array of descriptors, one entry for each constructor parameter
-
toDescriptors
Create an array of descriptors from an array of classes.- Parameters:
types- the input array of classes- Returns:
- an array of descriptors
-
insertOptimalLoad
Create the optimal instruction for loading a number on the stack.- Parameters:
mv- where to insert the bytecodevalue- the value to be loaded
-
insertArrayStore
Produce appropriate bytecode to store a stack item in an array. The instruction to use varies depending on whether the type is a primitive or reference type.- Parameters:
mv- where to insert the bytecodearrayElementType- the type of the array elements
-
arrayCodeFor
Determine the appropriate T tag to use for the NEWARRAY bytecode.- Parameters:
arrayType- the array primitive component type- Returns:
- the T tag to use for NEWARRAY
-
isReferenceTypeArray
Return if the supplied array type has a core component reference type. -
insertNewArrayCode
Produce the correct bytecode to build an array. The opcode to use and the signature to pass along with the opcode can vary depending on the signature of the array type.- Parameters:
mv- the method visitor into which code should be insertedsize- the size of the arrayarrayType- the type of the array
-
insertNumericUnboxOrPrimitiveTypeCoercion
public static void insertNumericUnboxOrPrimitiveTypeCoercion(MethodVisitor mv, @Nullable String stackDescriptor, char targetDescriptor) For use in mathematical operators, handles converting from a (possibly boxed) number on the stack to a primitive numeric type.For example, from an Integer to a double, just need to call 'Number.doubleValue()' but from an int to a double, need to use the bytecode 'i2d'.
- Parameters:
mv- the method visitor when instructions should be appendedstackDescriptor- a descriptor of the operand on the stacktargetDescriptor- a primitive type descriptor
-
toBoxedDescriptor
-