| /* |
| * 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) |
| OFFSETS[if_icmpge] = 2; // 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) |
| OFFSETS[if_icmpgt] = 2; // branchbyte1, branchbyte2 value1, |
| // value2 ? if value1 is greater |
| // than value2, branch to |
| // instruction at branchoffset |
| // (signed short constructed from |
| // unsigned bytes branchbyte1 << 8 + |
| // branchbyte2) |
| OFFSETS[if_icmple] = 2; // 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) |
| OFFSETS[if_acmpeq] = 2; // branchbyte1, branchbyte2 value1, |
| // value2 ? if references are equal, |
| // branch to instruction at |
| // branchoffset (signed short |
| // constructed from unsigned bytes |
| // branchbyte1 << 8 + branchbyte2) |
| OFFSETS[if_acmpne] = 2; // branchbyte1, branchbyte2 value1, |
| // value2 ? if references are not |
| // equal, branch to instruction at |
| // branchoffset (signed short |
| // constructed from unsigned bytes |
| // branchbyte1 << 8 + branchbyte2) |
| OFFSETS[goto_] = 2; // branchbyte1, branchbyte2 [no change] |
| // goes to another instruction at |
| // branchoffset (signed short |
| // constructed from unsigned bytes |
| // branchbyte1 << 8 + branchbyte2) |
| OFFSETS[jsr] = 2; // 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 |
| OFFSETS[ret] = 1; // index [No change] continue execution |
| // from address taken from a local |
| // variable #index (the asymmetry with |
| // jsr is intentional) |
| OFFSETS[tableswitch] = -1; // [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 |
| OFFSETS[lookupswitch] = -1; // <0-3 bytes padding>, |
| // defaultbyte1, defaultbyte2, |
| // defaultbyte3, defaultbyte4, |
| // npairs1, npairs2, npairs3, |
| // npairs4, match-offset |
| // pairs... key ? a target |
| // address is looked up from a |
| // table using a key and |
| // execution continues from the |
| // instruction at that address |
| OFFSETS[getstatic] = 2; // 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) |
| OFFSETS[putstatic] = 2; // 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) |
| OFFSETS[getfield] = 2; // 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) |
| OFFSETS[putfield] = 2; // 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) |
| OFFSETS[invokevirtual] = 2; // 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) |
| OFFSETS[invokespecial] = 2; // 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) |
| OFFSETS[invokestatic] = 2; // indexbyte1, indexbyte2 [arg1, |
| // arg2, ...] ? invoke a static |
| // method, where the method is |
| // identified by method |
| // reference index in constant |
| // pool (indexbyte1 << 8 + |
| // indexbyte2) |
| OFFSETS[invokeinterface] = 2; // 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) |
| |
| OFFSETS[invokedynamic] = 4; // 4: indexbyte1, indexbyte2, 0, 0 |
| |
| OFFSETS[new_] = 2; // indexbyte1, indexbyte2 ? objectref |
| // creates new object of type identified |
| // by class reference in constant pool |
| // index (indexbyte1 << 8 + indexbyte2) |
| OFFSETS[newarray] = 1; // atype count ? arrayref creates |
| // new array with count elements of |
| // primitive type identified by |
| // atype |
| OFFSETS[anewarray] = 2; // indexbyte1, indexbyte2 count ? |
| // arrayref creates a new array of |
| // references of length count and |
| // component type identified by the |
| // class reference index (indexbyte1 |
| // << 8 + indexbyte2) in the |
| // constant pool |
| OFFSETS[checkcast] = 2; // 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) |
| OFFSETS[instanceof_] = 2; // indexbyte1, indexbyte2 objectref |
| // ? result determines if an object |
| // objectref is of a given type, |
| // identified by class reference |
| // index in constant pool |
| // (indexbyte1 << 8 + indexbyte2) |
| OFFSETS[wide] = 3; // opcode, indexbyte1, indexbyte2 |
| OFFSETS[multianewarray] = 3; // 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] |
| OFFSETS[ifnull] = 2; // branchbyte1, branchbyte2 value ? if |
| // value is null, branch to instruction |
| // at branchoffset (signed short |
| // constructed from unsigned bytes |
| // branchbyte1 << 8 + branchbyte2) |
| OFFSETS[ifnonnull] = 2; // branchbyte1, branchbyte2 value ? |
| // if value is not null, branch to |
| // instruction at branchoffset |
| // (signed short constructed from |
| // unsigned bytes branchbyte1 << 8 + |
| // branchbyte2) |
| OFFSETS[goto_w] = 4; // 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) |
| OFFSETS[jsr_w] = 4; // branchbyte1, branchbyte2, |
| // branchbyte3, branchbyte4 ? address |
| // jump to subroutine at branchoffset |
| // (signed int constructed from unsigned |
| // bytes branchbyte1 << 24 + branchbyte2 |
| // << 16 + branchbyte3 << 8 + |
| // branchbyte4) and place the return |
| // address on the stack |
| } |
| } |
| } |