Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
mockit.asm.metadata.ClassMetadataReader Maven / Gradle / Ivy
/*
* Copyright (c) 2006 JMockit developers
* This file is subject to the terms of the MIT license (see LICENSE.txt).
*/
package mockit.asm.metadata;
import java.nio.charset.*;
import java.util.*;
import javax.annotation.*;
import mockit.asm.jvmConstants.*;
public final class ClassMetadataReader extends ObjectWithAttributes
{
private static final Charset UTF8 = Charset.forName("UTF-8");
private static final ConstantPoolTag[] CONSTANT_POOL_TAGS = ConstantPoolTag.values();
enum ConstantPoolTag { // values from JVM spec Table 4.4.A
No0, // 0
Utf8(2), // 1 (has variable size)
No2, // 2
Integer(4), // 3
Float(4), // 4
Long(8), // 5
Double(8), // 6
Class(2), // 7
String(2), // 8
FieldRef(4), // 9
MethodRef(4), // 10
InterfaceMethodRef(4), // 11
NameAndType(4), // 12
No13,
No14,
MethodHandle(3), // 15, added in Java 7
MethodType(2), // 16, added in Java 7
ConstantDynamic(4), // 17, added in Java 11
InvokeDynamic(4), // 18, added in Java 7
Module(2), // 19, added in Java 9
Package(2); // 20, added in Java 9
@Nonnegative final int itemSize;
ConstantPoolTag() { itemSize = 0; }
ConstantPoolTag(@Nonnegative int itemSize) { this.itemSize = itemSize; }
}
public enum Attribute {
Annotations,
Parameters,
Signature
}
@Nonnull private final byte[] code;
@Nonnull private final int[] cpItemCodeIndexes;
@Nullable private final EnumSet attributesToRead;
/**
* The constant pool starts at index 10 in the code array; this is the end index, which must be computed as it's not stored anywhere.
*/
@Nonnegative private final int cpEndIndex;
@Nonnegative private int fieldsEndIndex;
@Nonnegative private int methodsEndIndex;
@Nonnegative
public static int readVersion(@Nonnull byte[] code) {
int byte0 = (code[4] & 0xFF) << 24;
int byte1 = (code[5] & 0xFF) << 16;
int byte2 = (code[6] & 0xFF) << 8;
int byte3 = code[7] & 0xFF;
return byte0 | byte1 | byte2 | byte3;
}
public ClassMetadataReader(@Nonnull byte[] code) { this(code, null); }
public ClassMetadataReader(@Nonnull byte[] code, @Nullable EnumSet attributesToRead) {
this.code = code;
int cpItemCount = readUnsignedShort(8);
int[] cpTable = new int[cpItemCount];
cpItemCodeIndexes = cpTable;
this.attributesToRead = attributesToRead;
cpEndIndex = findEndIndexOfConstantPoolTable(cpTable);
}
@Nonnegative
private int readUnsignedShort(@Nonnegative int codeIndex) {
byte[] b = code;
int i = codeIndex;
int byte0 = (b[i++] & 0xFF) << 8;
int byte1 = b[i] & 0xFF;
return byte0 | byte1;
}
private int readInt(@Nonnegative int codeIndex) {
byte[] b = code;
int i = codeIndex;
int byte0 = (b[i++] & 0xFF) << 24;
int byte1 = (b[i++] & 0xFF) << 16;
int byte2 = (b[i++] & 0xFF) << 8;
int byte3 = b[i] & 0xFF;
return byte0 | byte1 | byte2 | byte3;
}
@Nonnegative
private int findEndIndexOfConstantPoolTable(@Nonnull int[] cpTable) {
byte[] b = code;
int codeIndex = 10;
for (int cpItemIndex = 1, n = cpTable.length; cpItemIndex < n; cpItemIndex++) {
int tagValue = b[codeIndex++];
ConstantPoolTag tag = CONSTANT_POOL_TAGS[tagValue];
cpTable[cpItemIndex] = codeIndex;
int cpItemSize = tag.itemSize;
if (tag == ConstantPoolTag.Long || tag == ConstantPoolTag.Double) {
cpItemIndex++;
}
else if (tag == ConstantPoolTag.Utf8) {
int stringLength = readUnsignedShort(codeIndex);
cpItemSize += stringLength;
}
codeIndex += cpItemSize;
}
return codeIndex;
}
@Nonnegative
public int getVersion() {
return readVersion(code);
}
@Nonnegative
public int getAccessFlags() {
return readUnsignedShort(cpEndIndex);
}
@Nonnull
public String getThisClass() {
int cpClassIndex = readUnsignedShort(cpEndIndex + 2);
return getTypeDescription(cpClassIndex);
}
@Nonnull
private String getTypeDescription(@Nonnegative int cpClassIndex) {
int cpClassCodeIndex = cpItemCodeIndexes[cpClassIndex];
int cpDescriptionIndex = readUnsignedShort(cpClassCodeIndex);
return getString(cpDescriptionIndex);
}
@Nonnull
private String getString(@Nonnegative int cpStringIndex) {
int codeIndex = cpItemCodeIndexes[cpStringIndex];
int stringLength = readUnsignedShort(codeIndex);
return new String(code, codeIndex + 2, stringLength, UTF8);
}
@Nullable
public String getSuperClass() {
int cpClassIndex = readUnsignedShort(cpEndIndex + 4);
if (cpClassIndex == 0) {
return null;
}
return getTypeDescription(cpClassIndex);
}
@Nullable
public String[] getInterfaces() {
int codeIndex = cpEndIndex + 6;
int interfaceCount = readUnsignedShort(codeIndex);
if (interfaceCount == 0) {
return null;
}
codeIndex += 2;
String[] interfaces = new String[interfaceCount];
for (int i = 0; i < interfaceCount; i++) {
int cpInterfaceIndex = readUnsignedShort(codeIndex);
codeIndex += 2;
interfaces[i] = getTypeDescription(cpInterfaceIndex);
}
return interfaces;
}
private static class MemberInfo extends ObjectWithAttributes {
@Nonnegative public final int accessFlags;
@Nonnull public final String name;
@Nonnull public final String desc;
@Nullable public String signature;
MemberInfo(@Nonnegative int accessFlags, @Nonnull String name, @Nonnull String desc, @Nonnegative int attributeCount) {
this.accessFlags = accessFlags;
this.name = name;
this.desc = desc;
}
public final boolean isStatic() { return (accessFlags & Access.STATIC) != 0; }
public final boolean isAbstract() { return (accessFlags & Access.ABSTRACT) != 0; }
public final boolean isSynthetic() { return (accessFlags & Access.SYNTHETIC) != 0; }
}
public static final class FieldInfo extends MemberInfo {
FieldInfo(int accessFlags, @Nonnull String name, @Nonnull String desc, @Nonnegative int attributeCount) {
super(accessFlags, name, desc, attributeCount);
}
}
@Nonnull
public List getFields() {
int codeIndex = cpEndIndex + 6;
int interfaceCount = readUnsignedShort(codeIndex);
codeIndex += 2 + 2 * interfaceCount;
int fieldCount = readUnsignedShort(codeIndex);
codeIndex += 2;
List fields;
if (fieldCount == 0) {
fields = Collections.emptyList();
}
else {
fields = new ArrayList<>(fieldCount);
for (int i = 0; i < fieldCount; i++) {
int accessFlags = readUnsignedShort(codeIndex);
codeIndex += 2;
int cpNameIndex = readUnsignedShort(codeIndex);
codeIndex += 2;
String fieldName = getString(cpNameIndex);
int cpDescIndex = readUnsignedShort(codeIndex);
codeIndex += 2;
String fieldDesc = getString(cpDescIndex);
int attributeCount = readUnsignedShort(codeIndex);
codeIndex += 2;
FieldInfo fieldInfo = new FieldInfo(accessFlags, fieldName, fieldDesc, attributeCount);
codeIndex = readAttributes(attributeCount, fieldInfo, codeIndex);
fields.add(fieldInfo);
}
}
fieldsEndIndex = codeIndex;
return fields;
}
@Nonnegative
private int readAttributes(@Nonnegative int attributeCount, @Nullable ObjectWithAttributes attributeOwner, @Nonnegative int codeIndex) {
EnumSet attributes = attributesToRead;
boolean readAnnotations = false;
if (attributes == null) {
//noinspection AssignmentToMethodParameter
attributeOwner = null;
}
else {
readAnnotations = attributes.contains(Attribute.Annotations);
}
MethodInfo method = attributeOwner instanceof MethodInfo ? (MethodInfo) attributeOwner : null;
for (int i = 0; i < attributeCount; i++) {
int cpNameIndex = readUnsignedShort(codeIndex);
codeIndex += 2;
String attributeName = getString(cpNameIndex);
int attributeLength = readInt(codeIndex);
codeIndex += 4;
if (attributeOwner != null) {
if (method != null) {
method.readAttributes(attributeName, codeIndex);
}
if (readAnnotations && "RuntimeVisibleAnnotations".equals(attributeName)) {
attributeOwner.annotations = readAnnotations(codeIndex);
}
}
codeIndex += attributeLength;
}
return codeIndex;
}
public static final class AnnotationInfo {
@Nonnull public final String name;
AnnotationInfo(@Nonnull String name) { this.name = name; }
}
@Nonnull
private List readAnnotations(@Nonnegative int codeIndex) {
int numAnnotations = readUnsignedShort(codeIndex);
codeIndex += 2;
List annotationInfos = new ArrayList<>(numAnnotations);
for (int i = 0; i < numAnnotations; i++) {
codeIndex = readAnnotation(annotationInfos, codeIndex);
}
return annotationInfos;
}
@Nonnegative
private int readAnnotation(@Nonnull List currentAnnotations, @Nonnegative int codeIndex) {
int cpTypeIndex = readUnsignedShort(codeIndex);
codeIndex += 2;
String annotationTypeDesc = getString(cpTypeIndex);
int numElementValuePairs = readUnsignedShort(codeIndex);
codeIndex += 2;
// for (int i = 0; i < numElementValuePairs; i++) {
// int cpElementNameIndex = readUnsignedShort(codeIndex);
// codeIndex += 2;
//
// int tag = code[codeIndex++];
// // TODO: continue implementing
// }
AnnotationInfo annotation = new AnnotationInfo(annotationTypeDesc);
currentAnnotations.add(annotation);
return codeIndex;
}
@Nonnegative
private int getFieldsEndIndex() {
int codeIndex = fieldsEndIndex;
if (codeIndex == 0) {
codeIndex = cpEndIndex + 6;
int interfaceCount = readUnsignedShort(codeIndex);
codeIndex += 2 + 2 * interfaceCount;
int fieldCount = readUnsignedShort(codeIndex);
codeIndex += 2;
for (int i = 0; i < fieldCount; i++) {
codeIndex += 6;
int attributeCount = readUnsignedShort(codeIndex);
codeIndex += 2;
codeIndex = readAttributes(attributeCount, null, codeIndex);
}
fieldsEndIndex = codeIndex;
}
return codeIndex;
}
public final class MethodInfo extends MemberInfo {
@Nullable public String[] parameters;
MethodInfo(int accessFlags, @Nonnull String name, @Nonnull String desc, @Nonnegative int attributeCount) {
super(accessFlags, name, desc, attributeCount);
}
public boolean isMethod() { return name.charAt(0) != '<'; }
public boolean isConstructor() { return "".equals(name); }
void readAttributes(@Nonnull String attributeName, @Nonnegative int codeIndex) {
assert attributesToRead != null;
if ("Code".equals(attributeName)) {
if (attributesToRead.contains(Attribute.Parameters)) {
readParameters(codeIndex);
}
}
else if ("Signature".equals(attributeName)) {
if (attributesToRead.contains(Attribute.Signature)) {
readSignature(codeIndex);
}
}
}
private void readParameters(@Nonnegative int codeIndex) {
codeIndex += 4;
int codeLength = readInt(codeIndex);
codeIndex += 4 + codeLength;
int exceptionTableLength = readUnsignedShort(codeIndex);
codeIndex += 2 + 8 * exceptionTableLength;
int attributeCount = readUnsignedShort(codeIndex);
codeIndex += 2;
readParameters(attributeCount, codeIndex);
}
private void readParameters(@Nonnegative int attributeCount, @Nonnegative int codeIndex) {
for (int i = 0; i < attributeCount; i++) {
int cpNameIndex = readUnsignedShort(codeIndex);
codeIndex += 2;
String attributeName = getString(cpNameIndex);
int attributeLength = readInt(codeIndex);
codeIndex += 4;
if ("LocalVariableTable".equals(attributeName)) {
parameters = readParametersFromLocalVariableTable(codeIndex);
break;
}
codeIndex += attributeLength;
}
}
@Nullable
private String[] readParametersFromLocalVariableTable(@Nonnegative int codeIndex) {
int localVariableTableLength = readUnsignedShort(codeIndex);
codeIndex += 2;
int arraySize = getSumOfArgumentSizes(desc);
if (arraySize == 0) {
return null;
}
if (!isStatic()) {
arraySize++;
}
String[] parameterNames = new String[arraySize];
for (int i = 0; i < localVariableTableLength; i++) {
codeIndex += 4;
int cpLocalVarNameIndex = readUnsignedShort(codeIndex);
codeIndex += 2;
String localVarName = getString(cpLocalVarNameIndex);
if ("this".equals(localVarName)) {
codeIndex += 4;
continue;
}
codeIndex += 2;
int localVarIndex = readUnsignedShort(codeIndex);
codeIndex += 2;
if (localVarIndex < arraySize) {
parameterNames[localVarIndex] = localVarName;
}
}
return compactArray(parameterNames);
}
@Nonnegative
private int getSumOfArgumentSizes(@Nonnull String memberDesc) {
int sum = 0;
int i = 1;
while (true) {
char c = memberDesc.charAt(i);
i++;
if (c == ')') {
return sum;
}
if (c == 'L') {
while (memberDesc.charAt(i) != ';') i++;
i++;
sum++;
}
else if (c == '[') {
while ((c = memberDesc.charAt(i)) == '[') i++;
if (isDoubleSizeType(c)) { // if the array element type is double size...
i++;
sum++; // ...then count it here, otherwise let the outer loop count it
}
}
else if (isDoubleSizeType(c)) {
sum += 2;
}
else {
sum++;
}
}
}
private boolean isDoubleSizeType(char typeCode) { return typeCode == 'D' || typeCode == 'J'; }
@Nullable
private String[] compactArray(@Nonnull String[] arrayPossiblyWithNulls) {
int n = arrayPossiblyWithNulls.length;
int j = n - 1;
int i = 0;
while (i < j) {
if (arrayPossiblyWithNulls[i] == null) {
System.arraycopy(arrayPossiblyWithNulls, i + 1, arrayPossiblyWithNulls, i, j - i);
arrayPossiblyWithNulls[j] = null;
j--;
}
else {
i++;
}
}
return n == 1 && arrayPossiblyWithNulls[0] == null ? null : arrayPossiblyWithNulls;
}
private void readSignature(@Nonnegative int codeIndex) {
int cpSignatureIndex = readUnsignedShort(codeIndex);
signature = getString(cpSignatureIndex);
}
}
@Nonnull
public List getMethods() {
int codeIndex = getFieldsEndIndex();
int methodCount = readUnsignedShort(codeIndex);
codeIndex += 2;
List methods = new ArrayList<>(methodCount);
for (int i = 0; i < methodCount; i++) {
int accessFlags = readUnsignedShort(codeIndex);
codeIndex += 2;
int cpNameIndex = readUnsignedShort(codeIndex);
codeIndex += 2;
String methodName = getString(cpNameIndex);
int cpDescIndex = readUnsignedShort(codeIndex);
codeIndex += 2;
String methodDesc = getString(cpDescIndex);
int attributeCount = readUnsignedShort(codeIndex);
codeIndex += 2;
MethodInfo methodInfo = new MethodInfo(accessFlags, methodName, methodDesc, attributeCount);
codeIndex = readAttributes(attributeCount, methodInfo, codeIndex);
methods.add(methodInfo);
}
methodsEndIndex = codeIndex;
return methods;
}
@Nonnegative
private int getMethodsEndIndex() {
int codeIndex = methodsEndIndex;
if (codeIndex == 0) {
codeIndex = getFieldsEndIndex();
int methodCount = readUnsignedShort(codeIndex);
codeIndex += 2;
for (int i = 0; i < methodCount; i++) {
codeIndex += 6;
int attributeCount = readUnsignedShort(codeIndex);
codeIndex += 2;
codeIndex = readAttributes(attributeCount, null, codeIndex);
}
methodsEndIndex = codeIndex;
}
return codeIndex;
}
@Nonnull
public List getAnnotations() {
if (annotations == null) {
int codeIndex = getMethodsEndIndex();
int attributeCount = readUnsignedShort(codeIndex);
codeIndex += 2;
readAttributes(attributeCount, this, codeIndex);
if (annotations == null) {
annotations = Collections.emptyList();
}
}
return annotations;
}
}