/*A ClassVisitor that generates a corresponding ClassFile structure, as defined in the Java Virtual Machine Specification (JVMS). It can be used alone, to generate a Java class "from scratch", or with one or more ClassReader and adapter ClassVisitor to generate a modified class from one or more existing Java classes.*/publicclassClassWriterextendsClassVisitor
// A node that represents a class.publicclassClassNodeextendsClassVisitor
ClassVisitor定义了两个字段api和cv
publicabstractclassClassVisitor { /** * The ASM API version implemented by this visitor. The value of this field must be one of Opcodes */protectedfinalint api; /** The class visitor to which this visitor must delegate method calls. May be null. */protectedClassVisitor cv;publicClassVisitor(finalint api,finalClassVisitor classVisitor) {this.api= api;this.cv= classVisitor; }}
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
field_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
method_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
// method的方法体由Code属性表示
Code_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 max_stack;
u2 max_locals;
u4 code_length;
u1 code[code_length];
u2 exception_table_length;
{ u2 start_pc;
u2 end_pc;
u2 handler_pc;
u2 catch_type;
} exception_table[exception_table_length];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
// u? 表示占用?个字节
/*Visits the end of the class. This method, which is the last one to be called, is used to inform the visitor that all the fields and methods of the class have been visited.*/publicvoidvisitEnd()
这四个方法内部都是调用的cv.visitXxx()
这些visitXxx方法是有调用顺序的
/*A visitor to visit a Java class. The methods of this class must be called in the following order: visit [visitSource][visitModule][visitNestHost][visitOuterClass](visitAnnotation|visitTypeAnnotation|visitAttribute)*(visitNestMember|[*visitPermittedSubclass]|visitInnerClass|visitRecordComponent|visitField|visitMethod)*visitEnd*/publicabstractclassClassVisitor
[]表示最多调用一次
*表示调用0次或多次
()和|表示可以任选一个方法,不分先后顺序
由于我们只关注四个visitXxx方法,上面的调用可以简化为
visit( visitField | visitMethod)*visitEnd
ClassWriter
/*A ClassVisitor that generates a corresponding ClassFile structure, as defined in the Java Virtual Machine Specification (JVMS). It can be used alone, to generate a Java class "from scratch", or with one or more ClassReader and adapter ClassVisitor to generate a modified class from one or more existing Java classes.*/publicclassClassWriterextendsClassVisitor{publicstaticfinalint COMPUTE_MAXS =1;publicstaticfinalint COMPUTE_FRAMES =2;// The minor_version and major_version fields of the JVMS ClassFile structure.privateint version;// The symbol table for this class (contains the constant_pool and the BootstrapMethods)privatefinalSymbolTable symbolTable;// The access_flags field of the JVMS ClassFile structureprivateint accessFlags;// The this_class field of the JVMS ClassFile structureprivateint thisClass;// The super_class field of the JVMS ClassFile structureprivateint superClass;// The interface_count field of the JVMS ClassFile structureprivateint interfaceCount;// The 'interfaces' array of the JVMS ClassFile structureprivateint[] interfaces;/* * The fields of this class, stored in a linked list of FieldWriter linked via their * FieldWriter#fv field. This field stores the first element of this list. */privateFieldWriter firstField; /** * The fields of this class, stored in a linked list of FieldWriter linked via their * FieldWriter#fv field. This field stores the last element of this list. */privateFieldWriter lastField; /** * The methods of this class, stored in a linked list of MethodWriter linked via their * MethodWriter#mv field. This field stores the first element of this list. */privateMethodWriter firstMethod; /** * The methods of this class, stored in a linked list of MethodWriter linked via their * MethodWriter#mv field. This field stores the last element of this list. */privateMethodWriter lastMethod;// The number_of_classes field of the InnerClasses attributeprivateint numberOfInnerClasses;// The 'classes' array of the InnerClasses attributeprivateByteVector innerClasses;}
COMPUTE_MAXS: A flag to automatically compute the maximum stack size and the maximum number of local variables of methods. If this flag is set, then the arguments of the MethodVisitor.visitMaxs method of the MethodVisitor returned by the visitMethod method will be ignored, and computed automatically from the signature and the bytecode of each method.
COMPUTE_FRAMES: A flag to automatically compute the stack map frames of methods from scratch. If this flag is set, then the calls to the MethodVisitor.visitFrame method are ignored, and the stack map frames are recomputed from the methods bytecode. The arguments of the MethodVisitor.visitMaxs method are also ignored and recomputed from the bytecode. In other words, COMPUTE_FRAMES implies COMPUTE_MAXS.
COMPUTE_FRAMES包含了COMPUTE_MAXS的功能,不仅计算max stack size和max local variables,还计算stack map frames,所以一般都使用COMPUTE_FRAMES
注意:虽然这个字段能够自动计算,但代码中仍要调用visitMaxs,否则会报错
使用ClassWriter创建一个字节码文件,分为三步:
创建ClassWriter对象
调用ClassWriter#visitXxx方法
调用ClassWriter#toByteArray方法
Best Practice
尝试生成如下类
packagesample;publicclassHelloWorld {publicstaticfinalint NUM =17;publicString name;publicHelloWorld() { }publicinthi(Object a) {return6; }}
// A visitor to visit a Java field.publicabstractclassFieldVisitor {protectedfinalint api;protectedFieldVisitor fv;publicFieldVisitor(finalint api) {this(api,null); }}
// A visitor to visit a Java methodpublicabstractclassMethodVisitor {protectedfinalint api;protectedMethodVisitor mv;publicMethodVisitor(finalint api) {this(api,null); }}
/*A position in the bytecode of a method. Labels are used for jump, goto, and switch instructions, and for try catch blocks. A label designates the instruction that is just after. */publicclassLabel {// The offset of this label in the bytecode of its methodint bytecodeOffset;}
Label类的bytecodeOffset即对应上面的相对偏移量
通过调用MethodVisitor#visitLabel(Label)来标记跳转目标
Best Practice
If
packageexample;publicclassHellWorld {publicvoidtest(boolean flag) {if (flag) {System.out.println("value is true"); } else {System.out.println("value is false"); } }}
MethodVisitor mv2 =cw.visitMethod(ACC_PUBLIC,"test","(Z)V",null,null);// 准备两个LabelLabel elseLabel =newLabel();Label returnLabel =newLabel();mv2.visitCode();mv2.visitVarInsn(ILOAD,1);mv2.visitJumpInsn(IFEQ, elseLabel);mv2.visitFieldInsn(GETSTATIC,"java/lang/System","out","Ljava/io/PrintStream;");mv2.visitLdcInsn("value is true");mv2.visitMethodInsn(INVOKEVIRTUAL,"java/io/PrintStream","println","(Ljava/lang/String;)V",false);mv2.visitJumpInsn(GOTO, returnLabel);mv2.visitLabel(elseLabel); // label在要跳转语句的前面mv2.visitFieldInsn(GETSTATIC,"java/lang/System","out","Ljava/io/PrintStream;");mv2.visitLdcInsn("value is false");mv2.visitMethodInsn(INVOKEVIRTUAL,"java/io/PrintStream","println","(Ljava/lang/String;)V",false);mv2.visitLabel(returnLabel);mv2.visitInsn(RETURN);mv2.visitMaxs(0,0);mv2.visitEnd();
/** * Visits a LOOKUPSWITCH instruction. * * @param dflt beginning of the default handler block. * @param keys the values of the keys. * @param labels beginnings of the handler blocks. labels[i] is the beginning of * the handler block for the keys[i] key.*/publicvoidvisitLookupSwitchInsn(finalLabel dflt,finalint[] keys,finalLabel[] labels)
/** * Visits a TABLESWITCH instruction. * * @param min the minimum key value. * @param max the maximum key value. * @param dflt beginning of the default handler block. * @param labels beginnings of the handler blocks. labels[i] is the beginning of * the handler block for the min + i key.*/publicvoidvisitTableSwitchInsn(finalint min,finalint max,finalLabel dflt,finalLabel...labels)
/** * Visits a try catch block. * * @param start the beginning of the exception handler's scope (inclusive). * @param end the end of the exception handler's scope (exclusive). * @param handler the beginning of the exception handler's code. * @param type the internal name of the type of exceptions handled by the handler, * or null to catch any exceptions (for "finally" blocks). */publicvoidvisitTryCatchBlock(finalLabel start,finalLabel end,finalLabel handler,finalString type)