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)); |
+ } |
+} |