
jasm.io.ClassFileReader Maven / Gradle / Ivy
// 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;
};
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy