ClassReader的属性
在 jacoco 中忽略 code 属性值
/** * 跳过 Code 属性的标志。 如果设置了此标志,则不会解析也不访问Code属性。 */ public static final int SKIP_CODE = 1;
ClassReader#accept
在调用ClassReader的accept方法时,它解析字节码中常量池之后的所有元素
accept中开始 visitor 的各种visit方法的调用.
比如visit(访问类声明)、visitSource、visitOuterClass、visitInnerClass、readMethod(读取方法,让指定visitor访问)、readField、visitAnnotaiton、visitTypeAnnotation、visitAttribute、visitEnd,这些方法按一定顺序被执行。
在这里我们关注readMethod的调用。
/** * 使给定的访问者访问传递给此 ClassReader 构造方法的JVMS Class 文件结构 * * @param classVisitor 必须访问此类的访问者。 * @param parsingOptions 用于解析此类的选项。 #SKIP_CODE #SKIP_DEBUG #SKIP_FRAMES或#EXPAND_FRAMES中的一个或多个 */ public void accept(final ClassVisitor classVisitor, final int parsingOptions) { accept(classVisitor, new Attribute[0], parsingOptions); }
/** * @param attributePrototypes 在类访问期间必须解析的属性的原型.任何类型不等于原型类型的属性都不会被解析:其字节数组值也将一成不变地传递给 ClassWriter * 如果该值包含对常量池的引用,或者与由 reader 和 writer 之间的类适配器转换的类元素具有句法或语义链接,则可能会破坏它 */ public void accept( final ClassVisitor classVisitor, final Attribute[] attributePrototypes, final int parsingOptions) { Context context = new Context(); context.attributePrototypes = attributePrototypes; context.parsingOptions = parsingOptions; context.charBuffer = new char[maxStringLength]; // Read the access_flags, this_class, super_class, interface_count and interfaces fields. char[] charBuffer = context.charBuffer; int currentOffset = header; int accessFlags = readUnsignedShort(currentOffset); // 本类名 String thisClass = readClass(currentOffset + 2, charBuffer); // 父类名 String superClass = readClass(currentOffset + 4, charBuffer); String[] interfaces = new String[readUnsignedShort(currentOffset + 6)]; currentOffset += 8; for (int i = 0; i < interfaces.length; ++i) { interfaces[i] = readClass(currentOffset, charBuffer); currentOffset += 2; } // 读取 class 属性(变量按照JVMS的4.7节的顺序排列)。 // 属性偏移量不包括attribute_name_index和attribute_length字段。 // InnerClasses 属性的偏移量,或0。 int innerClassesOffset = 0; // EnclosingMethod 属性的偏移量, or 0. int enclosingMethodOffset = 0; // - The string corresponding to the Signature attribute, or null. String signature = null; // - The string corresponding to the SourceFile attribute, or null. String sourceFile = null; // - The string corresponding to the SourceDebugExtension attribute, or null. String sourceDebugExtension = null; // - 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 RuntimeVisibleTypeAnnotations attribute, or 0. int runtimeVisibleTypeAnnotationsOffset = 0; // - The offset of the RuntimeInvisibleTypeAnnotations attribute, or 0. int runtimeInvisibleTypeAnnotationsOffset = 0; // - The offset of the Module attribute, or 0. int moduleOffset = 0; // - The offset of the ModulePackages attribute, or 0. int modulePackagesOffset = 0; // - The string corresponding to the ModuleMainClass attribute, or null. String moduleMainClass = null; // - The string corresponding to the NestHost attribute, or null. String nestHostClass = null; // - The offset of the NestMembers attribute, or 0. int nestMembersOffset = 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 currentAttributeOffset = getFirstAttributeOffset(); for (int i = readUnsignedShort(currentAttributeOffset - 2); i > 0; --i) { // Read the attribute_info's attribute_name and attribute_length fields. String attributeName = readUTF8(currentAttributeOffset, charBuffer); int attributeLength = readInt(currentAttributeOffset + 2); currentAttributeOffset += 6; // The tests are sorted in decreasing frequency order (based on frequencies observed on // typical classes). if (Constants.SOURCE_FILE.equals(attributeName)) { sourceFile = readUTF8(currentAttributeOffset, charBuffer); } else if (Constants.INNER_CLASSES.equals(attributeName)) { innerClassesOffset = currentAttributeOffset; } else if (Constants.ENCLOSING_METHOD.equals(attributeName)) { enclosingMethodOffset = currentAttributeOffset; } else if (Constants.NEST_HOST.equals(attributeName)) { nestHostClass = readClass(currentAttributeOffset, charBuffer); } else if (Constants.NEST_MEMBERS.equals(attributeName)) { nestMembersOffset = currentAttributeOffset; } else if (Constants.SIGNATURE.equals(attributeName)) { signature = readUTF8(currentAttributeOffset, charBuffer); } else if (Constants.RUNTIME_VISIBLE_ANNOTATIONS.equals(attributeName)) { runtimeVisibleAnnotationsOffset = currentAttributeOffset; } else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) { runtimeVisibleTypeAnnotationsOffset = currentAttributeOffset; } else if (Constants.DEPRECATED.equals(attributeName)) { accessFlags |= Opcodes.ACC_DEPRECATED; } else if (Constants.SYNTHETIC.equals(attributeName)) { accessFlags |= Opcodes.ACC_SYNTHETIC; } else if (Constants.SOURCE_DEBUG_EXTENSION.equals(attributeName)) { sourceDebugExtension = readUtf(currentAttributeOffset, attributeLength, new char[attributeLength]); } else if (Constants.RUNTIME_INVISIBLE_ANNOTATIONS.equals(attributeName)) { runtimeInvisibleAnnotationsOffset = currentAttributeOffset; } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) { runtimeInvisibleTypeAnnotationsOffset = currentAttributeOffset; } else if (Constants.MODULE.equals(attributeName)) { moduleOffset = currentAttributeOffset; } else if (Constants.MODULE_MAIN_CLASS.equals(attributeName)) { moduleMainClass = readClass(currentAttributeOffset, charBuffer); } else if (Constants.MODULE_PACKAGES.equals(attributeName)) { modulePackagesOffset = currentAttributeOffset; } else if (!Constants.BOOTSTRAP_METHODS.equals(attributeName)) { // The BootstrapMethods attribute is read in the constructor. Attribute attribute = readAttribute( attributePrototypes, attributeName, currentAttributeOffset, attributeLength, charBuffer, -1, null); attribute.nextAttribute = attributes; attributes = attribute; } currentAttributeOffset += attributeLength; } // Visit class declaration. The minor_version and major_version fields start 6 bytes before // the first constant pool entry, which itself starts at cpInfoOffsets[1] - 1 (by definition). classVisitor.visit( readInt(cpInfoOffsets[1] - 7), accessFlags, thisClass, signature, superClass, interfaces); // Visit the SourceFile and SourceDebugExtenstion attributes. if ((parsingOptions & SKIP_DEBUG) == 0 && (sourceFile != null || sourceDebugExtension != null)) { classVisitor.visitSource(sourceFile, sourceDebugExtension); } // Visit the Module, ModulePackages and ModuleMainClass attributes. if (moduleOffset != 0) { readModuleAttributes( classVisitor, context, moduleOffset, modulePackagesOffset, moduleMainClass); } // Visit the NestHost attribute. if (nestHostClass != null) { classVisitor.visitNestHost(nestHostClass); } // Visit the EnclosingMethod attribute. if (enclosingMethodOffset != 0) { String className = readClass(enclosingMethodOffset, charBuffer); int methodIndex = readUnsignedShort(enclosingMethodOffset + 2); String name = methodIndex == 0 ? null : readUTF8(cpInfoOffsets[methodIndex], charBuffer); String type = methodIndex == 0 ? null : readUTF8(cpInfoOffsets[methodIndex] + 2, charBuffer); classVisitor.visitOuterClass(className, name, type); } // 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( classVisitor.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( classVisitor.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( classVisitor.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( classVisitor.visitTypeAnnotation( context.currentTypeAnnotationTarget, context.currentTypeAnnotationTargetPath, annotationDescriptor, /* visible = */ false), currentAnnotationOffset, /* named = */ true, charBuffer); } } // Visit the non standard attributes. while (attributes != null) { // Copy and reset the nextAttribute field so that it can also be used in ClassWriter. Attribute nextAttribute = attributes.nextAttribute; attributes.nextAttribute = null; classVisitor.visitAttribute(attributes); attributes = nextAttribute; } // Visit the NestedMembers attribute. if (nestMembersOffset != 0) { int numberOfNestMembers = readUnsignedShort(nestMembersOffset); int currentNestMemberOffset = nestMembersOffset + 2; while (numberOfNestMembers-- > 0) { classVisitor.visitNestMember(readClass(currentNestMemberOffset, charBuffer)); currentNestMemberOffset += 2; } } // Visit the InnerClasses attribute. if (innerClassesOffset != 0) { int numberOfClasses = readUnsignedShort(innerClassesOffset); int currentClassesOffset = innerClassesOffset + 2; while (numberOfClasses-- > 0) { classVisitor.visitInnerClass( readClass(currentClassesOffset, charBuffer), readClass(currentClassesOffset + 2, charBuffer), readUTF8(currentClassesOffset + 4, charBuffer), readUnsignedShort(currentClassesOffset + 6)); currentClassesOffset += 8; } } // 访问字段和方法 int fieldsCount = readUnsignedShort(currentOffset); currentOffset += 2; while (fieldsCount-- > 0) { currentOffset = readField(classVisitor, context, currentOffset); } int methodsCount = readUnsignedShort(currentOffset); currentOffset += 2; while (methodsCount-- > 0) { // 读取方法 currentOffset = readMethod(classVisitor, context, currentOffset); } // Visit the end of the class. classVisitor.visitEnd(); }
紧接着常量池的2个字节是该类的access标签:ACC_PUBLIC、ACC_FINAL等
之后2个字节为当前类名在常量池CONSTANT_Utf8_Info类型的索引
之后2个字节为其父类名在常量池CONSTANT_Utf8_Info类型的索引(索引值0表示父类为null,即直接继承自Object类)
再之后为其实现的接口数长度和对应各个接口名在常量池中CONSTANT_Utf8_Info类型的索引值
暂时先跳过Field和Method定义信息,解析类的attribute表,它用两个字节表达attribute数组的长度,每个attribute项中最前面2个字节是attribute名称:
SourceFile(读取sourceFile值)
InnerClasses(暂时纪录起始索引)
EnclosingMethod(记录当前匿名类、本地类包含者类名以及包含者的方法名和描述符)
Signature(类的签名信息,用于范型)
RuntimeVisibleAnnotations(暂时纪录起始索引)
Deprecated(表识属性)
Synthetic(标识属性)
SourceDebugExtension(为调试器提供的自定义扩展信息,读取成一个字符串)
RuntimeInvisibleAnnotations(暂时纪录起始索引)