readMethod
JVMS
method_info { u2 access_flags; u2 name_index; u2 descriptor_index; u2 attributes_count; attribute_info attributes[attributes_count]; }
/** * 读取 JVMS method_info 结构并使得给定的 visitor 访问之. * * @param classVisitor the visitor that must visit the method. * @param context information about the class being parsed. * @param methodInfoOffset the start offset of the method_info structure. * @return the offset of the first byte following the method_info structure. */ private int readMethod( final ClassVisitor classVisitor, final Context context, final int methodInfoOffset) { char[] charBuffer = context.charBuffer; // Read the access_flags, name_index and descriptor_index fields. int currentOffset = methodInfoOffset; context.currentMethodAccessFlags = readUnsignedShort(currentOffset); context.currentMethodName = readUTF8(currentOffset + 2, charBuffer); context.currentMethodDescriptor = readUTF8(currentOffset + 4, charBuffer); currentOffset += 6; // 读方法属性attributes ( Section 4.7 of the JVMS). // Attribute offsets exclude the attribute_name_index and attribute_length fields. // - The offset of the Code attribute, or 0. int codeOffset = 0; // - The offset of the Exceptions attribute, or 0. int exceptionsOffset = 0; // - The strings corresponding to the Exceptions attribute, or null. String[] exceptions = null; // - Whether the method has a Synthetic attribute. boolean synthetic = false; // - The constant pool index contained in the Signature attribute, or 0. int signatureIndex = 0; // - The offset of the RuntimeVisibleAnnotations attribute, or 0. int runtimeVisibleAnnotationsOffset = 0; // - The offset of the RuntimeInvisibleAnnotations attribute, or 0. int runtimeInvisibleAnnotationsOffset = 0; // - The offset of the RuntimeVisibleParameterAnnotations attribute, or 0. int runtimeVisibleParameterAnnotationsOffset = 0; // - The offset of the RuntimeInvisibleParameterAnnotations attribute, or 0. int runtimeInvisibleParameterAnnotationsOffset = 0; // - The offset of the RuntimeVisibleTypeAnnotations attribute, or 0. int runtimeVisibleTypeAnnotationsOffset = 0; // - The offset of the RuntimeInvisibleTypeAnnotations attribute, or 0. int runtimeInvisibleTypeAnnotationsOffset = 0; // - The offset of the AnnotationDefault attribute, or 0. int annotationDefaultOffset = 0; // - The offset of the MethodParameters attribute, or 0. int methodParametersOffset = 0; // - The non standard attributes (linked with their {@link Attribute#nextAttribute} field). // This list in the <i>reverse order</i> or their order in the ClassFile structure. Attribute attributes = null; int attributesCount = readUnsignedShort(currentOffset); currentOffset += 2; while (attributesCount-- > 0) { // Read the attribute_info's attribute_name and attribute_length fields. String attributeName = readUTF8(currentOffset, charBuffer); int attributeLength = readInt(currentOffset + 2); currentOffset += 6; // The tests are sorted in decreasing frequency order (based on frequencies observed on // typical classes). if (Constants.CODE.equals(attributeName)) { if ((context.parsingOptions & SKIP_CODE) == 0) { codeOffset = currentOffset; } } else if (Constants.EXCEPTIONS.equals(attributeName)) { exceptionsOffset = currentOffset; exceptions = new String[readUnsignedShort(exceptionsOffset)]; int currentExceptionOffset = exceptionsOffset + 2; for (int i = 0; i < exceptions.length; ++i) { exceptions[i] = readClass(currentExceptionOffset, charBuffer); currentExceptionOffset += 2; } } else if (Constants.SIGNATURE.equals(attributeName)) { signatureIndex = readUnsignedShort(currentOffset); } else if (Constants.DEPRECATED.equals(attributeName)) { context.currentMethodAccessFlags |= Opcodes.ACC_DEPRECATED; } else if (Constants.RUNTIME_VISIBLE_ANNOTATIONS.equals(attributeName)) { runtimeVisibleAnnotationsOffset = currentOffset; } else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) { runtimeVisibleTypeAnnotationsOffset = currentOffset; } else if (Constants.ANNOTATION_DEFAULT.equals(attributeName)) { annotationDefaultOffset = currentOffset; } else if (Constants.SYNTHETIC.equals(attributeName)) { synthetic = true; context.currentMethodAccessFlags |= Opcodes.ACC_SYNTHETIC; } else if (Constants.RUNTIME_INVISIBLE_ANNOTATIONS.equals(attributeName)) { runtimeInvisibleAnnotationsOffset = currentOffset; } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) { runtimeInvisibleTypeAnnotationsOffset = currentOffset; } else if (Constants.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS.equals(attributeName)) { runtimeVisibleParameterAnnotationsOffset = currentOffset; } else if (Constants.RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS.equals(attributeName)) { runtimeInvisibleParameterAnnotationsOffset = currentOffset; } else if (Constants.METHOD_PARAMETERS.equals(attributeName)) { methodParametersOffset = currentOffset; } else { Attribute attribute = readAttribute( context.attributePrototypes, attributeName, currentOffset, attributeLength, charBuffer, -1, null); attribute.nextAttribute = attributes; attributes = attribute; } currentOffset += attributeLength; } // Visit the method declaration. MethodVisitor methodVisitor = classVisitor.visitMethod( context.currentMethodAccessFlags, context.currentMethodName, context.currentMethodDescriptor, signatureIndex == 0 ? null : readUtf(signatureIndex, charBuffer), exceptions); if (methodVisitor == null) { return currentOffset; } // If the returned MethodVisitor is in fact a MethodWriter, it means there is no method // adapter between the reader and the writer. In this case, it might be possible to copy // the method attributes directly into the writer. If so, return early without visiting // the content of these attributes. if (methodVisitor instanceof MethodWriter) { MethodWriter methodWriter = (MethodWriter) methodVisitor; if (methodWriter.canCopyMethodAttributes( this, synthetic, (context.currentMethodAccessFlags & Opcodes.ACC_DEPRECATED) != 0, readUnsignedShort(methodInfoOffset + 4), signatureIndex, exceptionsOffset)) { methodWriter.setMethodAttributesSource(methodInfoOffset, currentOffset - methodInfoOffset); return currentOffset; } } // Visit the MethodParameters attribute. if (methodParametersOffset != 0) { int parametersCount = readByte(methodParametersOffset); int currentParameterOffset = methodParametersOffset + 1; while (parametersCount-- > 0) { // Read the name_index and access_flags fields and visit them. methodVisitor.visitParameter( readUTF8(currentParameterOffset, charBuffer), readUnsignedShort(currentParameterOffset + 2)); currentParameterOffset += 4; } } // Visit the AnnotationDefault attribute. if (annotationDefaultOffset != 0) { AnnotationVisitor annotationVisitor = methodVisitor.visitAnnotationDefault(); readElementValue(annotationVisitor, annotationDefaultOffset, null, charBuffer); if (annotationVisitor != null) { annotationVisitor.visitEnd(); } } // Visit the RuntimeVisibleAnnotations attribute. if (runtimeVisibleAnnotationsOffset != 0) { int numAnnotations = readUnsignedShort(runtimeVisibleAnnotationsOffset); int currentAnnotationOffset = runtimeVisibleAnnotationsOffset + 2; while (numAnnotations-- > 0) { // Parse the type_index field. String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer); currentAnnotationOffset += 2; // Parse num_element_value_pairs and element_value_pairs and visit these values. currentAnnotationOffset = readElementValues( methodVisitor.visitAnnotation(annotationDescriptor, /* visible = */ true), currentAnnotationOffset, /* named = */ true, charBuffer); } } // Visit the RuntimeInvisibleAnnotations attribute. if (runtimeInvisibleAnnotationsOffset != 0) { int numAnnotations = readUnsignedShort(runtimeInvisibleAnnotationsOffset); int currentAnnotationOffset = runtimeInvisibleAnnotationsOffset + 2; while (numAnnotations-- > 0) { // Parse the type_index field. String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer); currentAnnotationOffset += 2; // Parse num_element_value_pairs and element_value_pairs and visit these values. currentAnnotationOffset = readElementValues( methodVisitor.visitAnnotation(annotationDescriptor, /* visible = */ false), currentAnnotationOffset, /* named = */ true, charBuffer); } } // Visit the RuntimeVisibleTypeAnnotations attribute. if (runtimeVisibleTypeAnnotationsOffset != 0) { int numAnnotations = readUnsignedShort(runtimeVisibleTypeAnnotationsOffset); int currentAnnotationOffset = runtimeVisibleTypeAnnotationsOffset + 2; while (numAnnotations-- > 0) { // Parse the target_type, target_info and target_path fields. currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset); // Parse the type_index field. String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer); currentAnnotationOffset += 2; // Parse num_element_value_pairs and element_value_pairs and visit these values. currentAnnotationOffset = readElementValues( methodVisitor.visitTypeAnnotation( context.currentTypeAnnotationTarget, context.currentTypeAnnotationTargetPath, annotationDescriptor, /* visible = */ true), currentAnnotationOffset, /* named = */ true, charBuffer); } } // Visit the RuntimeInvisibleTypeAnnotations attribute. if (runtimeInvisibleTypeAnnotationsOffset != 0) { int numAnnotations = readUnsignedShort(runtimeInvisibleTypeAnnotationsOffset); int currentAnnotationOffset = runtimeInvisibleTypeAnnotationsOffset + 2; while (numAnnotations-- > 0) { // Parse the target_type, target_info and target_path fields. currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset); // Parse the type_index field. String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer); currentAnnotationOffset += 2; // Parse num_element_value_pairs and element_value_pairs and visit these values. currentAnnotationOffset = readElementValues( methodVisitor.visitTypeAnnotation( context.currentTypeAnnotationTarget, context.currentTypeAnnotationTargetPath, annotationDescriptor, /* visible = */ false), currentAnnotationOffset, /* named = */ true, charBuffer); } } // Visit the RuntimeVisibleParameterAnnotations attribute. if (runtimeVisibleParameterAnnotationsOffset != 0) { readParameterAnnotations( methodVisitor, context, runtimeVisibleParameterAnnotationsOffset, /* visible = */ true); } // Visit the RuntimeInvisibleParameterAnnotations attribute. if (runtimeInvisibleParameterAnnotationsOffset != 0) { readParameterAnnotations( methodVisitor, context, runtimeInvisibleParameterAnnotationsOffset, /* visible = */ false); } // Visit the non standard attributes. while (attributes != null) { // Copy and reset the nextAttribute field so that it can also be used in MethodWriter. Attribute nextAttribute = attributes.nextAttribute; attributes.nextAttribute = null; methodVisitor.visitAttribute(attributes); attributes = nextAttribute; } // Visit the Code attribute. if (codeOffset != 0) { methodVisitor.visitCode(); readCode(methodVisitor, context, codeOffset); } // Visit the end of the method. methodVisitor.visitEnd(); return currentOffset; }
对其他不识别的属性,纪录成Attribute链,如果attribute名称符合在accept中attribute数组中指定的attribute名,则替换传入的attribute数组对应的项;根据解析出来的信息调用以下visit方法:
void visit(int version, int access, String name, String signature, String superName, String[] interfaces); // sourceFile, sourceDebug void visitSource(String source, String debug); // EnclosingMethod attribute: enclosingOwner, enclosingName, enclosingDesc. // Note: only when the class has EnclosingMethod attribute, meaning the class is a local class or an anonymous class void visitOuterClass(String owner, String name, String desc);
依次解析RuntimeVisibleAnnotations和RuntimeInvisibleAnnotations属性,首先解析定义的Annotation的描述符以及运行时可见flag,返回用户自定义的AnnotationVisitor:
AnnotationVisitor visitAnnotation(String desc, boolean visible);
对每个定义的Annotation,解析其键值对,并根据不同的Annotation字段值调用AnnotationVisitor中的方法,在所有解析结束后,调用AnnotationVisitor.visitEnd方法:
public interface AnnotationVisitor { // 对基本类型的数组,依然采用该方法,visitArray只是在非基本类型时调用。 void visit(String name, Object value); void visitEnum(String name, String desc, String value); AnnotationVisitor visitAnnotation(String name, String desc); AnnotationVisitor visitArray(String name); void visitEnd(); }
之前解析出的attribute链表(非标准的Attribute定义),对每个Attribute实例,调用ClassVisitor中的visitAttribute方法:
void visitAttribute(Attribute attr); Attribute类包含type字段和一个字节数组: public class Attribute { public final String type; byte[] value; Attribute next; }
对每个InnerClasses属性,解析并调用ClassVisitor的visitInnerClass方法(该属性事实上保存了所有其直接内部类以及它本身到最顶层类的路径):
void visitInnerClass(String name, String outerName, String innerName, int access);
解析字段,它紧跟接口数组定义之后,最前面的2个字节为字段数组的长度,对每个字段,前面2个字节为访问flag定义,再后2个字节为Name索引,以及2个字节的描述符索引,然后解析其Attribute信息:ConstantValue、Signature、Deprecated、Synthetic、RuntimeVisibleAnnotations、RuntimeInvisibleAnnotations以及非标准定义的Attribute链,而后调用ClassVisitor的visitField方法,返回FieldVisitor实例:
// 其中value为静态字段的初始化值(对非静态字段,它的初始化必须由构造函数实现),如果没有初始化值,该值为null。
FieldVisitor visitField(int access, String name, String desc, String signature, Object value);
对返回的FieldVisitor依次对其Annotation以及非标准Attribute解析,调用其visit方法,并在完成后调用它的visitEnd方法:
public interface FieldVisitor { AnnotationVisitor visitAnnotation(String desc, boolean visible); void visitAttribute(Attribute attr); void visitEnd(); }
解析方法定义,它紧跟字段定义之后,最前面的2个字节为方法数组长度,对每个方法,前面2个字节为访问flag定义,再后2个字节为Name索引,以及2个字节的方法描述符索引,然后解析其Attribute信息:Code、Exceptions、Signature、Deprecated、RuntimeVisibleAnnotations、AnnotationDefault、Synthetic、RuntimeInvisibleAnnotations、RuntimeVisibleParameterAnnotations、RuntimeInvisibleParameterAnnotations以及非标准定义的Attribute链,如果存在Exceptions属性,解析其异常类数组,之后调用
ClassVisitor#visitMethod方法
返回MethodVisitor实例:
/** * 访问类的方法. * 每次调用此方法时,都必须返回一个新的 MethodVisitor实例(或null) * 即它不应返回以前返回的visitor * * @param access the method's access flags (see {@link Opcodes}). This parameter also indicates if * the method is synthetic and/or deprecated. * @param name the method's name. * @param descriptor the method's descriptor (see {@link Type}). * @param signature the method's signature. May be {@literal null} if the method parameters, * return type and exceptions do not use generic types. * @param exceptions the internal names of the method's exception classes (see {@link * Type#getInternalName()}). May be {@literal null}. * @return an object to visit the byte code of the method, or {@literal null} if this class * visitor is not interested in visiting the code of this method. */ public MethodVisitor visitMethod( final int access, final String name, final String descriptor, final String signature, final String[] exceptions) { if (cv != null) { return cv.visitMethod(access, name, descriptor, signature, exceptions); } return null; }
AnnotationDefault为对Annotation定义时指定默认值的解析;然后依次解析RuntimeVisibleAnnotations、RuntimeInvisibleAnnotations、RuntimeVisibleParameterAnnotations、RuntimeInvisibleParameterAnnotations等属性,调用相关AnnotationVisitor的visit方法;对非标准定义的Attribute链,依次调用MethodVisitor的visitAttribute方法:
public interface MethodVisitor { AnnotationVisitor visitAnnotationDefault(); AnnotationVisitor visitAnnotation(String desc, boolean visible); AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible); void visitAttribute(Attribute attr); }
对Code属性解析,读取2个字节的最深栈大小、最大local变量数、code占用字节数,调用MethodVisitor的visitCode()方法表示开始解析Code属性,对每条指令,创建一个Label实例并构成Label数组,解析Code属性中的异常表,对每个异常项,调用visitTryCatchBlock方法:
void visitTryCatchBlock(Label start, Label end, Label handler, String type);
Label包含以下信息:
/** * A label represents a position in the bytecode of a method. Labels are used * for jump, goto, and switch instructions, and for try catch blocks. * * @author Eric Bruneton */ public class Label { public Object info; int status; int line; int position; private int referenceCount; private int[] srcAndRefPositions; int inputStackTop; int outputStackMax; Frame frame; Label successor; Edge successors; Label next; }
解析Code属性中的内部属性信息:LocalVariableTable、LocalVariableTypeTable、LineNumberTable、StackMapTable、StackMap以及非标准定义的Attribute链,对每个Label调用其visitLineNumber方法以及对每个Frame调用visitFrame方法,并且对相应的指令调用相应的方法:
void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack); // Visits a zero operand instruction. void visitInsn(int opcode); // Visits an instruction with a single int operand. void visitIntInsn(int opcode, int operand); // Visits a local variable instruction. A local variable instruction is an instruction that loads or stores the value of a local variable. void visitVarInsn(int opcode, int var); // Visits a type instruction. A type instruction is an instruction that takes the internal name of a class as parameter. void visitTypeInsn(int opcode, String type); // Visits a field instruction. A field instruction is an instruction that loads or stores the value of a field of an object. void visitFieldInsn(int opcode, String owner, String name, String desc); // Visits a method instruction. A method instruction is an instruction that invokes a method. void visitMethodInsn(int opcode, String owner, String name, String desc); // Visits a jump instruction. A jump instruction is an instruction that may jump to another instruction. void visitJumpInsn(int opcode, Label label); // Visits a label. A label designates the instruction that will be visited just after it. void visitLabel(Label label); // Visits a LDC instruction. void visitLdcInsn(Object cst); // Visits an IINC instruction. void visitIincInsn(int var, int increment); // Visits a TABLESWITCH instruction. void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels); // Visits a LOOKUPSWITCH instruction. void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels); // Visits a MULTIANEWARRAY instruction. void visitMultiANewArrayInsn(String desc, int dims); // Visits a try catch block. void visitTryCatchBlock(Label start, Label end, Label handler, String type); void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index); // Visits a line number declaration. void visitLineNumber(int line, Label start); // Visits the maximum stack size and the maximum number of local variables of the method. void visitMaxs(int maxStack, int maxLocals);
最后调用ClassVisitor的visitEnd方法:
void visitEnd();