diff src/com/go/trove/classfile/CodeBuilder.java @ 0:3dc0c5604566

Initial checkin of blitz 2.0 fcs - no installer yet.
author Dan Creswell <dan.creswell@gmail.com>
date Sat, 21 Mar 2009 11:00:06 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/com/go/trove/classfile/CodeBuilder.java	Sat Mar 21 11:00:06 2009 +0000
@@ -0,0 +1,1844 @@
+/* ====================================================================
+ * Trove - Copyright (c) 1997-2000 Walt Disney Internet Group
+ * ====================================================================
+ * The Tea Software License, Version 1.1
+ *
+ * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. 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.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:
+ *       "This product includes software developed by the
+ *        Walt Disney Internet Group (http://opensource.go.com/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
+ *    not be used to endorse or promote products derived from this
+ *    software without prior written permission. For written
+ *    permission, please contact opensource@dig.com.
+ *
+ * 5. Products derived from this software may not be called "Tea",
+ *    "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
+ *    "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
+ *    written permission of the Walt Disney Internet Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 THE WALT DISNEY INTERNET GROUP OR ITS
+ * CONTRIBUTORS 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.
+ * ====================================================================
+ *
+ * For more information about Tea, please see http://opensource.go.com/.
+ */
+
+package com.go.trove.classfile;
+
+import java.lang.reflect.*;
+
+/******************************************************************************
+ * This class is used as an aid in generating code for a method.
+ * It controls the max stack, local variable allocation, labels and bytecode.
+ * 
+ * @author Brian S O'Neill
+ * @version
+ * <!--$$Revision: 1.1 $-->, <!--$$JustDate:--> 01/05/14 <!-- $-->
+ */
+public class CodeBuilder implements CodeBuffer, CodeAssembler {
+    private CodeAttr mCodeAttr;
+    private ClassFile mClassFile;
+    private ConstantPool mCp;
+
+    private InstructionList mInstructions = new InstructionList();
+
+    private LocalVariable mThisReference;
+    private LocalVariable[] mParameters;
+
+    private boolean mSaveLineNumberInfo;
+    private boolean mSaveLocalVariableInfo;
+
+    /**
+     * Construct a CodeBuilder for the CodeAttr of the given MethodInfo. The
+     * CodeBuffer for the CodeAttr is automatically set to this CodeBuilder.
+     */
+    public CodeBuilder(MethodInfo info) {
+        this(info, true, false);
+    }
+
+    /**
+     * Construct a CodeBuilder for the CodeAttr of the given MethodInfo. The
+     * CodeBuffer for the CodeAttr is automatically set to this CodeBuilder.
+     *
+     * @param saveLineNumberInfo When set false, all calls to mapLineNumber
+     * are ignored. By default, this value is true.
+     *
+     * @param saveLocalVariableInfo When set true, all local variable
+     * usage information is saved in the ClassFile. By default, this value
+     * is false.
+     *
+     * @see #mapLineNumber
+     */
+    public CodeBuilder(MethodInfo info, boolean saveLineNumberInfo,
+                       boolean saveLocalVariableInfo) {
+        mCodeAttr = info.getCodeAttr();
+        mClassFile = info.getClassFile();
+        mCp = mClassFile.getConstantPool();
+
+        mCodeAttr.setCodeBuffer(this);
+
+        mSaveLineNumberInfo = saveLineNumberInfo;
+        mSaveLocalVariableInfo = saveLocalVariableInfo;
+
+        // Create LocalVariable references for "this" reference and other
+        // passed in parameters.
+
+        LocalVariable localVar;
+        int varNum = 0;
+
+        if (!info.getAccessFlags().isStatic()) {
+            localVar = mInstructions.createLocalParameter
+                ("this", mClassFile.getType(), varNum++);
+            mThisReference = localVar;
+
+            if (saveLocalVariableInfo) {
+                mCodeAttr.localVariableUse(localVar);
+            }
+        }
+
+        TypeDescriptor[] paramTypes = 
+            info.getMethodDescriptor().getParameterTypes();
+        int paramSize = paramTypes.length;
+
+        mParameters = new LocalVariable[paramSize];
+
+        for (int i = 0; i<paramTypes.length; i++) {
+            localVar = mInstructions.createLocalParameter
+                (null, paramTypes[i], varNum);
+            varNum += (localVar.isDoubleWord() ? 2 : 1);
+            mParameters[i] = localVar;
+
+            if (saveLocalVariableInfo) {
+                mCodeAttr.localVariableUse(localVar);
+            }
+        }
+    }
+
+    public int getMaxStackDepth() {
+        return mInstructions.getMaxStackDepth();
+    }
+
+    public int getMaxLocals() {
+        return mInstructions.getMaxLocals();
+    }
+
+    public byte[] getByteCodes() {
+        return mInstructions.getByteCodes();
+    }
+
+    public ExceptionHandler[] getExceptionHandlers() {
+        return mInstructions.getExceptionHandlers();
+    }
+
+    private void addCode(int stackAdjust, byte opcode) {
+        mInstructions.new CodeInstruction(stackAdjust, new byte[] {opcode});
+    }
+
+    private void addCode(int stackAdjust, byte opcode, byte operand) {
+        mInstructions.new CodeInstruction
+            (stackAdjust, new byte[] {opcode, operand});
+    }
+
+    private void addCode(int stackAdjust, byte opcode, short operand) {
+        mInstructions.new CodeInstruction
+            (stackAdjust, 
+             new byte[] {opcode, (byte)(operand >> 8), (byte)operand});
+    }
+
+    private void addCode(int stackAdjust, byte opcode, int operand) {
+        byte[] bytes = new byte[5];
+
+        bytes[0] = opcode;
+        bytes[1] = (byte)(operand >> 24);
+        bytes[2] = (byte)(operand >> 16);
+        bytes[3] = (byte)(operand >> 8);
+        bytes[4] = (byte)operand;
+
+        mInstructions.new CodeInstruction(stackAdjust, bytes);
+    }
+
+    private void addCode(int stackAdjust, byte opcode, ConstantInfo info) {
+        // The zeros get filled in later, when the ConstantInfo index 
+        // is resolved.
+        mInstructions.new ConstantOperandInstruction
+            (stackAdjust,
+             new byte[] {opcode, (byte)0, (byte)0}, info);
+    }
+
+    /**
+     * Returns LocalVariable references for all the parameters passed into
+     * the method being assembled, not including any "this" reference.
+     * Returns a zero-length array if there are no passed in parameters.
+     *
+     * <p>The names of the LocalVariables returned by this method are initially
+     * set to null. It is encouraged that a name be provided.
+     */
+    public LocalVariable[] getParameters() {
+        return (LocalVariable[])mParameters.clone();
+    }
+
+    /**
+     * Creates a LocalVariable reference from a name and type. Although name
+     * is optional, it is encouraged that a name be provided. Names do not 
+     * need to be unique.
+     *
+     * @param name Optional name for the LocalVariable.
+     * @param type The type of data that the requested LocalVariable can 
+     * store. 
+     */
+    public LocalVariable createLocalVariable(String name, 
+                                             TypeDescriptor type) {
+        LocalVariable localVar = mInstructions.createLocalVariable(name, type);
+
+        if (mSaveLocalVariableInfo) {
+            mCodeAttr.localVariableUse(localVar);
+        }
+
+        return localVar;
+    }
+
+    /**
+     * Creates a label, whose location must be set. To create a label and
+     * locate it here, the following example demonstrates how the call to
+     * setLocation can be chained:
+     *
+     * <pre>
+     * CodeBuilder builder;
+     * ...
+     * Label label = builder.createLabel().setLocation();
+     * </pre>
+     *
+     * @see Label#setLocation
+     */ 
+    public Label createLabel() {
+        return mInstructions.new LabelInstruction();
+    }
+
+    /**
+     * Sets up an exception handler located here, the location of the next
+     * code to be generated.
+     *
+     * @param startLocation Location or label at the start of the section of
+     * code to be wrapped by an exception handler.
+     * @param endLocation Location or label directly after the end of the
+     * section of code.
+     * @param catchClassName The name of the type of exception to be caught;
+     * if null, then catch every object.
+     */
+    public void exceptionHandler(Location startLocation,
+                                 Location endLocation,
+                                 String catchClassName) {
+        Location catchLocation = createLabel().setLocation();
+
+        ConstantClassInfo catchClass;
+        if (catchClassName == null) {
+            catchClass = null;
+        }
+        else {
+            catchClass = ConstantClassInfo.make(mCp, catchClassName);
+        }
+
+        ExceptionHandler handler = 
+            new ExceptionHandler(startLocation, endLocation, 
+                                 catchLocation, catchClass);
+
+        mInstructions.addExceptionHandler(handler);
+    }
+    
+    /**
+     * Map the location of the next code to be generated to a line number 
+     * in source code. This enables line numbers in a stack trace from the 
+     * generated code.
+     */
+    public void mapLineNumber(int lineNumber) {
+        if (mSaveLineNumberInfo) {
+            mCodeAttr.mapLineNumber(createLabel().setLocation(), lineNumber);
+        }
+    }
+
+    // load-constant-to-stack style instructions
+
+    /**
+     * Generates code that loads a constant string value onto the stack.
+     * If value is null, the generated code loads a null onto the stack.
+     * Strings that exceed 65535 UTF encoded bytes in length are loaded by
+     * creating a StringBuffer, appending substrings, and then converting to a
+     * String.
+     */
+    public void loadConstant(String value) {
+        if (value == null) {
+            addCode(1, Opcode.ACONST_NULL);
+            return;
+        }
+
+        int strlen = value.length();
+
+        if (strlen <= (65535 / 3)) {
+            // Guaranteed to fit in a Java UTF encoded string.
+            ConstantInfo info = ConstantStringInfo.make(mCp, value);
+            mInstructions.new LoadConstantInstruction(1, info);
+            return;
+        }
+
+        // Compute actual UTF length.
+
+        int utflen = 0;
+
+        for (int i=0; i<strlen; i++) {
+            int c = value.charAt(i);
+            if ((c >= 0x0001) && (c <= 0x007F)) {
+                utflen++;
+            }
+            else if (c > 0x07FF) {
+                utflen += 3;
+            }
+            else {
+                utflen += 2;
+            }
+        }
+
+        if (utflen <= 65535) {
+            ConstantInfo info = ConstantStringInfo.make(mCp, value);
+            mInstructions.new LoadConstantInstruction(1, info);
+            return;
+        }
+
+        // Break string up into chunks and construct in a StringBuffer.
+
+        TypeDescriptor stringBufferDesc = 
+            new TypeDescriptor(StringBuffer.class);
+        
+        TypeDescriptor intDesc = new TypeDescriptor(int.class);
+        TypeDescriptor stringDesc = new TypeDescriptor(String.class);
+        TypeDescriptor[] stringParam = new TypeDescriptor[] {stringDesc};
+        
+        newObject(stringBufferDesc);
+        dup();
+        loadConstant(strlen);
+        invokeConstructor("java.lang.StringBuffer",
+                          new TypeDescriptor[] {intDesc});
+            
+        int beginIndex;
+        int endIndex = 0;
+        
+        while (endIndex < strlen) {
+            beginIndex = endIndex;
+
+            // Make each chunk as large as possible.
+            utflen = 0;
+            for (; endIndex < strlen; endIndex++) {
+                int c = value.charAt(endIndex);
+                int size;
+                if ((c >= 0x0001) && (c <= 0x007F)) {
+                    size = 1;
+                }
+                else if (c > 0x07FF) {
+                    size = 3;
+                }
+                else {
+                    size = 2;
+                }
+
+                if ((utflen + size) > 65535) {
+                    break;
+                }
+                else {
+                    utflen += size;
+                }
+            }
+
+            String substr = value.substring(beginIndex, endIndex);
+
+            ConstantInfo info = ConstantStringInfo.make(mCp, substr);
+            mInstructions.new LoadConstantInstruction(1, info);
+
+            invokeVirtual("java.lang.StringBuffer", "append",
+                          stringBufferDesc, stringParam);
+        }
+        
+        invokeVirtual("java.lang.StringBuffer", "toString",
+                      stringDesc, null);
+    }
+
+    /**
+     * Generates code that loads a constant boolean value onto the stack.
+     */
+    public void loadConstant(boolean value) {
+        loadConstant(value?1:0);
+    }
+
+    /**
+     * Generates code that loads a constant int, char, short or byte value 
+     * onto the stack.
+     */
+    public void loadConstant(int value) {
+        if (-1 <= value && value <= 5) {
+            byte op;
+
+            switch(value) {
+            case -1:
+                op = Opcode.ICONST_M1;
+                break;
+            case 0:
+                op = Opcode.ICONST_0;
+                break;
+            case 1:
+                op = Opcode.ICONST_1;
+                break;
+            case 2:
+                op = Opcode.ICONST_2;
+                break;
+            case 3:
+                op = Opcode.ICONST_3;
+                break;
+            case 4:
+                op = Opcode.ICONST_4;
+                break;
+            case 5:
+                op = Opcode.ICONST_5;
+                break;
+            default:
+                op = Opcode.NOP;
+            }
+
+            addCode(1, op);
+        }
+        else if (-128 <= value && value <= 127) {
+            addCode(1, Opcode.BIPUSH, (byte)value);
+        }
+        else if (-32768 <= value && value <= 32767) {
+            addCode(1, Opcode.SIPUSH, (short)value);
+        }
+        else {
+            ConstantInfo info = ConstantIntegerInfo.make(mCp, value);
+            mInstructions.new LoadConstantInstruction(1, info);
+        }
+    }
+
+    /**
+     * Generates code that loads a constant long value onto the stack.
+     */
+    public void loadConstant(long value) {
+        if (value == 0) {
+            addCode(2, Opcode.LCONST_0);
+        }
+        else if (value == 1) {
+            addCode(2, Opcode.LCONST_1);
+        }
+        else {
+            ConstantInfo info = ConstantLongInfo.make(mCp, value);
+            mInstructions.new LoadConstantInstruction(2, info, true);
+        }
+    }
+
+    /**
+     * Generates code that loads a constant float value onto the stack.
+     */
+    public void loadConstant(float value) {
+        if (value == 0) {
+            addCode(1, Opcode.FCONST_0);
+        }
+        else if (value == 1) {
+            addCode(1, Opcode.FCONST_1);
+        }
+        else if (value == 2) {
+            addCode(1, Opcode.FCONST_2);
+        }
+        else {
+            ConstantInfo info = ConstantFloatInfo.make(mCp, value);
+            mInstructions.new LoadConstantInstruction(1, info);
+        }
+    }
+
+    /**
+     * Generates code that loads a constant double value onto the stack.
+     */
+    public void loadConstant(double value) {
+        if (value == 0) {
+            addCode(2, Opcode.DCONST_0);
+        }
+        else if (value == 1) {
+            addCode(2, Opcode.DCONST_1);
+        }
+        else {
+            ConstantInfo info = ConstantDoubleInfo.make(mCp, value);
+            mInstructions.new LoadConstantInstruction(2, info, true);
+        }
+    }
+
+    // load-local-to-stack style instructions
+
+    /**
+     * Generates code that loads a local variable onto the stack. Parameters
+     * passed to a method and the "this" reference are all considered local
+     * variables, as well as any that were created.
+     *
+     * @param local The local variable reference
+     * @see #getParameters
+     * @see #createLocalVariable
+     */
+    public void loadLocal(LocalVariable local) {
+        if (local == null) {
+            throw new NullPointerException("No local variable specified");
+        }
+
+        TypeDescriptor type = local.getType();
+        Class clazz = type.getClassArg();
+
+        int stackAdjust = 1;
+
+        if (clazz != null && type.getDimensions() == 0 && 
+            (clazz == long.class || clazz == double.class)) {
+
+            stackAdjust++;
+        }
+
+        mInstructions.new LoadLocalInstruction(stackAdjust, local);
+    }
+
+    /**
+     * Loads a reference to "this" onto the stack. Static methods have no 
+     * "this" reference, and an exception is thrown when attempting to
+     * generate "this" in a static method.
+     */
+    public void loadThis() {
+        if (mThisReference != null) {
+            loadLocal(mThisReference);
+        }
+        else {
+            throw new RuntimeException
+                ("Attempt to load \"this\" reference in a static method");
+        }
+    }
+
+    // store-from-stack-to-local style instructions
+
+    /**
+     * Generates code that pops a value off of the stack into a local variable.
+     * Parameters passed to a method and the "this" reference are all 
+     * considered local variables, as well as any that were created.
+     *
+     * @param local The local variable reference
+     * @see #getParameters
+     * @see #createLocalVariable
+     */
+    public void storeLocal(LocalVariable local) {
+        if (local == null) {
+            throw new NullPointerException("No local variable specified");
+        }
+
+        TypeDescriptor type = local.getType();
+        Class clazz = type.getClassArg();
+
+        int stackAdjust = -1;
+
+        if (clazz != null && type.getDimensions() == 0 && 
+            (clazz == long.class || clazz == double.class)) {
+
+            stackAdjust--;
+        }
+
+        mInstructions.new StoreLocalInstruction(stackAdjust, local);
+    }
+
+    // load-to-stack-from-array style instructions
+
+    /**
+     * Generates code that loads a value from an array. An array
+     * reference followed by an index must be on the stack. The array 
+     * reference and index are replaced by the value retrieved from the array 
+     * after the generated instruction has executed.
+     *
+     * <p>The type doesn't need to be an exact match for objects. Object.class
+     * works fine for all objects. For primitive types, use the class that
+     * matches that type. For an int the type is int.class.
+     *
+     * @param type The type of data stored in the array.
+     */
+    public void loadFromArray(Class type) {
+        byte op;
+        int stackAdjust;
+
+        if (type == int.class) {
+            stackAdjust = -1;
+            op = Opcode.IALOAD;
+        }
+        else if (type == boolean.class || type == byte.class) {
+            stackAdjust = -1;
+            op = Opcode.BALOAD;
+        }
+        else if (type == short.class) {
+            stackAdjust = -1;
+            op = Opcode.SALOAD;
+        }
+        else if (type == char.class) {
+            stackAdjust = -1;
+            op = Opcode.CALOAD;
+        }
+        else if (type == long.class) {
+            stackAdjust = 0;
+            op = Opcode.LALOAD;
+        }
+        else if (type == float.class) {
+            stackAdjust = -1;
+            op = Opcode.FALOAD;
+        }
+        else if (type == double.class) {
+            stackAdjust = 0;
+            op = Opcode.DALOAD;
+        }
+        else {
+            stackAdjust = -1;
+            op = Opcode.AALOAD;
+        }
+
+        addCode(stackAdjust, op);
+    }
+
+    // store-to-array-from-stack style instructions
+
+    /**
+     * Generates code that stores a value to an array. An array
+     * reference followed by an index, followed by a value (or two if a long
+     * or double) must be on the stack. All items on the stack are gone
+     * after the generated instruction has executed.
+     *
+     * <p>The type doesn't need to be an exact match for objects. Object.class
+     * works fine for all objects. For primitive types, use the class that
+     * matches that type. For an int the type is int.class.
+     *
+     * @param type The type of data stored in the array.
+     */
+    public void storeToArray(Class type) {
+        byte op;
+        int stackAdjust;
+
+        if (type == int.class) {
+            stackAdjust = -3;
+            op = Opcode.IASTORE;
+        }
+        else if (type == boolean.class || type == byte.class) {
+            stackAdjust = -3;
+            op = Opcode.BASTORE;
+        }
+        else if (type == short.class) {
+            stackAdjust = -3;
+            op = Opcode.SASTORE;
+        }
+        else if (type == char.class) {
+            stackAdjust = -3;
+            op = Opcode.CASTORE;
+        }
+        else if (type == long.class) {
+            stackAdjust = -4;
+            op = Opcode.LASTORE;
+        }
+        else if (type == float.class) {
+            stackAdjust = -3;
+            op = Opcode.FASTORE;
+        }
+        else if (type == double.class) {
+            stackAdjust = -4;
+            op = Opcode.DASTORE;
+        }
+        else {
+            stackAdjust = -3;
+            op = Opcode.AASTORE;
+        }
+
+        addCode(stackAdjust, op);
+    }
+
+    // load-field-to-stack style instructions
+
+    /**
+     * Generates code that loads a value from a field from this class. 
+     * An object reference must be on the stack. After the generated code 
+     * has executed, the object reference is replaced by the value retrieved 
+     * from the field.
+     */
+    public void loadField(String fieldName,
+                          TypeDescriptor type) {
+        getfield(0, Opcode.GETFIELD, constantField(fieldName, type), type);
+    }
+
+    /**
+     * Generates code that loads a value from a field from any class. 
+     * An object reference must be on the stack. After the generated code 
+     * has executed, the object reference is replaced by the value retrieved 
+     * from the field.
+     */
+    public void loadField(String className,
+                          String fieldName,
+                          TypeDescriptor type) {
+
+        getfield(0, Opcode.GETFIELD,
+                 mCp.addConstantField(className, fieldName, type), 
+                 type);
+    }
+
+    /**
+     * Generates code that loads a value from a static field from this class. 
+     * After the generated code has executed, the value retrieved is placed
+     * on the stack.
+     */
+    public void loadStaticField(String fieldName,
+                                TypeDescriptor type) {
+
+        getfield(1, Opcode.GETSTATIC, constantField(fieldName, type), type);
+    }
+
+    /**
+     * Generates code that loads a value from a static field from any class. 
+     * After the generated code has executed, the value retrieved is placed
+     * on the stack.
+     */
+    public void loadStaticField(String className,
+                                String fieldName,
+                                TypeDescriptor type) {
+
+        getfield(1, Opcode.GETSTATIC,
+                 mCp.addConstantField(className, fieldName, type), 
+                 type);
+    }
+
+    private void getfield(int stackAdjust, byte opcode, ConstantInfo info, 
+                          TypeDescriptor type) {
+
+        Class clazz = type.getClassArg();
+        if (clazz != null && type.getDimensions() == 0 &&
+            (clazz == long.class || clazz == double.class)) {
+            stackAdjust++;
+        }
+
+        addCode(stackAdjust, opcode, info);
+    }
+
+    private ConstantFieldInfo constantField(String fieldName,
+                                            TypeDescriptor type) {
+        return mCp.addConstantField
+            (mClassFile.getClassName(), fieldName, type);
+    }
+
+    // store-to-field-from-stack style instructions
+
+    /**
+     * Generates code that stores a value into a field from this class. 
+     * An object reference and value must be on the stack. After the generated 
+     * code has executed, the object reference and value are gone from
+     * the stack.
+     */
+    public void storeField(String fieldName,
+                           TypeDescriptor type) {
+
+        putfield(-1, Opcode.PUTFIELD, constantField(fieldName, type), type);
+    }
+
+    /**
+     * Generates code that stores a value into a field from any class. 
+     * An object reference and value must be on the stack. After the generated 
+     * code has executed, the object reference and value are gone from
+     * the stack.
+     */
+    public void storeField(String className,
+                           String fieldName,
+                           TypeDescriptor type) {
+
+        putfield(-1, Opcode.PUTFIELD, 
+                 mCp.addConstantField(className, fieldName, type), 
+                 type);
+    }
+
+    /**
+     * Generates code that stores a value into a field from this class. 
+     * A value must be on the stack. After the generated 
+     * code has executed, the value is gone from the stack.
+     */
+    public void storeStaticField(String fieldName,
+                                 TypeDescriptor type) {
+
+        putfield(0, Opcode.PUTSTATIC, constantField(fieldName, type), type);
+    }
+
+    /**
+     * Generates code that stores a value into a field from any class. 
+     * A value must be on the stack. After the generated 
+     * code has executed, the value is gone from the stack.
+     */
+    public void storeStaticField(String className,
+                                 String fieldName,
+                                 TypeDescriptor type) {
+
+        putfield(0, Opcode.PUTSTATIC,
+                 mCp.addConstantField(className, fieldName, type), 
+                 type);
+    }
+
+    private void putfield(int stackAdjust, byte opcode, ConstantInfo info, 
+                          TypeDescriptor type) {
+
+        Class clazz = type.getClassArg();
+        if (clazz != null && type.getDimensions() == 0 && 
+            (clazz == long.class || clazz == double.class)) {
+        
+            stackAdjust -= 2;
+        }
+        else {
+            stackAdjust--;
+        }
+
+        addCode(stackAdjust, opcode, info);
+    }
+
+    // return style instructions
+
+    /**
+     * Generates code that returns void.
+     */
+    public void returnVoid() {
+        addCode(0, Opcode.RETURN);
+    }
+
+    /**
+     * Generates code that returns an object or primitive type. The value to
+     * return must be on the stack.
+     *
+     * <p>The type doesn't need to be an exact match for objects. Object.class
+     * works fine for all objects. For primitive types, use the class that
+     * matches that type. For an int the type is int.class.
+     */
+    public void returnValue(Class type) {
+        int stackAdjust = -1;
+        byte op;
+
+        if (type == int.class || 
+            type == boolean.class || 
+            type == byte.class ||
+            type == short.class ||
+            type == char.class) {
+
+            op = Opcode.IRETURN;
+        }
+        else if (type == long.class) {
+            stackAdjust--;
+            op = Opcode.LRETURN;
+        }
+        else if (type == float.class) {
+            op = Opcode.FRETURN;
+        }
+        else if (type == double.class) {
+            stackAdjust--;
+            op = Opcode.DRETURN;
+        }
+        else if (type == void.class) {
+            stackAdjust++;
+            op = Opcode.RETURN;
+        }
+        else {
+            op = Opcode.ARETURN;
+        }
+
+        addCode(stackAdjust, op);
+    }
+
+    // numerical conversion style instructions
+
+    /**
+     * Generates code that converts the value of a primitive type already
+     * on the stack.
+     */
+    public void convert(Class fromType, Class toType) {
+        int stackAdjust = 0;
+        byte op;
+
+        if (fromType == int.class ||
+            fromType == boolean.class || 
+            fromType == byte.class ||
+            fromType == short.class ||
+            fromType == char.class) {
+            
+            if (toType == byte.class) {
+                op = Opcode.I2B;
+            }
+            else if (toType == short.class) {
+                op = Opcode.I2S;
+            }
+            else if (toType == char.class) {
+                op = Opcode.I2C;
+            }
+            else if (toType == float.class) {
+                op = Opcode.I2F;
+            }
+            else if (toType == long.class) {
+                stackAdjust = 1;
+                op = Opcode.I2L;
+            }
+            else if (toType == double.class) {
+                stackAdjust = 1;
+                op = Opcode.I2D;
+            }
+            else if (toType == int.class) {
+                return;
+            }
+            else {
+                throw new RuntimeException("Invalid conversion: int to " +
+                                           toType);
+            }
+
+            addCode(stackAdjust, op);
+            return;
+        }
+        else if (fromType == long.class) {
+            if (toType == int.class) {
+                stackAdjust = -1;
+                op = Opcode.L2I;
+            }
+            else if (toType == float.class) {
+                stackAdjust = -1;
+                op = Opcode.L2F;
+            }
+            else if (toType == double.class) {
+                op = Opcode.L2D;
+            }
+            else if (toType == byte.class ||
+                     toType == char.class ||
+                     toType == short.class) {
+                
+                convert(fromType, int.class);
+                convert(int.class, toType);
+                return;
+            }
+            else if (toType == long.class) {
+                return;
+            }
+            else {
+                throw new RuntimeException("Invalid conversion: long to " +
+                                           toType);
+            }
+
+            addCode(stackAdjust, op);
+            return;
+        }
+        else if (fromType == float.class) {
+            if (toType == int.class) {
+                op = Opcode.F2I;
+            }
+            else if (toType == long.class) {
+                stackAdjust = 1;
+                op = Opcode.F2L;
+            }
+            else if (toType == double.class) {
+                stackAdjust = 1;
+                op = Opcode.F2D;
+            }
+            else if (toType == byte.class ||
+                     toType == char.class ||
+                     toType == short.class) {
+                
+                convert(fromType, int.class);
+                convert(int.class, toType);
+                return;
+            }
+            else if (toType == float.class) {
+                return;
+            }
+            else {
+                throw new RuntimeException("Invalid conversion: float to " +
+                                           toType);
+            }
+            
+            addCode(stackAdjust, op);
+            return;
+        }
+        else if (fromType == double.class) {
+            if (toType == int.class) {
+                stackAdjust = -1;
+                op = Opcode.D2I;
+            }
+            else if (toType == float.class) {
+                stackAdjust = -1;
+                op = Opcode.D2F;
+            }
+            else if (toType == long.class) {
+                op = Opcode.D2L;
+            }
+            else if (toType == byte.class ||
+                     toType == char.class ||
+                     toType == short.class) {
+                
+                convert(fromType, int.class);
+                convert(int.class, toType);
+                return;
+            }
+            else if (toType == double.class) {
+                return;
+            }
+            else {
+                throw new RuntimeException("Invalid conversion: double to " +
+                                           toType);
+            }
+
+            addCode(stackAdjust, op);
+            return;
+        }
+        else {
+            throw new RuntimeException("Invalid conversion: " + fromType +
+                                       " to " + toType);
+        }
+    }
+
+    // invocation style instructions
+
+    /**
+     * Generates code to invoke a method in any class. If the method is
+     * non-static, the object reference and the method's argument(s) must be 
+     * on the stack. If the method is static and has any arguments, just 
+     * the method's arguments must be on the stack.
+     */
+    public void invoke(Method method) {
+        TypeDescriptor ret = new TypeDescriptor(method.getReturnType());
+
+        Class[] paramClasses = method.getParameterTypes();
+        TypeDescriptor[] params = new TypeDescriptor[paramClasses.length];
+        for (int i=0; i<params.length; i++) {
+            params[i] = new TypeDescriptor(paramClasses[i]);
+        }
+
+        Class clazz = method.getDeclaringClass();
+
+        if (Modifier.isStatic(method.getModifiers())) {
+            invokeStatic(clazz.getName(),
+                         method.getName(),
+                         ret, 
+                         params);
+        }
+        else if (clazz.isInterface()) {
+            invokeInterface(clazz.getName(),
+                            method.getName(),
+                            ret, 
+                            params);
+        }
+        else {
+            invokeVirtual(clazz.getName(),
+                          method.getName(),
+                          ret, 
+                          params);
+        }
+    }
+
+    /**
+     * Generates code to invoke a class constructor in any class. The object 
+     * reference and the constructor's argument(s) must be on the stack.
+     */
+    public void invoke(Constructor constructor) {
+        Class[] paramClasses = constructor.getParameterTypes();
+        TypeDescriptor[] params = new TypeDescriptor[paramClasses.length];
+        for (int i=0; i<params.length; i++) {
+            params[i] = new TypeDescriptor(paramClasses[i]);
+        }
+
+        invokeConstructor(constructor.getDeclaringClass().toString(), params);
+    }
+
+    /**
+     * Generates code to invoke a virtual method in this class. The object
+     * reference and the method's argument(s) must be on the stack.
+     *
+     * @param ret May be null if method returns void.
+     * @param params May be null if method takes no parameters.
+     */
+    public void invokeVirtual(String methodName,
+                              TypeDescriptor ret,
+                              TypeDescriptor[] params) {
+
+        ConstantInfo info = mCp.addConstantMethod
+            (mClassFile.getClassName(), methodName, ret, params);
+
+        int stackAdjust = returnSize(ret) - 1;
+        if (params != null) {
+            stackAdjust -= argSize(params);
+        }
+
+        addCode(stackAdjust, Opcode.INVOKEVIRTUAL, info);
+    }
+
+    /**
+     * Generates code to invoke a virtual method in any class. The object
+     * reference and the method's argument(s) must be on the stack.
+     *
+     * @param ret May be null if method returns void.
+     * @param params May be null if method takes no parameters.
+     */
+    public void invokeVirtual(String className,
+                              String methodName,
+                              TypeDescriptor ret,
+                              TypeDescriptor[] params) {
+        ConstantInfo info = 
+            mCp.addConstantMethod(className, methodName, ret, params);
+
+        int stackAdjust = returnSize(ret) - 1;
+        if (params != null) {
+            stackAdjust -= argSize(params);
+        }
+
+        addCode(stackAdjust, Opcode.INVOKEVIRTUAL, info);
+    }
+
+    /**
+     * Generates code to invoke a static method in this class. The method's
+     * argument(s) must be on the stack.
+     *
+     * @param ret May be null if method returns void.
+     * @param params May be null if method takes no parameters.
+     */
+    public void invokeStatic(String methodName,
+                             TypeDescriptor ret,
+                             TypeDescriptor[] params) {
+        ConstantInfo info = mCp.addConstantMethod
+            (mClassFile.getClassName(), methodName, ret, params);
+
+        int stackAdjust = returnSize(ret) - 0;
+        if (params != null) {
+            stackAdjust -= argSize(params);
+        }
+
+        addCode(stackAdjust, Opcode.INVOKESTATIC, info);
+    }
+
+    /**
+     * Generates code to invoke a static method in any class. The method's
+     * argument(s) must be on the stack.
+     *
+     * @param ret May be null if method returns void.
+     * @param params May be null if method takes no parameters.
+     */
+    public void invokeStatic(String className,
+                             String methodName,
+                             TypeDescriptor ret,
+                             TypeDescriptor[] params) {
+        ConstantInfo info =
+            mCp.addConstantMethod(className, methodName, ret, params);
+
+        int stackAdjust = returnSize(ret) - 0;
+        if (params != null) {
+            stackAdjust -= argSize(params);
+        }
+
+        addCode(stackAdjust, Opcode.INVOKESTATIC, info);
+    }
+
+    /**
+     * Generates code to invoke an interface method in any class. The object
+     * reference and the method's argument(s) must be on the stack.
+     *
+     * @param ret May be null if method returns void.
+     * @param params May be null if method takes no parameters.
+     */
+    public void invokeInterface(String className,
+                                String methodName,
+                                TypeDescriptor ret,
+                                TypeDescriptor[] params) {
+
+        ConstantInfo info = 
+            mCp.addConstantInterfaceMethod(className, methodName, ret, params);
+
+        int paramCount = 1;
+        if (params != null) {
+            paramCount += argSize(params);
+        }
+
+        int stackAdjust = returnSize(ret) - paramCount;
+
+        byte[] bytes = new byte[5];
+
+        bytes[0] = Opcode.INVOKEINTERFACE;
+        bytes[1] = (byte)0;
+        bytes[2] = (byte)0;
+        bytes[3] = (byte)paramCount;
+        bytes[4] = (byte)0;
+
+        mInstructions.new ConstantOperandInstruction(stackAdjust, bytes, info);
+    }
+
+    /**
+     * Generates code to invoke a private method in this class.
+     * The object reference and the method's argument(s) must be on the stack.
+     *
+     * @param ret May be null if method returns void.
+     * @param params May be null if method takes no parameters.
+     */
+    public void invokePrivate(String methodName,
+                              TypeDescriptor ret,
+                              TypeDescriptor[] params) {
+        ConstantInfo info = mCp.addConstantMethod
+            (mClassFile.getClassName(), methodName, ret, params);
+
+        int stackAdjust = returnSize(ret) - 1;
+        if (params != null) {
+            stackAdjust -= argSize(params);
+        }
+
+        addCode(stackAdjust, Opcode.INVOKESPECIAL, info);
+    }
+
+    /**
+     * Generates code to invoke a method in the super class.
+     * The object reference and the method's argument(s) must be on the stack.
+     *
+     * @param ret May be null if method returns void.
+     * @param params May be null if method takes no parameters.
+     */
+    public void invokeSuper(String superClassName,
+                            String methodName,
+                            TypeDescriptor ret,
+                            TypeDescriptor[] params) {
+        ConstantInfo info = 
+            mCp.addConstantMethod(superClassName, methodName, ret, params);
+
+        int stackAdjust = returnSize(ret) - 1;
+        if (params != null) {
+            stackAdjust -= argSize(params);
+        }
+
+        addCode(stackAdjust, Opcode.INVOKESPECIAL, info);
+    }
+
+    /**
+     * Generates code to invoke a method in the super class.
+     * The object reference and the method's argument(s) must be on the stack.
+     */
+    public void invokeSuper(Method method) {
+        TypeDescriptor ret = new TypeDescriptor(method.getReturnType());
+
+        Class[] paramClasses = method.getParameterTypes();
+        TypeDescriptor[] params = new TypeDescriptor[paramClasses.length];
+        for (int i=0; i<params.length; i++) {
+            params[i] = new TypeDescriptor(paramClasses[i]);
+        }
+
+        invokeSuper(method.getDeclaringClass().getName(),
+                    method.getName(),
+                    ret, 
+                    params);
+    }
+
+    /**
+     * Generates code to invoke a class constructor in this class. The object 
+     * reference and the constructor's argument(s) must be on the stack.
+     *
+     * @param params May be null if constructor takes no parameters.
+     */
+    public void invokeConstructor(TypeDescriptor[] params) {
+        ConstantInfo info = 
+            mCp.addConstantConstructor(mClassFile.getClassName(), params);
+
+        int stackAdjust = -1;
+        if (params != null) {
+            stackAdjust -= argSize(params);
+        }
+
+        addCode(stackAdjust, Opcode.INVOKESPECIAL, info);
+    }
+
+    /**
+     * Generates code to invoke a class constructor in any class. The object 
+     * reference and the constructor's argument(s) must be on the stack.
+     *
+     * @param params May be null if constructor takes no parameters.
+     */
+    public void invokeConstructor(String className, TypeDescriptor[] params) {
+        ConstantInfo info = mCp.addConstantConstructor(className, params);
+
+        int stackAdjust = -1;
+        if (params != null) {
+            stackAdjust -= argSize(params);
+        }
+
+        addCode(stackAdjust, Opcode.INVOKESPECIAL, info);
+    }
+
+    /**
+     * Generates code to invoke a super class constructor. The object 
+     * reference and the constructor's argument(s) must be on the stack.
+     *
+     * @param params May be null if constructor takes no parameters.
+     */
+    public void invokeSuperConstructor(TypeDescriptor[] params) {
+        invokeConstructor(mClassFile.getSuperClassName(), params);
+    }
+
+    /**
+     * Generates code to invoke a super class constructor. The object 
+     * reference and the constructor's argument(s) must be on the stack.
+     */
+    public void invokeSuper(Constructor constructor) {
+        Class[] paramClasses = constructor.getParameterTypes();
+        TypeDescriptor[] params = new TypeDescriptor[paramClasses.length];
+        for (int i=0; i<params.length; i++) {
+            params[i] = new TypeDescriptor(paramClasses[i]);
+        }
+
+        invokeSuperConstructor(params);
+    }
+
+    private int returnSize(TypeDescriptor ret) {
+        if (ret == null) return 0;
+
+        String className = ret.getClassName();
+        
+        if (className.equals(void.class.getName())) {
+            return 0;
+        }
+        else if (className.equals(long.class.getName()) ||
+                 className.equals(double.class.getName())) {
+            return 2;
+        }
+        else {
+            return 1;
+        }
+    }
+
+    private int argSize(TypeDescriptor[] params) {
+        int size = 0;
+        if (params != null) {
+            for (int i=0; i<params.length; i++) {
+                String className = params[i].getClassName();
+                if (params[i].getDimensions() == 0 &&
+                    (className.equals(long.class.getName()) ||
+                     className.equals(double.class.getName()))) {
+                    size += 2;
+                }
+                else {
+                    size++;
+                }
+            }
+        }
+
+        return size;
+    }
+
+    // creation style instructions
+
+    /**
+     * Generates code to create a new object. Unless the new object is an 
+     * array, it is invalid until a constructor method is invoked on it.
+     * When creating arrays, the size for each dimension must be on the
+     * operand stack.
+     *
+     * @see #invokeConstructor
+     */
+    public void newObject(TypeDescriptor type) {
+        int dim = type.getSpecifiedDimensions();
+
+        if (dim == 0) {
+            ConstantInfo info = mCp.addConstantClass(type);
+            addCode(1, Opcode.NEW, info);
+            return;
+        }
+
+        TypeDescriptor componentType = type.getComponentType();
+
+        if (dim == 1) {
+            if (componentType.getDimensions() == 0) {
+                Class clazz = componentType.getClassArg();
+
+                if (clazz != null && clazz.isPrimitive()) {
+                    byte atype = (byte)0;
+                    
+                    if (clazz == int.class) {
+                        atype = (byte)10;
+                    }
+                    else if (clazz == byte.class) {
+                        atype = (byte)8;
+                    }
+                    else if (clazz == boolean.class) {
+                        atype = (byte)4;
+                    }
+                    else if (clazz == char.class) {
+                        atype = (byte)5;
+                    }
+                    else if (clazz == float.class) {
+                        atype = (byte)6;
+                    }
+                    else if (clazz == double.class) {
+                        atype = (byte)7;
+                    }
+                    else if (clazz == short.class) {
+                        atype = (byte)9;
+                    }
+                    else if (clazz == long.class) {
+                        atype = (byte)11;
+                    }
+                    
+                    addCode(0, Opcode.NEWARRAY, atype);
+                    return;
+                }
+            }
+
+            ConstantInfo info = mCp.addConstantClass(componentType);
+            addCode(0, Opcode.ANEWARRAY, info);
+            return;
+        }
+
+        // multidimensional
+        int stackAdjust = -(dim - 1);
+        
+        ConstantInfo info = mCp.addConstantClass(componentType);
+        
+        byte[] bytes = new byte[4];
+        
+        bytes[0] = Opcode.MULTIANEWARRAY;
+        bytes[1] = (byte)0;
+        bytes[2] = (byte)0;
+        bytes[3] = (byte)dim;
+        
+        mInstructions.new ConstantOperandInstruction(stackAdjust, bytes, info);
+    }
+
+    // stack operation style instructions
+
+    /**
+     * Generates code for the dup instruction.
+     */
+    public void dup() {
+        addCode(1, Opcode.DUP);
+    }
+
+    /**
+     * Generates code for the dup_x1 instruction.
+     */
+    public void dupX1() {
+        addCode(1, Opcode.DUP_X1);
+    }
+
+    /**
+     * Generates code for the dup_x2 instruction.
+     */
+    public void dupX2() {
+        addCode(1, Opcode.DUP_X2);
+    }
+
+    /**
+     * Generates code for the dup2 instruction.
+     */
+    public void dup2() {
+        addCode(2, Opcode.DUP2);
+    }
+
+    /**
+     * Generates code for the dup2_x1 instruction.
+     */
+    public void dup2X1() {
+        addCode(2, Opcode.DUP2_X1);
+    }
+
+    /**
+     * Generates code for the dup2_x2 instruction.
+     */
+    public void dup2X2() {
+        addCode(2, Opcode.DUP2_X2);
+    }
+
+    /**
+     * Generates code for the pop instruction.
+     */
+    public void pop() {
+        addCode(-1, Opcode.POP);
+    }
+
+    /**
+     * Generates code for the pop2 instruction.
+     */
+    public void pop2() {
+        addCode(-2, Opcode.POP2);
+    }
+
+    /**
+     * Generates code for the swap instruction.
+     */
+    public void swap() {
+        addCode(0, Opcode.SWAP);
+    }
+
+    /**
+     * Generates code for a swap2 instruction.
+     */
+    public void swap2() {
+        dup2X2();
+        pop2();
+    }
+
+    // flow control instructions
+
+    private void branch(int stackAdjust, Location location, byte opcode) {
+        mInstructions.new BranchInstruction(stackAdjust, opcode, location);
+    }
+
+    /**
+     * Generates code that performs an unconditional branch to the specified
+     * location or label.
+     *
+     * @param location The location or label to branch to
+     */
+    public void branch(Location location) {
+        branch(0, location, Opcode.GOTO);
+    }
+
+    /**
+     * Generates code that performs a conditional branch based on the
+     * value of an object on the stack. A branch is performed based on whether
+     * the object reference on the stack is null or not.
+     *
+     * @param location The location or label to branch to
+     * @param choice If true, do branch when null, else branch when not null
+     */
+    public void ifNullBranch(Location location, boolean choice) {
+        branch(-1, location, choice ? Opcode.IFNULL : Opcode.IFNONNULL);
+    }
+
+
+    /**
+     * Generates code that performs a conditional branch based on the value of
+     * two object references on the stack. A branch is performed based on
+     * whether the two objects are equal.
+     *
+     * @param location The location or label to branch to
+     * @param choice If true, branch when equal, else branch when not equal
+     */
+    public void ifEqualBranch(Location location, boolean choice) {
+        branch(-2, location, choice ? Opcode.IF_ACMPEQ : Opcode.IF_ACMPNE);
+    }
+
+    /**
+     * Generates code the performs a conditional branch based on a comparison
+     * between an int value on the stack and zero. The int value on the
+     * stack is on the left side of the comparison expression.
+     *
+     * @param location The location or label to branch to
+     * @param choice One of "==", "!=", "<", ">=", ">" or "<="
+     * @exception IllegalArgumentException When the choice is not valid
+     */
+    public void ifZeroComparisonBranch(Location location, String choice) 
+        throws IllegalArgumentException {
+
+        choice = choice.intern();
+
+        byte opcode;
+        if (choice ==  "==") {
+            opcode = Opcode.IFEQ;
+        }
+        else if (choice == "!=") {
+            opcode = Opcode.IFNE;
+        }
+        else if (choice == "<") {
+            opcode = Opcode.IFLT;
+        }
+        else if (choice == ">=") {
+            opcode = Opcode.IFGE;
+        }
+        else if (choice == ">") {
+            opcode = Opcode.IFGT;
+        }
+        else if (choice == "<=") {
+            opcode = Opcode.IFLE;
+        }
+        else {
+            throw new IllegalArgumentException
+                ("Invalid comparision choice: " + choice);
+        }
+        
+        branch(-1, location, opcode);
+    }
+
+    /**
+     * Generates code the performs a conditional branch based on a comparison
+     * between two int values on the stack. The first int value on the stack
+     * is on the left side of the comparison expression.
+     *
+     * @param location The location or label to branch to
+     * @param choice One of "==", "!=", "<", ">=", ">" or "<="
+     * @exception IllegalArgumentException When the choice is not valid
+     */
+    public void ifComparisonBranch(Location location, String choice)
+        throws IllegalArgumentException {
+
+        choice = choice.intern();
+
+        byte opcode;
+        if (choice ==  "==") {
+            opcode = Opcode.IF_ICMPEQ;
+        }
+        else if (choice == "!=") {
+            opcode = Opcode.IF_ICMPNE;
+        }
+        else if (choice == "<") {
+            opcode = Opcode.IF_ICMPLT;
+        }
+        else if (choice == ">=") {
+            opcode = Opcode.IF_ICMPGE;
+        }
+        else if (choice == ">") {
+            opcode = Opcode.IF_ICMPGT;
+        }
+        else if (choice == "<=") {
+            opcode = Opcode.IF_ICMPLE;
+        }
+        else {
+            throw new IllegalArgumentException
+                ("Invalid comparision choice: " + choice);
+        }
+
+        branch(-2, location, opcode);
+    }
+
+    /**
+     * Generates code for a switch statement. The generated code is either a
+     * lookupswitch or tableswitch. The choice of which switch type to generate
+     * is made based on the amount of bytes to be generated. A tableswitch
+     * is usually smaller, unless the cases are sparse.
+     *
+     * <p>The key value to switch on must already be on the stack when this
+     * instruction executes.
+     *
+     * @param cases The values to match on. The array length must be the same
+     * as for locations.
+     * @param locations The locations or labels to branch to for each case.
+     * The array length must be the same as for cases.
+     * @param defaultLocation The location or label to branch to if the key on
+     * the stack was not matched.
+     */
+    public void switchBranch(int[] cases, 
+                             Location[] locations, Location defaultLocation) {
+
+        mInstructions.new SwitchInstruction(cases, locations, defaultLocation);
+    }
+
+    /**
+     * Generates code that performs a subroutine branch to the specified 
+     * location. The instruction generated is either jsr or jsr_w. It is most
+     * often used for implementing a finally block.
+     *
+     * @param location The location or label to branch to
+     */
+    public void jsr(Location location) {
+        // Adjust the stack by one to make room for the return address.
+        branch(1, location, Opcode.JSR);
+    }
+
+    /**
+     * Generates code that returns from a subroutine invoked by jsr.
+     *
+     * @param local The local variable reference that contains the return
+     * address. The local variable must be of an object type.
+     */
+    public void ret(LocalVariable local) {
+        if (local == null) {
+            throw new NullPointerException("No local variable specified");
+        }
+
+        mInstructions.new RetInstruction(local);
+    }
+
+    // math instructions
+
+    /**
+     * Generates code for either a unary or binary math operation on one
+     * or two values pushed on the stack.
+     *
+     * <p>Pass in an opcode from the the Opcode class. The only valid math
+     * opcodes are:
+     *
+     * <pre>
+     * IADD, ISUB, IMUL, IDIV, IREM, INEG, IAND, IOR, IXOR, ISHL, ISHR, IUSHR
+     * LADD, LSUB, LMUL, LDIV, LREM, LNEG, LAND, LOR, LXOR, LSHL, LSHR, LUSHR
+     * FADD, FSUB, FMUL, FDIV, FREM, FNEG
+     * DADD, DSUB, DMUL, DDIV, DREM, DNEG
+     *
+     * LCMP
+     * FCMPG, FCMPL
+     * DCMPG, DCMPL
+     * </pre>
+     *
+     * A not operation (~) is performed by doing a loadConstant with either
+     * -1 or -1L followed by math(Opcode.IXOR) or math(Opcode.LXOR).
+     *
+     * @param opcode An opcode from the Opcode class.
+     * @exception IllegalArgumentException When the opcode selected is not
+     * a math operation.
+     * @see Opcode
+     */
+    public void math(byte opcode) {
+        int stackAdjust;
+        
+        switch(opcode) {
+        case Opcode.INEG:
+        case Opcode.LNEG:
+        case Opcode.FNEG:
+        case Opcode.DNEG:
+            stackAdjust = 0;
+            break;
+        case Opcode.IADD:
+        case Opcode.ISUB:
+        case Opcode.IMUL:
+        case Opcode.IDIV:
+        case Opcode.IREM:
+        case Opcode.IAND:
+        case Opcode.IOR:
+        case Opcode.IXOR:
+        case Opcode.ISHL:
+        case Opcode.ISHR:
+        case Opcode.IUSHR:
+        case Opcode.FADD:
+        case Opcode.FSUB:
+        case Opcode.FMUL:
+        case Opcode.FDIV:
+        case Opcode.FREM:
+        case Opcode.FCMPG:
+        case Opcode.FCMPL:
+            stackAdjust = -1;
+            break;
+        case Opcode.LADD:
+        case Opcode.LSUB:
+        case Opcode.LMUL:
+        case Opcode.LDIV:
+        case Opcode.LREM:
+        case Opcode.LAND:
+        case Opcode.LOR:
+        case Opcode.LXOR:
+        case Opcode.LSHL:
+        case Opcode.LSHR:
+        case Opcode.LUSHR:
+        case Opcode.DADD:
+        case Opcode.DSUB:
+        case Opcode.DMUL:
+        case Opcode.DDIV:
+        case Opcode.DREM:
+            stackAdjust = -2;
+            break;
+        case Opcode.LCMP:
+        case Opcode.DCMPG:
+        case Opcode.DCMPL:
+            stackAdjust = -3;
+            break;
+        default:
+            throw new IllegalArgumentException
+                ("Not a math opcode: " + Opcode.getMnemonic(opcode));
+        }
+
+        addCode(stackAdjust, opcode);
+    }
+
+    // miscellaneous instructions
+
+    /**
+     * Generates code for an arraylength instruction. The object to get the
+     * length from must already be on the stack.
+     */
+    public void arrayLength() {
+        addCode(0, Opcode.ARRAYLENGTH);
+    }
+
+    /**
+     * Generates code that throws an exception. The object to throw must
+     * already be on the stack.
+     */
+    public void throwObject() {
+        addCode(-1, Opcode.ATHROW);
+    }
+
+    /**
+     * Generates code that performs an object cast operation. The object
+     * to check must already be on the stack.
+     */
+    public void checkCast(TypeDescriptor type) {
+        ConstantInfo info = mCp.addConstantClass(type);
+        addCode(0, Opcode.CHECKCAST, info);
+    }
+
+    /**
+     * Generates code that performs an instanceof operation. The object to
+     * check must already be on the stack.
+     */
+    public void instanceOf(TypeDescriptor type) {
+        ConstantInfo info = mCp.addConstantClass(type);
+        addCode(0, Opcode.INSTANCEOF, info);
+    }
+
+    /**
+     * Generates code that increments a local integer variable by a signed 
+     * constant amount.
+     */
+    public void integerIncrement(LocalVariable local, int amount) {
+        if (local == null) {
+            throw new NullPointerException("No local variable specified");
+        }
+
+        if (-32768 <= amount && amount <= 32767) {
+            mInstructions.new ShortIncrementInstruction(local, (short)amount);
+        }
+        else {
+            // Amount can't possibly fit in a 16-bit value, so use regular
+            // instructions instead.
+
+            loadLocal(local);
+            loadConstant(amount);
+            math(Opcode.IADD);
+            storeLocal(local);
+        }
+    }
+
+    /**
+     * Generates code to enter the monitor on an object loaded on the stack.
+     */
+    public void monitorEnter() {
+        addCode(-1, Opcode.MONITORENTER);
+    }
+
+    /**
+     * Generates code to exit the monitor on an object loaded on the stack.
+     */
+    public void monitorExit() {
+        addCode(-1, Opcode.MONITOREXIT);
+    }
+
+    /**
+     * Generates an instruction that does nothing. (No-OPeration)
+     */
+    public void nop() {
+        addCode(0, Opcode.NOP);
+    }
+
+    /**
+     * Generates a breakpoint instruction for use in a debugging environment.
+     */
+    public void breakpoint() {
+        addCode(0, Opcode.BREAKPOINT);
+    }
+}