| Index: third_party/jmake/src/org/pantsbuild/jmake/ClassFileReader.java
|
| diff --git a/third_party/jmake/src/org/pantsbuild/jmake/ClassFileReader.java b/third_party/jmake/src/org/pantsbuild/jmake/ClassFileReader.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..3ece22d9c336884a617caceb1d4afa33023cd9db
|
| --- /dev/null
|
| +++ b/third_party/jmake/src/org/pantsbuild/jmake/ClassFileReader.java
|
| @@ -0,0 +1,595 @@
|
| +/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
|
| + *
|
| + * This program is distributed under the terms of
|
| + * the GNU General Public License Version 2. See the LICENSE file
|
| + * at the top of the source tree.
|
| + */
|
| +package org.pantsbuild.jmake;
|
| +
|
| +import java.lang.reflect.Modifier;
|
| +
|
| +
|
| +/**
|
| + * This class implements reading a byte array representing a class file and converting it into ClassInfo.
|
| + *
|
| + * @author Misha Dmitriev
|
| + * 2 March 2005
|
| + */
|
| +public class ClassFileReader extends BinaryFileReader {
|
| +
|
| + public static final int JAVA_MAGIC = -889275714; // 0xCAFEBABE
|
| + public static final int JAVA_MINOR_VERSION = 0;
|
| + public static final int JAVA_MIN_MAJOR_VERSION = 45;
|
| + public static final int JAVA_MIN_MINOR_VERSION = 3;
|
| + public static final int DEFAULT_MAJOR_VERSION = 46;
|
| + public static final int DEFAULT_MINOR_VERSION = 0;
|
| + public static final int JDK14_MAJOR_VERSION = 48;
|
| + public static final int JDK15_MAJOR_VERSION = 49;
|
| + public static final int JDK16_MAJOR_VERSION = 50;
|
| + public static final int JDK17_MAJOR_VERSION = 51;
|
| + public static final int JDK18_MAJOR_VERSION = 52;
|
| + public static final int CONSTANT_Utf8 = 1;
|
| + public static final int CONSTANT_Unicode = 2;
|
| + public static final int CONSTANT_Integer = 3;
|
| + public static final int CONSTANT_Float = 4;
|
| + public static final int CONSTANT_Long = 5;
|
| + public static final int CONSTANT_Double = 6;
|
| + public static final int CONSTANT_Class = 7;
|
| + public static final int CONSTANT_String = 8;
|
| + public static final int CONSTANT_Fieldref = 9;
|
| + public static final int CONSTANT_Methodref = 10;
|
| + public static final int CONSTANT_InterfaceMethodref = 11;
|
| + public static final int CONSTANT_NameandType = 12;
|
| + public static final int CONSTANT_MethodHandle = 15;
|
| + public static final int CONSTANT_MethodType = 16;
|
| + public static final int CONSTANT_InvokeDynamic = 18;
|
| + private ClassInfo classInfo = null;
|
| + private int cpOffsets[];
|
| + private Object cpObjectCache[];
|
| + private byte cpTags[];
|
| +
|
| + public void readClassFile(byte[] classFile, ClassInfo classInfo, String classFileFullPath, boolean readFullInfo) {
|
| + initBuf(classFile, classFileFullPath);
|
| + this.classInfo = classInfo;
|
| +
|
| + readPreamble();
|
| + readConstantPool(readFullInfo);
|
| + readIntermediate();
|
| + if (readFullInfo) {
|
| + readFields();
|
| + readMethods();
|
| + readAttributes();
|
| + }
|
| + }
|
| +
|
| + private int versionWord(int major, int minor) {
|
| + return major * 1000 + minor;
|
| + }
|
| +
|
| + private void readPreamble() {
|
| + int magic = nextInt();
|
| + if (magic != JAVA_MAGIC) {
|
| + throw classFileParseException("Illegal start of class file");
|
| + }
|
| + int minorVersion = nextChar();
|
| + int majorVersion = nextChar();
|
| + if (majorVersion > JDK14_MAJOR_VERSION ||
|
| + versionWord(majorVersion, minorVersion) <
|
| + versionWord(JAVA_MIN_MAJOR_VERSION, JAVA_MIN_MINOR_VERSION) ) {
|
| + if (majorVersion == JDK18_MAJOR_VERSION) {
|
| + classInfo.javacTargetRelease = Utils.JAVAC_TARGET_RELEASE_18;
|
| + } else if (majorVersion == JDK17_MAJOR_VERSION) {
|
| + classInfo.javacTargetRelease = Utils.JAVAC_TARGET_RELEASE_17;
|
| + } else if (majorVersion == JDK16_MAJOR_VERSION) {
|
| + classInfo.javacTargetRelease = Utils.JAVAC_TARGET_RELEASE_16;
|
| + } else if (majorVersion == JDK15_MAJOR_VERSION) {
|
| + classInfo.javacTargetRelease = Utils.JAVAC_TARGET_RELEASE_15;
|
| + } else {
|
| + throw classFileParseException("Wrong version: " + majorVersion + "." + minorVersion);
|
| + }
|
| + } else {
|
| + classInfo.javacTargetRelease = Utils.JAVAC_TARGET_RELEASE_OLDEST;
|
| + }
|
| + }
|
| +
|
| + private void readConstantPool(boolean readFullInfo) {
|
| + int classRefsNo = 0;
|
| + int fieldRefsNo = 0;
|
| + int methodRefsNo = 0;
|
| +
|
| + cpOffsets = new int[nextChar()];
|
| + cpTags = new byte[cpOffsets.length];
|
| + int ofs, len, classIdx, nameAndTypeIdx, nameIdx, sigIdx, utf8Idx;
|
| + int i = 1;
|
| + while (i < cpOffsets.length) {
|
| + byte tag = buf[curBufPos++];
|
| + cpOffsets[i] = curBufPos;
|
| + cpTags[i] = tag;
|
| + i++;
|
| + switch (tag) {
|
| + case CONSTANT_Utf8:
|
| + len = nextChar();
|
| + curBufPos += len;
|
| + break;
|
| +
|
| + case CONSTANT_Class:
|
| + classRefsNo++;
|
| + curBufPos += 2;
|
| + break;
|
| +
|
| + case CONSTANT_String:
|
| + case CONSTANT_MethodType:
|
| + curBufPos += 2;
|
| + break;
|
| +
|
| + case CONSTANT_Fieldref:
|
| + fieldRefsNo++;
|
| + curBufPos += 4;
|
| + break;
|
| +
|
| + case CONSTANT_Methodref:
|
| + case CONSTANT_InterfaceMethodref:
|
| + methodRefsNo++;
|
| + curBufPos += 4;
|
| + break;
|
| +
|
| + case CONSTANT_MethodHandle:
|
| + curBufPos += 3;
|
| + break;
|
| +
|
| + case CONSTANT_NameandType:
|
| + case CONSTANT_Integer:
|
| + case CONSTANT_Float:
|
| + case CONSTANT_InvokeDynamic:
|
| + curBufPos += 4;
|
| + break;
|
| +
|
| + case CONSTANT_Long:
|
| + case CONSTANT_Double:
|
| + curBufPos += 8;
|
| + i++;
|
| + break;
|
| +
|
| + default:
|
| + throw classFileParseException("Bad constant pool tag: " + tag + " at " + Integer.toString(curBufPos - 1));
|
| + }
|
| + }
|
| +
|
| + cpObjectCache = new Object[cpOffsets.length];
|
| + if (!readFullInfo) {
|
| + return;
|
| + }
|
| +
|
| + classInfo.cpoolRefsToClasses = new String[classRefsNo];
|
| + classInfo.isRefClassArray = new boolean[classRefsNo];
|
| + classInfo.cpoolRefsToFieldClasses = new String[fieldRefsNo];
|
| + classInfo.cpoolRefsToFieldNames = new String[fieldRefsNo];
|
| + classInfo.cpoolRefsToFieldSignatures = new String[fieldRefsNo];
|
| + classInfo.cpoolRefsToMethodClasses = new String[methodRefsNo];
|
| + classInfo.cpoolRefsToMethodNames = new String[methodRefsNo];
|
| + classInfo.cpoolRefsToMethodSignatures = new String[methodRefsNo];
|
| +
|
| + int curClassRef = 0;
|
| + int curFieldRef = 0;
|
| + int curMethodRef = 0;
|
| +
|
| + for (i = 0; i < cpOffsets.length; i++) {
|
| + ofs = cpOffsets[i];
|
| + switch (cpTags[i]) {
|
| + case CONSTANT_Class:
|
| + utf8Idx = getChar(ofs);
|
| + classInfo.cpoolRefsToClasses[curClassRef++] =
|
| + classNameAtCPIndex(utf8Idx, classInfo.isRefClassArray, curClassRef - 1);
|
| + //System.out.println("Read cpool ref to class: " + classInfo.cpoolRefsToClasses[curClassRef-1]);
|
| + break;
|
| +
|
| + case CONSTANT_Fieldref:
|
| + classIdx = getChar(ofs);
|
| + nameAndTypeIdx = getChar(ofs + 2);
|
| + if (cpTags[classIdx] != CONSTANT_Class || cpTags[nameAndTypeIdx] != CONSTANT_NameandType) {
|
| + badCPReference(ofs, i);
|
| + }
|
| + classInfo.cpoolRefsToFieldClasses[curFieldRef] =
|
| + classNameAtCPIndex(getChar(cpOffsets[classIdx]));
|
| +
|
| + ofs = cpOffsets[nameAndTypeIdx];
|
| + nameIdx = getChar(ofs);
|
| + sigIdx = getChar(ofs + 2);
|
| + if (cpTags[nameIdx] != CONSTANT_Utf8 || cpTags[sigIdx] != CONSTANT_Utf8) {
|
| + badCPReference(ofs, i);
|
| + }
|
| + classInfo.cpoolRefsToFieldNames[curFieldRef] =
|
| + utf8AtCPIndex(nameIdx);
|
| + classInfo.cpoolRefsToFieldSignatures[curFieldRef] =
|
| + signatureAtCPIndex(sigIdx);
|
| + //System.out.println("Read cpool ref to field: " + classInfo.cpoolRefsToFieldNames[curFieldRef] + " " +
|
| + // classInfo.cpoolRefsToFieldSignatures[curFieldRef]);
|
| + curFieldRef++;
|
| + break;
|
| +
|
| + case CONSTANT_Methodref:
|
| + case CONSTANT_InterfaceMethodref:
|
| + classIdx = getChar(ofs);
|
| + nameAndTypeIdx = getChar(ofs + 2);
|
| + if (cpTags[classIdx] != CONSTANT_Class || cpTags[nameAndTypeIdx] != CONSTANT_NameandType) {
|
| + badCPReference(ofs, i);
|
| + }
|
| + classInfo.cpoolRefsToMethodClasses[curMethodRef] =
|
| + classNameAtCPIndex(getChar(cpOffsets[classIdx]));
|
| +
|
| + ofs = cpOffsets[nameAndTypeIdx];
|
| + nameIdx = getChar(ofs);
|
| + sigIdx = getChar(ofs + 2);
|
| + if (cpTags[nameIdx] != CONSTANT_Utf8 || cpTags[sigIdx] != CONSTANT_Utf8) {
|
| + badCPReference(ofs, i);
|
| + }
|
| + classInfo.cpoolRefsToMethodNames[curMethodRef] =
|
| + utf8AtCPIndex(nameIdx);
|
| + classInfo.cpoolRefsToMethodSignatures[curMethodRef] =
|
| + signatureAtCPIndex(sigIdx);
|
| + //System.out.println("Read cpool ref to method: " + classInfo.cpoolRefsToMethodNames[curMethodRef] + " " +
|
| + // classInfo.cpoolRefsToMethodSignatures[curMethodRef]);
|
| + curMethodRef++;
|
| + break;
|
| + }
|
| + }
|
| + }
|
| +
|
| + private void readIntermediate() {
|
| + int i, classIdx, superClassIdx;
|
| +
|
| + classInfo.accessFlags = nextChar();
|
| + classIdx = nextChar();
|
| + if (cpTags[classIdx] != CONSTANT_Class) {
|
| + throw classFileParseException("Bad reference to this class name");
|
| + }
|
| + classInfo.name = classNameAtCPIndex(getChar(cpOffsets[classIdx]));
|
| + superClassIdx = nextChar();
|
| + if (!"java/lang/Object".equals(classInfo.name)) {
|
| + if (cpTags[superClassIdx] != CONSTANT_Class) {
|
| + throw classFileParseException("Bad reference to super class name");
|
| + }
|
| + classInfo.superName =
|
| + classNameAtCPIndex(getChar(cpOffsets[superClassIdx]));
|
| + }
|
| +
|
| + char intfCount = nextChar();
|
| + if (intfCount != 0) {
|
| + classInfo.interfaces = new String[intfCount];
|
| + for (i = 0; i < intfCount; i++) {
|
| + classIdx = nextChar();
|
| + if (cpTags[classIdx] != CONSTANT_Class) {
|
| + throw classFileParseException("Bad reference to an implemented interface");
|
| + }
|
| + classInfo.interfaces[i] =
|
| + classNameAtCPIndex(getChar(cpOffsets[classIdx]));
|
| + }
|
| + }
|
| + }
|
| +
|
| + private void readFields() {
|
| + int i, j;
|
| +
|
| + char definedFieldCount = nextChar();
|
| + if (definedFieldCount == 0) {
|
| + return;
|
| + }
|
| +
|
| + String names[] = new String[definedFieldCount];
|
| + String signatures[] = new String[definedFieldCount];
|
| + char accessFlags[] = new char[definedFieldCount];
|
| +
|
| + // We are not going to record information on private fields which have either primitive or non-project-class
|
| + // (typically core-class) types. Such fields cannot affect anything except their own class, so we don't need them.
|
| + int ri = 0;
|
| +
|
| + for (i = 0; i < definedFieldCount; i++) {
|
| + char flags = nextChar();
|
| + String name = utf8AtCPIndex(nextChar());
|
| + String sig = signatureAtCPIndex(nextChar());
|
| +
|
| + boolean recordField =
|
| + !(Modifier.isPrivate(flags) &&
|
| + (ClassInfo.isPrimitiveFieldSig(sig) || classInfo.isNonProjectClassTypeFieldSig(sig)));
|
| +
|
| + int attrCount = nextChar();
|
| + for (j = 0; j < attrCount; j++) {
|
| + int attrNameIdx = nextChar();
|
| + int attrLen = nextInt();
|
| + if (recordField && utf8AtCPIndex(attrNameIdx).equals("ConstantValue") &&
|
| + Modifier.isFinal(flags)) {
|
| + if (classInfo.primitiveConstantInitValues == null) {
|
| + classInfo.primitiveConstantInitValues =
|
| + new Object[definedFieldCount];
|
| + }
|
| + int constValueIdx = nextChar();
|
| + switch (cpTags[constValueIdx]) {
|
| + case CONSTANT_String:
|
| + classInfo.primitiveConstantInitValues[ri] =
|
| + utf8AtCPIndex(getChar(cpOffsets[constValueIdx]));
|
| + break;
|
| +
|
| + case CONSTANT_Integer:
|
| + classInfo.primitiveConstantInitValues[ri] =
|
| + Integer.valueOf(getInt(cpOffsets[constValueIdx]));
|
| + break;
|
| +
|
| + case CONSTANT_Long:
|
| + classInfo.primitiveConstantInitValues[ri] =
|
| + Long.valueOf(getLong(cpOffsets[constValueIdx]));
|
| + break;
|
| +
|
| + case CONSTANT_Float:
|
| + classInfo.primitiveConstantInitValues[ri] =
|
| + Float.valueOf(getFloat(cpOffsets[constValueIdx]));
|
| + break;
|
| +
|
| + case CONSTANT_Double:
|
| + classInfo.primitiveConstantInitValues[ri] =
|
| + Double.valueOf(getDouble(cpOffsets[constValueIdx]));
|
| + break;
|
| +
|
| + default:
|
| + badCPEntry(constValueIdx);
|
| + }
|
| +
|
| + } else {
|
| + curBufPos += attrLen;
|
| + }
|
| + }
|
| +
|
| + if (recordField) {
|
| + names[ri] = name;
|
| + signatures[ri] = sig;
|
| + accessFlags[ri] = flags;
|
| + ri++;
|
| + }
|
| + }
|
| +
|
| + if (ri == definedFieldCount) {
|
| + classInfo.fieldNames = names;
|
| + classInfo.fieldSignatures = signatures;
|
| + classInfo.fieldAccessFlags = accessFlags;
|
| + } else if (ri > 0) {
|
| + classInfo.fieldNames = new String[ri];
|
| + classInfo.fieldSignatures = new String[ri];
|
| + classInfo.fieldAccessFlags = new char[ri];
|
| + System.arraycopy(names, 0, classInfo.fieldNames, 0, ri);
|
| + System.arraycopy(signatures, 0, classInfo.fieldSignatures, 0, ri);
|
| + System.arraycopy(accessFlags, 0, classInfo.fieldAccessFlags, 0, ri);
|
| + }
|
| + }
|
| +
|
| + private void readMethods() {
|
| + int i, j;
|
| +
|
| + char methodCount = nextChar();
|
| + if (methodCount == 0) {
|
| + return;
|
| + }
|
| +
|
| + String names[] = new String[methodCount];
|
| + String signatures[] = new String[methodCount];
|
| + char accessFlags[] = new char[methodCount];
|
| +
|
| + for (i = 0; i < methodCount; i++) {
|
| + accessFlags[i] = nextChar();
|
| + names[i] = utf8AtCPIndex(nextChar());
|
| + signatures[i] = signatureAtCPIndex(nextChar());
|
| +
|
| + int attrCount = nextChar();
|
| + for (j = 0; j < attrCount; j++) {
|
| + int attrNameIdx = nextChar();
|
| + int attrLen = nextInt();
|
| + if (utf8AtCPIndex(attrNameIdx).equals("Exceptions")) {
|
| + if (classInfo.checkedExceptions == null) {
|
| + classInfo.checkedExceptions = new String[methodCount][];
|
| + }
|
| + int nExceptions = nextChar();
|
| + String exceptions[] = new String[nExceptions];
|
| + for (int k = 0; k < nExceptions; k++) {
|
| + int excClassIdx = nextChar();
|
| + if (cpTags[excClassIdx] != CONSTANT_Class) {
|
| + badCPEntry(excClassIdx);
|
| + }
|
| + exceptions[k] =
|
| + classNameAtCPIndex(getChar(cpOffsets[excClassIdx]));
|
| + }
|
| + classInfo.checkedExceptions[i] = exceptions;
|
| + } else {
|
| + curBufPos += attrLen;
|
| + }
|
| + }
|
| + }
|
| +
|
| + classInfo.methodNames = names;
|
| + classInfo.methodSignatures = signatures;
|
| + classInfo.methodAccessFlags = accessFlags;
|
| + }
|
| +
|
| + /**
|
| + * This method actually reads only the information related to the nested classes, and
|
| + * records only those of them which are first level nested classes of this class. The class
|
| + * may also reference other classes which are not package members through the same
|
| + * InnerClasses attribute - their names would be processed when their respective enclosing
|
| + * classes are read.
|
| + */
|
| + private void readAttributes() {
|
| + String nestedClassPrefix = classInfo.name + "$";
|
| +
|
| + char attrCount = nextChar();
|
| +
|
| + for (int i = 0; i < attrCount; i++) {
|
| + int attrNameIdx = nextChar();
|
| + int attrLen = nextInt();
|
| + if (utf8AtCPIndex(attrNameIdx).equals("InnerClasses")) {
|
| + int nOfClasses = nextChar();
|
| + String nestedClasses[] = new String[nOfClasses];
|
| + char nestedClassAccessFlags[] = new char[nOfClasses];
|
| + boolean nestedClassNonMember[] = new boolean[nOfClasses];
|
| + int curIdx = 0;
|
| + for (int j = 0; j < nOfClasses; j++) {
|
| + int innerClassInfoIdx = nextChar();
|
| + int outerClassInfoIdx = nextChar();
|
| + int innerClassNameIdx = nextChar();
|
| + char innerClassAccessFlags = nextChar();
|
| +
|
| + // Even if a class is private or non-member (innerClassAccessFlags has private bit set or
|
| + // outerClassInfoIdx == 0), we still should take this class into account, since it may e.g. extend
|
| + // a public class/implement a public interface, which, in turn, may be changed incompatibly.
|
| +
|
| + String nestedClassFullName = classNameAtCPIndex(getChar(cpOffsets[innerClassInfoIdx]));
|
| +
|
| + // We are only interested the nested classes whose enclosing class is this one.
|
| + if (!nestedClassFullName.startsWith(nestedClassPrefix))
|
| + continue;
|
| +
|
| + // We are only interested in the directly nested classes of this class.
|
| + String nestedClassNameSuffix = nestedClassFullName.substring(nestedClassPrefix.length());
|
| +
|
| + if (innerClassNameIdx == 0) {
|
| + // Nested class is anonymous. Suffix must be all digits.
|
| + if (findFirstNonDigit(nestedClassNameSuffix) != -1)
|
| + continue;
|
| + } else {
|
| + // Nested class is named.
|
| + String nestedClassSimpleName = utf8AtCPIndex(innerClassNameIdx);
|
| + // The simple case is Outer$Inner.
|
| + if (!nestedClassNameSuffix.equals(nestedClassSimpleName)) {
|
| + // The more complicated case is a local class. In JDK 1.5+ These are named,
|
| + // e.g., Outer$1Inner. Pre-JDK 1.5 they are named e.g., Outer$1$Inner.
|
| + int p = findFirstNonDigit(nestedClassNameSuffix);
|
| + if (p == -1)
|
| + continue;
|
| + if (classInfo.javacTargetRelease == Utils.JAVAC_TARGET_RELEASE_OLDEST &&
|
| + nestedClassNameSuffix.charAt(p++) != '$')
|
| + continue;
|
| + if (!nestedClassNameSuffix.substring(p).equals(nestedClassSimpleName))
|
| + continue;
|
| + }
|
| + }
|
| +
|
| + // The name has passed all checks, so register it.
|
| +
|
| + nestedClasses[curIdx] = nestedClassFullName;
|
| + nestedClassAccessFlags[curIdx] = innerClassAccessFlags;
|
| + nestedClassNonMember[curIdx] = (outerClassInfoIdx == 0);
|
| + curIdx++;
|
| + }
|
| + if (curIdx == nOfClasses) {
|
| + classInfo.nestedClasses = nestedClasses;
|
| + classInfo.nestedClassAccessFlags = nestedClassAccessFlags;
|
| + classInfo.nestedClassNonMember = nestedClassNonMember;
|
| + } else if (curIdx > 0) {
|
| + // We found fewer nested classes for this class than we originally expected, but still more than 0.
|
| + // Create a new array to fit their number exactly.
|
| + classInfo.nestedClasses = new String[curIdx];
|
| + classInfo.nestedClassAccessFlags = new char[curIdx];
|
| + classInfo.nestedClassNonMember = new boolean[curIdx];
|
| + System.arraycopy(nestedClasses, 0, classInfo.nestedClasses, 0, curIdx);
|
| + System.arraycopy(nestedClassAccessFlags, 0, classInfo.nestedClassAccessFlags, 0, curIdx);
|
| + System.arraycopy(nestedClassNonMember, 0, classInfo.nestedClassNonMember, 0, curIdx);
|
| + }
|
| + } else {
|
| + curBufPos += attrLen;
|
| + }
|
| + }
|
| + }
|
| +
|
| + private int findFirstNonDigit(String s) {
|
| + for (int i = 0; i < s.length(); i++) {
|
| + if (!Character.isDigit(s.charAt(i)))
|
| + return i;
|
| + }
|
| + return -1;
|
| + }
|
| +
|
| + private String utf8AtCPIndex(int idx) {
|
| + if (cpTags[idx] != CONSTANT_Utf8) {
|
| + throw classFileParseException("Constant pool entry " + idx + " should be UTF8 constant");
|
| + }
|
| + if (cpObjectCache[idx] == null) {
|
| + int utf8Len = getChar(cpOffsets[idx]);
|
| + // String interning reduces the size of the disk database very significantly
|
| + // (by one-third in one observed case), and also speeds up database search.
|
| + cpObjectCache[idx] =
|
| + (new String(buf, cpOffsets[idx] + 2, utf8Len)).intern();
|
| + }
|
| + return (String) cpObjectCache[idx];
|
| + }
|
| +
|
| + private String classNameAtCPIndex(int idx) {
|
| + return classNameAtCPIndex(idx, null, 0);
|
| + }
|
| +
|
| + /**
|
| + * Read class name at the given CONSTANT_Utf8 constant pool index, and return it
|
| + * trimmed of the possible '[' and 'L' prefixes and the ';' suffix.
|
| + */
|
| + private String classNameAtCPIndex(int idx, boolean isRefClassArray[], int isArrayIdx) {
|
| + if (cpTags[idx] != CONSTANT_Utf8) {
|
| + throw classFileParseException("Constant pool entry " + idx + " should be UTF8 constant");
|
| + }
|
| + boolean isArray = false;
|
| + if (cpObjectCache[idx] == null) {
|
| + int utf8Len = getChar(cpOffsets[idx]);
|
| + int stPos = cpOffsets[idx] + 2;
|
| + int initStPos = stPos;
|
| + while (buf[stPos] == '[') {
|
| + stPos++;
|
| + }
|
| + if (stPos != initStPos) {
|
| + isArray = true;
|
| + if (buf[stPos] == 'L') {
|
| + stPos++;
|
| + utf8Len--; // To get rid of the terminating ';'
|
| + }
|
| + }
|
| + utf8Len = utf8Len - (stPos - initStPos);
|
| + cpObjectCache[idx] = (new String(buf, stPos, utf8Len)).intern();
|
| + if (isRefClassArray != null) {
|
| + isRefClassArray[isArrayIdx] = isArray;
|
| + }
|
| + }
|
| + return (String) cpObjectCache[idx];
|
| + }
|
| +
|
| + // We replace all "Lclassname;" in signatures with "@classname#" to simplify signature parsing during reference checking
|
| + private String signatureAtCPIndex(int idx) {
|
| + if (cpTags[idx] != CONSTANT_Utf8) {
|
| + throw classFileParseException("Constant pool entry " + idx + " should be UTF8 constant");
|
| + }
|
| + if (cpObjectCache[idx] == null) {
|
| + int utf8Len = getChar(cpOffsets[idx]);
|
| + byte tmp[] = new byte[utf8Len];
|
| + System.arraycopy(buf, cpOffsets[idx] + 2, tmp, 0, utf8Len);
|
| + boolean inClassName = false;
|
| + for (int i = 0; i < utf8Len; i++) {
|
| + if (!inClassName) {
|
| + if (tmp[i] == 'L') {
|
| + tmp[i] = '@';
|
| + inClassName = true;
|
| + }
|
| + } else if (tmp[i] == ';') {
|
| + tmp[i] = '#';
|
| + inClassName = false;
|
| + }
|
| + }
|
| + cpObjectCache[idx] = (new String(tmp)).intern();
|
| + }
|
| + return (String) cpObjectCache[idx];
|
| + }
|
| +
|
| + private void badCPReference(int ofs, int i) {
|
| + throw classFileParseException("Bad constant pool reference: " + ofs + " from entry " + i);
|
| + }
|
| +
|
| + private void badCPEntry(int entryNo) {
|
| + throw classFileParseException("Constant pool entry " + entryNo + " : invalid type");
|
| + }
|
| +
|
| + private PrivateException classFileParseException(String msg) {
|
| + return new PrivateException(new PublicExceptions.ClassFileParseException(
|
| + "Error reading class file " + fileFullPath + ":\n" + msg));
|
| + }
|
| +}
|
|
|