You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by pa...@apache.org on 2018/04/13 21:13:17 UTC
svn commit: r1829104 [2/4] - in /felix/trunk/osgi-r7/framework/src/main:
java/org/apache/felix/framework/ java/org/apache/felix/framework/util/
resources/
Added: felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/util/ClassParser.java
URL: http://svn.apache.org/viewvc/felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/util/ClassParser.java?rev=1829104&view=auto
==============================================================================
--- felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/util/ClassParser.java (added)
+++ felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/util/ClassParser.java Fri Apr 13 21:13:16 2018
@@ -0,0 +1,2808 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util;
+
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Modifier;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This class is based on code developed at https://github.com/bndtools/bnd
+ */
+public class ClassParser
+{
+ Map<String, TypeRef> typeRefCache = new HashMap<String, TypeRef>();
+ Map<String, Descriptor> descriptorCache = new HashMap<String, Descriptor>();
+ Map<String, PackageRef> packageCache = new HashMap<String, PackageRef>();
+
+ // MUST BE BEFORE PRIMITIVES, THEY USE THE DEFAULT PACKAGE!!
+ final static PackageRef DEFAULT_PACKAGE = new PackageRef();
+ final static PackageRef PRIMITIVE_PACKAGE = new PackageRef();
+
+ final static TypeRef VOID = new ConcreteRef("V", "void", PRIMITIVE_PACKAGE);
+ final static TypeRef BOOLEAN = new ConcreteRef("Z", "boolean", PRIMITIVE_PACKAGE);
+ final static TypeRef BYTE = new ConcreteRef("B", "byte", PRIMITIVE_PACKAGE);
+ final static TypeRef CHAR = new ConcreteRef("C", "char", PRIMITIVE_PACKAGE);
+ final static TypeRef SHORT = new ConcreteRef("S", "short", PRIMITIVE_PACKAGE);
+ final static TypeRef INTEGER = new ConcreteRef("I", "int", PRIMITIVE_PACKAGE);
+ final static TypeRef LONG = new ConcreteRef("J", "long", PRIMITIVE_PACKAGE);
+ final static TypeRef DOUBLE = new ConcreteRef("D", "double", PRIMITIVE_PACKAGE);
+ final static TypeRef FLOAT = new ConcreteRef("F", "float", PRIMITIVE_PACKAGE);
+
+
+ {
+ packageCache.put("", DEFAULT_PACKAGE);
+ }
+
+ private interface TypeRef extends Comparable<TypeRef>
+ {
+ String getBinary();
+
+ String getFQN();
+
+ String getPath();
+
+ boolean isPrimitive();
+
+ TypeRef getClassRef();
+
+ PackageRef getPackageRef();
+
+ String getShortName();
+
+ String getSourcePath();
+
+ String getDottedOnly();
+
+ }
+
+ private static class PackageRef implements Comparable<PackageRef>
+ {
+ final String binaryName;
+ final String fqn;
+
+ PackageRef(String binaryName)
+ {
+ this.binaryName = fqnToBinary(binaryName);
+ this.fqn = binaryToFQN(binaryName);
+ }
+
+ PackageRef()
+ {
+ this.binaryName = "";
+ this.fqn = ".";
+ }
+
+ public String getFQN()
+ {
+ return fqn;
+ }
+
+ @Override
+ public String toString()
+ {
+ return fqn;
+ }
+
+ boolean isPrimitivePackage()
+ {
+ return this == PRIMITIVE_PACKAGE;
+ }
+
+ @Override
+ public int compareTo(PackageRef other)
+ {
+ return fqn.compareTo(other.fqn);
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ assert o instanceof PackageRef;
+ return o == this;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return super.hashCode();
+ }
+
+
+ }
+
+ // We "intern" the
+ private static class ConcreteRef implements TypeRef
+ {
+ final String binaryName;
+ final String fqn;
+ final boolean primitive;
+ final PackageRef packageRef;
+
+ ConcreteRef(PackageRef packageRef, String binaryName)
+ {
+ this.binaryName = binaryName;
+ this.fqn = binaryToFQN(binaryName);
+ this.primitive = false;
+ this.packageRef = packageRef;
+ }
+
+ ConcreteRef(String binaryName, String fqn, PackageRef pref)
+ {
+ this.binaryName = binaryName;
+ this.fqn = fqn;
+ this.primitive = true;
+ this.packageRef = pref;
+ }
+
+ @Override
+ public String getBinary()
+ {
+ return binaryName;
+ }
+
+ @Override
+ public String getPath()
+ {
+ return binaryName + ".class";
+ }
+
+ @Override
+ public String getSourcePath()
+ {
+ return binaryName + ".java";
+ }
+
+ @Override
+ public String getFQN()
+ {
+ return fqn;
+ }
+
+ @Override
+ public String getDottedOnly()
+ {
+ return fqn.replace('$', '.');
+ }
+
+ @Override
+ public boolean isPrimitive()
+ {
+ return primitive;
+ }
+
+ @Override
+ public TypeRef getClassRef()
+ {
+ return this;
+ }
+
+ @Override
+ public PackageRef getPackageRef()
+ {
+ return packageRef;
+ }
+
+ @Override
+ public String getShortName()
+ {
+ int n = binaryName.lastIndexOf('/');
+ return binaryName.substring(n + 1);
+ }
+
+ @Override
+ public String toString()
+ {
+ return fqn;
+ }
+
+ @Override
+ public boolean equals(Object other)
+ {
+ assert other instanceof TypeRef;
+ return this == other;
+ }
+
+ @Override
+ public int compareTo(TypeRef other)
+ {
+ if (this == other)
+ {
+ return 0;
+ }
+ return fqn.compareTo(other.getFQN());
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return super.hashCode();
+ }
+
+ }
+
+ private static class ArrayRef implements TypeRef
+ {
+ final TypeRef component;
+
+ ArrayRef(TypeRef component)
+ {
+ this.component = component;
+ }
+
+ @Override
+ public String getBinary()
+ {
+ return "[" + component.getBinary();
+ }
+
+ @Override
+ public String getFQN()
+ {
+ return component.getFQN() + "[]";
+ }
+
+ @Override
+ public String getPath()
+ {
+ return component.getPath();
+ }
+
+ @Override
+ public String getSourcePath()
+ {
+ return component.getSourcePath();
+ }
+
+ @Override
+ public boolean isPrimitive()
+ {
+ return false;
+ }
+
+ @Override
+ public TypeRef getClassRef()
+ {
+ return component.getClassRef();
+ }
+
+ @Override
+ public boolean equals(Object other)
+ {
+ if (other == null || other.getClass() != getClass())
+ {
+ return false;
+ }
+
+ return component.equals(((ArrayRef) other).component);
+ }
+
+ @Override
+ public PackageRef getPackageRef()
+ {
+ return component.getPackageRef();
+ }
+
+ @Override
+ public String getShortName()
+ {
+ return component.getShortName() + "[]";
+ }
+
+ @Override
+ public String toString()
+ {
+ return component.toString() + "[]";
+ }
+
+ @Override
+ public String getDottedOnly()
+ {
+ return component.getDottedOnly();
+ }
+
+ @Override
+ public int compareTo(TypeRef other)
+ {
+ if (this == other)
+ {
+ return 0;
+ }
+
+ return getFQN().compareTo(other.getFQN());
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return super.hashCode();
+ }
+ }
+
+ private TypeRef getTypeRef(String binaryClassName)
+ {
+ TypeRef ref = typeRefCache.get(binaryClassName);
+ if (ref != null)
+ {
+ return ref;
+ }
+
+ if (binaryClassName.startsWith("["))
+ {
+ ref = getTypeRef(binaryClassName.substring(1));
+ ref = new ArrayRef(ref);
+ }
+ else
+ {
+ if (binaryClassName.length() == 1)
+ {
+ switch (binaryClassName.charAt(0))
+ {
+ case 'V':
+ return VOID;
+ case 'B':
+ return BYTE;
+ case 'C':
+ return CHAR;
+ case 'I':
+ return INTEGER;
+ case 'S':
+ return SHORT;
+ case 'D':
+ return DOUBLE;
+ case 'F':
+ return FLOAT;
+ case 'J':
+ return LONG;
+ case 'Z':
+ return BOOLEAN;
+ }
+ // falls trough for other 1 letter class names
+ }
+ if (binaryClassName.startsWith("L") && binaryClassName.endsWith(";"))
+ {
+ binaryClassName = binaryClassName.substring(1, binaryClassName.length() - 1);
+ }
+ ref = typeRefCache.get(binaryClassName);
+ if (ref != null)
+ {
+ return ref;
+ }
+
+ PackageRef pref;
+ int n = binaryClassName.lastIndexOf('/');
+ if (n < 0)
+ {
+ pref = DEFAULT_PACKAGE;
+ }
+ else
+ {
+ pref = getPackageRef(binaryClassName.substring(0, n));
+ }
+
+ ref = new ConcreteRef(pref, binaryClassName);
+ }
+
+ typeRefCache.put(binaryClassName, ref);
+ return ref;
+ }
+
+ private PackageRef getPackageRef(String binaryPackName)
+ {
+ if (binaryPackName.indexOf('.') >= 0)
+ {
+ binaryPackName = binaryPackName.replace('.', '/');
+ }
+ PackageRef ref = packageCache.get(binaryPackName);
+ if (ref != null)
+ {
+ return ref;
+ }
+
+ ref = new PackageRef(binaryPackName);
+ packageCache.put(binaryPackName, ref);
+ return ref;
+ }
+
+ private Descriptor getDescriptor(String descriptor)
+ {
+ Descriptor d = descriptorCache.get(descriptor);
+ if (d != null)
+ {
+ return d;
+ }
+ d = new Descriptor(descriptor);
+ descriptorCache.put(descriptor, d);
+ return d;
+ }
+
+ private class Descriptor
+ {
+ final TypeRef type;
+ final TypeRef[] prototype;
+ final String descriptor;
+
+ Descriptor(String descriptor)
+ {
+ this.descriptor = descriptor;
+ int index = 0;
+ List<TypeRef> types = new ArrayList<TypeRef>();
+ if (descriptor.charAt(index) == '(')
+ {
+ index++;
+ while (descriptor.charAt(index) != ')')
+ {
+ index = parse(types, descriptor, index);
+ }
+ index++; // skip )
+ prototype = types.toArray(new TypeRef[0]);
+ types.clear();
+ }
+ else
+ {
+ prototype = null;
+ }
+
+ index = parse(types, descriptor, index);
+ type = types.get(0);
+ }
+
+ int parse(List<TypeRef> types, String descriptor, int index)
+ {
+ char c;
+ StringBuilder sb = new StringBuilder();
+ while ((c = descriptor.charAt(index++)) == '[')
+ {
+ sb.append('[');
+ }
+
+ switch (c)
+ {
+ case 'L':
+ while ((c = descriptor.charAt(index++)) != ';')
+ {
+ // TODO
+ sb.append(c);
+ }
+ break;
+
+ case 'V':
+ case 'B':
+ case 'C':
+ case 'I':
+ case 'S':
+ case 'D':
+ case 'F':
+ case 'J':
+ case 'Z':
+ sb.append(c);
+ break;
+
+ default:
+ throw new IllegalArgumentException(
+ "Invalid type in descriptor: " + c + " from " + descriptor + "[" + index + "]");
+ }
+ types.add(getTypeRef(sb.toString()));
+ return index;
+ }
+
+ @Override
+ public boolean equals(Object other)
+ {
+ if (other == null || other.getClass() != getClass())
+ {
+ return false;
+ }
+
+ return Arrays.equals(prototype, ((Descriptor) other).prototype) && type == ((Descriptor) other).type;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = prime + type.hashCode();
+ result = prime * result + ((prototype == null) ? 0 : Arrays.hashCode(prototype));
+ return result;
+ }
+
+ @Override
+ public String toString()
+ {
+ return descriptor;
+ }
+ }
+
+ private static String binaryToFQN(String binary)
+ {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0, l = binary.length(); i < l; i++)
+ {
+ char c = binary.charAt(i);
+
+ if (c == '/')
+ {
+ sb.append('.');
+ }
+ else
+ {
+ sb.append(c);
+ }
+ }
+ String result = sb.toString();
+ assert result.length() > 0;
+ return result;
+ }
+
+ private static String fqnToBinary(String binary)
+ {
+ return binary.replace('.', '/');
+ }
+
+
+ TypeRef getTypeRefFromFQN(String fqn)
+ {
+ if (fqn.equals("boolean"))
+ {
+ return BOOLEAN;
+ }
+
+ if (fqn.equals("byte"))
+ {
+ return BOOLEAN;
+ }
+
+ if (fqn.equals("char"))
+ {
+ return CHAR;
+ }
+
+ if (fqn.equals("short"))
+ {
+ return SHORT;
+ }
+
+ if (fqn.equals("int"))
+ {
+ return INTEGER;
+ }
+
+ if (fqn.equals("long"))
+ {
+ return LONG;
+ }
+
+ if (fqn.equals("float"))
+ {
+ return FLOAT;
+ }
+
+ if (fqn.equals("double"))
+ {
+ return DOUBLE;
+ }
+
+ return getTypeRef(fqnToBinary(fqn));
+ }
+
+
+ public Set<String> parseClassFileUses(String path, InputStream in) throws Exception
+ {
+ DataInputStream din = new DataInputStream(in);
+ try
+ {
+ return new Clazz(this, path).parseClassFileData(din);
+ }
+ finally
+ {
+ din.close();
+ }
+ }
+
+ private static class Clazz
+ {
+
+ class ClassConstant
+ {
+ int cname;
+ boolean referred;
+
+ ClassConstant(int class_index)
+ {
+ this.cname = class_index;
+ }
+
+ public String getName()
+ {
+ return (String) pool[cname];
+ }
+
+ @Override
+ public String toString()
+ {
+ return "ClassConstant[" + getName() + "]";
+ }
+ }
+
+
+ enum CONSTANT
+ {
+ Zero(0),
+ Utf8,
+ Two,
+ Integer(4),
+ Float(4),
+ Long(8),
+ Double(8),
+ Class(2),
+ String(2),
+ Fieldref(4),
+ Methodref(4),
+ InterfaceMethodref(4),
+ NameAndType(4),
+ Thirteen,
+ Fourteen,
+ MethodHandle(3),
+ MethodType(2),
+ Seventeen,
+ InvokeDynamic(4),
+ Module(2),
+ Package(2);
+ private final int skip;
+
+ CONSTANT(int skip)
+ {
+ this.skip = skip;
+ }
+
+ CONSTANT()
+ {
+ this.skip = -1;
+ }
+
+ int skip()
+ {
+ return skip;
+ }
+ }
+
+ final static int ACC_MODULE = 0x8000;
+
+ static protected class Assoc
+ {
+ Assoc(CONSTANT tag, int a, int b)
+ {
+ this.tag = tag;
+ this.a = a;
+ this.b = b;
+ }
+
+ CONSTANT tag;
+ int a;
+ int b;
+
+ @Override
+ public String toString()
+ {
+ return "Assoc[" + tag + ", " + a + "," + b + "]";
+ }
+ }
+
+ public abstract class Def
+ {
+
+ final int access;
+
+ public Def(int access)
+ {
+ this.access = access;
+ }
+
+ }
+
+ public class FieldDef extends Def
+ {
+ final String name;
+ final Descriptor descriptor;
+ String signature;
+ Object constant;
+
+
+ public FieldDef(int access, String name, String descriptor)
+ {
+ super(access);
+ this.name = name;
+ this.descriptor = Clazz.this.classParser.getDescriptor(descriptor);
+ }
+
+
+ @Override
+ public String toString()
+ {
+ return name;
+ }
+ }
+
+ public class MethodDef extends FieldDef
+ {
+ public MethodDef(int access, String method, String descriptor)
+ {
+ super(access, method, descriptor);
+ }
+ }
+
+ boolean hasDefaultConstructor;
+
+ int depth = 0;
+
+ TypeRef className;
+ Object pool[];
+ int intPool[];
+ Set<String> imports = new HashSet<String>();
+ String path;
+ int minor = 0;
+ int major = 0;
+ int accessx = 0;
+ int forName = 0;
+ int class$ = 0;
+ TypeRef[] interfaces;
+ TypeRef zuper;
+ FieldDef last = null;
+ final ClassParser classParser;
+ String classSignature;
+
+ private boolean detectLdc;
+
+ public Clazz(ClassParser classParser, String path)
+ {
+ this.path = path;
+ this.classParser = classParser;
+ }
+
+ Set<String> parseClassFileData(DataInput in) throws Exception
+ {
+
+ ++depth;
+
+ boolean crawl = false; // Crawl the byte code if we have a
+ // collector
+ int magic = in.readInt();
+ if (magic != 0xCAFEBABE)
+ {
+ throw new IOException("Not a valid class file (no CAFEBABE header)");
+ }
+
+ minor = in.readUnsignedShort(); // minor version
+ major = in.readUnsignedShort(); // major version
+ int count = in.readUnsignedShort();
+ pool = new Object[count];
+ intPool = new int[count];
+
+ CONSTANT[] tags = CONSTANT.values();
+ process:
+ for (int poolIndex = 1; poolIndex < count; poolIndex++)
+ {
+ int tagValue = in.readUnsignedByte();
+ if (tagValue >= tags.length)
+ {
+ throw new IOException("Unrecognized constant pool tag value " + tagValue);
+ }
+ CONSTANT tag = tags[tagValue];
+ switch (tag)
+ {
+ case Zero:
+ break process;
+ case Utf8:
+ constantUtf8(in, poolIndex);
+ break;
+ case Integer:
+ constantInteger(in, poolIndex);
+ break;
+ case Float:
+ constantFloat(in, poolIndex);
+ break;
+ // For some insane optimization reason,
+ // the long and double entries take two slots in the
+ // constant pool. See 4.4.5
+ case Long:
+ constantLong(in, poolIndex);
+ poolIndex++;
+ break;
+ case Double:
+ constantDouble(in, poolIndex);
+ poolIndex++;
+ break;
+ case Class:
+ constantClass(in, poolIndex);
+ break;
+ case String:
+ constantString(in, poolIndex);
+ break;
+ case Fieldref:
+ case Methodref:
+ case InterfaceMethodref:
+ ref(in, poolIndex);
+ break;
+ case NameAndType:
+ nameAndType(in, poolIndex, tag);
+ break;
+ case MethodHandle:
+ methodHandle(in, poolIndex, tag);
+ break;
+ case MethodType:
+ methodType(in, poolIndex, tag);
+ break;
+ case InvokeDynamic:
+ invokeDynamic(in, poolIndex, tag);
+ break;
+ default:
+ int skip = tag.skip();
+ if (skip == -1)
+ {
+ throw new IOException("Invalid tag " + tag);
+ }
+ in.skipBytes(skip);
+ break;
+ }
+ }
+
+ pool(pool, intPool);
+
+ // All name& type and class constant records contain classParser we must
+ // treat
+ // as references, though not API
+ for (Object o : pool)
+ {
+ if (o == null)
+ {
+ continue;
+ }
+
+ if (o instanceof Assoc)
+ {
+ Assoc assoc = (Assoc) o;
+ switch (assoc.tag)
+ {
+ case Fieldref:
+ case Methodref:
+ case InterfaceMethodref:
+ classConstRef(assoc.a);
+ break;
+
+ case NameAndType:
+ case MethodType:
+ referTo(assoc.b, 0); // Descriptor
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ //
+ // There is a bug in J8 compiler that leaves an
+ // orphan class constant. So when we have a CC that
+ // is not referenced by fieldrefs, method refs, or other
+ // refs then we need to crawl the byte code.
+ //
+ for (Object o : pool)
+ {
+ if (o instanceof ClassConstant)
+ {
+ ClassConstant cc = (ClassConstant) o;
+ if (cc.referred == false)
+ {
+ detectLdc = true;
+ }
+ }
+ }
+
+ /*
+ * Parse after the constant pool, code thanks to Hans Christian
+ * Falkenberg
+ */
+
+ accessx = in.readUnsignedShort(); // access
+
+ int this_class = in.readUnsignedShort();
+ className = classParser.getTypeRef((String) pool[intPool[this_class]]);
+ if (!isModule())
+ {
+ referTo(className, Modifier.PUBLIC);
+ }
+
+ int super_class = in.readUnsignedShort();
+ String superName = (String) pool[intPool[super_class]];
+ if (superName != null)
+ {
+ zuper = classParser.getTypeRef(superName);
+ }
+
+ if (zuper != null)
+ {
+ referTo(zuper, accessx);
+ }
+
+ int interfacesCount = in.readUnsignedShort();
+ if (interfacesCount > 0)
+ {
+ interfaces = new TypeRef[interfacesCount];
+ for (int i = 0; i < interfacesCount; i++)
+ {
+ interfaces[i] = classParser.getTypeRef((String) pool[intPool[in.readUnsignedShort()]]);
+ referTo(interfaces[i], accessx);
+ }
+ }
+
+ int fieldsCount = in.readUnsignedShort();
+ for (int i = 0; i < fieldsCount; i++)
+ {
+ int access_flags = in.readUnsignedShort(); // skip access flags
+ int name_index = in.readUnsignedShort();
+ int descriptor_index = in.readUnsignedShort();
+
+ // Java prior to 1.5 used a weird
+ // static variable to hold the com.X.class
+ // result construct. If it did not find it
+ // it would create a variable class$com$X
+ // that would be used to hold the class
+ // object gotten with Class.forName ...
+ // Stupidly, they did not actively use the
+ // class name for the field type, so bnd
+ // would not see a reference. We detect
+ // this case and add an artificial descriptor
+ String name = pool[name_index].toString(); // name_index
+ if (name.startsWith("class$") || name.startsWith("$class$"))
+ {
+ crawl = true;
+ }
+
+ referTo(descriptor_index, access_flags);
+ doAttributes(in, ElementType.FIELD, false, access_flags);
+ }
+
+ //
+ // Check if we have to crawl the code to find
+ // the ldc(_w) <string constant> invokestatic Class.forName
+ // if so, calculate the method ref index so we
+ // can do this efficiently
+ //
+ if (crawl)
+ {
+ forName = findMethodReference("java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
+ class$ = findMethodReference(className.getBinary(), "class$", "(Ljava/lang/String;)Ljava/lang/Class;");
+ }
+ else if (major == 48)
+ {
+ forName = findMethodReference("java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
+ if (forName > 0)
+ {
+ crawl = true;
+ class$ = findMethodReference(className.getBinary(), "class$",
+ "(Ljava/lang/String;)Ljava/lang/Class;");
+ }
+ }
+
+ // There are some serious changes in the
+ // class file format. So we do not do any crawling
+ // it has also become less important
+ // however, jDK8 has a bug that leaves an orphan ClassConstnat
+ // so if we have those, we need to also crawl the byte codes.
+ // if (major >= JAVA.OpenJDK7.major)
+
+ crawl |= detectLdc;
+
+ //
+ // Handle the methods
+ //
+ int methodCount = in.readUnsignedShort();
+ for (int i = 0; i < methodCount; i++)
+ {
+ int access_flags = in.readUnsignedShort();
+ int name_index = in.readUnsignedShort();
+ int descriptor_index = in.readUnsignedShort();
+ String name = pool[name_index].toString();
+ String descriptor = pool[descriptor_index].toString();
+ MethodDef mdef = null;
+ referTo(descriptor_index, access_flags);
+
+ if ("<init>".equals(name))
+ {
+ if (Modifier.isPublic(access_flags) && "()V".equals(descriptor))
+ {
+ hasDefaultConstructor = true;
+ }
+ doAttributes(in, ElementType.CONSTRUCTOR, crawl, access_flags);
+ }
+ else
+ {
+ doAttributes(in, ElementType.METHOD, crawl, access_flags);
+ }
+ }
+ last = null;
+
+ doAttributes(in, ElementType.TYPE, false, accessx);
+
+ //
+ // Parse all the classParser we found
+ //
+ reset();
+ return imports;
+ }
+
+ private void constantFloat(DataInput in, int poolIndex) throws IOException
+ {
+ in.skipBytes(4);
+ }
+
+ private void constantInteger(DataInput in, int poolIndex) throws IOException
+ {
+ intPool[poolIndex] = in.readInt();
+ pool[poolIndex] = intPool[poolIndex];
+ }
+
+ private void pool(@SuppressWarnings("unused") Object[] pool, @SuppressWarnings("unused") int[] intPool)
+ {
+ }
+
+ private void nameAndType(DataInput in, int poolIndex, CONSTANT tag) throws IOException
+ {
+ int name_index = in.readUnsignedShort();
+ int descriptor_index = in.readUnsignedShort();
+ pool[poolIndex] = new Assoc(tag, name_index, descriptor_index);
+ }
+
+ private void methodType(DataInput in, int poolIndex, CONSTANT tag) throws IOException
+ {
+ int descriptor_index = in.readUnsignedShort();
+ pool[poolIndex] = new Assoc(tag, 0, descriptor_index);
+ }
+
+ private void methodHandle(DataInput in, int poolIndex, CONSTANT tag) throws IOException
+ {
+ int reference_kind = in.readUnsignedByte();
+ int reference_index = in.readUnsignedShort();
+ pool[poolIndex] = new Assoc(tag, reference_kind, reference_index);
+ }
+
+ private void invokeDynamic(DataInput in, int poolIndex, CONSTANT tag) throws IOException
+ {
+ int bootstrap_method_attr_index = in.readUnsignedShort();
+ int name_and_type_index = in.readUnsignedShort();
+ pool[poolIndex] = new Assoc(tag, bootstrap_method_attr_index, name_and_type_index);
+ }
+
+ private void ref(DataInput in, int poolIndex) throws IOException
+ {
+ int class_index = in.readUnsignedShort();
+ int name_and_type_index = in.readUnsignedShort();
+ pool[poolIndex] = new Assoc(Clazz.CONSTANT.Methodref, class_index, name_and_type_index);
+ }
+
+ private void constantString(DataInput in, int poolIndex) throws IOException
+ {
+ int string_index = in.readUnsignedShort();
+ intPool[poolIndex] = string_index;
+ }
+
+ private void constantClass(DataInput in, int poolIndex) throws IOException
+ {
+ int class_index = in.readUnsignedShort();
+ intPool[poolIndex] = class_index;
+ ClassConstant c = new ClassConstant(class_index);
+ pool[poolIndex] = c;
+ }
+
+ private void constantDouble(DataInput in, int poolIndex) throws IOException
+ {
+ in.skipBytes(8);
+ }
+
+ private void constantLong(DataInput in, int poolIndex) throws IOException
+ {
+ in.skipBytes(8);
+ }
+
+ private void constantUtf8(DataInput in, int poolIndex) throws IOException
+ {
+ // CONSTANT_Utf8
+
+ String name = in.readUTF();
+ pool[poolIndex] = name;
+ }
+
+ private int findMethodReference(String clazz, String methodname, String descriptor)
+ {
+ for (int i = 1; i < pool.length; i++)
+ {
+ if (pool[i] instanceof Assoc)
+ {
+ Assoc methodref = (Assoc) pool[i];
+ if (methodref.tag == CONSTANT.Methodref)
+ {
+ // Method ref
+ int class_index = methodref.a;
+ int class_name_index = intPool[class_index];
+ if (clazz.equals(pool[class_name_index]))
+ {
+ int name_and_type_index = methodref.b;
+ Assoc name_and_type = (Assoc) pool[name_and_type_index];
+ if (name_and_type.tag == CONSTANT.NameAndType)
+ {
+ // Name and Type
+ int name_index = name_and_type.a;
+ int type_index = name_and_type.b;
+ if (methodname.equals(pool[name_index]))
+ {
+ if (descriptor.equals(pool[type_index]))
+ {
+ return i;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return -1;
+ }
+
+ private void doAttributes(DataInput in, ElementType member, boolean crawl, int access_flags) throws Exception
+ {
+ int attributesCount = in.readUnsignedShort();
+ for (int j = 0; j < attributesCount; j++)
+ {
+ // skip name CONSTANT_Utf8 pointer
+ doAttribute(in, member, crawl, access_flags);
+ }
+ }
+
+ private static long getUnsignedInt(int x)
+ {
+ return x & 0x00000000ffffffffL;
+ }
+
+ private static int getUnsingedByte(byte b)
+ {
+ return b & 0xFF;
+ }
+
+ private static int getUnsingedShort(short s)
+ {
+ return s & 0xFFFF;
+ }
+
+ private void doAttribute(DataInput in, ElementType member, boolean crawl, int access_flags) throws Exception
+ {
+ final int attribute_name_index = in.readUnsignedShort();
+ final String attributeName = (String) pool[attribute_name_index];
+ final long attribute_length = getUnsignedInt(in.readInt());
+ if (attributeName.equals("Deprecated"))
+ {
+ }
+ else if (attributeName.equals("RuntimeVisibleAnnotations"))
+ {
+ doAnnotations(in, member, RetentionPolicy.RUNTIME, access_flags);
+
+ }
+ else if (attributeName.equals("RuntimeInvisibleAnnotations"))
+ {
+ doAnnotations(in, member, RetentionPolicy.CLASS, access_flags);
+
+ }
+ else if (attributeName.equals("RuntimeVisibleParameterAnnotations"))
+ {
+ doParameterAnnotations(in, member, RetentionPolicy.RUNTIME, access_flags);
+
+ }
+ else if (attributeName.equals("RuntimeInvisibleParameterAnnotations"))
+ {
+ doParameterAnnotations(in, member, RetentionPolicy.CLASS, access_flags);
+
+ }
+ else if (attributeName.equals("RuntimeVisibleTypeAnnotations"))
+ {
+ doTypeAnnotations(in, member, RetentionPolicy.RUNTIME, access_flags);
+
+ }
+ else if (attributeName.equals("RuntimeInvisibleTypeAnnotations"))
+ {
+ doTypeAnnotations(in, member, RetentionPolicy.CLASS, access_flags);
+
+ }
+ else if (attributeName.equals("InnerClasses"))
+ {
+ doInnerClasses(in);
+
+ }
+ else if (attributeName.equals("EnclosingMethod"))
+ {
+ doEnclosingMethod(in);
+
+ }
+ else if (attributeName.equals("SourceFile"))
+ {
+ doSourceFile(in);
+
+ }
+ else if (attributeName.equals("Code"))
+ {
+ doCode(in, crawl);
+
+ }
+ else if (attributeName.equals("Signature"))
+ {
+ doSignature(in, member, access_flags);
+
+ }
+ else if (attributeName.equals("ConstantValue"))
+ {
+ doConstantValue(in);
+
+ }
+ else if (attributeName.equals("AnnotationDefault"))
+ {
+ doElementValue(in, member, RetentionPolicy.RUNTIME, access_flags);
+ }
+ else if (attributeName.equals("Exceptions"))
+ {
+ doExceptions(in, access_flags);
+
+ }
+ else if (attributeName.equals("BootstrapMethods"))
+ {
+ doBootstrapMethods(in);
+
+ }
+ else if (attributeName.equals("StackMapTable"))
+ {
+ doStackMapTable(in);
+
+ }
+ else
+ {
+ if (attribute_length > 0x7FFFFFFF)
+ {
+ throw new IllegalArgumentException("Attribute > 2Gb");
+ }
+ in.skipBytes((int) attribute_length);
+
+ }
+ }
+
+ private void doEnclosingMethod(DataInput in) throws IOException
+ {
+ int cIndex = in.readUnsignedShort();
+ int mIndex = in.readUnsignedShort();
+ classConstRef(cIndex);
+ }
+
+ private void doInnerClasses(DataInput in) throws Exception
+ {
+ int number_of_classes = in.readUnsignedShort();
+ for (int i = 0; i < number_of_classes; i++)
+ {
+ int inner_class_info_index = in.readUnsignedShort();
+ int outer_class_info_index = in.readUnsignedShort();
+ int inner_name_index = in.readUnsignedShort();
+ int inner_class_access_flags = in.readUnsignedShort();
+ }
+ }
+
+ void doSignature(DataInput in, ElementType member, int access_flags) throws IOException
+ {
+ int signature_index = in.readUnsignedShort();
+ String signature = (String) pool[signature_index];
+ try
+ {
+
+ parseDescriptor(signature, access_flags);
+ if (last != null)
+ {
+ last.signature = signature;
+ }
+
+ if (member == ElementType.TYPE)
+ {
+ classSignature = signature;
+ }
+
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("Signature failed for " + signature, e);
+ }
+ }
+
+ void doConstantValue(DataInput in) throws IOException
+ {
+ int constantValue_index = in.readUnsignedShort();
+ }
+
+ void doExceptions(DataInput in, int access_flags) throws IOException
+ {
+ int exception_count = in.readUnsignedShort();
+ for (int i = 0; i < exception_count; i++)
+ {
+ int index = in.readUnsignedShort();
+ ClassConstant cc = (ClassConstant) pool[index];
+ TypeRef clazz = classParser.getTypeRef(cc.getName());
+ referTo(clazz, access_flags);
+ }
+ }
+
+ private void doCode(DataInput in, boolean crawl) throws Exception
+ {
+ /* int max_stack = */
+ in.readUnsignedShort();
+ /* int max_locals = */
+ in.readUnsignedShort();
+ int code_length = in.readInt();
+ byte code[] = new byte[code_length];
+ in.readFully(code, 0, code_length);
+ if (crawl)
+ {
+ crawl(code);
+ }
+ int exception_table_length = in.readUnsignedShort();
+ for (int i = 0; i < exception_table_length; i++)
+ {
+ int start_pc = in.readUnsignedShort();
+ int end_pc = in.readUnsignedShort();
+ int handler_pc = in.readUnsignedShort();
+ int catch_type = in.readUnsignedShort();
+ classConstRef(catch_type);
+ }
+ doAttributes(in, ElementType.METHOD, false, 0);
+ }
+
+ private void crawl(byte[] code)
+ {
+ ByteBuffer bb = ByteBuffer.wrap(code);
+ int lastReference = -1;
+
+ while (bb.remaining() > 0)
+ {
+ int instruction = getUnsingedByte(bb.get());
+ switch (instruction)
+ {
+ case ldc:
+ lastReference = getUnsingedByte(bb.get());
+ classConstRef(lastReference);
+ break;
+
+ case ldc_w:
+ lastReference = getUnsingedShort(bb.getShort());
+ classConstRef(lastReference);
+ break;
+
+ case anewarray:
+ case checkcast:
+ case instanceof_:
+ case new_:
+ {
+ int cref = getUnsingedShort(bb.getShort());
+ classConstRef(cref);
+ lastReference = -1;
+ break;
+ }
+
+ case multianewarray:
+ {
+ int cref = getUnsingedShort(bb.getShort());
+ classConstRef(cref);
+ bb.get();
+ lastReference = -1;
+ break;
+ }
+
+ case invokespecial:
+ {
+ int mref = getUnsingedShort(bb.getShort());
+ break;
+ }
+
+ case invokevirtual:
+ {
+ int mref = getUnsingedShort(bb.getShort());
+ break;
+ }
+
+ case invokeinterface:
+ {
+ int mref = getUnsingedShort(bb.getShort());
+ bb.get(); // read past the 'count' operand
+ bb.get(); // read past the reserved space for future operand
+ break;
+ }
+
+ case invokestatic:
+ {
+ int methodref = getUnsingedShort(bb.getShort());
+
+ if ((methodref == forName || methodref == class$) && lastReference != -1
+ && pool[intPool[lastReference]] instanceof String)
+ {
+ String fqn = (String) pool[intPool[lastReference]];
+ if (!fqn.equals("class") && fqn.indexOf('.') > 0)
+ {
+ TypeRef clazz = classParser.getTypeRefFromFQN(fqn);
+ referTo(clazz, 0);
+ }
+ lastReference = -1;
+ }
+ break;
+ }
+
+ /*
+ * 3/5: opcode, indexbyte1, indexbyte2 or iinc, indexbyte1,
+ * indexbyte2, countbyte1, countbyte2
+ */
+ case wide:
+ int opcode = getUnsingedByte(bb.get());
+ bb.getShort(); // at least 3 bytes
+ if (opcode == iinc)
+ {
+ bb.getShort();
+ }
+ break;
+
+ case tableswitch:
+ // Skip to place divisible by 4
+ while ((bb.position() & 0x3) != 0)
+ {
+ bb.get();
+ }
+ /* int deflt = */
+ bb.getInt();
+ int low = bb.getInt();
+ int high = bb.getInt();
+ bb.position(bb.position() + (high - low + 1) * 4);
+ lastReference = -1;
+ break;
+
+ case lookupswitch:
+ // Skip to place divisible by 4
+ while ((bb.position() & 0x3) != 0)
+ {
+ int n = bb.get();
+ assert n == 0; // x
+ }
+ /* deflt = */
+ int deflt = bb.getInt();
+ int npairs = bb.getInt();
+ bb.position(bb.position() + npairs * 8);
+ lastReference = -1;
+ break;
+
+ default:
+ lastReference = -1;
+ bb.position(bb.position() + OFFSETS[instruction]);
+ }
+ }
+ }
+
+ private void doSourceFile(DataInput in) throws IOException
+ {
+ int sourcefile_index = in.readUnsignedShort();
+ }
+
+ private void doParameterAnnotations(DataInput in, ElementType member, RetentionPolicy policy, int access_flags)
+ throws Exception
+ {
+ int num_parameters = in.readUnsignedByte();
+ for (int p = 0; p < num_parameters; p++)
+ {
+ doAnnotations(in, member, policy, access_flags);
+ }
+ }
+
+ private void doTypeAnnotations(DataInput in, ElementType member, RetentionPolicy policy, int access_flags)
+ throws Exception
+ {
+ int num_annotations = in.readUnsignedShort();
+ for (int p = 0; p < num_annotations; p++)
+ {
+
+ // type_annotation {
+ // u1 target_type;
+ // union {
+ // type_parameter_target;
+ // supertype_target;
+ // type_parameter_bound_target;
+ // empty_target;
+ // method_formal_parameter_target;
+ // throws_target;
+ // localvar_target;
+ // catch_target;
+ // offset_target;
+ // type_argument_target;
+ // } target_info;
+ // type_path target_path;
+ // u2 type_index;
+ // u2 num_element_value_pairs;
+ // { u2 element_name_index;
+ // element_value value;
+ // } element_value_pairs[num_element_value_pairs];
+ // }
+
+ // Table 4.7.20-A. Interpretation of target_type values (Part 1)
+
+ int target_type = in.readUnsignedByte();
+ switch (target_type)
+ {
+ case 0x00: // type parameter declaration of generic class or
+ // interface
+ case 0x01: // type parameter declaration of generic method or
+ // constructor
+ //
+ // type_parameter_target {
+ // u1 type_parameter_index;
+ // }
+ in.skipBytes(1);
+ break;
+
+ case 0x10: // type in extends clause of class or interface
+ // declaration (including the direct superclass of
+ // an anonymous class declaration), or in implements
+ // clause of interface declaration
+ // supertype_target {
+ // u2 supertype_index;
+ // }
+
+ in.skipBytes(2);
+ break;
+
+ case 0x11: // type in bound of type parameter declaration of
+ // generic class or interface
+ case 0x12: // type in bound of type parameter declaration of
+ // generic method or constructor
+ // type_parameter_bound_target {
+ // u1 type_parameter_index;
+ // u1 bound_index;
+ // }
+ in.skipBytes(2);
+ break;
+
+ case 0x13: // type in field declaration
+ case 0x14: // return type of method, or type of newly
+ // constructed object
+ case 0x15: // receiver type of method or constructor
+ break;
+
+ case 0x16: // type in formal parameter declaration of method,
+ // constructor, or lambda expression
+ // formal_parameter_target {
+ // u1 formal_parameter_index;
+ // }
+ in.skipBytes(1);
+ break;
+
+ case 0x17: // type in throws clause of method or constructor
+ // throws_target {
+ // u2 throws_type_index;
+ // }
+ in.skipBytes(2);
+ break;
+
+ case 0x40: // type in local variable declaration
+ case 0x41: // type in resource variable declaration
+ // localvar_target {
+ // u2 table_length;
+ // { u2 start_pc;
+ // u2 length;
+ // u2 index;
+ // } table[table_length];
+ // }
+ int table_length = in.readUnsignedShort();
+ in.skipBytes(table_length * 6);
+ break;
+
+ case 0x42: // type in exception parameter declaration
+ // catch_target {
+ // u2 exception_table_index;
+ // }
+ in.skipBytes(2);
+ break;
+
+ case 0x43: // type in instanceof expression
+ case 0x44: // type in new expression
+ case 0x45: // type in method reference expression using ::new
+ case 0x46: // type in method reference expression using
+ // ::Identifier
+ // offset_target {
+ // u2 offset;
+ // }
+ in.skipBytes(2);
+ break;
+
+ case 0x47: // type in cast expression
+ case 0x48: // type argument for generic constructor in new
+ // expression or explicit constructor invocation
+ // statement
+
+ case 0x49: // type argument for generic method in method
+ // invocation expression
+ case 0x4A: // type argument for generic constructor in method
+ // reference expression using ::new
+ case 0x4B: // type argument for generic method in method
+ // reference expression using ::Identifier
+ // type_argument_target {
+ // u2 offset;
+ // u1 type_argument_index;
+ // }
+ in.skipBytes(3);
+ break;
+
+ }
+
+ // The value of the target_path item denotes precisely which part of
+ // the type indicated by target_info is annotated. The format of the
+ // type_path structure is specified in §4.7.20.2.
+ //
+ // type_path {
+ // u1 path_length;
+ // { u1 type_path_kind;
+ // u1 type_argument_index;
+ // } path[path_length];
+ // }
+
+ int path_length = in.readUnsignedByte();
+ in.skipBytes(path_length * 2);
+
+ //
+ // Rest is identical to the normal annotations
+ doAnnotation(in, member, policy, access_flags);
+ }
+ }
+
+ private void doAnnotations(DataInput in, ElementType member, RetentionPolicy policy, int access_flags)
+ throws Exception
+ {
+ int num_annotations = in.readUnsignedShort(); // # of annotations
+ for (int a = 0; a < num_annotations; a++)
+ {
+ doAnnotation(in, member, policy, access_flags);
+ }
+ }
+
+ // annotation {
+ // u2 type_index;
+ // u2 num_element_value_pairs; {
+ // u2 element_name_index;
+ // element_value value;
+ // }
+ // element_value_pairs[num_element_value_pairs];
+ // }
+
+ private void doAnnotation(DataInput in, ElementType member, RetentionPolicy policy, int access_flags) throws IOException
+ {
+ int type_index = in.readUnsignedShort();
+
+ String typeName = (String) pool[type_index];
+ if (typeName != null)
+ {
+ if (policy == RetentionPolicy.RUNTIME)
+ {
+ referTo(type_index, 0);
+ }
+ }
+ int num_element_value_pairs = in.readUnsignedShort();
+
+ for (int v = 0; v < num_element_value_pairs; v++)
+ {
+ in.readUnsignedShort();
+ doElementValue(in, member, policy, access_flags);
+ }
+ }
+
+ private Object doElementValue(DataInput in, ElementType member, RetentionPolicy policy, int access_flags) throws IOException
+ {
+ char tag = (char) in.readUnsignedByte();
+ switch (tag)
+ {
+ case 'B': // Byte
+ case 'C': // Character
+ case 'I': // Integer
+ case 'S': // Short
+ int const_value_index = in.readUnsignedShort();
+ return intPool[const_value_index];
+
+ case 'D': // Double
+ case 'F': // Float
+ case 's': // String
+ case 'J': // Long
+ const_value_index = in.readUnsignedShort();
+ return pool[const_value_index];
+
+ case 'Z': // Boolean
+ const_value_index = in.readUnsignedShort();
+ return pool[const_value_index] == null || pool[const_value_index].equals(0) ? false : true;
+
+ case 'e': // enum constant
+ int type_name_index = in.readUnsignedShort();
+ if (policy == RetentionPolicy.RUNTIME)
+ {
+ referTo(type_name_index, 0);
+ }
+ int const_name_index = in.readUnsignedShort();
+ return pool[const_name_index];
+
+ case 'c': // Class
+ int class_info_index = in.readUnsignedShort();
+ TypeRef name = classParser.getTypeRef((String) pool[class_info_index]);
+ if (policy == RetentionPolicy.RUNTIME)
+ {
+ referTo(class_info_index, 0);
+ }
+ return name;
+
+ case '@': // Annotation type
+ doAnnotation(in, member, policy, access_flags);
+
+ case '[': // Array
+ int num_values = in.readUnsignedShort();
+ Object[] result = new Object[num_values];
+ for (int i = 0; i < num_values; i++)
+ {
+ result[i] = doElementValue(in, member, policy, access_flags);
+ }
+ return result;
+
+ default:
+ throw new IllegalArgumentException("Invalid value for Annotation ElementValue tag " + tag);
+ }
+ }
+
+ /*
+ * We don't currently process BootstrapMethods. We walk the data structure
+ * to consume the attribute.
+ */
+ private void doBootstrapMethods(DataInput in) throws IOException
+ {
+ final int num_bootstrap_methods = in.readUnsignedShort();
+ for (int v = 0; v < num_bootstrap_methods; v++)
+ {
+ final int bootstrap_method_ref = in.readUnsignedShort();
+ final int num_bootstrap_arguments = in.readUnsignedShort();
+ for (int a = 0; a < num_bootstrap_arguments; a++)
+ {
+ final int bootstrap_argument = in.readUnsignedShort();
+ }
+ }
+ }
+
+ /*
+ * The verifier can require access to types only referenced in StackMapTable
+ * attributes.
+ */
+ private void doStackMapTable(DataInput in) throws IOException
+ {
+ final int number_of_entries = in.readUnsignedShort();
+ for (int v = 0; v < number_of_entries; v++)
+ {
+ final int frame_type = in.readUnsignedByte();
+ if (frame_type <= 63)
+ { // same_frame
+ // nothing else to do
+ }
+ else if (frame_type <= 127)
+ { // same_locals_1_stack_item_frame
+ verification_type_info(in);
+ }
+ else if (frame_type <= 246)
+ { // RESERVED
+ // nothing else to do
+ }
+ else if (frame_type <= 247)
+ { // same_locals_1_stack_item_frame_extended
+ final int offset_delta = in.readUnsignedShort();
+ verification_type_info(in);
+ }
+ else if (frame_type <= 250)
+ { // chop_frame
+ final int offset_delta = in.readUnsignedShort();
+ }
+ else if (frame_type <= 251)
+ { // same_frame_extended
+ final int offset_delta = in.readUnsignedShort();
+ }
+ else if (frame_type <= 254)
+ { // append_frame
+ final int offset_delta = in.readUnsignedShort();
+ final int number_of_locals = frame_type - 251;
+ for (int n = 0; n < number_of_locals; n++)
+ {
+ verification_type_info(in);
+ }
+ }
+ else if (frame_type <= 255)
+ { // full_frame
+ final int offset_delta = in.readUnsignedShort();
+ final int number_of_locals = in.readUnsignedShort();
+ for (int n = 0; n < number_of_locals; n++)
+ {
+ verification_type_info(in);
+ }
+ final int number_of_stack_items = in.readUnsignedShort();
+ for (int n = 0; n < number_of_stack_items; n++)
+ {
+ verification_type_info(in);
+ }
+ }
+ }
+ }
+
+ private void verification_type_info(DataInput in) throws IOException
+ {
+ final int tag = in.readUnsignedByte();
+ switch (tag)
+ {
+ case 7:// Object_variable_info
+ final int cpool_index = in.readUnsignedShort();
+ classConstRef(cpool_index);
+ break;
+ case 8:// ITEM_Uninitialized
+ final int offset = in.readUnsignedShort();
+ break;
+ }
+ }
+
+ void referTo(TypeRef typeRef, int modifiers)
+ {
+ if (typeRef.isPrimitive())
+ {
+ return;
+ }
+
+ PackageRef packageRef = typeRef.getPackageRef();
+ if (packageRef.isPrimitivePackage())
+ {
+ return;
+ }
+
+ imports.add(packageRef.getFQN());
+ }
+
+ void referTo(int index, int modifiers)
+ {
+ String descriptor = (String) pool[index];
+ parseDescriptor(descriptor, modifiers);
+ }
+
+ /*
+ * This method parses a descriptor and adds the package of the descriptor to
+ * the referenced packages. The syntax of the descriptor is:
+ *
+ * <pre>
+ * descriptor ::= ( '(' reference * ')' )? reference reference ::= 'L'
+ * classname ( '<' references '>' )? ';' | 'B' | 'Z' | ... | '+' | '-'
+ * | '['
+ * </pre>
+ *
+ * This methods uses heavy recursion to parse the descriptor and a roving
+ * pointer to limit the creation of string objects.
+ *
+ * @param descriptor The to be parsed descriptor
+ * @param modifiers
+ */
+
+ public void parseDescriptor(String descriptor, int modifiers)
+ {
+ // Some classParser are weird, they start with a generic
+ // declaration that contains ':', not sure what they mean ...
+ int rover = 0;
+ if (descriptor.charAt(0) == '<')
+ {
+ rover = parseFormalTypeParameters(descriptor, rover, modifiers);
+ }
+
+ if (descriptor.charAt(rover) == '(')
+ {
+ rover = parseReferences(descriptor, rover + 1, ')', modifiers);
+ rover++;
+ }
+ parseReferences(descriptor, rover, (char) 0, modifiers);
+ }
+
+ /*
+ * Parse a sequence of references. A sequence ends with a given character or
+ * when the string ends.
+ *
+ * @param descriptor The whole descriptor.
+ * @param rover The index in the descriptor
+ * @param delimiter The end character or 0
+ * @return the last index processed, one character after the delimeter
+ */
+ int parseReferences(String descriptor, int rover, char delimiter, int modifiers)
+ {
+ int r = rover;
+ while (r < descriptor.length() && descriptor.charAt(r) != delimiter)
+ {
+ r = parseReference(descriptor, r, modifiers);
+ }
+ return r;
+ }
+
+ /*
+ * Parse a single reference. This can be a single character or an object
+ * reference when it starts with 'L'.
+ *
+ * @param descriptor The descriptor
+ * @param rover The place to start
+ * @return The return index after the reference
+ */
+ int parseReference(String descriptor, int rover, int modifiers)
+ {
+ int r = rover;
+ char c = descriptor.charAt(r);
+ while (c == '[')
+ {
+ c = descriptor.charAt(++r);
+ }
+
+ if (c == '<')
+ {
+ r = parseReferences(descriptor, r + 1, '>', modifiers);
+ }
+ else if (c == 'T')
+ {
+ // Type variable name
+ r++;
+ while (descriptor.charAt(r) != ';')
+ {
+ r++;
+ }
+ }
+ else if (c == 'L')
+ {
+ StringBuilder sb = new StringBuilder();
+ r++;
+ while ((c = descriptor.charAt(r)) != ';')
+ {
+ if (c == '<')
+ {
+ r = parseReferences(descriptor, r + 1, '>', modifiers);
+ }
+ else
+ {
+ sb.append(c);
+ }
+ r++;
+ }
+ TypeRef ref = classParser.getTypeRef(sb.toString());
+
+ referTo(ref, modifiers);
+ }
+ else
+ {
+ if ("+-*BCDFIJSZV".indexOf(c) < 0)
+ {
+ ;// System.err.println("Should not skip: " + c);
+ }
+ }
+
+ // this skips a lot of characters
+ // [, *, +, -, B, etc.
+
+ return r + 1;
+ }
+
+ /*
+ * FormalTypeParameters
+ *
+ * @param descriptor
+ * @param index
+ */
+ private int parseFormalTypeParameters(String descriptor, int index, int modifiers)
+ {
+ index++;
+ while (descriptor.charAt(index) != '>')
+ {
+ // Skip IDENTIFIER
+ index = descriptor.indexOf(':', index) + 1;
+ if (index == 0)
+ {
+ throw new IllegalArgumentException("Expected ClassBound or InterfaceBounds: " + descriptor);
+ }
+
+ // ClassBound? InterfaceBounds
+ char c = descriptor.charAt(index);
+
+ if (c != ':')
+ {
+ // ClassBound?
+ index = parseReference(descriptor, index, modifiers);
+ c = descriptor.charAt(index);
+ }
+
+ // InterfaceBounds*
+ while (c == ':')
+ {
+ index++;
+ index = parseReference(descriptor, index, modifiers);
+ c = descriptor.charAt(index);
+ } // for each interface
+
+ } // for each formal parameter
+ return index + 1; // skip >
+ }
+
+ public Set<String> getReferred()
+ {
+ return imports;
+ }
+
+ /*
+ * .class construct for different compilers sun 1.1 Detect static variable
+ * class$com$acme$MyClass 1.2 " 1.3 " 1.4 " 1.5 ldc_w (class) 1.6 " eclipse
+ * 1.1 class$0, ldc (string), invokestatic Class.forName 1.2 " 1.3 " 1.5 ldc
+ * (class) 1.6 " 1.5 and later is not an issue, sun pre 1.5 is easy to
+ * detect the static variable that decodes the class name. For eclipse, the
+ * class$0 gives away we have a reference encoded in a string.
+ * compilerversions/compilerversions.jar contains test versions of all
+ * versions/compilers.
+ */
+
+ public void reset()
+ {
+ if (--depth == 0)
+ {
+ pool = null;
+ intPool = null;
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ if (className != null)
+ {
+ return className.getFQN();
+ }
+ return super.toString();
+ }
+
+
+ public boolean isModule()
+ {
+ return (ACC_MODULE & accessx) != 0;
+ }
+
+ private void classConstRef(int lastReference)
+ {
+ Object o = pool[lastReference];
+ if (o == null)
+ {
+ return;
+ }
+
+ if (o instanceof ClassConstant)
+ {
+ ClassConstant cc = (ClassConstant) o;
+ if (cc.referred)
+ {
+ return;
+ }
+ cc.referred = true;
+ String name = cc.getName();
+ if (name != null)
+ {
+ TypeRef tr = classParser.getTypeRef(name);
+ referTo(tr, 0);
+ }
+ }
+
+ }
+
+
+ // the stack
+ final static short bipush = 0x10; // byte ? value
+ // pushes a
+ // byte
+ // onto the stack as an integer
+ // value
+ final static short sipush = 0x11; // byte1, byte2 ?
+ // value
+ // pushes a
+ // signed integer (byte1 << 8 +
+ // byte2) onto the stack
+ final static short ldc = 0x12; // index ? value
+ // pushes
+ // a
+ // constant #index from a
+ // constant pool (String, int,
+ // float or class type) onto the
+ // stack
+ final static short ldc_w = 0x13; // indexbyte1,
+ // indexbyte2 ?
+ // value pushes a constant
+ // #index from a constant pool
+ // (String, int, float or class
+ // type) onto the stack (wide
+ // index is constructed as
+ // indexbyte1 << 8 + indexbyte2)
+ final static short ldc2_w = 0x14; // indexbyte1,
+ // indexbyte2 ?
+ // value pushes a constant
+ // #index from a constant pool
+ // (double or long) onto the
+ // stack (wide index is
+ // constructed as indexbyte1 <<
+ // 8 + indexbyte2)
+ final static short iload = 0x15; // index ? value
+ // loads
+ // an int
+ // value from a variable #index
+ final static short lload = 0x16; // index ? value
+ // load a
+ // long
+ // value from a local variable
+ // #index
+ final static short fload = 0x17; // index ? value
+ // loads a
+ // float
+ // value from a local variable
+ // #index
+ final static short dload = 0x18; // index ? value
+ // loads a
+ // double
+ // value from a local variable
+ // #index
+ final static short aload = 0x19; // index ? objectref
+ // loads a
+ // reference onto the stack from
+ // short from array
+ final static short istore = 0x36; // index value ?
+ // store
+ // int value
+ // into variable #index
+ final static short lstore = 0x37; // index value ?
+ // store a
+ // long
+ // value in a local variable
+ // #index
+ final static short fstore = 0x38; // index value ?
+ // stores
+ // a float
+ // value into a local variable
+ // #index
+ final static short dstore = 0x39; // index value ?
+ // stores
+ // a double
+ // longs
+ final static short iinc = 0x84; // index, const [No
+ // change]
+ // increment local variable
+ // compares two doubles
+ final static short ifeq = 0x99; // branchbyte1,
+ // branchbyte2
+ // a long from an array
+ final static short astore = 0x3a; // index objectref ?
+ // stores a
+ // reference into a local
+ // double to a long
+ final static short ifne = 0x9a; // branchbyte1,
+ // branchbyte2
+ // value ? if value is not 0,
+ // branch to instruction at
+ // branchoffset (signed short
+ // constructed from unsigned
+ // bytes branchbyte1 << 8 +
+ // branchbyte2)
+ final static short iflt = 0x9b; // branchbyte1,
+ // branchbyte2
+ // value ? if value is less than
+ // 0, branch to instruction at
+ // branchoffset (signed short
+ // constructed from unsigned
+ // bytes branchbyte1 << 8 +
+ // branchbyte2)
+ final static short ifge = 0x9c; // branchbyte1,
+ // branchbyte2
+ // value ? if value is greater
+ // than or equal to 0, branch to
+ // instruction at branchoffset
+ // (signed short constructed
+ // from unsigned bytes
+ // branchbyte1 << 8 +
+ // branchbyte2)
+ final static short ifgt = 0x9d; // branchbyte1,
+ // branchbyte2
+ // value ? if value is greater
+ // than 0, branch to instruction
+ // at branchoffset (signed short
+ // constructed from unsigned
+ // bytes branchbyte1 << 8 +
+ // branchbyte2)
+ final static short ifle = 0x9e; // branchbyte1,
+ // branchbyte2
+ // value ? if value is less than
+ // or equal to 0, branch to
+ // instruction at branchoffset
+ // (signed short constructed
+ // from unsigned bytes
+ // branchbyte1 << 8 +
+ // branchbyte2)
+ final static short if_icmpeq = 0x9f; // branchbyte1,
+ // branchbyte2
+ // value1, value2 ? if ints are
+ // equal, branch to instruction
+ // at branchoffset (signed short
+ // constructed from unsigned
+ // bytes branchbyte1 << 8 +
+ // branchbyte2)
+ final static short if_icmpne = 0xa0; // branchbyte1,
+ // branchbyte2
+ // value1, value2 ? if ints are
+ // not equal, branch to
+ // instruction at branchoffset
+ // (signed short constructed
+ // from unsigned bytes
+ // branchbyte1 << 8 +
+ // branchbyte2)
+ final static short if_icmplt = 0xa1; // branchbyte1,
+ // branchbyte2
+ // value1, value2 ? if value1 is
+ // less than value2, branch to
+ // instruction at branchoffset
+ // (signed short constructed
+ // from unsigned bytes
+ // branchbyte1 << 8 +
+ // branchbyte2)
+ final static short if_icmpge = 0xa2; // branchbyte1,
+ // branchbyte2
+ // value1, value2 ? if value1 is
+ // greater than or equal to
+ // value2, branch to instruction
+ // at branchoffset (signed short
+ // constructed from unsigned
+ // bytes branchbyte1 << 8 +
+ // branchbyte2)
+ final static short if_icmpgt = 0xa3; // branchbyte1,
+ // branchbyte2
+ // value1, value2 ? if value1 is
+ // greater than value2, branch
+ // to instruction at
+ // branchoffset (signed short
+ // constructed from unsigned
+ // bytes branchbyte1 << 8 +
+ // branchbyte2)
+ final static short if_icmple = 0xa4; // branchbyte1,
+ // branchbyte2
+ // value1, value2 ? if value1 is
+ // less than or equal to value2,
+ // branch to instruction at
+ // branchoffset (signed short
+ // constructed from unsigned
+ // bytes branchbyte1 << 8 +
+ // branchbyte2)
+ final static short if_acmpeq = 0xa5; // branchbyte1,
+ // branchbyte2
+ // value1, value2 ? if
+ // references are equal, branch
+ // to instruction at
+ // branchoffset (signed short
+ // constructed from unsigned
+ // bytes branchbyte1 << 8 +
+ // branchbyte2)
+ final static short if_acmpne = 0xa6; // branchbyte1,
+ // branchbyte2
+ // value1, value2 ? if
+ // references are not equal,
+ // branch to instruction at
+ // branchoffset (signed short
+ // constructed from unsigned
+ // bytes branchbyte1 << 8 +
+ // branchbyte2)
+ final static short goto_ = 0xa7; // branchbyte1,
+ // branchbyte2 [no
+ // change] goes to another
+ // instruction at branchoffset
+ // (signed short constructed
+ // from unsigned bytes
+ // branchbyte1 << 8 +
+ // branchbyte2)
+ final static short jsr = 0xa8; // branchbyte1,
+ // branchbyte2 ?
+ // address jump to subroutine at
+ // branchoffset (signed short
+ // constructed from unsigned
+ // bytes branchbyte1 << 8 +
+ // branchbyte2) and place the
+ // return address on the stack
+ final static short ret = 0xa9; // index [No change]
+ // continue
+ // execution from address taken
+ // from a local variable #index
+ // (the asymmetry with jsr is
+ // intentional)
+ final static short tableswitch = 0xaa; // [0-3 bytes
+ // padding],
+ // defaultbyte1, defaultbyte2,
+ // defaultbyte3, defaultbyte4,
+ // lowbyte1, lowbyte2, lowbyte3,
+ // lowbyte4, highbyte1,
+ // highbyte2, highbyte3,
+ // highbyte4, jump offsets...
+ // index ? continue execution
+ // from an address in the table
+ // at offset index
+ final static short lookupswitch = 0xab; // <0-3 bytes
+ // padding>,
+ // defaultbyte1, defaultbyte2,
+ // from
+ // method
+ final static short getstatic = 0xb2; // index1, index2 ?
+ // value gets a
+ // static field value of a
+ // class, where the field is
+ // identified by field reference
+ // in the constant pool index
+ // (index1 << 8 + index2)
+ final static short putstatic = 0xb3; // indexbyte1,
+ // indexbyte2 value
+ // ? set static field to value
+ // in a class, where the field
+ // is identified by a field
+ // reference index in constant
+ // pool (indexbyte1 << 8 +
+ // indexbyte2)
+ final static short getfield = 0xb4; // index1, index2
+ // objectref ?
+ // value gets a field value of
+ // an object objectref, where
+ // the field is identified by
+ // field reference in the
+ // constant pool index (index1
+ // << 8 + index2)
+ final static short putfield = 0xb5; // indexbyte1,
+ // indexbyte2
+ // objectref, value ? set field
+ // to value in an object
+ // objectref, where the field is
+ // identified by a field
+ // reference index in constant
+ // pool (indexbyte1 << 8 +
+ // indexbyte2)
+ final static short invokevirtual = 0xb6; // indexbyte1,
+ // indexbyte2
+ // objectref, [arg1, arg2, ...]
+ // ? invoke virtual method on
+ // object objectref, where the
+ // method is identified by
+ // method reference index in
+ // constant pool (indexbyte1 <<
+ // 8 + indexbyte2)
+ final static short invokespecial = 0xb7; // indexbyte1,
+ // indexbyte2
+ // objectref, [arg1, arg2, ...]
+ // ? invoke instance method on
+ // object objectref, where the
+ // method is identified by
+ // method reference index in
+ // constant pool (indexbyte1 <<
+ // 8 + indexbyte2)
+ final static short invokestatic = 0xb8; // indexbyte1,
+ // indexbyte2 [arg1,
+ // arg2, ...] ? invoke a static
+ // method, where the method is
+ // identified by method
+ // reference index in constant
+ // pool (indexbyte1 << 8 +
+ // indexbyte2)
+ final static short invokeinterface = 0xb9; // indexbyte1,
+ // indexbyte2,
+ // count, 0 objectref, [arg1,
+ // arg2, ...] ? invokes an
+ // interface method on object
+ // objectref, where the
+ // interface method is
+ // identified by method
+ // reference index in constant
+ // pool (indexbyte1 << 8 +
+ // indexbyte2)
+ final static short invokedynamic = 0xba; // introduced in J7
+
+ final static short new_ = 0xbb; // indexbyte1,
+ // indexbyte2 ?
+ // objectref creates new object
+ // of type identified by class
+ // reference in constant pool
+ // index (indexbyte1 << 8 +
+ // indexbyte2)
+ final static short newarray = 0xbc; // atype count ?
+ // arrayref
+ // creates new array with count
+ // elements of primitive type
+ // identified by atype
+ final static short anewarray = 0xbd; // indexbyte1,
+ // indexbyte2 count
+ // objectref throws an error or
+ // exception (notice that the
+ // rest of the stack is cleared,
+ // leaving only a reference to
+ // the Throwable)
+ final static short checkcast = 0xc0; // indexbyte1,
+ // indexbyte2
+ // objectref ? objectref checks
+ // whether an objectref is of a
+ // certain type, the class
+ // reference of which is in the
+ // constant pool at index
+ // (indexbyte1 << 8 +
+ // indexbyte2)
+ final static short instanceof_ = 0xc1; // indexbyte1,
+ // indexbyte2
+ // object ("release the lock" -
+ // end of synchronized()
+ // section)
+ final static short wide = 0xc4; // opcode,
+ // indexbyte1,
+ // indexbyte2
+ final static short multianewarray = 0xc5; // indexbyte1,
+ // indexbyte2,
+ // dimensions count1,
+ // [count2,...] ? arrayref
+ // create a new array of
+ // dimensions dimensions with
+ // elements of type identified
+ // by class reference in
+ // constant pool index
+ // (indexbyte1 << 8 +
+ // indexbyte2); the sizes of
+ // each dimension is identified
+ // by count1, [count2, etc]
+ final static short ifnull = 0xc6; // branchbyte1,
+ // branchbyte2
+ // value ? if value is null,
+ // branch to instruction at
+ // branchoffset (signed short
+ // constructed from unsigned
+ // bytes branchbyte1 << 8 +
+ // branchbyte2)
+ final static short ifnonnull = 0xc7; // branchbyte1,
+ // branchbyte2
+ // value ? if value is not null,
+ // branch to instruction at
+ // branchoffset (signed short
+ // constructed from unsigned
+ // bytes branchbyte1 << 8 +
+ // branchbyte2)
+ final static short goto_w = 0xc8; // branchbyte1,
+ // branchbyte2,
+ // branchbyte3, branchbyte4 [no
+ // change] goes to another
+ // instruction at branchoffset
+ // (signed int constructed from
+ // unsigned bytes branchbyte1 <<
+ // 24 + branchbyte2 << 16 +
+ // branchbyte3 << 8 +
+ // branchbyte4)
+ final static short jsr_w = 0xc9; // branchbyte1,
+ // branchbyte2,
+
+
+ final static byte OFFSETS[] = new byte[256];
+
+ static
+ {
+ OFFSETS[bipush] = 1; // byte ? value pushes a byte onto the
+ // stack as an integer value
+ OFFSETS[sipush] = 2; // byte1, byte2 ? value pushes a signed
+ // integer (byte1 << 8 + byte2) onto the
+ // stack
+ OFFSETS[ldc] = 1; // index ? value pushes a constant
+ // #index from a constant pool (String,
+ // int, float or class type) onto the
+ // stack
+ OFFSETS[ldc_w] = 2; // indexbyte1, indexbyte2 ? value pushes
+ // a constant #index from a constant
+ // pool (String, int, float or class
+ // type) onto the stack (wide index is
+ // constructed as indexbyte1 << 8 +
+ // indexbyte2)
+ OFFSETS[ldc2_w] = 2; // indexbyte1, indexbyte2 ? value pushes
+ // a constant #index from a constant
+ // pool (double or long) onto the stack
+ // (wide index is constructed as
+ // indexbyte1 << 8 + indexbyte2)
+ OFFSETS[iload] = 1; // index ? value loads an int value from
+ // a variable #index
+ OFFSETS[lload] = 1; // index ? value load a long value from
+ // a local variable #index
+ OFFSETS[fload] = 1; // index ? value loads a float value
+ // from a local variable #index
+ OFFSETS[dload] = 1; // index ? value loads a double value
+ // from a local variable #index
+ OFFSETS[aload] = 1; // index ? objectref loads a reference
+ // onto the stack from a local variable
+ // #index
+ OFFSETS[istore] = 1; // index value ? store int value into
+ // variable #index
+ OFFSETS[lstore] = 1; // index value ? store a long value in a
+ // local variable #index
+ OFFSETS[fstore] = 1; // index value ? stores a float value
+ // into a local variable #index
+ OFFSETS[dstore] = 1; // index value ? stores a double value
+ // into a local variable #index
+ OFFSETS[iinc] = 2; // index, const [No change] increment
+ // local variable #index by signed byte
+ // const
+ OFFSETS[ifeq] = 2; // branchbyte1, branchbyte2 value ? if
+ // value is 0, branch to instruction at
+ // branchoffset (signed short
+ // constructed from unsigned bytes
+ // branchbyte1 << 8 + branchbyte2)
+ OFFSETS[astore] = 1; // index objectref ? stores a reference
+ // into a local variable #index
+ OFFSETS[ifne] = 2; // branchbyte1, branchbyte2 value ? if
+ // value is not 0, branch to instruction
+ // at branchoffset (signed short
+ // constructed from unsigned bytes
+ // branchbyte1 << 8 + branchbyte2)
+ OFFSETS[iflt] = 2; // branchbyte1, branchbyte2 value ? if
+ // value is less than 0, branch to
+ // instruction at branchoffset (signed
+ // short constructed from unsigned bytes
+ // branchbyte1 << 8 + branchbyte2)
+ OFFSETS[ifge] = 2; // branchbyte1, branchbyte2 value ? if
+ // value is greater than or equal to 0,
+ // branch to instruction at branchoffset
+ // (signed short constructed from
+ // unsigned bytes branchbyte1 << 8 +
+ // branchbyte2)
+ OFFSETS[ifgt] = 2; // branchbyte1, branchbyte2 value ? if
+ // value is greater than 0, branch to
+ // instruction at branchoffset (signed
+ // short constructed from unsigned bytes
+ // branchbyte1 << 8 + branchbyte2)
+ OFFSETS[ifle] = 2; // branchbyte1, branchbyte2 value ? if
+ // value is less than or equal to 0,
+ // branch to instruction at branchoffset
+ // (signed short constructed from
+ // unsigned bytes branchbyte1 << 8 +
+ // branchbyte2)
+ OFFSETS[if_icmpeq] = 2; // branchbyte1, branchbyte2 value1,
+ // value2 ? if ints are equal,
+ // branch to instruction at
+ // branchoffset (signed short
+ // constructed from unsigned bytes
+ // branchbyte1 << 8 + branchbyte2)
+ OFFSETS[if_icmpne] = 2; // branchbyte1, branchbyte2 value1,
+ // value2 ? if ints are not equal,
+ // branch to instruction at
+ // branchoffset (signed short
+ // constructed from unsigned bytes
+ // branchbyte1 << 8 + branchbyte2)
+ OFFSETS[if_icmplt] = 2; // branchbyte1, branchbyte2 value1,
+ // value2 ? if value1 is less than
+ // value2, branch to instruction at
+ // branchoffset (signed short
+ // constructed from unsigned bytes
+ // branchbyte1 << 8 + branchbyte2)
[... 198 lines stripped ...]