ClassReader 解析
1. ClassReader的字段
public final byte[] b;
包含了要解析的Class字节数组,该数组内容不能被修改。此字段适用于Attribute
子类,通常不需要类访问者。需要注意的是:Class文件可以从该数组中的任何偏移量开始,即它不必从偏移地址0开始,使用getItem()
方法和header
属性来获取字节码在此数组中的偏移量。
private final int[] cpInfoOffsets;
在常量池中的第i个常量的偏移地址为cpInfoOffsets [i] -1,那么第i个常量的tag值为b[cpInfoOffsets [i] -1]。
private final String[] constantUtf8Values;
private final ConstantDynamic[] constantDynamicValues;
010101010...01011100---> "abxsc" 。设定缓存的目的是:避免了对给定CONSTANT_Utf8,CONSTANT_Dynamic常量池项的多次解析。
private final int[] bootstrapMethodOffsets;
bootstrap_methods数组的每个元素的在b[]的起始偏移量。
private final int maxStringLength;
保守估计类的常量池中包含的字符串的最大长度。
public final int header;
ClassFile的access_flags字段在b[]中的字节偏移量。
在构建ClassReader
实例时,它首先保存字节码二进制数组为byte[] b
,
2. ClassReader 的构造器
有4个重载的构造器,目的是初始化上述字段。
1.读取的二进制字节数组byte[] classFile。
public ClassReader(final byte[] classFile) {
this(classFile, 0, classFile.length);
}
2.包含了字节码的数组classFileBuffer
,字节码数组的由偏移量classFileOffset
和字节码长度classFileLength
决定。
public ClassReader(byte[] classFileBuffer,int classFileOffset, int classFileLength ) {
this(classFileBuffer, classFileOffset, true);
}
3.使用InputStream或类加载器来获取类的二进制字节码。
public ClassReader(final InputStream inputStream) throws IOException {
this(readStream(inputStream, false));
}
public ClassReader(final String className) throws IOException {
this(readStream(ClassLoader.getSystemResourceAsStream(
className.replace('.', '/') + ".class"), true));
}
4.所有的构造器都会调用如下构造器:
创建一个新的构造器,并且该构造器的不应该暴露为public。
classFileBuffer
为包含字节码的数组;classFileOffset
为字节码的第一个字节在b[]中的偏移量 ;checkClassVersion
是否检查Class文件的版本号。
ClassReader(
final byte[] classFileBuffer,
final int classFileOffset,
final boolean checkClassVersion) {
b = classFileBuffer;
// 检查类的主版本号(major version)
if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V12) {
throw new IllegalArgumentException(
"Unsupported class file major version " + readShort(classFileOffset + 6));
}
// 创建常量池数组
int constantPoolCount = readUnsignedShort(classFileOffset + 8);// 常量池大小
cpInfoOffsets = new int[constantPoolCount];// 创建一个常量池数组
constantUtf8Values = new String[constantPoolCount];// 创建一个 字符串数组
// 其他条目的偏移量取决于所有先前条目的总大小
int currentCpInfoIndex = 1;
int currentCpInfoOffset = classFileOffset + 10;
int currentMaxStringLength = 0;
boolean hasConstantDynamic = false;
boolean hasConstantInvokeDynamic = false;
while (currentCpInfoIndex < constantPoolCount) {
cpInfoOffsets[currentCpInfoIndex++] = currentCpInfoOffset + 1;
int cpInfoSize;
// 不同类型的常量的所占的字节码不同
switch (classFileBuffer[currentCpInfoOffset]) {
//...
case Symbol.CONSTANT_LONG_TAG:
cpInfoSize = 9;
currentCpInfoIndex++;
break;
case Symbol.CONSTANT_METHOD_HANDLE_TAG:
cpInfoSize = 4;
break;
case Symbol.CONSTANT_STRING_TAG:
cpInfoSize = 3;
break;
default:
throw new IllegalArgumentException();
}
currentCpInfoOffset += cpInfoSize;
}
maxStringLength = currentMaxStringLength;
// The Classfile's access_flags field is just after the last constant pool entry.
header = currentCpInfoOffset;
// Allocate the cache of ConstantDynamic values, if there is at least one.
constantDynamicValues = hasConstantDynamic ? new ConstantDynamic[constantPoolCount] : null;
// Read the BootstrapMethods attribute, if any (only get the offset of each method).
bootstrapMethodOffsets =
(hasConstantDynamic | hasConstantInvokeDynamic)
? readBootstrapMethodsAttribute(currentMaxStringLength)
: null;
}
3.获取类、父类和接口的名称
// 类的访问标识
public int getAccess() {
return readUnsignedShort(header);
}
// 类的名称
public String getClassName() {
return readClass(header + 2, new char[maxStringLength]);
}
// 获取父类名称
public String getSuperName() {
return readClass(header + 4, new char[maxStringLength]);
}
// 获取实现的接口名称
public String[] getInterfaces() {
int currentOffset = header + 6;
int interfacesCount = readUnsignedShort(currentOffset);
String[] interfaces = new String[interfacesCount];
if (interfacesCount > 0) {
char[] charBuffer = new char[maxStringLength];
for (int i = 0; i < interfacesCount; ++i) {
currentOffset += 2;
interfaces[i] = readClass(currentOffset, charBuffer);
}
}
return interfaces;
}
4. 公有方法
在调用ClassReader的accept
方法时,它解析字节码中常量池之后的所有元素。
// 使得给定的访问器访问JVM字节码结构, classVisitor是类的访问者
public void accept(final ClassVisitor classVisitor, final int parsingOptions) {
accept(classVisitor, new Attribute[0], parsingOptions);
}
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;
}
// Visit ....
// Visit the 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 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;
}
}
// Visit the fields and methods.
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();
}
5.私有方法
读取代码块、属性和方法
读取代码块、属性和方法 读取注解 image.png image.png image.png