Dynamically Create Java Classes With JavaClassCreatorBy James Jianbo Huang March 2004 non-printer versionAbstract
1. JavaClassCreator and Jamaica
The
The class name for the API is
2. Create a Class or Interface
Class
The following is the flow to create a JVM class using
API Method Partial Listing
Here is a list of the methods mentioned so far. Most of them are merely listed along with their parameter names; some are briefly annotated with Java comments. void startClass(int accessFlags, String className, String superName, String[] implementList); void startInterface(String itfName, String[] extendList); void setSourceFileName(String fileName); String getSourceFileName(); String getClassName(); String getClassRootName(); String getSuperclassName(); String[] getInterfaceNames(); void addField(int accessFlags, String name, String type); void addConstant(int accessFlags, String fieldName, String type, Object value); String getFieldType(String fieldName); boolean isStaticField(String fieldName); void addDefaultConstructor(int accessFlags); void addAbstractMethod(int accessFlags, String name, String[] paramTypes, String[] paramNames, String returnType, String[] exceptions); void startMethod(int accessFlags, String name, String[] paramTypes, String[] paramNames, String returnType, String[] exceptions); int getMethodAccessFlags(); String getMethodName(); void addLocalVariable(String name, String type); int getLocalVariableIndex(String varName); String getVariableType(String varName); void setLabel(String label); void addCatchClause(String exceptionType, String startLabel, String endLabel, String actionLabel); void endMethod(); byte[] endClass(); void endClassToFile(String fileName);
3. Bytecode Instructions and Macros
Bytecode Instructions
Group 1 includes simple instructions with no parameters: aconst_null iconst_m1 iconst_0 iconst_1 iconst_2 iconst_3 iconst_4 iconst_5 lconst_0 lconst_1 fconst_0 fconst_1 fconst_2 dconst_0 dconst_1 iload_0 iload_1 iload_2 iload_3 lload_0 lload_1 lload_2 lload_3 fload_0 fload_1 fload_2 fload_3 dload_0 dload_1 dload_2 dload_3 aload_0 aload_1 aload_2 aload_3 iaload laload faload daload aaload baload caload saload istore_0 istore_1 istore_2 istore_3 lstore_0 lstore_1 lstore_2 lstore_3 fstore_0 fstore_1 fstore_2 fstore_3 dstore_0 dstore_1 dstore_2 dstore_3 astore_0 astore_1 astore_2 astore_3 iastore lastore fastore dastore aastore bastore castore sastore pop pop2 dup dup_x1 dup_x2 dup2 dup2_x1 dup2_x2 swap iadd ladd fadd dadd isub lsub fsub dsub imul lmul fmul dmul idiv ldiv fdiv ddiv irem lrem frem drem ineg lneg fneg dneg ishl lshl ishr lshr iushr lushr iand land ior lor ixor lxor i2l i2f i2d l2i l2f l2d f2i f2l f2d d2i d2l d2f i2b i2c i2s lcmp fcmpl fcmpg dcmpl dcmpg ireturn lreturn freturn dreturn areturn return nop arraylength athrow monitorenter monitorexit Group 2 includes load, store and ret instructions: These instructions all take a variable name as parameter, e.g.,iload lload fload dload aload istore lstore fstore dstore astore ret void inst_iload(String varName); Group 3 are instructions dealing with object types; they all take a type name as parameter: new newarray anewarray checkcast instanceof Group 4 are instructions to get and put data fields: The method signature is like:getstatic putstatic getfield putfield void inst_getstatic(String className, String fieldName, String fieldType); Group 5 is Each of them has two overloaded methods:ldc ldc_w ldc2_w wherevoid inst_ldc(Object value); void inst_ldc(Object value, String type); value is a Java primitive type wrapper or a String, and
type is the intended type. The constant will be added to the constant
pool. For certain values, implementation may optimize to such as bipush
or iconst_0 , etc.
Group 6 are for jump instructions; they all take a target label as parameter: ifeq ifne iflt ifge ifgt ifle if_icmpeq if_icmpne if_icmplt if_icmpge if_icmpgt if_icmple if_acmpeq if_acmpne goto jsr ifnull ifnonnull goto_w jsr_w Group 7 is invocation methods: Their signature is:invokevirtual invokespecial invokestatic invokeinterface void invokevirtual(String className, String methodName, String[] paramTypes, String returnType); Group 8 is for instructions otherwise not grouped. Their signatures are listed below: void inst_bipush(byte value); void inst_sipush(short value); void inst_iinc(String varName, int increment); void inst_multianewarray(String type, short dim); void inst_tableswitch(int[] consts, String[] labels, String defaultLabel); void inst_lookupswitch(int startConst, String[] labels, String defaultLabel); Macros
Beyond individual instructions,
Access to variables, fields and arrays is represented in If it is a single name, the API will try to find a variable in the current method or a field in the class. When its index is set, this is an array element accessing operation. The index can be a number or apublic class JavaClassCreator { public static class VarAccess { public VarAccess(String name, int lineNum); public VarAccess(String name); public boolean isArray(); public void setIndex(Object index); public String toString(); } } VarAccess , or can be an array of those as a
multi-dimensional array access.
Macros can be classified into two categories: Jamaica Macros and non-Jamaica ones. Jamaica macros are direct counterparts of those defined in the Jamaica language. They are all quite sophisticated. Other macros are simpler, usually are expanded into just one instruction, which is almost always context-sensitive. Most of them are used to implement Jamaica macros. void macroSet(Object dest, Object value); // dest can be a String, a VarAccess or null; // if null, the value is left on the stack; // otherwise, the value is set to the dest. void macroPrint(String cmd, String target, Object[] params); // Prints to java.lang.System.out or err. // cmd is "print", "println" or "flush". // target is null, "out" or "err". // params can be values or VarAccess's. void macroObject(String type, String[] paramTypes, Object[] paramValues); // creates an object on the stack. void macroArray(String type, int dims, Object[] sizes); // creates an array on the stack. void macroStringConcat(Object[] vals); // vals can be values or VarAccess's. The concatenated string is left on stack. void macroIterate(VarAccess coll, String var, String id); // coll must reference either a java.util.Iterator or Enumeration. // var must reference an Object, holding the object during each iteration. // var can be null, where the object is left on the stack. // This macro must be ended by a call to macroEndIterator() with a matchin id. void macroEndIterate(String id); void macroArrayIterate(VarAccess array, String var, String id); // array must reference an array. // var must reference an int to serve as the index used by the iteration. // The stack is not used. // This macro must be ended by a call to macroEndArrayIterator() with a matching id. void macroEndArrayIterate(String, String id); void macroIf(String op, Object left, Object right, String id, boolean hasElse); // op is one of these: <, <=, >, >=, == or !=. // left and right can be constants or references. // This macro must be ended by a macroElse() or macroEndIf() with a matching id. void macroElse(String id); // This macro must be proceded by a macroIf() and // ended by a macroEndIf() with a matching id. void macroEndIf(String id); // This macro must be proceeded by a macroIf() or macroElse() and // ended by a macroEndIf() with a matching id. Non-Jamaica Macros are simple ones that do "intelligent" instruction generation. void macroTypeCast(String fromType, String toType); String macroGetField(String name); // Returns the type of the value loaded on the stack. String macroLoadConstantOrVarOrField(Object value, String intendedType); // value can be a constant (including String) or a VarAccess. // Returns the type of the value loaded on the stack. String macroLoadVarOrField(String name, String intendedType); // Returns the type of the value loaded on the stack. String macroLoadVarOrField(VarAccess var, String intendedType); // Returns the type of the value loaded on the stack. Indirect Instructions
In addition to the direct bytecode instructions and powerful macros,
void inst(int opcode); // For all group 1 instructions. void instLoadStoreRet(int opcode, String varName); // For all group 2 instructions. void instType(int opcode, String type); // For all group 3 instructions. void instGetPut(int opcode, String className, String fieldName, String type); // For all group 4 instructions. void instLdc(int opcode, Object value, String type); // For all group 5 instructions. void instJump(int opcode, String label); // For all group 6 instructions. void instInvoke(int opcode, String className, String methodName, String[] paramTypes, String returnType); // For all group 7 instructions. Helper Methods
These are helper methods that can be handy at times. String getMnemonic(int opcode); String getType(Object value); static String getConstTypeName(Object value); static boolean isPrimitiveType(String type); static boolean isInt(String type); static int getLoadInstruction(String type); static int getStoreInstruction(String type); static int getArrayLoadStoreInstruction(String type, boolean isStore); static VarAccess createVarAccess(String name); static VarAccess createArrayAccess(String name, Object index); static VarAccess createArrayAccess(String name, Object index1, Object index2);
4. Service Provider Methods
Implementation classes of void setSourceFileName(String fileName); String getSourceFileName(); String getClassName(); String getSuperclassName(); String[] getInterfaceNames(); String getClassRootName(); void startClass(int accessFlags, String className, String superName, String[] implementList); void startInterface(String itfName, String[] extendList); byte[] endClass(); void endClassToFile(String fileName); void addField(int accessFlags, String name, String type); void addConstant(int accessFlags, String fieldName, String type, Object value); String getFieldType(String fieldName); boolean isStaticField(String fieldName); void addDefaultConstructor(int accessFlags); void addAbstractMethod(int accessFlags, String name, String[] paramTypes, String[] paramNames, String returnType, String[] exceptions); void startMethod(int accessFlags, String name, String[] paramTypes, String[] paramNames, String returnType, String[] exceptions); void endMethod(); int getMethodAccessFlags(); String getMethodName(); void addLocalVariable(String name, String type); int getLocalVariableIndex(String varName); String getVariableType(String varName); void addCatchClause(String exceptionType, String startLabel, String endLabel, String actionLabel); void setLabel(String label); void inst(int opcode); void instLoadStoreRet(int opcode, String varName); void instType(int opcode, String typeName); void instGetPut(int opcode, String className, String fieldName, String type); void instLdc(int opcode, Object value, String type); void instJump(int opcode, String label); void instInvoke(int opcode, String className, String methodName, String[] paramTypes, String returnType); void inst_tableswitch(int[] consts, String[] labels, String defaultLabel); void inst_lookupswitch(int startConst, String[] labels, String defaultLabel); void inst_bipush(byte value); void inst_sipush(short value); void inst_iinc(String varName, int increment); void inst_multianewarray(String arrayType, short dims); String getMnemonic(int opcode); Opcode Constants
5. Examples
Let's write some Java code to generate a Java class and use it instantly. The generated class will be like the following class written in Jamaica:
This is the Java code that generates and uses a created class:
The output is: % javac JCCTest.java % Java JCCTest String event: Cool! int event: 5 Array event: length=3 e[0]=3 e[1]=6 e[2]=9 Array event: length=0
6. Code Listings
|