mockit.asm.classes.ClassReader Maven / Gradle / Ivy
package mockit.asm.classes;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import mockit.asm.AnnotatedReader;
import mockit.asm.fields.FieldReader;
import mockit.asm.jvmConstants.Access;
import mockit.asm.jvmConstants.ClassVersion;
import mockit.asm.methods.MethodReader;
import org.checkerframework.checker.index.qual.NonNegative;
/**
* A Java class parser to make a {@link ClassVisitor} visit an existing class.
*
* The Java type to be parsed is given in the form of a byte array conforming to the
* Java class file format. For each field
* and method encountered, the appropriate visit method of a given class visitor is called.
*/
public final class ClassReader extends AnnotatedReader {
/**
* Start index of the class header information (access, name...) in {@link #code}.
*/
@NonNegative
final int header;
@NonNegative
private final int version;
@NonNull
private final ClassInfo classInfo;
private ClassVisitor cv;
@NonNegative
private int innerClassesCodeIndex;
@NonNegative
private int attributesCodeIndex;
/**
* The start index of each bootstrap method.
*/
@Nullable
private int[] bootstrapMethods;
/**
* Initializes a new class reader with the given bytecode array for a classfile.
*/
public ClassReader(@NonNull byte[] code) {
super(code);
header = codeIndex; // the class header information starts just after the constant pool
version = readShort(6);
access = readUnsignedShort();
classInfo = new ClassInfo();
codeIndex += 2;
classInfo.superName = readClass();
}
/**
* Returns the classfile {@linkplain ClassVersion version} of the class being read.
*/
public int getVersion() {
return version;
}
/**
* Returns the class's {@linkplain Access access} flags.
*/
public int getAccess() {
return access;
}
/**
* Returns the internal of name of the super class. For interfaces, the super class is {@link Object}.
*/
@NonNull
public String getSuperName() {
assert classInfo.superName != null;
return classInfo.superName;
}
/**
* Returns the bytecode array of the Java classfile that was read.
*/
@NonNull
public byte[] getBytecode() {
return code;
}
/**
* Makes the given visitor visit the Java class of this Class Reader.
*/
public void accept(ClassVisitor visitor) {
cv = visitor;
codeIndex = header + 2;
String classDesc = readNonnullClass();
codeIndex += 2;
readInterfaces();
readClassAttributes();
visitor.visit(version, access, classDesc, classInfo);
readAnnotations(visitor);
readInnerClasses();
readFieldsAndMethods();
visitor.visitEnd();
}
private void readInterfaces() {
int interfaceCount = readUnsignedShort();
if (interfaceCount > 0) {
String[] interfaces = new String[interfaceCount];
for (int i = 0; i < interfaceCount; i++) {
interfaces[i] = readNonnullClass();
}
classInfo.interfaces = interfaces;
}
}
private void readClassAttributes() {
innerClassesCodeIndex = 0;
codeIndex = getAttributesStartIndex();
readAttributes();
classInfo.signature = signature;
}
@Nullable
@Override
protected Boolean readAttribute(@NonNull String attributeName) {
if ("SourceFile".equals(attributeName)) {
classInfo.sourceFileName = readNonnullUTF8();
return true;
}
if ("EnclosingMethod".equals(attributeName)) {
return false;
}
if ("NestHost".equals(attributeName)) {
classInfo.hostClassName = readNonnullClass();
return true;
}
if ("NestMembers".equals(attributeName)) {
readNestMembers();
return true;
}
if ("BootstrapMethods".equals(attributeName)) {
readBootstrapMethods();
return true;
}
if ("InnerClasses".equals(attributeName)) {
innerClassesCodeIndex = codeIndex;
return false;
}
return null;
}
private void readNestMembers() {
int numberOfClasses = readUnsignedShort();
String[] nestMembers = new String[numberOfClasses];
for (int i = 0; i < numberOfClasses; i++) {
nestMembers[i] = readNonnullClass();
}
classInfo.nestMembers = nestMembers;
}
private void readBootstrapMethods() {
int bsmCount = readUnsignedShort();
bootstrapMethods = new int[bsmCount];
for (int i = 0; i < bsmCount; i++) {
bootstrapMethods[i] = codeIndex;
codeIndex += 2;
int codeOffset = readUnsignedShort();
codeIndex += codeOffset << 1;
}
}
private void readInnerClasses() {
int startIndex = innerClassesCodeIndex;
if (startIndex != 0) {
codeIndex = startIndex;
for (int innerClassCount = readUnsignedShort(); innerClassCount > 0; innerClassCount--) {
String innerName = readNonnullClass();
String outerName = readClass();
String simpleInnerName = readUTF8();
int innerAccess = readUnsignedShort();
cv.visitInnerClass(innerName, outerName, simpleInnerName, innerAccess);
}
}
}
private void readFieldsAndMethods() {
codeIndex = getCodeIndexAfterInterfaces(classInfo.interfaces.length);
FieldReader fieldReader = new FieldReader(this, cv);
codeIndex = fieldReader.readFields();
MethodReader methodReader = new MethodReader(this, cv);
codeIndex = methodReader.readMethods();
}
@NonNegative
private int getCodeIndexAfterInterfaces(@NonNegative int interfaceCount) {
return header + 8 + 2 * interfaceCount;
}
/**
* Returns the start index of the attribute_info structure of this class.
*/
@NonNegative
private int getAttributesStartIndex() {
if (attributesCodeIndex == 0) {
skipHeader();
skipClassMembers(); // fields
skipClassMembers(); // methods
attributesCodeIndex = codeIndex;
}
return attributesCodeIndex;
}
private void skipHeader() {
int interfaceCount = readUnsignedShort(header + 6);
codeIndex = getCodeIndexAfterInterfaces(interfaceCount);
}
private void skipClassMembers() {
for (int memberCount = readUnsignedShort(); memberCount > 0; memberCount--) {
codeIndex += 6; // skips access, name and desc
skipMemberAttributes();
}
}
private void skipMemberAttributes() {
for (int attributeCount = readUnsignedShort(); attributeCount > 0; attributeCount--) {
codeIndex += 2; // skips attribute name
int codeOffsetToNextAttribute = readInt();
codeIndex += codeOffsetToNextAttribute;
}
}
boolean positionAtBootstrapMethodsAttribute() {
codeIndex = getAttributesStartIndex();
for (int attributeCount = readUnsignedShort(); attributeCount > 0; attributeCount--) {
String attrName = readNonnullUTF8();
if ("BootstrapMethods".equals(attrName)) {
return true;
}
int codeOffsetToNextAttribute = readInt();
codeIndex += codeOffsetToNextAttribute;
}
return false;
}
@NonNegative
public int getBSMCodeIndex(@NonNegative int bsmStartIndex) {
assert bootstrapMethods != null;
return bootstrapMethods[bsmStartIndex];
}
}