jasm.io.ClassFileReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jasm Show documentation
Show all versions of jasm Show documentation
Jasm is an Assembler / Disassembler for Java Bytecode. Using Jasm
you can easily read or write Java Classfiles. Jasm was originally
developed as part of the Java Compiler Kit (JKit), and is now used
primarily within the Whiley Compiler.
// Copyright (c) 2011, David J. Pearce ([email protected])
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL DAVID J. PEARCE BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package jasm.io;
import jasm.attributes.*;
import jasm.io.BinaryInputStream;
import jasm.lang.*;
import jasm.util.*;
import java.io.*;
import java.util.*;
/**
* The ClassFileReader
is responsible for reading in a class file
* and constructing a ClassFile
object from it.
*
* @author David J. Pearce
*
*/
public final class ClassFileReader {
private final byte[] bytes; // byte array of class
private final int[] items; // start indices of constant pool items
private final HashMap constantPool;
private final HashMap attributeReaders;
/**
* Construct reader for classfile from InputStream
*
* @param in
* InputStream from which class file is read
* @param readers
* Collection of attribute readers
*/
public ClassFileReader(InputStream in,
BytecodeAttribute.Reader... readers) throws IOException {
this(readStream(in), readers);
}
/**
* Construct reader for classfile from InputStream
*
* @param in
* InputStream from which class file is read
* @param readers
* Collection of attribute readers
*/
public ClassFileReader(InputStream in,
Collection readers) throws IOException {
this(readStream(in), readers);
}
/**
* Construct reader from byte array representing classfile.
*
* @param bytes
* the byte array from which this class file is read.
* @param readers
* Collection of attribute readers
* @throws ClassFormatError
* if the classfile is invalid.
*/
public ClassFileReader(byte[] bytes, BytecodeAttribute.Reader... readers) {
this.bytes = bytes;
int nitems = read_u2(8);
items = new int[nitems];
constantPool = new HashMap();
this.attributeReaders = new HashMap();
for(BytecodeAttribute.Reader c : readers) {
this.attributeReaders.put(c.name(),c);
}
}
/**
* Construct reader from byte array representing classfile.
*
* @param bytes
* the byte array from which this class file is read.
* @param readers
* Collection of attribute readers
* @throws ClassFormatError if the classfile is invalid.
*/
public ClassFileReader(byte[] bytes, Collection readers) {
this.bytes = bytes;
int nitems = read_u2(8);
items = new int[nitems];
constantPool = new HashMap();
this.attributeReaders = new HashMap();
for(BytecodeAttribute.Reader c : readers) {
this.attributeReaders.put(c.name(),c);
}
}
/**
* Parse classfile and construct ClassInfo object. Currently, this does the
* same thing as readSkeletons.
*
* @throws ClassFormatError
* if the classfile is invalid.
*/
public ClassFile readClass() {
if(read_u2(0) != 0xCAFE || read_u2(2) != 0xBABE) {
throw new ClassFormatError("bad magic number");
}
int version = read_i4(4);
// parse constant pool
int index = 4+2+2+2;
int nitems = read_u2(8);
// process each item
for(int i=1;i!=nitems;++i) {
int type = read_u1(index);
items[i] = index + 1;
switch(type) {
case CONSTANT_Class:
case CONSTANT_String:
index += 3;
break;
case CONSTANT_FieldRef:
case CONSTANT_MethodRef:
case CONSTANT_InterfaceMethodRef:
case CONSTANT_Integer:
case CONSTANT_Float:
case CONSTANT_NameAndType:
index += 5;
break;
case CONSTANT_Long:
case CONSTANT_Double:
index += 9;
++i; // longs and doubles are two entries
break;
case CONSTANT_Utf8:
// could simply turn into string here?
int length = read_u2(index+1);
index += length + 2 + 1;
break;
}
}
// Now, fully prime the cache
for (int i = 1; i < nitems; ++i) {
Constant.Info c = getConstant(i);
if (c instanceof Constant.Double || c instanceof Constant.Long) {
++i; // longs and doubles are two entries.
}
}
int modifiers = read_u2(index);
String name = getString(read_u2(items[read_u2(index+2)]));
String className = name.substring(name.lastIndexOf('/')+1);
String superClass = read_u2(index + 4) == 0 ? null
: getString(read_u2(items[read_u2(index + 4)]));
index += 6;
List interfaces = parseInterfaces(index);
int count = read_u2(index);
index += 2 + (count * 2);
ArrayList fields = parseFields(index);
count = read_u2(index);
index += 2;
for(int i=0;i!=count;++i) {
int acount = read_u2(index+6);
index += 8;
for(int j=0;j!=acount;++j) {
int len = read_i4(index+2);
index += len + 6;
}
}
ArrayList methods = parseMethods(index,className);
count = read_u2(index);
index += 2;
for(int i=0;i!=count;++i) {
int acount = read_u2(index+6);
index += 8;
for(int j=0;j!=acount;++j) {
int len = read_i4(index+2);
index += len + 6;
}
}
JvmType.Clazz type = parseClassDescriptor("L" + name + ";");
JvmType.Clazz superType = superClass == null ? null
: parseClassDescriptor("L" + superClass + ";");
// now, try and figure out the full type of this class
List lmodifiers = parseClassModifiers(modifiers);
ClassFile cfile = new ClassFile(version, type, superType, interfaces, lmodifiers);
ArrayList attributes = parseAttributes(index, null,
cfile);
// We need to update the class' modifiers here, in the special case that
// it is an inner class.
for (BytecodeAttribute a : attributes) {
if (a instanceof InnerClasses) {
InnerClasses ic = (InnerClasses) a;
for (Triple> p : ic
.inners()) {
if (matchedInnerTypes(type, p.second())) {
List tmp = p.third();
for (Modifier m : tmp) {
if (!lmodifiers.contains(m)) {
lmodifiers.add(m);
}
}
}
}
}
}
cfile.attributes().addAll(attributes);
cfile.methods().addAll(methods);
cfile.fields().addAll(fields);
return cfile;
}
// ============================================================
// PARSING HELPERS
// ============================================================
/**
* Get array of interfaces implemented by this class.
*
* @return
*/
protected ArrayList parseInterfaces(int interfaces) {
int count = read_u2(interfaces);
int index = interfaces + 2;
ArrayList r = new ArrayList();
for(int i=0;i!=count;++i,index+=2) {
JvmType.Clazz t = parseClassDescriptor("L"+getString(read_u2(items[read_u2(index)])));
r.add(t);
}
return r;
}
/**
* parse array of fields defined in this class
*
* @return
*/
protected ArrayList parseFields(int fields) {
int count = read_u2(fields);
ArrayList r = new ArrayList();
int index = fields + 2;
for(int i=0;i!=count;++i) {
r.add(parseField(index));
int acount = read_u2(index+6);
index += 8;
for(int j=0;j!=acount;++j) {
int alen = read_i4(index+2);
index += alen + 6;
}
}
return r;
}
protected ClassFile.Field parseField(int offset) {
int modifiers = read_u2(offset);
String name = getString(read_u2(offset+2));
String desc = getString(read_u2(offset+4));
// parse attributes
int acount = read_u2(offset+6);
ArrayList attributes = new ArrayList();
int index = offset + 8;
for(int j=0;j!=acount;++j) {
int len = read_i4(index+2);
attributes.add(parseAttribute(index, null, null));
index += len + 6;
}
JvmType type = parseDescriptor(desc);
List mods = parseFieldModifiers(modifiers);
ClassFile.Field f = new ClassFile.Field(name, type,
mods);
f.attributes().addAll(attributes);
return f;
}
/**
* parse array of methods defined in this class
*
* @return
*/
protected ArrayList parseMethods(int methods, String owner) {
int count = read_u2(methods);
ArrayList r = new ArrayList();
int index = methods + 2;
for(int i=0;i!=count;++i) {
r.add(parseMethod(index,owner));
int acount = read_u2(index+6);
index += 8;
for(int j=0;j!=acount;++j) {
int alen = read_i4(index+2);
index += alen + 6;
}
}
return r;
}
protected ClassFile.Method parseMethod(int offset, String owner) {
String name = getString(read_u2(offset+2));
String desc = getString(read_u2(offset+4));
int modifiers = read_u2(offset);
JvmType.Function type = parseMethodDescriptor(desc);
List mods = parseMethodModifiers(modifiers);
ClassFile.Method cm = new ClassFile.Method(name, type,
mods);
// parse attributes
int acount = read_u2(offset+6);
ArrayList attributes = new ArrayList();
int index = offset + 8;
for(int j=0;j!=acount;++j) {
int len = read_i4(index+2);
attributes.add(parseAttribute(index, cm, null));
index += len + 6;
}
cm.attributes().addAll(attributes);
return cm;
}
/**
* parse any attributes associated with this field.
* @return
*/
protected ArrayList parseAttributes(int attributes,
ClassFile.Method enclosingMethod, ClassFile enclosingClass) {
int acount = read_u2(attributes);
ArrayList r = new ArrayList();
int index = attributes + 2;
for (int j = 0; j != acount; ++j) {
int len = read_i4(index + 2);
r.add(parseAttribute(index, enclosingMethod, enclosingClass));
index += len + 6;
}
return r;
}
protected BytecodeAttribute parseAttribute(int offset,
ClassFile.Method enclosingMethod, ClassFile enclosingClass) {
String name = getString(read_u2(offset));
if (name.equals("Code")) {
return parseCode(offset, name, enclosingMethod);
} else if (name.equals("Exceptions")) {
return parseExceptions(offset, name);
} else if (name.equals("InnerClasses")) {
return parseInnerClasses(offset, name, enclosingClass.type());
} else if (name.equals("ConstantValue")) {
return parseConstantValue(offset, name);
}
int len = read_i4(offset + 2) + 6;
byte[] bs = new byte[len];
for (int i = 0; i != len; ++i) {
bs[i] = bytes[offset + i];
}
BytecodeAttribute.Reader reader = attributeReaders.get(name);
if (reader != null) {
try {
return reader.read(new BinaryInputStream(
new ByteArrayInputStream(bs)), constantPool);
} catch (IOException ioex) {
throw new RuntimeException(ioex.getMessage(), ioex);
}
} else {
// unknown attribute
return new BytecodeAttribute.Unknown(name, bs);
}
}
protected Exceptions parseExceptions(int offset, String name) {
ArrayList exceptions = new ArrayList();
int numExceptions = read_u2(offset + 6);
offset += 8;
for(int i=0;i!=numExceptions;++i) {
exceptions.add(parseClassDescriptor("L" + getClassName(read_u2(offset)) + ";"));
offset += 2;
}
return new Exceptions(exceptions);
}
protected ConstantValue parseConstantValue(int offset, String name) {
Constant.Info c = getConstant(read_u2(offset+6));
Object constant;
if(c instanceof Constant.Integer) {
Constant.Integer v = (Constant.Integer) c;
constant = v.value;
} else if(c instanceof Constant.Long) {
Constant.Long v = (Constant.Long) c;
constant = v.value;
} else if(c instanceof Constant.Float) {
Constant.Float v = (Constant.Float) c;
constant = v.value;
} else if(c instanceof Constant.Double) {
Constant.Double v = (Constant.Double) c;
constant = v.value;
} else if(c instanceof Constant.String) {
Constant.String v = (Constant.String) c;
constant = v.str;
} else {
throw new RuntimeException("invalid constant value requested");
}
return new ConstantValue(constant);
}
protected InnerClasses parseInnerClasses(int offset, String name, JvmType.Clazz type) {
offset += 6;
int numClasses = read_u2(offset);
offset += 2;
ArrayList>> inners = new ArrayList();
for(int i=0;i!=numClasses;++i,offset=offset+8) {
int inner_class_idx = read_u2(offset);
int outer_class_idx = read_u2(offset+2);
int inner_class_access_flags = read_u2(offset+6);
List mods = parseInnerClassModifiers(inner_class_access_flags);
// Note, either the inner or outer indices can be zero. This
// indicates an anonymous class.
if(inner_class_idx == 0) {
String outer_class_name = getClassName(outer_class_idx);
JvmType.Clazz outerT = parseClassDescriptor("L" + outer_class_name + ";");
inners.add(new Triple(outerT,null,mods));
} else if(outer_class_idx == 0) {
String inner_class_name = getClassName(inner_class_idx);
JvmType.Clazz innerT = parseClassDescriptor("L" + inner_class_name + ";");
inners.add(new Triple(null,innerT,mods));
} else {
String outer_class_name = getClassName(outer_class_idx);
String inner_class_name = getClassName(inner_class_idx);
JvmType.Clazz innerT = parseClassDescriptor("L" + inner_class_name + ";");
JvmType.Clazz outerT = parseClassDescriptor("L" + outer_class_name + ";");
inners.add(new Triple(outerT,innerT,mods));
}
}
return new InnerClasses(type,inners);
}
/**
* This method parses a general type descriptor.
*
* @param descriptor
* @return
*/
public static JvmType parseDescriptor(String descriptor) {
return parseInternalDescriptor(descriptor,0).first();
}
/**
* This method parses a descriptor of the form "Lxxx.yyy.zzz$aaa$bbb;"
* @param descriptor
* @return
*/
public static JvmType.Clazz parseClassDescriptor(String descriptor) {
return parseInternalClassDescriptor(descriptor,0).first();
}
/**
* The class signature provides information about the generic type
* parameters declared for a class.
*
* @return
*/
protected Triple, JvmType.Clazz, List> parseClassSigDesc(
String descriptor) {
int pos = 0;
ArrayList targs = new ArrayList();
if (descriptor.charAt(pos) == '<') {
pos = pos + 1; // skip '<'
while (descriptor.charAt(pos) != '>') {
Pair rt = parseFormalType(descriptor,
pos);
targs.add(rt.first());
pos = rt.second();
}
pos = pos + 1; // skip '>'
}
Pair state = parseInternalClassDescriptor(
descriptor, pos);
JvmType.Clazz superT = state.first();
pos = state.second();
ArrayList interfaces = new ArrayList();
while (pos < descriptor.length()) {
state = parseInternalClassDescriptor(descriptor, pos);
interfaces.add(state.first());
pos = state.second();
}
return new Triple, JvmType.Clazz, List>(targs,
superT, interfaces);
}
public static Pair parseInternalDescriptor(
String descriptor, int pos) {
char c = descriptor.charAt(pos);
if(c == 'L') {
Pair p = parseInternalClassDescriptor(descriptor,pos);
return new Pair(p.first(),p.second());
} else if(c == '[') {
int num = 0;
while(pos < descriptor.length() && descriptor.charAt(pos) == '[') {
++num; ++pos;
}
Pair tmp = parseInternalDescriptor(descriptor,pos);
JvmType type = tmp.first();
for(int i=0;i!=num;++i) {
type = new JvmType.Array(type);
}
return new Pair(type,tmp.second());
} else if(c == 'T') {
// this is a type variable
int start = ++pos;
while(descriptor.charAt(pos) != ';') { ++pos; }
JvmType type = new JvmType.Variable(descriptor.substring(start,pos), JvmTypes.JAVA_LANG_OBJECT);
return new Pair(type,pos+1);
} else if(c == '+') {
Pair r = parseInternalDescriptor(descriptor,pos+1);
return new Pair(new JvmType.Wildcard((JvmType.Reference)r.first(),null),r.second());
} else if(c == '-') {
Pair r = parseInternalDescriptor(descriptor,pos+1);
return new Pair(new JvmType.Wildcard(null,(JvmType.Reference)r.first()),r.second());
} else {
// is primitive type ...
switch(c) {
case 'B':
return new Pair(JvmTypes.BYTE,pos+1);
case 'C':
return new Pair(JvmTypes.CHAR,pos+1);
case 'D':
return new Pair(JvmTypes.DOUBLE,pos+1);
case 'F':
return new Pair(JvmTypes.FLOAT,pos+1);
case 'I':
return new Pair(JvmTypes.INT,pos+1);
case 'J':
return new Pair(JvmTypes.LONG,pos+1);
case 'S':
return new Pair(JvmTypes.SHORT,pos+1);
case 'Z':
return new Pair(JvmTypes.BOOL,pos+1);
case 'V':
return new Pair(JvmTypes.VOID,pos+1);
case '*':
// FIXME: wildcard bounds.
return new Pair(new JvmType.Wildcard(null,null),pos+1);
default:
throw new RuntimeException("Unknown type qualifier: " + c);
}
}
}
public static Pair parseInternalClassDescriptor(
String descriptor, int pos) {
assert descriptor.charAt(pos) == 'L';
int start = ++pos;
int last = pos;
while(pos < descriptor.length() && descriptor.charAt(pos) != ';' && descriptor.charAt(pos) != '<') {
if(descriptor.charAt(pos) == '/') { last = pos; }
++pos;
}
String pkg = descriptor.substring(start,last).replace('/','.');
ArrayList>> classes = new ArrayList>>();
// back track to make my life easier
pos = last;
while (pos < descriptor.length() && descriptor.charAt(pos) != ';') {
if (descriptor.charAt(pos) == '.' || descriptor.charAt(pos) == '/'
|| descriptor.charAt(pos) == '$') {
pos++;
}
last = pos;
while (pos < descriptor.length() && descriptor.charAt(pos) != '$'
&& descriptor.charAt(pos) != ';'
&& descriptor.charAt(pos) != '<') {
pos++;
}
String name = descriptor.substring(last, pos);
ArrayList targs;
if (pos < descriptor.length() && descriptor.charAt(pos) == '<') {
ArrayList ts = new ArrayList();
pos = pos + 1; // skip '<'
while(descriptor.charAt(pos) != '>') {
Pair ti = parseInternalDescriptor(descriptor,pos);
ts.add((JvmType.Reference) ti.first());
pos=ti.second();
}
pos=pos+1; // skip '>'
targs = ts;
} else {
targs = new ArrayList();
}
classes.add(new Pair>(name, targs));
}
JvmType.Clazz r = new JvmType.Clazz(pkg,classes);
return new Pair(r,pos+1);
}
public static Pair parseFormalType(String descriptor, int pos) {
int start = pos;
while(descriptor.charAt(pos) != ':') { pos++; }
String id = descriptor.substring(start,pos);
pos = pos + 1; // skip ':'
ArrayList lowerBounds = new ArrayList();
while(pos < descriptor.length() && descriptor.charAt(pos) == 'L') {
Pair rt = parseInternalClassDescriptor(descriptor,pos);
lowerBounds.add(rt.first());
pos = rt.second();
}
JvmType.Reference lb = null;
if(lowerBounds.size() > 1) {
lb = new JvmType.Intersection(lowerBounds);
} else if(lowerBounds.size() == 1) {
lb = lowerBounds.get(0);
} else {
lb = JvmTypes.JAVA_LANG_OBJECT;
}
return new Pair(new JvmType.Variable(id,lb),pos);
}
public static JvmType.Function parseMethodDescriptor(String descriptor) {
ArrayList targs = new ArrayList();
int pos = 0;
// parse generic parameters (if there are any)
if(descriptor.charAt(pos) == '<') {
pos = pos + 1; // skip '<'
while(descriptor.charAt(pos) != '>') {
Pair rt = parseFormalType(descriptor,pos);
targs.add(rt.first());
pos = rt.second();
}
pos = pos + 1; // skip '>'
}
// now parse the methods parameters
ArrayList params = new ArrayList();
assert descriptor.charAt(pos) == '(';
pos++;
while (descriptor.charAt(pos) != ')') {
Pair tmp = parseInternalDescriptor(descriptor, pos);
params.add(tmp.first());
pos = tmp.second();
}
// finally, parse the return type
JvmType rtype = parseInternalDescriptor(descriptor, pos + 1).first();
JvmType.Function rf = new JvmType.Function(rtype, params, targs);
if(targs.size() > 0) {
// First, build the binding.
HashMap binding = new HashMap();
for(JvmType.Variable v : targs) {
binding.put(v.variable(), v);
}
rf = JvmTypes.substitute(rf, binding);
}
return rf;
}
protected Code parseCode(int offset, String name,
ClassFile.Method enclosingMethod) {
int codeAttributeLength = read_i4(offset + 10);
int index = offset + 14 + codeAttributeLength;
// ====================================================================
// Parse exception table
// ====================================================================
int exceptionTableOffset = index;
int exceptionTableLength = read_u2(index); // length of exception table
ArrayList exceptionTable = new ArrayList();
HashMap labels = new HashMap();
index += 2;
for(int i=0;i!=exceptionTableLength;++i,index+=8) {
int start_pc = read_u2(index);
int end_pc = read_u2(index+2);
int handler_pc = read_u2(index+4);
int catch_type = read_u2(index+6);
String handlerLabel = createBranchLabel(handler_pc,labels);
String className = ((Constant.Class)getConstant(catch_type)).name.str;
JvmType.Clazz type = parseClassDescriptor("L" + className + ";");
exceptionTable.add(new Code.Handler(start_pc, end_pc, handlerLabel, type));
}
// ====================================================================
// Parse attributes
// ====================================================================
int numberOfAttributes = read_u2(index); // length of attributes table
index += 2;
int lineMapOffset = -1;
ArrayList attributes = new ArrayList();
for(int j=0;j!=numberOfAttributes;++j) {
int len = read_i4(index + 2);
BytecodeAttribute attribute = parseAttribute(index, null, null);
attributes.add(attribute);
index += 6;
if(attribute instanceof LineNumberTable) {
lineMapOffset = index;
}
index += len;
}
// ====================================================================
// Parse instructions
// ====================================================================
ArrayList instructions = new ArrayList();
ArrayList offsets = new ArrayList();
int start = offset + 14;
int line = -1;
int ltp = lineMapOffset + 2;
int ltlen = lineMapOffset > 0 ? read_u2(lineMapOffset) : -1;
for (int pc = start; pc < start + codeAttributeLength;) {
if (ltlen > 0) {
if (read_u2(ltp) <= (pc - start)) {
line = read_u2(ltp + 2);
ltp = ltp + 4;
ltlen--;
}
}
Bytecode i = decodeInstruction(pc, start, labels, line);
instructions.add(i);
offsets.add(pc - start);
pc += decodeInstructionLength(pc, start);
}
// ====================================================================
// Insert labels
// ====================================================================
for (int i = 0, nLabels = 0; i != offsets.size(); ++i) {
offset = offsets.get(i);
String label = labels.get(offset);
if (label != null) {
instructions.add(i + nLabels, new Bytecode.Label(label));
nLabels++;
}
}
// ====================================================================
// Fix up Exception Table
// ====================================================================
// For whatever reason, a Code.Handler is expressed in terms of
// instruction indices, rather than byte offsets. Therefore, we need to
// convert them now.
for(int i=0,nlabels=0;i!=offsets.size();++i) {
int o = offsets.get(i);
// Note: at this point, i does not correspond the index of the
// expected bytecode in bytecodes. This is because an arbitrary
// number of labels may have been inserted into bytecodes, breaking
// the correlation between indices in offsets and indices in
// bytecodes. Hence, we need to find and account for labels.
if(labels.get(o) != null) { nlabels++; }
int target = i + nlabels;
for(int j=0;j!=exceptionTable.size();++j) {
Code.Handler handler = exceptionTable.get(j);
handler.start = handler.start == o ? target : handler.start;
handler.end = handler.end == o ? target : handler.end;
}
}
// ====================================================================
// Done.
// ====================================================================
Code ca = new Code(instructions, exceptionTable, enclosingMethod);
ca.attributes().addAll(attributes);
return ca;
}
protected Bytecode decodeInstruction(int offset, int start,
HashMap labels, int line) {
JvmType type = decodeInstructionType(offset);
int opcode = read_u1(offset);
int insn = opmap[opcode] & INSN_MASK;
switch (insn) {
case NOP:
return new Bytecode.Nop();
case SWAP:
// return new Bytecode.Swap();
throw new RuntimeException("Need to implement swap instruction");
case POP:
return new Bytecode.Pop(JvmTypes.INT);
case DUP:
return new Bytecode.Dup(null);
case DUPX1:
return new Bytecode.DupX1();
case DUPX2:
return new Bytecode.DupX2();
case MONITORENTER:
return new Bytecode.MonitorEnter();
case MONITOREXIT:
return new Bytecode.MonitorExit();
case ARRAYLENGTH:
return new Bytecode.ArrayLength();
case ADD:
return new Bytecode.BinOp(Bytecode.BinOp.ADD, type);
case SUB:
return new Bytecode.BinOp(Bytecode.BinOp.SUB, type);
case DIV:
return new Bytecode.BinOp(Bytecode.BinOp.DIV, type);
case MUL:
return new Bytecode.BinOp(Bytecode.BinOp.MUL, type);
case REM:
return new Bytecode.BinOp(Bytecode.BinOp.REM, type);
case NEG:
return new Bytecode.Neg(type);
case SHL:
return new Bytecode.BinOp(Bytecode.BinOp.SHL, type);
case SHR:
return new Bytecode.BinOp(Bytecode.BinOp.SHR, type);
case USHR:
return new Bytecode.BinOp(Bytecode.BinOp.USHR, type);
case AND:
return new Bytecode.BinOp(Bytecode.BinOp.AND, type);
case OR:
return new Bytecode.BinOp(Bytecode.BinOp.OR, type);
case XOR:
return new Bytecode.BinOp(Bytecode.BinOp.XOR, type);
case NEW:
return new Bytecode.New(type);
case CMP:
return new Bytecode.Cmp(type,Bytecode.Cmp.EQ);
case CMPL:
return new Bytecode.Cmp(type,Bytecode.Cmp.LT);
case CMPG:
return new Bytecode.Cmp(type,Bytecode.Cmp.GT);
case CONVERT:
return new Bytecode.Conversion(decodeConversionType(offset),(JvmType.Primitive) type);
case CHECKCAST:
return new Bytecode.CheckCast(type);
case RETURN:
return new Bytecode.Return(type);
case ARRAYLOAD:
return new Bytecode.ArrayLoad(new JvmType.Array(type));
case ARRAYSTORE:
return new Bytecode.ArrayStore(new JvmType.Array(type));
case LOADCONST:
return new Bytecode.LoadConst(decodeInstructionConstant(offset,
line));
case IINC:
return new Bytecode.Iinc(read_u1(offset+1), read_i1(offset+2));
case LOADVAR:
return new Bytecode.Load(decodeInstructionVariable(offset, line),
type);
case STOREVAR:
return new Bytecode.Store(decodeInstructionVariable(offset, line),
type);
case IF: {
Bytecode.IfMode mode;
switch(opcode) {
case Bytecode.IFEQ:
mode = Bytecode.IfMode.EQ;
break;
case Bytecode.IFNE:
mode = Bytecode.IfMode.NE;
break;
case Bytecode.IFLT:
mode = Bytecode.IfMode.LT;
break;
case Bytecode.IFLE:
mode = Bytecode.IfMode.LE;
break;
case Bytecode.IFGT:
mode = Bytecode.IfMode.GT;
break;
case Bytecode.IFGE:
mode = Bytecode.IfMode.GE;
break;
case Bytecode.IFNULL:
mode = Bytecode.IfMode.NULL;
break;
case Bytecode.IFNONNULL:
mode = Bytecode.IfMode.NONNULL;
break;
default:
throw new IllegalArgumentException("invalid if opcode");
}
return new Bytecode.If(mode, decodeInstructionBranchTarget(offset,
start, labels, line));
}
case IFCMP:
return new Bytecode.IfCmp(
(opcode - Bytecode.IF_ICMPEQ) % 6,
type,
decodeInstructionBranchTarget(offset, start, labels, line));
case GOTO:
return new Bytecode.Goto(decodeInstructionBranchTarget(offset, start, labels, line));
// === INDIRECT INSTRUCTIONS ===
case INVOKE: {
Triple ont = decodeInstructionOwnerNameType(
offset, line);
Bytecode.InvokeMode mode;
switch(opcode) {
case Bytecode.INVOKEVIRTUAL:
mode = Bytecode.InvokeMode.VIRTUAL;
break;
case Bytecode.INVOKESTATIC:
mode = Bytecode.InvokeMode.STATIC;
break;
case Bytecode.INVOKEINTERFACE:
mode = Bytecode.InvokeMode.INTERFACE;
break;
default:
mode = Bytecode.InvokeMode.SPECIAL;
break;
}
return new Bytecode.Invoke(ont.first(), ont.second(),
(JvmType.Function) ont.third(), mode);
}
case FIELDLOAD: {
Triple ont = decodeInstructionOwnerNameType(
offset, line);
Bytecode.FieldMode mode;
if(opcode == Bytecode.GETSTATIC) {
mode = Bytecode.FieldMode.STATIC;
} else {
mode = Bytecode.FieldMode.NONSTATIC;
}
return new Bytecode.GetField(ont.first(), ont.second(),
ont.third(), mode);
}
case FIELDSTORE: {
Triple ont = decodeInstructionOwnerNameType(
offset, line);
Bytecode.FieldMode mode;
if(opcode == Bytecode.PUTSTATIC) {
mode = Bytecode.FieldMode.STATIC;
} else {
mode = Bytecode.FieldMode.NONSTATIC;
}
return new Bytecode.PutField(ont.first(), ont.second(),
ont.third(), mode);
}
}
throw new RuntimeException(
"Internal failure parsing bytecode instruction ("
+ opmap[opcode] + ")");
}
protected int decodeInstructionVariable(int offset, int line) {
int opcode = read_u1(offset);
int fmt = opmap[opcode] & FMT_MASK;
switch (fmt) {
case FMT_INT0:
return 0;
case FMT_INT1:
return 1;
case FMT_INT2:
return 2;
case FMT_INT3:
return 3;
case FMT_VARIDX:
case FMT_VARIDX_I8:
return read_u1(offset + 1);
default:
throw new RuntimeException(
"Operation not supported for instruction!");
}
}
protected int decodeInstructionLength(int offset, int codeOffsetP14) {
int fmt = opmap[read_u1(offset)] & FMT_MASK;
switch(fmt) {
case FMT_INTM1:
case FMT_INT0:
case FMT_INT1:
case FMT_INT2:
case FMT_INT3:
case FMT_INT4:
case FMT_INT5:
case FMT_INTNULL:
case FMT_EMPTY: return 1;
case FMT_I8: return 2;
case FMT_I16: return 3;
case FMT_CONSTINDEX8: return 2;
case FMT_FIELDINDEX16: return 3;
case FMT_METHODINDEX16: return 3;
case FMT_TYPEINDEX16: return 3;
case FMT_TYPEAINDEX16: return 3;
case FMT_CONSTINDEX16: return 3;
case FMT_TYPEINDEX16_U8: return 4;
case FMT_METHODINDEX16_U8_0: return 5;
case FMT_VARIDX: return 2;
case FMT_VARIDX_I8: return 3;
case FMT_ATYPE: return 2;
case FMT_TARGET16: return 3;
case FMT_TARGET32: return 5;
case FMT_TABLESWITCH:
{
int cpos = offset+1;
int pos = 1 + offset - codeOffsetP14;
while((pos % 4) != 0) { pos++;cpos++; }
// first comes default word, then low, then high bytes
int low = read_i4(cpos+4);
int high = read_i4(cpos+8);
cpos += 12;
int count = high-low+1;
cpos += (count * 4);
return cpos - offset;
}
case FMT_LOOKUPSWITCH:
{
int cpos = offset+1;
int pos = 1 + offset - codeOffsetP14;
while((pos % 4) != 0) { pos++;cpos++; }
// first comes default word, then low, then high bytes
int count = read_i4(cpos+4);
cpos += 8;
cpos += (count * 8);
return cpos - offset;
}
default:
throw new RuntimeException("Should not get here");
}
}
/**
* Determine the relative offset for the destination of a branching
* instruction.
*
* @param offset
* @param start
* @param line
* @return
*/
protected String decodeInstructionBranchTarget(int offset,
int start, HashMap labels, int line) {
int opcode = read_u1(offset);
int fmt = opmap[opcode] & FMT_MASK;
int target;
switch (fmt) {
case FMT_TARGET16:
target = read_i2(offset + 1) + offset - start;
break;
case FMT_TARGET32:
target = read_i4(offset + 1) + offset - start;
break;
default:
throw new RuntimeException(
"Operation not supported for instruction!");
}
return createBranchLabel(target,labels);
}
protected String createBranchLabel(int target, HashMap labels) {
String label = labels.get(target);
if(label == null) {
label = "label" + labels.size();
labels.put(target,label);
}
return label;
}
protected JvmType decodeInstructionType(int offset) {
int opcode = read_u1(offset);
int data = opmap[opcode];
int type = data & TYPE_MASK;
switch(type) {
case T_BYTE:
return JvmTypes.BYTE;
case T_CHAR:
return JvmTypes.CHAR;
case T_SHORT:
return JvmTypes.SHORT;
case T_INT:
return JvmTypes.INT;
case T_LONG:
return JvmTypes.LONG;
case T_FLOAT:
return JvmTypes.FLOAT;
case T_DOUBLE:
return JvmTypes.DOUBLE;
case T_REF:
if(opcode == 1) {
// special case for FMT_INTNULL
return JvmTypes.NULL;
} else {
return new JvmType.Clazz("java.lang","Object");
}
case T_ARRAY:
return new JvmType.Array(JvmTypes.VOID);
}
int fmt = data & FMT_MASK;
switch(fmt) {
case FMT_TYPEINDEX16_U8: {
int dims = read_u1(offset+3);
String descriptor = getString(read_u2(read_u2(offset+1),0));
return parseDescriptor(descriptor);
}
case FMT_TYPEINDEX16: {
int dims = read_u1(offset+3);
// it's fair to say that I don't really see
// why this is necessary.
String descriptor = getString(read_u2(read_u2(offset+1),0));
if(descriptor.charAt(0) == '[') {
return parseDescriptor(descriptor);
} else {
StringBuffer buf = new StringBuffer("L");
buf.append(descriptor);
buf.append(";");
return parseDescriptor(buf.toString());
}
}
case FMT_TYPEAINDEX16: {
int dims = read_u1(offset+3);
// it's fair to say that I don't really see
// why this is necessary.
String descriptor = getString(read_u2(read_u2(offset+1),0));
StringBuffer buf = new StringBuffer("[");
if(descriptor.charAt(0) != '[') {
buf.append('L');
buf.append(descriptor);
buf.append(";");
} else {
buf.append(descriptor);
}
return parseDescriptor(buf.toString());
}
case FMT_CONSTINDEX8:
// for LDC and LDCW
return getConstantType(read_u1(offset+1) & 0xFF);
case FMT_CONSTINDEX16:
// for LDC and LDCW
return getConstantType(read_u2(offset+1));
case FMT_FIELDINDEX16:
int index = read_u2(offset+1);
return parseDescriptor(getString(read_u2(read_u2(index, 2), 2)));
case FMT_METHODINDEX16:
case FMT_METHODINDEX16_U8_0:
index = read_u2(offset+1);
return parseMethodDescriptor(getString(read_u2(read_u2(index, 2), 2)));
case FMT_ATYPE:
// must be NEWARRAY
int atype = read_u1(offset+1);
return buildArraytype(atype);
default:
return null;
}
}
protected static final JvmType.Array buildArraytype(int atype) {
JvmType elemType;
switch (atype) {
case VM_BOOLEAN:
elemType = JvmTypes.BOOL;
break;
case VM_CHAR:
elemType = JvmTypes.CHAR;
break;
case VM_FLOAT:
elemType = JvmTypes.FLOAT;
break;
case VM_DOUBLE:
elemType = JvmTypes.DOUBLE;
break;
case VM_BYTE:
elemType = JvmTypes.BYTE;
break;
case VM_SHORT:
elemType = JvmTypes.SHORT;
break;
case VM_INT:
elemType = JvmTypes.INT;
break;
case VM_LONG:
elemType = JvmTypes.LONG;
break;
default:
throw new RuntimeException("unrecognised NEWARRAY code");
}
return new JvmType.Array(elemType);
}
protected JvmType.Primitive decodeConversionType(int offset) {
int opcode = read_u1(offset);
int data = opmap[opcode];
int type = data & SRCTYPE_MASK;
switch(type) {
case S_INT:
return JvmTypes.INT;
case S_LONG:
return JvmTypes.LONG;
case S_FLOAT:
return JvmTypes.FLOAT;
case S_DOUBLE:
return JvmTypes.DOUBLE;
}
throw new RuntimeException("unrecognised source type");
}
protected Object decodeInstructionConstant(int offset, int line) {
int opcode = read_u1(offset);
int insn = opmap[opcode] & INSN_MASK;
int fmt = opmap[opcode] & FMT_MASK;
Object data;
switch(fmt) {
case FMT_INTNULL:
data = null;
break;
case FMT_INTM1:
data = new Integer(-1);
break;
case FMT_INT0:
case FMT_INT1:
case FMT_INT2:
case FMT_INT3:
int n = (fmt - FMT_INT0) >> FMT_SHIFT;
int rtype = opmap[opcode] & TYPE_MASK;
switch(rtype) {
case T_INT:
data = new Integer(n);
break;
case T_LONG:
data = new Long(n);
break;
case T_FLOAT:
data = new Float(n);
break;
case T_DOUBLE:
data = new Double(n);
break;
default:
throw new RuntimeException("Unreachable code reached!");
}
break;
case FMT_INT4:
data = new Integer(4);
break;
case FMT_INT5:
data = new Integer(5);
break;
case FMT_I8:
data = new Integer(read_u1(offset+1));
break;
case FMT_I16:
data = new Integer(read_i2(offset+1));
break;
case FMT_CONSTINDEX8:
data = convert(getConstant(read_u1(offset+1)));
break;
case FMT_CONSTINDEX16:
data = convert(getConstant(read_u2(offset+1)));
break;
case FMT_VARIDX_I8:
data = read_i1(offset+2);
break;
default:
throw new RuntimeException("Operation not supported for instruction!");
}
return data;
}
/**
* Convert a constant pool item into a Java constant object (e.g.
* java.lang.Integer
, java.lang.String
, etc).
*
* @param constant
* Constant pool item to be converted. Should be an instance of
* Constant.String
, Constant.Integer
,
* Constant.Long
, Constant.Float
,
* Constant.Double
.
* @return
*/
public Object convert(Constant.Info constant) {
if(constant instanceof Constant.String) {
Constant.String c = (Constant.String) constant;
return c.str.str;
} else if(constant instanceof Constant.Integer) {
Constant.Integer c = (Constant.Integer) constant;
return c.value;
} else if(constant instanceof Constant.Long) {
Constant.Long c = (Constant.Long) constant;
return c.value;
} else if(constant instanceof Constant.Float) {
Constant.Float c = (Constant.Float) constant;
return c.value;
} else if(constant instanceof Constant.Double) {
Constant.Double c = (Constant.Double) constant;
return c.value;
} else if(constant instanceof Constant.Class) {
Constant.Class c = (Constant.Class) constant;
return parseClassDescriptor("L" + c.name.str + ";");
} else {
throw new IllegalArgumentException(
"unknown constant pool item encountered: " + constant);
}
}
protected Triple decodeInstructionOwnerNameType(
int offset, int line) {
JvmType owner = null;
JvmType type;
String name;
int opcode = read_u1(offset);
int insn = opmap[opcode] & INSN_MASK;
int fmt = opmap[opcode] & FMT_MASK;
switch (fmt) {
case FMT_FIELDINDEX16:
case FMT_METHODINDEX16:
case FMT_METHODINDEX16_U8_0:
int index = read_u2(offset + 1);
String ownerString = getString(read_u2(read_u2(index, 0), 0));
// NOTE: it's possible to have an array type as an owner here. This
// is necessary to support cloning of arrays, which is implemented
// by an invokevirtual bytecode.
if(ownerString.startsWith("[")) {
owner = parseDescriptor(ownerString);
} else {
owner = parseClassDescriptor("L" + ownerString + ";");
}
name = getString(read_u2(read_u2(index, 2), 0));
if (fmt == FMT_FIELDINDEX16) {
type = parseDescriptor(getString(read_u2(read_u2(index, 2), 2)));
} else {
type = parseMethodDescriptor(getString(read_u2(
read_u2(index, 2), 2)));
}
break;
default:
throw new RuntimeException(
"Operation not supported for instruction!");
}
return new Triple(owner, name, type);
}
// This method computes the set of possible exception
// handlers for a given position in the bytecode.
public Pair[] exceptionHandlers(int offset, int exceptionTableOffset) {
Vector> handlers = new Vector>();
int len = read_u2(exceptionTableOffset);
int idx = exceptionTableOffset+2;
for(int i=0;i!=len;++i,idx+=8) {
int start = read_u2(idx);
int end = read_u2(idx+2);
int dest = read_u2(idx+4);
int ct = read_u2(idx+6);
JvmType type;
if(ct > 0) {
String desc = getClassName(ct);
type = parseDescriptor("L" + desc + ";");
} else {
// Not sure what type to use here. Maybe Throwable would
// be better.
type = new JvmType.Clazz("java.lang","Exception");
}
if(offset >= start && offset < end) {
handlers.add(new Pair(dest,type));
}
}
return handlers.toArray(new Pair[handlers.size()]);
}
/*=== END CODE */
protected static final char BYTE = 'B';
protected static final char CHAR = 'C';
protected static final char DOUBLE = 'D';
protected static final char FLOAT = 'F';
protected static final char INT = 'I';
protected static final char LONG = 'J';
protected static final char SHORT = 'S';
protected static final char BOOLEAN = 'Z';
protected static final char STRING = 's';
protected static final char ENUM = 'e';
protected static final char CLASS = 'c';
protected static final char ANNOTATION = '@';
protected static final char ARRAY = '[';
class Enumeration {
protected final int type_name;
protected final int const_name;
public Enumeration(int tname, int cname) {
type_name = tname;
const_name = cname;
}
public String getTypeName() {
return getString(type_name);
}
public String getConstName() {
return getString(const_name);
}
};
protected int annotationLength(int offset) {
int length=0;
int type = read_u2(offset);
int npairs = read_u2(offset+2);
length += 4;
for (int j = 0; j < npairs; j++) {
switch (type) {
case BOOLEAN:
case BYTE:
case SHORT:
case CHAR:
case INT:
case FLOAT:
case LONG:
case DOUBLE:
length = 3;
break;
case STRING:
length = 5;
break;
case ENUM:
length = 7;
break;
case CLASS:
length = 5;
break;
default:
}
}
return length;
}
// ============================================================
// OTHER HELPER METHODS
// ============================================================
// access flags for fields / methods / classes
public static final int ACC_PUBLIC = 0x0001;
public static final int ACC_PRIVATE = 0x0002;
public static final int ACC_PROTECTED = 0x0004;
public static final int ACC_STATIC = 0x0008;
public static final int ACC_FINAL = 0x0010;
public static final int ACC_VOLATILE = 0x0040;
public static final int ACC_TRANSIENT = 0x0080;
public static final int ACC_SYNTHETIC = 0x1000;
public static final int ACC_BRIDGE = 0x0040;
public static final int ACC_VARARGS = 0x0080;
public static final int ACC_ANNOTATION = 0x2000;
public static final int ACC_ENUM = 0x4000;
// access flags for methods / classes
public static final int ACC_SYNCHRONIZED = 0x0020;
public static final int ACC_NATIVE = 0x0100;
public static final int ACC_ABSTRACT = 0x0400;
public static final int ACC_STRICT = 0x0800;
// access flags for classes
public static final int ACC_INTERFACE = 0x0200;
public static final int ACC_SUPER = 0x0020;
private final List parseClassModifiers(int modifiers) {
ArrayList mods = new ArrayList();
if((modifiers & ACC_PUBLIC) != 0) {
mods.add(Modifier.ACC_PUBLIC);
}
if((modifiers & ACC_FINAL) != 0) {
mods.add(Modifier.ACC_FINAL);
}
if((modifiers & ACC_SUPER) != 0) {
mods.add(Modifier.ACC_SUPER);
}
if((modifiers & ACC_INTERFACE) != 0) {
mods.add(Modifier.ACC_INTERFACE);
}
if((modifiers & ACC_ABSTRACT) != 0) {
mods.add(Modifier.ACC_ABSTRACT);
}
if((modifiers & ACC_SYNTHETIC) != 0) {
mods.add(Modifier.ACC_SYNTHETIC);
}
if((modifiers & ACC_ANNOTATION) != 0) {
mods.add(Modifier.ACC_ANNOTATION);
}
if((modifiers & ACC_ENUM) != 0) {
mods.add(Modifier.ACC_ENUM);
}
return mods;
}
private final List parseFieldModifiers(int modifiers) {
ArrayList mods = new ArrayList();
if((modifiers & ACC_PUBLIC) != 0) {
mods.add(Modifier.ACC_PUBLIC);
}
if((modifiers & ACC_PRIVATE) != 0) {
mods.add(Modifier.ACC_PRIVATE);
}
if((modifiers & ACC_PROTECTED) != 0) {
mods.add(Modifier.ACC_PROTECTED);
}
if((modifiers & ACC_STATIC) != 0) {
mods.add(Modifier.ACC_STATIC);
}
if((modifiers & ACC_FINAL) != 0) {
mods.add(Modifier.ACC_FINAL);
}
if((modifiers & ACC_VOLATILE) != 0) {
mods.add(Modifier.ACC_VOLATILE);
}
if((modifiers & ACC_TRANSIENT) != 0) {
mods.add(Modifier.ACC_TRANSIENT);
}
if((modifiers & ACC_SYNTHETIC) != 0) {
mods.add(Modifier.ACC_SYNTHETIC);
}
if((modifiers & ACC_ENUM) != 0) {
mods.add(Modifier.ACC_ENUM);
}
return mods;
}
private final List parseMethodModifiers(int modifiers) {
ArrayList mods = new ArrayList();
if((modifiers & ACC_PUBLIC) != 0) {
mods.add(Modifier.ACC_PUBLIC);
}
if((modifiers & ACC_PRIVATE) != 0) {
mods.add(Modifier.ACC_PRIVATE);
}
if((modifiers & ACC_PROTECTED) != 0) {
mods.add(Modifier.ACC_PROTECTED);
}
if((modifiers & ACC_STATIC) != 0) {
mods.add(Modifier.ACC_STATIC);
}
if((modifiers & ACC_FINAL) != 0) {
mods.add(Modifier.ACC_FINAL);
}
if((modifiers & ACC_VOLATILE) != 0) {
mods.add(Modifier.ACC_VOLATILE);
}
if((modifiers & ACC_SYNCHRONIZED) != 0) {
mods.add(Modifier.ACC_SYNCHRONIZED);
}
if((modifiers & ACC_BRIDGE) != 0) {
mods.add(Modifier.ACC_BRIDGE);
}
if((modifiers & ACC_VARARGS) != 0) {
mods.add(Modifier.ACC_VARARGS);
}
if((modifiers & ACC_NATIVE) != 0) {
mods.add(Modifier.ACC_NATIVE);
}
if((modifiers & ACC_ABSTRACT) != 0) {
mods.add(Modifier.ACC_ABSTRACT);
}
if((modifiers & ACC_STRICT) != 0) {
mods.add(Modifier.ACC_STRICT);
}
if((modifiers & ACC_SYNTHETIC) != 0) {
mods.add(Modifier.ACC_SYNTHETIC);
}
return mods;
}
private static List parseInnerClassModifiers(int modifiers) {
ArrayList mods = new ArrayList();
if((modifiers & ACC_PUBLIC) != 0) {
mods.add(Modifier.ACC_PUBLIC);
}
if((modifiers & ACC_PRIVATE) != 0) {
mods.add(Modifier.ACC_PRIVATE);
}
if((modifiers & ACC_PROTECTED) != 0) {
mods.add(Modifier.ACC_PROTECTED);
}
if((modifiers & ACC_STATIC) != 0) {
mods.add(Modifier.ACC_STATIC);
}
if((modifiers & ACC_FINAL) != 0) {
mods.add(Modifier.ACC_FINAL);
}
if((modifiers & ACC_INTERFACE) != 0) {
mods.add(Modifier.ACC_INTERFACE);
}
if((modifiers & ACC_ABSTRACT) != 0) {
mods.add(Modifier.ACC_ABSTRACT);
}
if((modifiers & ACC_SYNTHETIC) != 0) {
mods.add(Modifier.ACC_SYNTHETIC);
}
if((modifiers & ACC_ANNOTATION) != 0) {
mods.add(Modifier.ACC_ANNOTATION);
}
if((modifiers & ACC_ENUM) != 0) {
mods.add(Modifier.ACC_ENUM);
}
return mods;
}
/**
* Read string from this classfile's constant pool.
*
* @param index index into constant pool
*/
public final String getString(int index) {
Constant.Utf8 cu = (Constant.Utf8) getConstant(index);
return cu.str;
}
/**
* Read constant value from this classfile's constant pool.
*
* @param index
* @return Object representing value (e.g. Integer for int etc).
*/
public final Constant.Info getConstant(int index) {
// index points to constant pool entry
Constant.Info ci = constantPool.get(index);
if(ci != null) { return ci; }
int type = read_u1(items[index]-1);
int p = items[index];
switch (type) {
case CONSTANT_NameAndType:
Constant.Utf8 name = (Constant.Utf8) getConstant(read_u2(p));
Constant.Utf8 t = (Constant.Utf8) getConstant(read_u2(p+2));
ci = new Constant.NameType(name,t);
break;
case CONSTANT_FieldRef:
Constant.Class owner = (Constant.Class) getConstant(read_u2(p));
Constant.NameType nt = (Constant.NameType) getConstant(read_u2(p+2));
ci = new Constant.FieldRef(owner,nt);
break;
case CONSTANT_MethodRef:
owner = (Constant.Class) getConstant(read_u2(p));
nt = (Constant.NameType) getConstant(read_u2(p+2));
ci = new Constant.MethodRef(owner,nt);
break;
case CONSTANT_InterfaceMethodRef:
owner = (Constant.Class) getConstant(read_u2(p));
nt = (Constant.NameType) getConstant(read_u2(p+2));
ci = new Constant.InterfaceMethodRef(owner,nt);
break;
case CONSTANT_Utf8:
int length = read_u2(p);
try {
ci = new Constant.Utf8(new String(bytes,p+2,length,"UTF-8"));
} catch(UnsupportedEncodingException e) {
throw new RuntimeException("UTF-8 Charset not supported?");
}
break;
case CONSTANT_String:
ci = new Constant.String((Constant.Utf8) getConstant(read_u2(p)));
break;
case CONSTANT_Double:
ci = new Constant.Double(readDouble(p));
break;
case CONSTANT_Float:
ci = new Constant.Float(readFloat(p));
break;
case CONSTANT_Integer:
ci = new Constant.Integer(read_i4(p));
break;
case CONSTANT_Long:
ci = new Constant.Long(read_i8(p));
break;
// in Java 1.5, LDC_W can read
// "class constants"
case CONSTANT_Class:
ci = new Constant.Class((Constant.Utf8) getConstant(read_u2(p)));
break;
default:
throw new RuntimeException("unreachable code reached");
}
// cache the pool item
constantPool.put(index,ci);
return ci;
}
/**
* Get the type of the constant value from this classfile's constant pool.
*
* @param index
* @return JvmType object representing type of value
*/
public final JvmType getConstantType(int index) {
// index points to constant pool entry
int type = read_u1(items[index]-1);
//int p = items[index];
switch (type) {
case CONSTANT_String:
if(getConstant(index) != null) {
return JvmTypes.JAVA_LANG_STRING;
} else {
return JvmTypes.NULL;
}
case CONSTANT_Double:
return JvmTypes.DOUBLE;
case CONSTANT_Float:
return JvmTypes.FLOAT;
case CONSTANT_Integer:
return JvmTypes.INT;
case CONSTANT_Long:
return JvmTypes.LONG;
// in Java 1.5, LDC_W can read
// "class constants"
case CONSTANT_Class:
// FIXME: this is broken, since Class takes a generic param.
return new JvmType.Clazz("java.lang","Class");
default:
throw new RuntimeException("unreachable code reached!");
}
}
private static final boolean matchedInnerTypes(JvmType.Clazz t1, JvmType.Clazz t2) {
List>> t1comps = t1.components();
List>> t2comps = t2.components();
int t1comps_size = t1comps.size();
if (!t1.pkg().equals(t2.pkg()) || t1comps_size != t2comps.size()) {
return false;
}
for (int i = 0; i != t1comps_size; ++i) {
String t1s = t1comps.get(i).first();
String t2s = t2comps.get(i).first();
if (!t1s.equals(t2s)) {
return false;
}
}
return true;
}
/**
* This method is slightly ugly and it would be nice to get rid of it.
* It's used for getting the name of an exception.
*
* @param index
* @return
*/
public final String getClassName(int index) {
return getString(read_u2(items[index]));
}
final int read_i1(int index) { return bytes[index]; }
final int read_u1(int index) { return bytes[index] & 0xFF; }
// I think this method should be renamed!
final int read_u2(int index, int offset) {
return read_u2(items[index]+offset);
}
final int read_u2(int index) {
return ((bytes[index] & 0xFF) << 8) | (bytes[index + 1] & 0xFF);
}
final short read_i2(int index) {
return (short) (((bytes[index] & 0xFF) << 8) | (bytes[index + 1] & 0xFF));
}
final int read_i4(int index) {
return ((bytes[index] & 0xFF) << 24) | ((bytes[index + 1] & 0xFF) << 16)
| ((bytes[index + 2] & 0xFF) << 8) | (bytes[index + 3] & 0xFF);
}
final long read_i8(int index) {
long upper = read_i4(index);
long lower = read_i4(index + 4) & 0xFFFFFFFFL;
return (upper << 32) | lower;
}
final float readFloat(int index) {
return Float.intBitsToFloat(read_i4(index));
}
final double readDouble(int index) {
return Double.longBitsToDouble(read_i8(index));
}
final String readUTF8(int index) {
// index points to constant pool entry
// which is a CONSTANT_Utf8_info construct
int p = items[index];
int length = read_u2(p);
try {
return new String(bytes,p+2,length,"UTF-8");
} catch(UnsupportedEncodingException e) {
throw new RuntimeException("UTF-8 Charset not supported?");
}
}
protected static byte[] readStream(final InputStream is) throws IOException {
// read in class
byte[] b = new byte[is.available()];
int length = 0;
while(true) {
// read as much as possible in one chunk!
int n = is.read(b, length, b.length - length);
if(n == -1) {
// end of stream!
if(length < b.length) {
// strip out unused bytes at end of array
byte[] c = new byte[length];
System.arraycopy(b, 0, c, 0, length);
b = c;
}
return b;
}
length += n;
if(length == b.length) {
// deal with overflow!
byte[] c = new byte[b.length + 1000];
System.arraycopy(b, 0, c, 0, length);
b = c;
}
}
}
/**
* This class represents a class constant. It is needed to distinguish between a
* class constant and a general String.
*
* @author David J. Pearce
*
*/
protected static class ClassConstant {
private String name;
public ClassConstant(String n) {
name = n;
}
public String name() { return name; }
}
// tags for constant pool entries
protected static final int CONSTANT_Class = 7;
protected static final int CONSTANT_FieldRef = 9;
protected static final int CONSTANT_MethodRef = 10;
protected static final int CONSTANT_InterfaceMethodRef = 11;
protected static final int CONSTANT_String = 8;
protected static final int CONSTANT_Integer = 3;
protected static final int CONSTANT_Float = 4;
protected static final int CONSTANT_Long = 5;
protected static final int CONSTANT_Double = 6;
protected static final int CONSTANT_NameAndType = 12;
protected static final int CONSTANT_Utf8 = 1;
// from the VM SPEC
public static final byte VM_BOOLEAN = 4;
public static final byte VM_CHAR = 5;
public static final byte VM_FLOAT = 6;
public static final byte VM_DOUBLE = 7;
public static final byte VM_BYTE = 8;
public static final byte VM_SHORT = 9;
public static final byte VM_INT = 10;
public static final byte VM_LONG = 11;
// RESULT TYPES.
static final int TYPE_SHIFT = 6;
static final int TYPE_MASK = 15 << TYPE_SHIFT;
static final int T_VOID = 0 << TYPE_SHIFT; // no result type
static final int T_BYTE = 1 << TYPE_SHIFT;
static final int T_CHAR = 2 << TYPE_SHIFT;
static final int T_SHORT = 3 << TYPE_SHIFT;
static final int T_INT = 4 << TYPE_SHIFT;
static final int T_LONG = 5 << TYPE_SHIFT;
static final int T_FLOAT = 6 << TYPE_SHIFT;
static final int T_DOUBLE = 7 << TYPE_SHIFT;
static final int T_REF = 8 << TYPE_SHIFT;
static final int T_ARRAY = 9 << TYPE_SHIFT;
// SRC TYPES. Used for the CONVERT instructions
static final int SRCTYPE_SHIFT = 10;
static final int SRCTYPE_MASK = 3 << SRCTYPE_SHIFT;
static final int S_INT = 0 << SRCTYPE_SHIFT;
static final int S_LONG = 1 << SRCTYPE_SHIFT;
static final int S_FLOAT = 2 << SRCTYPE_SHIFT;
static final int S_DOUBLE = 3 << SRCTYPE_SHIFT;
// INSTRUCTION FORMATS. These determine the different instruction formats.
static final int FMT_SHIFT = 12;
static final int FMT_MASK = 31 << FMT_SHIFT;
static final int FMT_EMPTY = 0 << FMT_SHIFT;
static final int FMT_I8 = 1 << FMT_SHIFT;
static final int FMT_I16 = 2 << FMT_SHIFT;
static final int FMT_TYPEINDEX16 = 4 << FMT_SHIFT; // INDEX into runtime pool for Type Descriptor
static final int FMT_TYPEAINDEX16 = 5 << FMT_SHIFT; // INDEX into runtime pool for Type Descriptor
static final int FMT_TYPEINDEX16_U8 = 6 << FMT_SHIFT; // INDEX into runtime pool for Type Descriptor
static final int FMT_CONSTINDEX8 = 7 << FMT_SHIFT; // INDEX into runtime pool for constant
static final int FMT_CONSTINDEX16 = 8 << FMT_SHIFT; // INDEX into runtime pool for constant
static final int FMT_FIELDINDEX16 = 9 << FMT_SHIFT; // INDEX into runtime pool for field
static final int FMT_METHODINDEX16 = 10 << FMT_SHIFT; // INDEX into runtime pool for method
static final int FMT_METHODINDEX16_U8_0 = 11 << FMT_SHIFT; // INDEX into runtime pool
static final int FMT_VARIDX = 12 << FMT_SHIFT; // INDEX into local var array (1 byte)
static final int FMT_VARIDX_I8 = 13 << FMT_SHIFT;
static final int FMT_ATYPE = 14 << FMT_SHIFT; // USED ONLY FOR NEWARRAY
static final int FMT_TABLESWITCH = 15 << FMT_SHIFT;
static final int FMT_LOOKUPSWITCH = 16 << FMT_SHIFT;
static final int FMT_TARGET16 = 17 << FMT_SHIFT;
static final int FMT_TARGET32 = 18 << FMT_SHIFT;
static final int FMT_INTM1 = 19 << FMT_SHIFT;
static final int FMT_INT0 = 20 << FMT_SHIFT;
static final int FMT_INT1 = 21 << FMT_SHIFT;
static final int FMT_INT2 = 22 << FMT_SHIFT;
static final int FMT_INT3 = 23 << FMT_SHIFT;
static final int FMT_INT4 = 24 << FMT_SHIFT;
static final int FMT_INT5 = 25 << FMT_SHIFT;
static final int FMT_INTNULL = 26 << FMT_SHIFT;
static final int MOD_SHIFT = 17;
static final int MOD_MASK = 3 << MOD_SHIFT;
static final int MOD_VIRTUAL = 0 << MOD_SHIFT;
static final int MOD_STATIC = 1 << MOD_SHIFT;
static final int MOD_SPECIAL = 2 << MOD_SHIFT;
static final int MOD_INTERFACE = 3 << MOD_SHIFT;
public static final int INSN_MASK = 63;
public static final int WIDE_INSN = 18;
// This table contains all the important info!
// It was constructed by hand, which was a bit of a pain ...
public static final int NOP = 0;
public static final int LOADVAR = 1;
public static final int STOREVAR = 2;
public static final int LOADCONST = 3;
public static final int STORECONST = 4;
public static final int ARRAYLOAD = 5;
public static final int ARRAYSTORE = 6;
public static final int ARRAYLENGTH = 7;
public static final int IINC = 8;
public static final int NEW = 9;
public static final int THROW = 10;
public static final int CHECKCAST = 11;
public static final int INSTANCEOF = 12;
public static final int MONITORENTER = 13;
public static final int MONITOREXIT = 14;
public static final int SWITCH = 15;
public static final int CONVERT = 16;
// STACK INSTRUCTIONS
public static final int POP = 19;
public static final int DUP = 20;
public static final int DUPX1 = 21;
public static final int DUPX2 = 22;
public static final int SWAP = 23;
// ARITHMETIC INSTRUCTIONS
public static final int ADD = 24;
public static final int SUB = 25;
public static final int MUL = 26;
public static final int DIV = 27;
public static final int REM = 28;
public static final int NEG = 29;
public static final int SHL = 30;
public static final int SHR = 31;
public static final int USHR = 32;
public static final int AND = 33;
public static final int OR = 34;
public static final int XOR = 35;
public static final int CMP = 36;
public static final int CMPL = 37;
public static final int CMPG = 38;
// BRANCHING INSTRUCTIONS
public static final int IF = 39;
public static final int IFCMP = 40;
public static final int GOTO = 41;
public static final int JSR = 42;
public static final int RET = 43;
public static final int RETURN = 44;
// INDIRECT INSTRUCTIONS
public static final int FIELDLOAD = 45;
public static final int FIELDSTORE = 46;
public static final int INVOKE = 47;
public static final int EQUALS = 0;
public static final int NOTEQUALS = 1;
public static final int LESSTHAN = 2;
public static final int GREATERTHANEQUALS = 3;
public static final int GREATERTHAN = 4;
public static final int LESSTHANEQUALS = 5;
public static final int NULL = 6;
public static final int NONNULL = 7;
public static final int[] opmap = new int[] {
NOP | FMT_EMPTY, // NOP = 0;
LOADCONST | FMT_INTNULL | T_REF, // ACONST_NULL = 1;
LOADCONST | FMT_INTM1 | T_INT, // ICONST_M1 = 2;
LOADCONST | FMT_INT0 | T_INT, // ICONST_0 = 3;
LOADCONST | FMT_INT1 | T_INT, // ICONST_1 = 4;
LOADCONST | FMT_INT2 | T_INT, // ICONST_2 = 5;
LOADCONST | FMT_INT3 | T_INT, // ICONST_3 = 6;
LOADCONST | FMT_INT4 | T_INT, // ICONST_4 = 7;
LOADCONST | FMT_INT5 | T_INT, // ICONST_5 = 8;
LOADCONST | FMT_INT0 | T_LONG, // LCONST_0 = 9;
LOADCONST | FMT_INT1 | T_LONG, // LCONST_1 = 10;
LOADCONST | FMT_INT0 | T_FLOAT, // FCONST_0 = 11;
LOADCONST | FMT_INT1 | T_FLOAT, // FCONST_1 = 12;
LOADCONST | FMT_INT2 | T_FLOAT, // FCONST_2 = 13;
LOADCONST | FMT_INT0 | T_DOUBLE, // DCONST_0 = 14;
LOADCONST | FMT_INT1 | T_DOUBLE, // DCONST_1 = 15;
LOADCONST | FMT_I8 | T_INT, // BIPUSH = 16;
LOADCONST | FMT_I16 | T_INT, // SIPUSH = 17;
LOADCONST | FMT_CONSTINDEX8, // LDC = 18
LOADCONST | FMT_CONSTINDEX16, // LDC_W = 19
LOADCONST | FMT_CONSTINDEX16, // LDC2_W = 20
LOADVAR | FMT_VARIDX | T_INT, // ILOAD = 21
LOADVAR | FMT_VARIDX | T_LONG, // LLOAD = 22
LOADVAR | FMT_VARIDX | T_FLOAT, // FLOAD = 23
LOADVAR | FMT_VARIDX | T_DOUBLE, // DLOAD = 24
LOADVAR | FMT_VARIDX | T_REF, // ALOAD = 25
LOADVAR | FMT_INT0 | T_INT, // ILOAD_0 = 26
LOADVAR | FMT_INT1 | T_INT, // ILOAD_1 = 27
LOADVAR | FMT_INT2 | T_INT, // ILOAD_2 = 28
LOADVAR | FMT_INT3 | T_INT, // ILOAD_3 = 29
LOADVAR | FMT_INT0 | T_LONG, // LLOAD_0 = 30
LOADVAR | FMT_INT1 | T_LONG, // LLOAD_1 = 31
LOADVAR | FMT_INT2 | T_LONG, // LLOAD_2 = 32
LOADVAR | FMT_INT3 | T_LONG, // LLOAD_3 = 33
LOADVAR | FMT_INT0 | T_FLOAT, // FLOAD_0 = 34
LOADVAR | FMT_INT1 | T_FLOAT, // FLOAD_1 = 35
LOADVAR | FMT_INT2 | T_FLOAT, // FLOAD_2 = 36
LOADVAR | FMT_INT3 | T_FLOAT, // FLOAD_3 = 37
LOADVAR | FMT_INT0 | T_DOUBLE, // DLOAD_0 = 38
LOADVAR | FMT_INT1 | T_DOUBLE, // DLOAD_1 = 39
LOADVAR | FMT_INT2 | T_DOUBLE, // DLOAD_2 = 40
LOADVAR | FMT_INT3 | T_DOUBLE, // DLOAD_3 = 41
LOADVAR | FMT_INT0 | T_REF, // ALOAD_0 = 42
LOADVAR | FMT_INT1 | T_REF, // ALOAD_1 = 43
LOADVAR | FMT_INT2 | T_REF, // ALOAD_2 = 44
LOADVAR | FMT_INT3 | T_REF, // ALOAD_3 = 45
ARRAYLOAD | FMT_EMPTY | T_INT, // IALOAD = 46
ARRAYLOAD | FMT_EMPTY | T_LONG, // LALOAD = 47
ARRAYLOAD | FMT_EMPTY | T_FLOAT, // FALOAD = 48
ARRAYLOAD | FMT_EMPTY | T_DOUBLE, // DALOAD = 49
ARRAYLOAD | FMT_EMPTY | T_REF, // AALOAD = 50
ARRAYLOAD | FMT_EMPTY | T_BYTE, // BALOAD = 51
ARRAYLOAD | FMT_EMPTY | T_CHAR, // AALOAD = 52
ARRAYLOAD | FMT_EMPTY | T_SHORT, // SALOAD = 53
STOREVAR | FMT_VARIDX | T_INT, // ISTORE = 54;
STOREVAR | FMT_VARIDX | T_LONG, // LSTORE = 55;
STOREVAR | FMT_VARIDX | T_FLOAT, // FSTORE = 56;
STOREVAR | FMT_VARIDX | T_DOUBLE, // DSTORE = 57;
STOREVAR | FMT_VARIDX | T_REF, // ASTORE = 58;
STOREVAR | FMT_INT0 | T_INT, // ISTORE_0 = 59;
STOREVAR | FMT_INT1 | T_INT, // ISTORE_1 = 60;
STOREVAR | FMT_INT2 | T_INT, // ISTORE_2 = 61;
STOREVAR | FMT_INT3 | T_INT, // ISTORE_3 = 62;
STOREVAR | FMT_INT0 | T_LONG, // LSTORE_0 = 63;
STOREVAR | FMT_INT1 | T_LONG, // LSTORE_1 = 64;
STOREVAR | FMT_INT2 | T_LONG, // LSTORE_2 = 65;
STOREVAR | FMT_INT3 | T_LONG, // LSTORE_3 = 66;
STOREVAR | FMT_INT0 | T_FLOAT, // FSTORE_0 = 67;
STOREVAR | FMT_INT1 | T_FLOAT, // FSTORE_1 = 68;
STOREVAR | FMT_INT2 | T_FLOAT, // FSTORE_2 = 69;
STOREVAR | FMT_INT3 | T_FLOAT, // FSTORE_3 = 70;
STOREVAR | FMT_INT0 | T_DOUBLE, // DSTORE_0 = 71;
STOREVAR | FMT_INT1 | T_DOUBLE, // DSTORE_1 = 72;
STOREVAR | FMT_INT2 | T_DOUBLE, // DSTORE_2 = 73;
STOREVAR | FMT_INT3 | T_DOUBLE, // DSTORE_3 = 74;
STOREVAR | FMT_INT0 | T_REF, // ASTORE_0 = 75;
STOREVAR | FMT_INT1 | T_REF, // ASTORE_1 = 76;
STOREVAR | FMT_INT2 | T_REF, // ASTORE_2 = 77;
STOREVAR | FMT_INT3 | T_REF, // ASTORE_3 = 78;
ARRAYSTORE | FMT_EMPTY | T_INT, // IASTORE = 79;
ARRAYSTORE | FMT_EMPTY | T_LONG, // LASTORE = 80;
ARRAYSTORE | FMT_EMPTY | T_FLOAT, // FASTORE = 81;
ARRAYSTORE | FMT_EMPTY | T_DOUBLE, // DASTORE = 82;
ARRAYSTORE | FMT_EMPTY | T_REF, // AASTORE = 83;
ARRAYSTORE | FMT_EMPTY | T_BYTE, // BASTORE = 84
ARRAYSTORE | FMT_EMPTY | T_CHAR, // CASTORE = 85;
ARRAYSTORE | FMT_EMPTY | T_SHORT, // SASTORE = 86;
POP | FMT_EMPTY, // POP = 87;
POP | FMT_EMPTY, // POP2 = 88;
DUP | FMT_EMPTY, // DUP = 89
// these ones are a real pain to deal with :(
DUPX1 | FMT_EMPTY, // DUP_X1 = 90
DUPX2 | FMT_EMPTY, // DUP_X2 = 91
DUP | FMT_EMPTY, // DUP2 = 92;
// again, these ones are a real pain to deal with :(
DUPX1 | FMT_EMPTY, // DUP2_X1 = 93
DUPX2 | FMT_EMPTY, // DUP2_X2 = 94
// PROBLEM HERE: how to specify no change to stack?
SWAP | FMT_EMPTY, // SWAP = 95;
ADD | FMT_EMPTY | T_INT, // IADD = 96
ADD | FMT_EMPTY | T_LONG, // LADD = 97
ADD | FMT_EMPTY | T_FLOAT, // FADD = 98
ADD | FMT_EMPTY | T_DOUBLE, // DADD = 99
SUB | FMT_EMPTY | T_INT, // ISUB = 100
SUB | FMT_EMPTY | T_LONG, // LSUB = 101
SUB | FMT_EMPTY | T_FLOAT, // FSUB = 102
SUB | FMT_EMPTY | T_DOUBLE, // DSUB = 103
MUL | FMT_EMPTY | T_INT, // IMUL = 104
MUL | FMT_EMPTY | T_LONG, // LMUL = 105
MUL | FMT_EMPTY | T_FLOAT, // FMUL = 106
MUL | FMT_EMPTY | T_DOUBLE, // DMUL = 107
DIV | FMT_EMPTY | T_INT, // IDIV = 108
DIV | FMT_EMPTY | T_LONG, // LDIV = 109
DIV | FMT_EMPTY | T_FLOAT, // FDIV = 110
DIV | FMT_EMPTY | T_DOUBLE, // DDIV = 111
REM | FMT_EMPTY | T_INT, // IREM = 112
REM | FMT_EMPTY | T_LONG, // LREM = 113
REM | FMT_EMPTY | T_FLOAT, // FREM = 114
REM | FMT_EMPTY | T_DOUBLE, // DREM = 115
NEG | FMT_EMPTY | T_INT, // INEG = 116
NEG | FMT_EMPTY | T_LONG, // LNEG = 117
NEG | FMT_EMPTY | T_FLOAT, // FNEG = 118
NEG | FMT_EMPTY | T_DOUBLE, // DNEG = 119
SHL | FMT_EMPTY | T_INT, // ISHL = 120
SHL | FMT_EMPTY | T_LONG, // LSHL = 121
SHR | FMT_EMPTY | T_INT, // ISHR = 122
SHR | FMT_EMPTY | T_LONG, // LSHR = 123
USHR | FMT_EMPTY | T_INT, // IUSHR = 124
USHR | FMT_EMPTY | T_LONG, // LUSHR = 125
AND | FMT_EMPTY | T_INT, // IAND = 126
AND | FMT_EMPTY | T_LONG, // LAND = 127
OR | FMT_EMPTY | T_INT, // IXOR = 128
OR | FMT_EMPTY | T_LONG, // LXOR = 129
XOR | FMT_EMPTY | T_INT, // IXOR = 130
XOR | FMT_EMPTY | T_LONG, // LXOR = 131
IINC | FMT_VARIDX_I8, // IINC = 132
CONVERT | FMT_EMPTY | S_INT | T_LONG, // I2L = 133
CONVERT | FMT_EMPTY | S_INT | T_FLOAT, // I2F = 134
CONVERT | FMT_EMPTY | S_INT | T_DOUBLE, // I2D = 135
CONVERT | FMT_EMPTY | S_LONG | T_INT, // L2I = 136
CONVERT | FMT_EMPTY | S_LONG | T_FLOAT, // L2F = 137
CONVERT | FMT_EMPTY | S_LONG | T_DOUBLE, // L2D = 138
CONVERT | FMT_EMPTY | S_FLOAT | T_INT, // F2I = 139
CONVERT | FMT_EMPTY | S_FLOAT | T_LONG, // F2L = 140
CONVERT | FMT_EMPTY | S_FLOAT | T_DOUBLE, // F2D = 141
CONVERT | FMT_EMPTY | S_DOUBLE | T_INT, // D2I = 142
CONVERT | FMT_EMPTY | S_DOUBLE | T_LONG, // D2L = 143
CONVERT | FMT_EMPTY | S_DOUBLE | T_FLOAT, // D2F = 144
CONVERT | FMT_EMPTY | S_INT | T_BYTE, // I2B = 145
CONVERT | FMT_EMPTY | S_INT | T_CHAR, // I2C = 146
CONVERT | FMT_EMPTY | S_INT | T_SHORT, // I2S = 147
CMP | FMT_EMPTY | T_LONG, // LCMP = 148
// why oh why are these done this way?
CMPL | FMT_EMPTY | T_FLOAT, // FCMPL = 149
CMPG | FMT_EMPTY | T_FLOAT, // FCMPG = 150
CMPL | FMT_EMPTY | T_DOUBLE, // DCMPL = 151
CMPG | FMT_EMPTY | T_DOUBLE, // DCMPG = 152
IF | FMT_TARGET16 | T_INT, // IFEQ = 153;
IF | FMT_TARGET16 | T_INT, // IFNE = 154;
IF | FMT_TARGET16 | T_INT, // IFLT = 155;
IF | FMT_TARGET16 | T_INT, // IFGE = 156;
IF | FMT_TARGET16 | T_INT, // IFGT = 157;
IF | FMT_TARGET16 | T_INT, // IFLE = 158;
IFCMP | FMT_TARGET16 | T_INT, // IF_ICMPEQ = 159;
IFCMP | FMT_TARGET16 | T_INT, // IF_ICMPNE = 160;
IFCMP | FMT_TARGET16 | T_INT, // IF_ICMPLT = 161;
IFCMP | FMT_TARGET16 | T_INT, // IF_ICMPGE = 162;
IFCMP | FMT_TARGET16 | T_INT, // IF_ICMPGT = 163;
IFCMP | FMT_TARGET16 | T_INT, // IF_ICMPLE = 164;
IFCMP | FMT_TARGET16 | T_REF, // IF_ACMPEQ = 165;
IFCMP | FMT_TARGET16 | T_REF, // IF_ACMPNE = 166;
GOTO | FMT_TARGET16, // GOTO = 167;
JSR | FMT_TARGET16, // JSR = 168;
RET, // RET = 169;
SWITCH | FMT_TABLESWITCH, // TABLESWITCH = 170;
SWITCH | FMT_LOOKUPSWITCH, // LOOKUPSWITCH = 171;
RETURN | FMT_EMPTY | T_INT, // IRETURN = 172;
RETURN | FMT_EMPTY | T_LONG, // LRETURN = 173;
RETURN | FMT_EMPTY | T_FLOAT, // FRETURN = 174;
RETURN | FMT_EMPTY | T_DOUBLE, // DRETURN = 175;
RETURN | FMT_EMPTY | T_REF, // ARETURN = 176;
RETURN | FMT_EMPTY, // RETURN = 177;
FIELDLOAD | MOD_STATIC | FMT_FIELDINDEX16, // GETSTATIC = 178;
FIELDSTORE | MOD_STATIC | FMT_FIELDINDEX16, // PUTSTATIC = 179;
FIELDLOAD | FMT_FIELDINDEX16, // GETFIELD = 180;
FIELDSTORE | FMT_FIELDINDEX16, // PUTFIELD = 181;
INVOKE | FMT_METHODINDEX16, // INVOKEVIRTUAL = 182;
INVOKE | MOD_SPECIAL | FMT_METHODINDEX16, // INVOKESPECIAL = 183;
INVOKE | MOD_STATIC | FMT_METHODINDEX16, // INVOKESTATIC = 184;
INVOKE | MOD_INTERFACE | FMT_METHODINDEX16_U8_0, // INVOKEINTERFACE = 185;
0, // UNUSED = 186;
NEW | FMT_TYPEINDEX16, // NEW = 187
NEW | FMT_ATYPE, // NEWARRAY = 188
NEW | FMT_TYPEAINDEX16, // ANEWARRAY = 189
ARRAYLENGTH | FMT_EMPTY, // ARRAYLENGTH = 190;
THROW | FMT_EMPTY, // ATHROW = 191
CHECKCAST | FMT_TYPEINDEX16, // CHECKCAST = 192;
INSTANCEOF | FMT_TYPEINDEX16, // INSTANCEOF = 193;
MONITORENTER | FMT_EMPTY, // MONITORENTER = 194;
MONITOREXIT | FMT_EMPTY, // MONITOREXIT = 195;
WIDE_INSN, // WIDE = 196;
NEW | FMT_TYPEINDEX16_U8, // MULTIANEWARRAY = 197;
IF | FMT_TARGET16 | T_REF, // IFNULL = 198;
IF | FMT_TARGET16 | T_REF, // IFNONNULL = 199;
GOTO | FMT_TARGET32, // GOTO_W = 200;
JSR | FMT_TARGET32, // JSR_W = 201;
};
}