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 ...]