All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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