Package org.jboss.classfilewriter.code

Source Code of org.jboss.classfilewriter.code.CodeAttribute

/*
* JBoss, Home of Professional Open Source.
*
* Copyright 2012 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jboss.classfilewriter.code;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.jboss.classfilewriter.ClassMethod;
import org.jboss.classfilewriter.InvalidBytecodeException;
import org.jboss.classfilewriter.attributes.Attribute;
import org.jboss.classfilewriter.attributes.StackMapTableAttribute;
import org.jboss.classfilewriter.constpool.ConstPool;
import org.jboss.classfilewriter.util.ByteArrayDataOutputStream;
import org.jboss.classfilewriter.util.DescriptorUtils;
import org.jboss.classfilewriter.util.LazySize;

public class CodeAttribute extends Attribute {

    public static final String NAME = "Code";

    private final ClassMethod method;

    private final ConstPool constPool;

    private final ByteArrayOutputStream finalDataBytes;

    private final DataOutputStream data;

    private int maxLocals = 0;

    private int maxStackDepth = 0;

    private final LinkedHashMap<Integer, StackFrame> stackFrames = new LinkedHashMap<Integer, StackFrame>();

    /**
     * maps bytecode offsets to jump locations. As these jump locations where not known when the instruction was written they
     * need to be overwritten when the final bytecode is written out
     */
    private final Map<Integer, Integer> jumpLocations = new HashMap<Integer, Integer>();

    private StackFrame currentFrame;

    private int currentOffset;

    private final List<Attribute> attributes = new ArrayList<Attribute>();

    //disable for now, as this has some issues
    private boolean stackMapAttributeValid = false;

    private final StackMapTableAttribute stackMapTableAttribute;

    private final List<ExceptionHandler> exceptionTable = new ArrayList<ExceptionHandler>();

    public CodeAttribute(ClassMethod method, ConstPool constPool) {
        super(NAME, constPool);
        this.method = method;
        this.constPool = constPool;
        this.finalDataBytes = new ByteArrayOutputStream();
        this.data = new DataOutputStream(finalDataBytes);

        if (!Modifier.isStatic(method.getAccessFlags())) {
            maxLocals++;
        }
        for (String param : method.getParameters()) {
            if (DescriptorUtils.isWide(param)) {
                maxLocals += 2;
            } else {
                maxLocals++;
            }
        }
        // creates a new initial stack frame
        currentFrame = new StackFrame(method);
        stackFrames.put(0, currentFrame);
        currentOffset = 0;
        stackMapTableAttribute = new StackMapTableAttribute(method, constPool);
    }

    @Override
    public void writeData(ByteArrayDataOutputStream stream) throws IOException {

        if (stackMapAttributeValid) {
            // add the stack map table
            attributes.add(stackMapTableAttribute);
        }

        if (finalDataBytes.size() == 0) {
            throw new RuntimeException("Code attribute is empty for method " + method.getName() + "  " + method.getDescriptor());
        }

        byte[] bytecode = finalDataBytes.toByteArray();
        for (Entry<Integer, Integer> e : jumpLocations.entrySet()) {
            overwriteShort(bytecode, e.getKey(), e.getValue());
        }

        LazySize size = stream.writeSize();
        stream.writeShort(maxStackDepth);
        stream.writeShort(maxLocals);
        stream.writeInt(bytecode.length);
        stream.write(bytecode);
        stream.writeShort(exceptionTable.size()); // exception table length
        for (ExceptionHandler exception : exceptionTable) {
            stream.writeShort(exception.getStart());
            stream.writeShort(exception.getEnd());
            stream.writeShort(exception.getHandler());
            stream.writeShort(exception.getExceptionIndex());
        }
        stream.writeShort(attributes.size()); // attributes count
        for (Attribute attribute : attributes) {
            attribute.write(stream);
        }
        size.markEnd();;
    }

    // -------------------------------------------
    // Instruction methods, in alphabetical order

    public void aaload() {
        assertTypeOnStack(StackEntryType.INT, "aaload requires int on top of stack");
        if (!getStack().top_1().getDescriptor().startsWith("[")) {
            throw new InvalidBytecodeException("aaload needs an array in position 2 on the stack");
        }
        writeByte(Opcode.AALOAD);
        currentOffset++;
        advanceFrame(currentFrame.pop2push1("Ljava/lang/Object;"));
    }

    public void aastore() {
        assertTypeOnStack(StackEntryType.OBJECT, "aastore requires reference type on top of stack");
        assertTypeOnStack(1, StackEntryType.INT, "aastore requires an int on position 2 stack");
        if (!getStack().top_2().getDescriptor().startsWith("[")) {
            throw new InvalidBytecodeException("aaload needs an array in position 3 on the stack");
        }
        writeByte(Opcode.AASTORE);
        currentOffset++;
        advanceFrame(currentFrame.pop3());
    }

    public void aconstNull() {
        writeByte(Opcode.ACONST_NULL);
        currentOffset++;
        advanceFrame(currentFrame.aconstNull());
    }

    public void aload(int no) {
        LocalVariableState locals = getLocalVars();
        if (locals.size() <= no) {
            throw new InvalidBytecodeException("Cannot load variable at " + no + ". Local Variables: " + locals.toString());
        }
        StackEntry entry = locals.get(no);
        if (entry.getType() != StackEntryType.OBJECT && entry.getType() != StackEntryType.NULL
                && entry.getType() != StackEntryType.UNINITIALIZED_THIS
                && entry.getType() != StackEntryType.UNITITIALIZED_OBJECT) {
            throw new InvalidBytecodeException("Invalid local variable at location " + no + " Local Variables "
                    + locals.toString());
        }

        if (no > 0xFF) {
            // wide version
            writeByte(Opcode.WIDE);
            writeByte(Opcode.ALOAD);
            writeShort(no);
            currentOffset += 4;
        } else if (no >= 0 && no < 4) {
            writeByte(Opcode.ALOAD_0 + no);
            currentOffset++;
        } else {
            writeByte(Opcode.ALOAD);
            writeByte(no);
            currentOffset += 2;
        }
        advanceFrame(currentFrame.push(entry));
    }

    public void anewarray(String arrayType) {
        assertTypeOnStack(StackEntryType.INT, "anewarray requires int on stack");
        int index = constPool.addClassEntry(arrayType);
        writeByte(Opcode.ANEWARRAY);
        writeShort(index);
        currentOffset += 3;
        if (arrayType.startsWith("[")) {
            advanceFrame(currentFrame.replace("[" + arrayType));
        } else {
            advanceFrame(currentFrame.replace("[L" + arrayType + ";"));
        }
    }

    public void arraylength() {
        assertTypeOnStack(StackEntryType.OBJECT, "arraylength requires array on stack");
        writeByte(Opcode.ARRAYLENGTH);
        currentOffset++;
        advanceFrame(currentFrame.replace("I"));
    }

    public void astore(int no) {
        assertTypeOnStack(StackEntryType.OBJECT, "aastore requires reference type on stack");
        if (no > 0xFF) {
            // wide version
            writeByte(Opcode.WIDE);
            writeByte(Opcode.ASTORE);
            writeShort(no);
            currentOffset += 4;
        } else if (no >= 0 && no < 4) {
            writeByte(Opcode.ASTORE_0 + no);
            currentOffset++;
        } else {
            writeByte(Opcode.ASTORE);
            writeByte(no);
            currentOffset += 2;
        }
        advanceFrame(currentFrame.store(no));
    }

    public void athrow() {
        assertTypeOnStack(StackEntryType.OBJECT, "athrow requires an object on the stack");
        writeByte(Opcode.ATHROW);
        currentOffset++;
        currentFrame = null;
    }

    public void baload() {
        assertTypeOnStack(StackEntryType.INT, "baload requires an int on top of the stack");
        assertTypeOnStack(1, StackEntryType.OBJECT, "baload requires an array in position 2 on the stack");
        writeByte(Opcode.BALOAD);
        currentOffset++;
        advanceFrame(currentFrame.pop2push1("I"));
    }

    public void bastore() {
        assertTypeOnStack(StackEntryType.INT, "bastore requires an int on top of the stack");
        assertTypeOnStack(1, StackEntryType.INT, "bastore requires an int in position 2 on the stack");
        assertTypeOnStack(2, StackEntryType.OBJECT, "bastore requires an array reference in position 3 on the stack");
        writeByte(Opcode.BASTORE);
        currentOffset++;
        advanceFrame(currentFrame.pop3());
    }

    public void caload() {
        assertTypeOnStack(StackEntryType.INT, "caload requires an int on top of the stack");
        assertTypeOnStack(1, StackEntryType.OBJECT, "caload requires an array in position 2 on the stack");
        writeByte(Opcode.CALOAD);
        currentOffset++;
        advanceFrame(currentFrame.pop2push1("I"));
    }

    public void castore() {
        assertTypeOnStack(StackEntryType.INT, "castore requires an int on top of the stack");
        assertTypeOnStack(1, StackEntryType.INT, "castore requires an int in position 2 on the stack");
        assertTypeOnStack(2, StackEntryType.OBJECT, "castore requires an array reference in position 3 on the stack");
        writeByte(Opcode.CASTORE);
        currentOffset++;
        advanceFrame(currentFrame.pop3());
    }

    public void bipush(byte value) {
        writeByte(Opcode.BIPUSH);
        writeByte(value);
        currentOffset += 2;
        advanceFrame(currentFrame.push("B"));
    }

    /**
     * marks the end of a branch. The current stack frame is checked for compatibility with the stack frame at the branch start
     */
    public void branchEnd(BranchEnd end) {
        mergeStackFrames(end.getStackFrame());
        jumpLocations.put(end.getBranchLocation() + 1, currentOffset - end.getBranchLocation());
    }

    /**
     * Do not use Descriptor format (e.g. Ljava/lang/Object;), the correct form is just java/lang/Object or java.lang.Object
     *
     */
    public void checkcast(String className) {
        assertTypeOnStack(StackEntryType.OBJECT, "checkcast requires reference type on stack");
        int classIndex = constPool.addClassEntry(className);
        writeByte(Opcode.CHECKCAST);
        writeShort(classIndex);
        currentOffset += 3;
        advanceFrame(currentFrame.replace(className));
    }

    public void checkcast(Class<?> clazz) {
        checkcast(clazz.getName());
    }

    public void d2f() {
        assertTypeOnStack(StackEntryType.DOUBLE, "d2f requires double on stack");
        writeByte(Opcode.D2F);
        currentOffset++;
        advanceFrame(currentFrame.pop2push1("F"));
    }

    public void d2i() {
        assertTypeOnStack(StackEntryType.DOUBLE, "d2i requires double on stack");
        writeByte(Opcode.D2I);
        currentOffset++;
        advanceFrame(currentFrame.pop2push1("I"));
    }

    public void d2l() {
        assertTypeOnStack(StackEntryType.DOUBLE, "d2l requires double on stack");
        writeByte(Opcode.D2L);
        currentOffset++;
        advanceFrame(currentFrame.pop2push1("J"));
    }

    public void dadd() {
        assertTypeOnStack(StackEntryType.DOUBLE, "dadd requires double on stack");
        assertTypeOnStack(2, StackEntryType.DOUBLE, "dadd requires double on stack");
        writeByte(Opcode.DADD);
        currentOffset++;
        advanceFrame(currentFrame.pop2());
    }

    public void daload() {
        assertTypeOnStack(StackEntryType.INT, "daload requires an int on top of the stack");
        assertTypeOnStack(1, StackEntryType.OBJECT, "daload requires an array in position 2 on the stack");
        writeByte(Opcode.DALOAD);
        currentOffset++;
        advanceFrame(currentFrame.pop2push1("D"));
    }

    public void dastore() {
        assertTypeOnStack(StackEntryType.DOUBLE, "dastore requires an int on top of the stack");
        assertTypeOnStack(2, StackEntryType.INT, "dastore requires an int in position 2 on the stack");
        assertTypeOnStack(3, StackEntryType.OBJECT, "dastore requires an array reference in position 3 on the stack");
        writeByte(Opcode.DASTORE);
        currentOffset++;
        advanceFrame(currentFrame.pop4());
    }

    public void dcmpg() {
        assertTypeOnStack(StackEntryType.DOUBLE, "dcmpg requires double on stack");
        assertTypeOnStack(2, StackEntryType.DOUBLE, "dcmpg requires double on stack");
        writeByte(Opcode.DCMPG);
        currentOffset++;
        advanceFrame(currentFrame.pop4push1("I"));
    }

    public void dcmpl() {
        assertTypeOnStack(StackEntryType.DOUBLE, "dcmpl requires double on stack");
        assertTypeOnStack(2, StackEntryType.DOUBLE, "dcmpl requires double in position 3 on stack");
        writeByte(Opcode.DCMPL);
        currentOffset++;
        advanceFrame(currentFrame.pop4push1("I"));
    }

    /**
     * Adds the appropriate dconst instruction.
     * <p>
     * note, if the value is not 0 or 1 then ldc is used instead
     *
     */
    public void dconst(double value) {
        if (value == 0.0) {
            writeByte(Opcode.DCONST_0);
        } else if (value == 1.0) {
            writeByte(Opcode.DCONST_1);
        } else {
            ldc2(value);
            return;
        }
        currentOffset++;
        advanceFrame(currentFrame.push("D"));
    }

    public void ddiv() {
        assertTypeOnStack(StackEntryType.DOUBLE, "ddiv requires double on stack");
        assertTypeOnStack(2, StackEntryType.DOUBLE, "ddiv requires double in position 3 on stack");
        writeByte(Opcode.DDIV);
        currentOffset++;
        advanceFrame(currentFrame.pop2());
    }

    public void dload(int no) {
        LocalVariableState locals = getLocalVars();
        if (locals.size() <= no) {
            throw new InvalidBytecodeException("Cannot load variable at " + no + ". Local Variables: " + locals.toString());
        }
        StackEntry entry = locals.get(no);
        if (entry.getType() != StackEntryType.DOUBLE) {
            throw new InvalidBytecodeException("Invalid local variable at location " + no + " Local Variables "
                    + locals.toString());
        }

        if (no > 0xFF) {
            // wide version
            writeByte(Opcode.WIDE);
            writeByte(Opcode.DLOAD);
            writeShort(no);
            currentOffset += 4;
        } else if (no >= 0 && no < 4) {
            writeByte(Opcode.DLOAD_0 + no);
            currentOffset++;
        } else {
            writeByte(Opcode.DLOAD);
            writeByte(no);
            currentOffset += 2;
        }
        advanceFrame(currentFrame.push(entry));
    }

    public void dmul() {
        assertTypeOnStack(StackEntryType.DOUBLE, "dmul requires double on stack");
        assertTypeOnStack(2, StackEntryType.DOUBLE, "dmul requires double in position 3 on stack");
        writeByte(Opcode.DMUL);
        currentOffset++;
        advanceFrame(currentFrame.pop2());
    }

    public void dneg() {
        assertTypeOnStack(StackEntryType.DOUBLE, "dneg requires double on stack");
        writeByte(Opcode.DNEG);
        currentOffset++;
        duplicateFrame();
    }

    public void drem() {
        assertTypeOnStack(StackEntryType.DOUBLE, "drem requires double on stack");
        assertTypeOnStack(2, StackEntryType.DOUBLE, "drem requires double in position 3 on stack");
        writeByte(Opcode.DREM);
        currentOffset++;
        advanceFrame(currentFrame.pop2());
    }

    public void dstore(int no) {
        assertTypeOnStack(StackEntryType.DOUBLE, "dastore requires double on stack");
        if (no > 0xFF) {
            // wide version
            writeByte(Opcode.WIDE);
            writeByte(Opcode.DSTORE);
            writeShort(no);
            currentOffset += 4;
        } else if (no >= 0 && no < 4) {
            writeByte(Opcode.DSTORE_0 + no);
            currentOffset++;
        } else {
            writeByte(Opcode.DSTORE);
            writeByte(no);
            currentOffset += 2;
        }
        advanceFrame(currentFrame.store(no));
    }

    public void dsub() {
        assertTypeOnStack(StackEntryType.DOUBLE, "dsub requires double on stack");
        assertTypeOnStack(2, StackEntryType.DOUBLE, "dsub requires double in position 3 on stack");
        writeByte(Opcode.DSUB);
        currentOffset++;
        advanceFrame(currentFrame.pop2());
    }

    public void dup() {
        assertNotWideOnStack("dup acnnot be used if double or long is on top of the stack");
        writeByte(Opcode.DUP);
        currentOffset++;
        advanceFrame(currentFrame.dup());
    }

    public void dupX1() {
        assertNotWideOnStack("dup_x1 cannot be used if double or long is on top of the stack");
        assertNotWideOnStack(1, "dup_x1 cannot be used if double or long is in position 2 on the stack");
        writeByte(Opcode.DUP_X1);
        currentOffset++;
        advanceFrame(currentFrame.dupX1());
    }

    public void dupX2() {
        assertNotWideOnStack("dup_x2 acnnot be used if double or long is on top of the stack");
        writeByte(Opcode.DUP_X2);
        currentOffset++;
        advanceFrame(currentFrame.dupX2());
    }

    public void dup2() {
        writeByte(Opcode.DUP2);
        currentOffset++;
        advanceFrame(currentFrame.dup2());
    }

    public void dup2X1() {
        assertNotWideOnStack(2, "dup2_x1 cannot be used if double or long is in position 3 on the stack");
        writeByte(Opcode.DUP2_X1);
        currentOffset++;
        advanceFrame(currentFrame.dup2X1());
    }

    public void dup2X2() {
        assertNotWideOnStack(3, "dup2_x2 cannot be used if double or long is in position 4 on the stack");
        writeByte(Opcode.DUP2_X2);
        currentOffset++;
        advanceFrame(currentFrame.dup2X2());
    }

    /**
     * Begin writing an exception handler block. The handler is not actually persisted until exceptionHandler is called.
     */
    public ExceptionHandler exceptionBlockStart(String exceptionType) {
        ExceptionHandler handler = new ExceptionHandler(currentOffset, constPool.addClassEntry(exceptionType), exceptionType,
                currentFrame);
        return handler;
    }

    /**
     * Mark the end of an exception handler block. The last instruction that was written will be the last instruction covered by
     * the handler
     *
     */
    public void exceptionBlockEnd(ExceptionHandler handler) {
        handler.setEnd(currentOffset);
    }

    /**
     * Marks the current code location as the exception handler and adds the handler to the exception handler table;
     */
    public void exceptionHandlerStart(ExceptionHandler handler) {
        if (handler.getEnd() == 0) {
            throw new InvalidBytecodeException(
                    "handler end location must be initialised via exceptionHandlerEnd before calling exceptionHandlerAdd");
        }
        handler.setHandler(currentOffset);
        exceptionTable.add(handler);
        mergeStackFrames(new StackFrame(new StackState(handler.getExceptionType(), constPool), handler.getFrame()
                .getLocalVariableState(), StackFrameType.FULL_FRAME));
    }

    public void f2d() {
        assertTypeOnStack(StackEntryType.FLOAT, "f2s requires float on stack");
        writeByte(Opcode.F2D);
        currentOffset++;
        advanceFrame(currentFrame.replace("D"));
    }

    public void f2i() {
        assertTypeOnStack(StackEntryType.FLOAT, "f2i requires float on stack");
        writeByte(Opcode.F2I);
        currentOffset++;
        advanceFrame(currentFrame.replace("I"));
    }

    public void f2l() {
        assertTypeOnStack(StackEntryType.FLOAT, "f2l requires float on stack");
        writeByte(Opcode.F2L);
        currentOffset++;
        advanceFrame(currentFrame.replace("J"));
    }

    public void fadd() {
        assertTypeOnStack(StackEntryType.FLOAT, "fadd requires float on stack");
        assertTypeOnStack(1, StackEntryType.FLOAT, "fadd requires float on stack");
        writeByte(Opcode.FADD);
        currentOffset++;
        advanceFrame(currentFrame.pop());
    }

    public void faload() {
        assertTypeOnStack(StackEntryType.INT, "faload requires an int on top of the stack");
        assertTypeOnStack(1, StackEntryType.OBJECT, "faload requires an array in position 2 on the stack");
        writeByte(Opcode.FALOAD);
        currentOffset++;
        advanceFrame(currentFrame.pop2push1("F"));
    }

    public void fastore() {
        assertTypeOnStack(StackEntryType.FLOAT, "fastore requires an int on top of the stack");
        assertTypeOnStack(1, StackEntryType.INT, "fastore requires an int in position 2 on the stack");
        assertTypeOnStack(2, StackEntryType.OBJECT, "fastore requires an array reference in position 3 on the stack");
        writeByte(Opcode.FASTORE);
        currentOffset++;
        advanceFrame(currentFrame.pop3());
    }

    public void fcmpg() {
        assertTypeOnStack(StackEntryType.FLOAT, "fcmpg requires float on stack");
        assertTypeOnStack(1, StackEntryType.FLOAT, "fcmpg requires float on stack");
        writeByte(Opcode.FCMPG);
        currentOffset++;
        advanceFrame(currentFrame.pop2push1("I"));
    }

    public void fcmpl() {
        assertTypeOnStack(StackEntryType.FLOAT, "fcmpl requires float on stack");
        assertTypeOnStack(1, StackEntryType.FLOAT, "fcmpl requires float in position 2 on stack");
        writeByte(Opcode.FCMPL);
        currentOffset++;
        advanceFrame(currentFrame.pop2push1("I"));
    }

    /**
     * Adds the appropriate fconst instruction.
     * <p>
     * note, if the value is not 0, 1, 2 then ldc is used instead
     *
     */
    public void fconst(float value) {
        if (value == 0) {
            writeByte(Opcode.FCONST_0);
        } else if (value == 1) {
            writeByte(Opcode.FCONST_1);
        } else if (value == 2) {
            writeByte(Opcode.FCONST_2);
        } else {
            ldc(value);
            return;
        }
        currentOffset++;
        advanceFrame(currentFrame.push("F"));
    }

    public void fdiv() {
        assertTypeOnStack(StackEntryType.FLOAT, "fdiv requires float on stack");
        assertTypeOnStack(1, StackEntryType.FLOAT, "fdiv requires float in position 2 on stack");
        writeByte(Opcode.FDIV);
        currentOffset++;
        advanceFrame(currentFrame.pop());
    }

    public void fload(int no) {
        LocalVariableState locals = getLocalVars();
        if (locals.size() <= no) {
            throw new InvalidBytecodeException("Cannot load variable at " + no + ". Local Variables: " + locals.toString());
        }
        StackEntry entry = locals.get(no);
        if (entry.getType() != StackEntryType.FLOAT) {
            throw new InvalidBytecodeException("Invalid local variable at location " + no + " Local Variables "
                    + locals.toString());
        }

        if (no > 0xFF) {
            // wide version
            writeByte(Opcode.WIDE);
            writeByte(Opcode.FLOAD);
            writeShort(no);
            currentOffset += 4;
        } else if (no >= 0 && no < 4) {
            writeByte(Opcode.FLOAD_0 + no);
            currentOffset++;
        } else {
            writeByte(Opcode.FLOAD);
            writeByte(no);
            currentOffset += 2;
        }
        advanceFrame(currentFrame.push(entry));
    }

    public void fmul() {
        assertTypeOnStack(StackEntryType.FLOAT, "fmul requires float on stack");
        assertTypeOnStack(1, StackEntryType.FLOAT, "fmul requires float in position 2 on stack");
        writeByte(Opcode.FMUL);
        currentOffset++;
        advanceFrame(currentFrame.pop());
    }

    public void fneg() {
        assertTypeOnStack(StackEntryType.FLOAT, "fneg requires float on stack");
        writeByte(Opcode.FNEG);
        currentOffset++;
        duplicateFrame();
    }

    public void frem() {
        assertTypeOnStack(StackEntryType.FLOAT, "frem requires float on stack");
        assertTypeOnStack(1, StackEntryType.FLOAT, "frem requires float in position 2 on stack");
        writeByte(Opcode.FREM);
        currentOffset++;
        advanceFrame(currentFrame.pop());
    }

    public void fstore(int no) {
        assertTypeOnStack(StackEntryType.FLOAT, "fstore requires float on stack");
        if (no > 0xFF) {
            // wide version
            writeByte(Opcode.WIDE);
            writeByte(Opcode.FSTORE);
            writeShort(no);
            currentOffset += 4;
        } else if (no >= 0 && no < 4) {
            writeByte(Opcode.FSTORE_0 + no);
            currentOffset++;
        } else {
            writeByte(Opcode.FSTORE);
            writeByte(no);
            currentOffset += 2;
        }
        advanceFrame(currentFrame.store(no));
    }

    public void fsub() {
        assertTypeOnStack(StackEntryType.FLOAT, "fsub requires float on stack");
        assertTypeOnStack(1, StackEntryType.FLOAT, "fsub requires float in position 2 on stack");
        writeByte(Opcode.FSUB);
        currentOffset++;
        advanceFrame(currentFrame.pop());
    }

    public void getfield(String className, String field, Class<?> fieldType) {
        getfield(className, field, DescriptorUtils.makeDescriptor(fieldType));
    }

    public void getfield(String className, String field, String descriptor) {
        assertTypeOnStack(StackEntryType.OBJECT, "getfield requires object on stack");
        int index = constPool.addFieldEntry(className, field, descriptor);
        writeByte(Opcode.GETFIELD);
        writeShort(index);
        currentOffset += 3;
        advanceFrame(currentFrame.replace(descriptor));
    }

    public void getstatic(String className, String field, Class<?> fieldType) {
        getstatic(className, field, DescriptorUtils.makeDescriptor(fieldType));
    }

    public void getstatic(String className, String field, String descriptor) {
        int index = constPool.addFieldEntry(className, field, descriptor);
        writeByte(Opcode.GETSTATIC);
        writeShort(index);
        currentOffset += 3;
        advanceFrame(currentFrame.push(descriptor));
    }

    /**
     * writes a goto instruction.
     * <p>
     * TODO: implemented goto_w
     */
    public void gotoInstruction(CodeLocation location) {
        writeByte(Opcode.GOTO);
        writeShort(location.getLocation() - currentOffset);
        mergeStackFrames(location.getStackFrame());
        currentOffset += 3;
        currentFrame = null;
    }

    /**
     * writes a goto instruction.
     * <p>
     * TODO: implemented goto_w
     */
    public BranchEnd gotoInstruction() {
        writeByte(Opcode.GOTO);
        writeShort(0);
        currentOffset += 3;
        BranchEnd ret = new BranchEnd(currentOffset - 3, currentFrame);
        currentFrame = null;
        return ret;
    }

    public void i2b() {
        assertTypeOnStack(StackEntryType.INT, "i2b requires int on stack");
        writeByte(Opcode.I2B);
        currentOffset++;
        advanceFrame(currentFrame.replace("B"));
    }

    public void i2c() {
        assertTypeOnStack(StackEntryType.INT, "i2c requires int on stack");
        writeByte(Opcode.I2C);
        currentOffset++;
        advanceFrame(currentFrame.replace("C"));
    }

    public void i2d() {
        assertTypeOnStack(StackEntryType.INT, "i2d requires int on stack");
        writeByte(Opcode.I2D);
        currentOffset++;
        advanceFrame(currentFrame.replace("D"));
    }

    public void i2f() {
        assertTypeOnStack(StackEntryType.INT, "i2f requires int on stack");
        writeByte(Opcode.I2F);
        currentOffset++;
        advanceFrame(currentFrame.replace("F"));
    }

    public void i2l() {
        assertTypeOnStack(StackEntryType.INT, "i2l requires int on stack");
        writeByte(Opcode.I2L);
        currentOffset++;
        advanceFrame(currentFrame.replace("J"));
    }

    public void i2s() {
        assertTypeOnStack(StackEntryType.INT, "i2s requires int on stack");
        writeByte(Opcode.I2S);
        currentOffset++;
        advanceFrame(currentFrame.replace("S"));
    }

    public void iadd() {
        assertTypeOnStack(StackEntryType.INT, "iadd requires int on stack");
        assertTypeOnStack(1, StackEntryType.INT, "iadd requires int on stack");
        writeByte(Opcode.IADD);
        currentOffset++;
        advanceFrame(currentFrame.pop());
    }

    public void iaload() {
        assertTypeOnStack(StackEntryType.INT, "iaload requires an int on top of the stack");
        assertTypeOnStack(1, StackEntryType.OBJECT, "iaload requires an array in position 2 on the stack");
        writeByte(Opcode.IALOAD);
        currentOffset++;
        advanceFrame(currentFrame.pop2push1("I"));
    }

    public void iand() {
        assertTypeOnStack(StackEntryType.INT, "iand requires int on stack");
        assertTypeOnStack(1, StackEntryType.INT, "iand requires int on stack");
        writeByte(Opcode.IAND);
        currentOffset++;
        advanceFrame(currentFrame.pop());
    }

    public void iastore() {
        assertTypeOnStack(StackEntryType.INT, "iastore requires an int on top of the stack");
        assertTypeOnStack(1, StackEntryType.INT, "iastore requires an int in position 2 on the stack");
        assertTypeOnStack(2, StackEntryType.OBJECT, "iastore requires an array reference in position 3 on the stack");
        writeByte(Opcode.IASTORE);
        currentOffset++;
        advanceFrame(currentFrame.pop3());
    }

    /**
     * Adds the appropriate iconst instruction.
     * <p>
     * note, if the value is not in the range -1 to 5 ldc is written instead
     *
     * @param value
     */
    public void iconst(int value) {
        if (value < -1 || value > 5) {
            ldc(value);
            return;
        }
        writeByte(Opcode.ICONST_0 + value);
        currentOffset++;
        advanceFrame(currentFrame.push("I"));
    }

    public void idiv() {
        assertTypeOnStack(StackEntryType.INT, "idiv requires int on stack");
        assertTypeOnStack(1, StackEntryType.INT, "idiv requires int in position 2 on stack");
        writeByte(Opcode.IDIV);
        currentOffset++;
        advanceFrame(currentFrame.pop());
    }

    public void ifAcmpeq(CodeLocation location) {
        assertTypeOnStack(StackEntryType.OBJECT, "ifAcmpeq requires reference type on stack");
        assertTypeOnStack(1, StackEntryType.OBJECT, "ifAcmpeq requires reference type in position 2 on stack");
        writeByte(Opcode.IF_ACMPEQ);
        writeShort(location.getLocation() - currentOffset);
        mergeStackFrames(location.getStackFrame());
        currentOffset += 3;
        advanceFrame(currentFrame.pop2());
    }

    public BranchEnd ifAcmpeq() {
        assertTypeOnStack(StackEntryType.OBJECT, "ifAcmpeq requires reference type on stack");
        assertTypeOnStack(1, StackEntryType.OBJECT, "ifAcmpeq requires reference type int position 2 on stack");
        writeByte(Opcode.IF_ACMPEQ);
        writeShort(0);
        currentOffset += 3;
        advanceFrame(currentFrame.pop2());
        BranchEnd ret = new BranchEnd(currentOffset - 3, currentFrame);
        return ret;
    }

    public void ifAcmpne(CodeLocation location) {
        assertTypeOnStack(StackEntryType.OBJECT, "ifAcmpne requires reference type on stack");
        assertTypeOnStack(1, StackEntryType.OBJECT, "ifAcmpne requires reference type in position 2 on stack");
        writeByte(Opcode.IF_ACMPNE);
        writeShort(location.getLocation() - currentOffset);
        mergeStackFrames(location.getStackFrame());
        currentOffset += 3;
        advanceFrame(currentFrame.pop2());
    }

    public BranchEnd ifAcmpne() {
        assertTypeOnStack(StackEntryType.OBJECT, "ifAcmpne requires reference type on stack");
        assertTypeOnStack(1, StackEntryType.OBJECT, "ifAcmpne requires reference type int position 2 on stack");
        writeByte(Opcode.IF_ACMPNE);
        writeShort(0);
        currentOffset += 3;
        advanceFrame(currentFrame.pop2());
        BranchEnd ret = new BranchEnd(currentOffset - 3, currentFrame);
        return ret;
    }

    public void ifIcmpeq(CodeLocation location) {
        addIfIcmp(location, Opcode.IF_ICMPEQ, "ifIcmpeq");
    }

    public BranchEnd ifIcmpeq() {
        return addIfIcmp(Opcode.IF_ICMPEQ, "ifIcmpeq");
    }

    public void ifIcmpne(CodeLocation location) {
        addIfIcmp(location, Opcode.IF_ICMPNE, "ifIcmpne");
    }

    public BranchEnd ifIcmpne() {
        return addIfIcmp(Opcode.IF_ICMPNE, "ifIcmpne");
    }

    public void ifIcmplt(CodeLocation location) {
        addIfIcmp(location, Opcode.IF_ICMPLT, "ifIcmplt");
    }

    public BranchEnd ifIcmplt() {
        return addIfIcmp(Opcode.IF_ICMPLT, "ifIcmplt");
    }

    public void ifIcmple(CodeLocation location) {
        addIfIcmp(location, Opcode.IF_ICMPLE, "ifIcmple");
    }

    public BranchEnd ifIcmple() {
        return addIfIcmp(Opcode.IF_ICMPLE, "ifIcmple");
    }

    public void ifIcmpgt(CodeLocation location) {
        addIfIcmp(location, Opcode.IF_ICMPGT, "ifIcmpgt");
    }

    public BranchEnd ifIcmpgt() {
        return addIfIcmp(Opcode.IF_ICMPGT, "ifIcmpgt");
    }

    public void ifIcmpge(CodeLocation location) {
        addIfIcmp(location, Opcode.IF_ICMPGE, "ifIcmpge");
    }

    public BranchEnd ifIcmpge() {
        return addIfIcmp(Opcode.IF_ICMPGE, "ifIcmpge");
    }

    public void ifEq(CodeLocation location) {
        addIf(location, Opcode.IFEQ, "ifeq");
    }

    public BranchEnd ifeq() {
        return addIf(Opcode.IFEQ, "ifeq");
    }

    public void ifne(CodeLocation location) {
        addIf(location, Opcode.IFNE, "ifne");
    }

    public BranchEnd ifne() {
        return addIf(Opcode.IFNE, "ifne");
    }

    public void iflt(CodeLocation location) {
        addIf(location, Opcode.IFLT, "iflt");
    }

    public BranchEnd iflt() {
        return addIf(Opcode.IFLT, "iflt");
    }

    public void ifle(CodeLocation location) {
        addIf(location, Opcode.IFLE, "ifle");
    }

    public BranchEnd ifle() {
        return addIf(Opcode.IFLE, "ifle");
    }

    public void ifgt(CodeLocation location) {
        addIf(location, Opcode.IFGT, "ifgt");
    }

    public BranchEnd ifgt() {
        return addIf(Opcode.IFGT, "ifgt");
    }

    public void ifge(CodeLocation location) {
        addIf(location, Opcode.IFGE, "ifge");
    }

    public BranchEnd ifge() {
        return addIf(Opcode.IFGE, "ifge");
    }

    public void ifnotnull(CodeLocation location) {
        addNullComparison(location, Opcode.IFNONNULL, "ifnotnull");
    }

    public BranchEnd ifnotnull() {
        return addNullComparison(Opcode.IFNONNULL, "ifnotnull");
    }

    /**
     * Jump to the given location if the reference type on the top of the stack is null
     */
    public void ifnull(CodeLocation location) {
        addNullComparison(location, Opcode.IFNULL, "ifnull");
    }

    /**
     * Jump to the given location if the reference type on the top of the stack is null.
     * <p>
     * The {@link BranchEnd} returned from this method is used to set the end point to a future point in the bytecode stream
     */
    public BranchEnd ifnull() {
        return addNullComparison(Opcode.IFNULL, "ifnull");
    }

    public void iinc(int local, int amount) {
        if (getLocalVars().get(local).getType() != StackEntryType.INT) {
            throw new InvalidBytecodeException("iinc requires int at local variable position " + local + " "
                    + getLocalVars().toString());
        }
        if (local > 0xFF || amount > 0xFF) {
            writeByte(Opcode.WIDE);
            writeByte(Opcode.IINC);
            writeShort(local);
            writeShort(amount);
            currentOffset += 6;
        } else {
            writeByte(Opcode.IINC);
            writeByte(local);
            writeByte(amount);
            currentOffset += 3;
        }
        duplicateFrame();
    }

    public void iload(int no) {
        LocalVariableState locals = getLocalVars();
        if (locals.size() <= no) {
            throw new InvalidBytecodeException("Cannot load variable at " + no + ". Local Variables: " + locals.toString());
        }
        StackEntry entry = locals.get(no);
        if (entry.getType() != StackEntryType.INT) {
            throw new InvalidBytecodeException("Invalid local variable at location " + no + " Local Variables "
                    + locals.toString());
        }

        if (no > 0xFF) {
            // wide version
            writeByte(Opcode.WIDE);
            writeByte(Opcode.ILOAD);
            writeShort(no);
            currentOffset += 4;
        } else if (no >= 0 && no < 4) {
            writeByte(Opcode.ILOAD_0 + no);
            currentOffset++;
        } else {
            writeByte(Opcode.ILOAD);
            writeByte(no);
            currentOffset += 2;
        }
        advanceFrame(currentFrame.push(entry));
    }

    public void imul() {
        assertTypeOnStack(StackEntryType.INT, "imul requires int on stack");
        assertTypeOnStack(1, StackEntryType.INT, "imul requires int in position 2 on stack");
        writeByte(Opcode.IMUL);
        currentOffset++;
        advanceFrame(currentFrame.pop());
    }

    public void ineg() {
        assertTypeOnStack(StackEntryType.INT, "ineg requires int on stack");
        writeByte(Opcode.INEG);
        currentOffset++;
        duplicateFrame();
    }

    public void instanceofInstruction(String className) {
        assertTypeOnStack(StackEntryType.OBJECT, "instanceof requires an object reference on the stack");
        int classIndex = constPool.addClassEntry(className);
        writeByte(Opcode.INSTANCEOF);
        writeShort(classIndex);
        currentOffset += 3;
        advanceFrame(currentFrame.replace("I"));
    }

    public void invokespecial(String className, String methodName, String descriptor) {
        String[] params = DescriptorUtils.parameterDescriptors(descriptor);
        String returnType = DescriptorUtils.returnType(descriptor);
        invokespecial(className, methodName, descriptor, returnType, params);
    }

    public void invokespecial(String className, String methodName, String returnType, String[] parameterTypes) {
        String descriptor = DescriptorUtils.methodDescriptor(parameterTypes, returnType);
        invokespecial(className, methodName, descriptor, returnType, parameterTypes);
    }

    public void invokespecial(Constructor<?> constructor) {
        invokespecial(constructor.getDeclaringClass().getName(), "<init>", DescriptorUtils
                .makeDescriptor(constructor), "V", DescriptorUtils.parameterDescriptors(constructor.getParameterTypes()));
    }

    public void invokespecial(Method method) {
        if (Modifier.isStatic(method.getModifiers())) {
            throw new InvalidBytecodeException("Cannot use invokespacial to invoke a static method");
        }
        invokespecial(method.getDeclaringClass().getName(), method.getName(), DescriptorUtils.methodDescriptor(method),
                DescriptorUtils.makeDescriptor(method.getReturnType()), DescriptorUtils.parameterDescriptors(method
                        .getParameterTypes()));
    }

    private void invokespecial(String className, String methodName, String descriptor, String returnType,
            String[] parameterTypes) {
        // TODO: validate stack
        int method = constPool.addMethodEntry(className, methodName, descriptor);
        writeByte(Opcode.INVOKESPECIAL);
        writeShort(method);
        currentOffset += 3;
        int pop = 1 + parameterTypes.length;
        for (String argument : parameterTypes) {
            if (argument.equals("D") || argument.equals("J")) {
                pop++;
            }
        }
        if (methodName.equals("<init>")) {
            advanceFrame(currentFrame.constructorCall(pop - 1));
        } else if (returnType.equals("V")) {
            advanceFrame(currentFrame.pop(pop));
        } else {
            advanceFrame(currentFrame.pop(pop).push(returnType));
        }
    }

    public void invokestatic(String className, String methodName, String descriptor) {
        String[] params = DescriptorUtils.parameterDescriptors(descriptor);
        String returnType = DescriptorUtils.returnType(descriptor);
        invokestatic(className, methodName, descriptor, returnType, params);
    }

    public void invokestatic(String className, String methodName, String returnType, String[] parameterTypes) {
        String descriptor = DescriptorUtils.methodDescriptor(parameterTypes, returnType);
        invokestatic(className, methodName, descriptor, returnType, parameterTypes);
    }

    public void invokestatic(Method method) {
        if (!Modifier.isStatic(method.getModifiers())) {
            throw new InvalidBytecodeException("Cannot use invokestatic to invoke a non static method");
        }
        invokestatic(method.getDeclaringClass().getName(), method.getName(), DescriptorUtils.methodDescriptor(method),
                DescriptorUtils.makeDescriptor(method.getReturnType()), DescriptorUtils.parameterDescriptors(method
                        .getParameterTypes()));
    }

    private void invokestatic(String className, String methodName, String descriptor, String returnType, String[] parameterTypes) {
        // TODO: validate stack
        int method = constPool.addMethodEntry(className, methodName, descriptor);
        writeByte(Opcode.INVOKESTATIC);
        writeShort(method);
        currentOffset += 3;
        int pop = parameterTypes.length;
        for (String argument : parameterTypes) {
            if (argument.equals("D") || argument.equals("J")) {
                pop++;
            }
        }
        if (returnType.equals("V")) {
            advanceFrame(currentFrame.pop(pop));
        } else {
            advanceFrame(currentFrame.pop(pop).push(returnType));
        }
    }

    public void invokevirtual(String className, String methodName, String descriptor) {
        String[] params = DescriptorUtils.parameterDescriptors(descriptor);
        String returnType = DescriptorUtils.returnType(descriptor);
        invokevirtual(className, methodName, descriptor, returnType, params);
    }

    public void invokevirtual(String className, String methodName, String returnType, String[] parameterTypes) {
        String descriptor = DescriptorUtils.methodDescriptor(parameterTypes, returnType);
        invokevirtual(className, methodName, descriptor, returnType, parameterTypes);
    }

    public void invokevirtual(Method method) {
        if (Modifier.isStatic(method.getModifiers())) {
            throw new InvalidBytecodeException("Cannot use invokevirtual to invoke a static method");
        } else if (Modifier.isPrivate(method.getModifiers())) {
            throw new InvalidBytecodeException("Cannot use invokevirtual to invoke a private method");
        } else if (method.getDeclaringClass().isInterface()) {
            throw new InvalidBytecodeException("Cannot use invokevirtual to invoke an interface method");
        }
        invokevirtual(method.getDeclaringClass().getName(), method.getName(), DescriptorUtils.methodDescriptor(method),
                DescriptorUtils.makeDescriptor(method.getReturnType()), DescriptorUtils.parameterDescriptors(method
                        .getParameterTypes()));
    }

    private void invokevirtual(String className, String methodName, String descriptor, String returnType,
            String[] parameterTypes) {
        // TODO: validate stack
        int method = constPool.addMethodEntry(className, methodName, descriptor);
        writeByte(Opcode.INVOKEVIRTUAL);
        writeShort(method);
        currentOffset += 3;
        int pop = 1 + parameterTypes.length;
        for (String argument : parameterTypes) {
            if (argument.equals("D") || argument.equals("J")) {
                pop++;
            }
        }
        if (returnType.equals("V")) {
            advanceFrame(currentFrame.pop(pop));
        } else {
            advanceFrame(currentFrame.pop(pop).push(returnType));
        }
    }

    public void invokeinterface(String className, String methodName, String descriptor) {
        String[] params = DescriptorUtils.parameterDescriptors(descriptor);
        String returnType = DescriptorUtils.returnType(descriptor);
        invokeinterface(className, methodName, descriptor, returnType, params);
    }

    public void invokeinterface(String className, String methodName, String returnType, String[] parameterTypes) {
        String descriptor = DescriptorUtils.methodDescriptor(parameterTypes, returnType);
        invokeinterface(className, methodName, descriptor, returnType, parameterTypes);
    }

    public void invokeinterface(Method method) {
        if (Modifier.isStatic(method.getModifiers())) {
            throw new InvalidBytecodeException("Cannot use invokeinterface to invoke a static method");
        } else if (Modifier.isPrivate(method.getModifiers())) {
            throw new InvalidBytecodeException("Cannot use invokeinterface to invoke a private method");
        } else if (!method.getDeclaringClass().isInterface()) {
            throw new InvalidBytecodeException("Cannot use invokeinterface to invoke a non interface method");
        }
        invokeinterface(method.getDeclaringClass().getName(), method.getName(), DescriptorUtils.methodDescriptor(method),
                DescriptorUtils.makeDescriptor(method.getReturnType()), DescriptorUtils.parameterDescriptors(method
                        .getParameterTypes()));
    }

    private void invokeinterface(String className, String methodName, String descriptor, String returnType,
            String[] parameterTypes) {
        // TODO: validate stack

        int pop = 1 + parameterTypes.length;
        for (String argument : parameterTypes) {
            if (argument.equals("D") || argument.equals("J")) {
                pop++;
            }
        }
        int method = constPool.addInterfaceMethodEntry(className, methodName, descriptor);
        writeByte(Opcode.INVOKEINTERFACE);
        writeShort(method);
        writeByte(pop);
        writeByte(0);
        currentOffset += 5;

        if (returnType.equals("V")) {
            advanceFrame(currentFrame.pop(pop));
        } else {
            advanceFrame(currentFrame.pop(pop).push(returnType));
        }
    }

    public void ior() {
        assertTypeOnStack(StackEntryType.INT, "ior requires int on stack");
        assertTypeOnStack(1, StackEntryType.INT, "ior requires int on stack");
        writeByte(Opcode.IOR);
        currentOffset++;
        advanceFrame(currentFrame.pop());
    }

    public void irem() {
        assertTypeOnStack(StackEntryType.INT, "irem requires int on stack");
        assertTypeOnStack(1, StackEntryType.INT, "irem requires int on stack");
        writeByte(Opcode.IREM);
        currentOffset++;
        advanceFrame(currentFrame.pop());
    }

    public void ishl() {
        assertTypeOnStack(StackEntryType.INT, "ishl requires int on stack");
        assertTypeOnStack(1, StackEntryType.INT, "ishl requires int on stack");
        writeByte(Opcode.ISHL);
        currentOffset++;
        advanceFrame(currentFrame.pop());
    }

    public void ishr() {
        assertTypeOnStack(StackEntryType.INT, "ishr requires int on stack");
        assertTypeOnStack(1, StackEntryType.INT, "ishr requires int on stack");
        writeByte(Opcode.ISHR);
        currentOffset++;
        advanceFrame(currentFrame.pop());
    }

    public void istore(int no) {
        assertTypeOnStack(StackEntryType.INT, "istore requires int on stack");
        if (no > 0xFF) {
            // wide version
            writeByte(Opcode.WIDE);
            writeByte(Opcode.ISTORE);
            writeShort(no);
            currentOffset += 4;
        } else if (no >= 0 && no < 4) {
            writeByte(Opcode.ISTORE_0 + no);
            currentOffset++;
        } else {
            writeByte(Opcode.ISTORE);
            writeByte(no);
            currentOffset += 2;
        }
        advanceFrame(currentFrame.store(no));
    }

    public void isub() {
        assertTypeOnStack(StackEntryType.INT, "isub requires int on stack");
        assertTypeOnStack(1, StackEntryType.INT, "isub requires int on stack");
        writeByte(Opcode.ISUB);
        currentOffset++;
        advanceFrame(currentFrame.pop());
    }

    public void iushr() {
        assertTypeOnStack(StackEntryType.INT, "iushr requires int on stack");
        assertTypeOnStack(1, StackEntryType.INT, "iushr requires int on stack");
        writeByte(Opcode.IUSHR);
        currentOffset++;
        advanceFrame(currentFrame.pop());
    }

    public void ixor() {
        assertTypeOnStack(StackEntryType.INT, "ixor requires int on stack");
        assertTypeOnStack(1, StackEntryType.INT, "ixor requires int on stack");
        writeByte(Opcode.IXOR);
        currentOffset++;
        advanceFrame(currentFrame.pop());
    }

    public void l2d() {
        assertTypeOnStack(StackEntryType.LONG, "l2d requires long on stack");
        writeByte(Opcode.L2D);
        currentOffset++;
        advanceFrame(currentFrame.pop2push1("D"));
    }

    public void l2f() {
        assertTypeOnStack(StackEntryType.LONG, "l2f requires long on stack");
        writeByte(Opcode.L2F);
        currentOffset++;
        advanceFrame(currentFrame.pop2push1("F"));
    }

    public void l2i() {
        assertTypeOnStack(StackEntryType.LONG, "l2i requires long on stack");
        writeByte(Opcode.L2I);
        currentOffset++;
        advanceFrame(currentFrame.pop2push1("I"));
    }

    public void ladd() {
        assertTypeOnStack(StackEntryType.LONG, "ladd requires long on stack");
        assertTypeOnStack(2, StackEntryType.LONG, "ladd requires long on stack");
        writeByte(Opcode.LADD);
        currentOffset++;
        advanceFrame(currentFrame.pop2());
    }

    public void laload() {
        assertTypeOnStack(StackEntryType.INT, "laload requires an int on top of the stack");
        assertTypeOnStack(1, StackEntryType.OBJECT, "laload requires an array in position 2 on the stack");
        writeByte(Opcode.LALOAD);
        currentOffset++;
        advanceFrame(currentFrame.pop2push1("J"));
    }

    public void land() {
        assertTypeOnStack(StackEntryType.LONG, "land requires long on stack");
        assertTypeOnStack(2, StackEntryType.LONG, "land requires long on stack");
        writeByte(Opcode.LAND);
        currentOffset++;
        advanceFrame(currentFrame.pop2());
    }

    public void lastore() {
        assertTypeOnStack(StackEntryType.LONG, "lastore requires an long on top of the stack");
        assertTypeOnStack(2, StackEntryType.INT, "lastore requires an int in position 2 on the stack");
        assertTypeOnStack(3, StackEntryType.OBJECT, "lastore requires an array reference in position 3 on the stack");
        writeByte(Opcode.LASTORE);
        currentOffset++;
        advanceFrame(currentFrame.pop4());
    }

    public void lcmp() {
        assertTypeOnStack(StackEntryType.LONG, "lcmp requires long on stack");
        assertTypeOnStack(2, StackEntryType.LONG, "lcmp requires long on stack");
        writeByte(Opcode.LCMP);
        currentOffset++;
        advanceFrame(currentFrame.pop4push1("I"));
    }

    /**
     * Adds the appropriate lconst instruction.
     * <p>
     * note, if the value is not 0 or 1 then ldc is used instead
     *
     */
    public void lconst(long value) {
        if (value == 0) {
            writeByte(Opcode.LCONST_0);
        } else if (value == 1) {
            writeByte(Opcode.LCONST_1);
        } else {
            ldc2(value);
            return;
        }
        currentOffset++;
        advanceFrame(currentFrame.push("J"));
    }

    /**
     * Adds an ldc instruction for an int.
     *
     * @param value
     */
    public void ldc(int value) {
        if (value > -2 && value < 6) {
            iconst(value);
            return;
        }
        int index = constPool.addIntegerEntry(value);
        ldcInternal(index);
        advanceFrame(currentFrame.push("I"));
    }

    /**
     * Adds an ldc instruction for float
     *
     */
    public void ldc(float value) {
        int index = constPool.addFloatEntry(value);
        ldcInternal(index);
        advanceFrame(currentFrame.push("F"));
    }

    /**
     * Adds an ldc instruction for a String
     * <p>
     * To load a class literal using ldc use the @{link #loadType(String)} method.
     *
     */
    public void ldc(String value) {
        int index = constPool.addStringEntry(value);
        ldcInternal(index);
        advanceFrame(currentFrame.push("Ljava/lang/String;"));
    }

    /**
     * Adds an ldc instruction for an int.
     *
     */
    private void ldcInternal(int index) {
        if (index > 0xFF) {
            writeByte(Opcode.LDC_W);
            writeShort(index);
            currentOffset += 3;
        } else {
            writeByte(Opcode.LDC);
            writeByte(index);
            currentOffset += 2;
        }
    }

    /**
     * Adds an ldc2_w instruction for double
     *
     */
    public void ldc2(double value) {
        int index = constPool.addDoubleEntry(value);
        writeByte(Opcode.LDC2_W);
        writeShort(index);
        currentOffset += 3;
        advanceFrame(currentFrame.push("D"));
    }

    /**
     * Adds an ldc2_w instruction for long
     *
     */
    public void ldc2(long value) {
        int index = constPool.addLongEntry(value);
        writeByte(Opcode.LDC2_W);
        writeShort(index);
        currentOffset += 3;
        advanceFrame(currentFrame.push("J"));
    }

    public void ldiv() {
        assertTypeOnStack(StackEntryType.LONG, "ldiv requires long on stack");
        assertTypeOnStack(2, StackEntryType.LONG, "ldiv requires long in position 3 on stack");
        writeByte(Opcode.LDIV);
        currentOffset++;
        advanceFrame(currentFrame.pop2());
    }

    public void lload(int no) {
        LocalVariableState locals = getLocalVars();
        if (locals.size() <= no) {
            throw new InvalidBytecodeException("Cannot load variable at " + no + ". Local Variables: " + locals.toString());
        }
        StackEntry entry = locals.get(no);
        if (entry.getType() != StackEntryType.LONG) {
            throw new InvalidBytecodeException("Invalid local variable at location " + no + " Local Variables "
                    + locals.toString());
        }

        if (no > 0xFF) {
            // wide version
            writeByte(Opcode.WIDE);
            writeByte(Opcode.LLOAD);
            writeShort(no);
            currentOffset += 4;
        } else if (no >= 0 && no < 4) {
            writeByte(Opcode.LLOAD_0 + no);
            currentOffset++;
        } else {
            writeByte(Opcode.LLOAD);
            writeByte(no);
            currentOffset += 2;
        }
        advanceFrame(currentFrame.push(entry));
    }

    public void lmul() {
        assertTypeOnStack(StackEntryType.LONG, "lmul requires long on stack");
        assertTypeOnStack(2, StackEntryType.LONG, "lmul requires long in position 3 on stack");
        writeByte(Opcode.LMUL);
        currentOffset++;
        advanceFrame(currentFrame.pop2());
    }

    public void lneg() {
        assertTypeOnStack(StackEntryType.LONG, "lneg requires long on stack");
        writeByte(Opcode.LNEG);
        currentOffset++;
        duplicateFrame();
    }

    /**
     * Generates the apprpriate load instruction for the given type
     *
     * @param type The type of variable
     * @param no local variable number
     */
    public void load(Class<?> type, int no) {
        load(DescriptorUtils.makeDescriptor(type), no);
    }

    /**
     * Generates the apprpriate load instruction for the given type
     *
     * @param descriptor The descriptor of the variable
     * @param no local variable number
     */
    public void load(String descriptor, int no) {
        if (descriptor.length() != 1) {
            aload(no);
        } else {
            char type = descriptor.charAt(0);
            switch (type) {
                case 'F':
                    fload(no);
                    break;
                case 'J':
                    lload(no);
                    break;
                case 'D':
                    dload(no);
                    break;
                case 'I':
                case 'S':
                case 'B':
                case 'C':
                case 'Z':
                    iload(no);
                    break;
                default:
                    throw new InvalidBytecodeException("Could not load primitive type: " + type);
            }
        }
    }

    public void loadClass(String className) {
        int index = constPool.addClassEntry(className);
        ldcInternal(index);
        advanceFrame(currentFrame.push("Ljava/lang/Class;"));
    }

    /**
     * Loads a java.lang.Class for the given descriptor into the stack.
     */
    public void loadType(String descriptor) {
        if (descriptor.length() != 1) {
            if (descriptor.startsWith("L") && descriptor.endsWith(";")) {
                descriptor = descriptor.substring(1, descriptor.length() - 1);
            }
            loadClass(descriptor);
        } else {
            char type = descriptor.charAt(0);
            switch (type) {
                case 'I':
                    getstatic(Integer.class.getName(), "TYPE", "Ljava/lang/Class;");
                    break;
                case 'J':
                    getstatic(Long.class.getName(), "TYPE", "Ljava/lang/Class;");
                    break;
                case 'S':
                    getstatic(Short.class.getName(), "TYPE", "Ljava/lang/Class;");
                    break;
                case 'F':
                    getstatic(Float.class.getName(), "TYPE", "Ljava/lang/Class;");
                    break;
                case 'D':
                    getstatic(Double.class.getName(), "TYPE", "Ljava/lang/Class;");
                    break;
                case 'B':
                    getstatic(Byte.class.getName(), "TYPE", "Ljava/lang/Class;");
                    break;
                case 'C':
                    getstatic(Character.class.getName(), "TYPE", "Ljava/lang/Class;");
                    break;
                case 'Z':
                    getstatic(Boolean.class.getName(), "TYPE", "Ljava/lang/Class;");
                    break;
                case 'V':
                    getstatic(Void.class.getName(), "TYPE", "Ljava/lang/Class;");
                    break;
                default:
                    throw new InvalidBytecodeException("Unkown primitive type: " + type);
            }
        }
    }

    public void lookupswitch() {
        throw new RuntimeException("Not implemented");
    }

    public void lor() {
        assertTypeOnStack(StackEntryType.LONG, "lor requires long on stack");
        assertTypeOnStack(2, StackEntryType.LONG, "lor requires long in position 3 on stack");
        writeByte(Opcode.LOR);
        currentOffset++;
        advanceFrame(currentFrame.pop2());
    }

    public void lrem() {
        assertTypeOnStack(StackEntryType.LONG, "lrem requires long on stack");
        assertTypeOnStack(2, StackEntryType.LONG, "lrem requires long in position 3 on stack");
        writeByte(Opcode.LREM);
        currentOffset++;
        advanceFrame(currentFrame.pop2());
    }

    public void lshl() {
        assertTypeOnStack(StackEntryType.INT, "lshl requires int on stack");
        assertTypeOnStack(1, StackEntryType.LONG, "lshl requires long in position 2 on stack");
        writeByte(Opcode.LSHL);
        currentOffset++;
        advanceFrame(currentFrame.pop());
    }

    public void lshr() {
        assertTypeOnStack(StackEntryType.INT, "lshr requires int on stack");
        assertTypeOnStack(1, StackEntryType.LONG, "lshr requires long in position 2 on stack");
        writeByte(Opcode.LSHR);
        currentOffset++;
        advanceFrame(currentFrame.pop());
    }

    public void lstore(int no) {
        assertTypeOnStack(StackEntryType.LONG, "lstore requires long on stack");
        if (no > 0xFF) {
            // wide version
            writeByte(Opcode.WIDE);
            writeByte(Opcode.LSTORE);
            writeShort(no);
            currentOffset += 4;
        } else if (no >= 0 && no < 4) {
            writeByte(Opcode.LSTORE_0 + no);
            currentOffset++;
        } else {
            writeByte(Opcode.LSTORE);
            writeByte(no);
            currentOffset += 2;
        }
        advanceFrame(currentFrame.store(no));
    }

    public void lsub() {
        assertTypeOnStack(StackEntryType.LONG, "lsub requires long on stack");
        assertTypeOnStack(2, StackEntryType.LONG, "lsub requires long in position 3 on stack");
        writeByte(Opcode.LSUB);
        currentOffset++;
        advanceFrame(currentFrame.pop2());
    }

    public void lushr() {
        assertTypeOnStack(StackEntryType.INT, "lushr requires int on stack");
        assertTypeOnStack(1, StackEntryType.LONG, "lushr requires long in position 2 on stack");
        writeByte(Opcode.LUSHR);
        currentOffset++;
        advanceFrame(currentFrame.pop());
    }

    public void lxor() {
        assertTypeOnStack(StackEntryType.LONG, "lxor requires long on stack");
        assertTypeOnStack(2, StackEntryType.LONG, "lxor requires long in position 3 on stack");
        writeByte(Opcode.LXOR);
        currentOffset++;
        advanceFrame(currentFrame.pop2());
    }

    /**
     * Gets the location object for the current location in the bytecode. Jumps to this location will begin executing the next
     * instruction that is written to the bytecode stream
     *
     */
    public CodeLocation mark() {
        return new CodeLocation(currentOffset, currentFrame);
    }

    public void monitorenter() {
        assertTypeOnStack(StackEntryType.OBJECT, "monitorenter requires object reference on stack");
        writeByte(Opcode.MONITORENTER);
        currentOffset++;
        advanceFrame(currentFrame.pop());
    }

    public void monitorexit() {
        assertTypeOnStack(StackEntryType.OBJECT, "monitorexit requires object reference on stack");
        writeByte(Opcode.MONITOREXIT);
        currentOffset++;
        advanceFrame(currentFrame.pop());
    }

    public void multianewarray(String arrayType, int dimensions) {
        StringBuilder newType = new StringBuilder();
        for (int i = 0; i < dimensions; ++i) {
            assertTypeOnStack(i, StackEntryType.INT, "multianewarray requires int on stack in position " + i);
            newType.append('[');
        }
        if (!arrayType.startsWith("[")) {
            newType.append('L');
            newType.append(arrayType);
            newType.append(";");
        } else {
            newType.append(arrayType);
        }
        int classIndex = constPool.addClassEntry(newType.toString());
        writeByte(Opcode.MULTIANEWARRAY);
        writeShort(classIndex);
        writeByte(dimensions);
        currentOffset += 4;

        advanceFrame(currentFrame.pop(dimensions).push(newType.toString()));
    }

    public void newInstruction(String classname) {
        int classIndex = constPool.addClassEntry(classname);
        writeByte(Opcode.NEW);
        writeShort(classIndex);
        StackEntry entry = new StackEntry(StackEntryType.UNITITIALIZED_OBJECT, classname, currentOffset);
        currentOffset += 3;
        advanceFrame(currentFrame.push(entry));
    }

    public void newInstruction(Class<?> clazz) {
        newInstruction(clazz.getName());
    }

    /**
     * arrayType must be a {@link Class} object that represents a primitive type
     */
    public void newarray(Class<?> arrayType) {
        assertTypeOnStack(StackEntryType.INT, "newarray requires int on stack");
        int type = 0;
        String desc;
        if (arrayType == boolean.class) {
            type = Opcode.T_BOOLEAN;
            desc = "[Z";
        } else if (arrayType == char.class) {
            type = Opcode.T_CHAR;
            desc = "[C";
        } else if (arrayType == float.class) {
            type = Opcode.T_FLOAT;
            desc = "[F";
        } else if (arrayType == double.class) {
            type = Opcode.T_DOUBLE;
            desc = "[D";
        } else if (arrayType == byte.class) {
            type = Opcode.T_BYTE;
            desc = "[B";
        } else if (arrayType == short.class) {
            type = Opcode.T_SHORT;
            desc="[S";
        } else if (arrayType == int.class) {
            type = Opcode.T_INT;
            desc = "[I";
        } else if (arrayType == long.class) {
            type = Opcode.T_LONG;
            desc ="[J";
        } else {
            throw new InvalidBytecodeException("Class " + arrayType + " is not a primitive type");
        }
        writeByte(Opcode.NEWARRAY);
        writeByte(type);
        currentOffset+=2;
        advanceFrame(currentFrame.replace(desc));
    }

    public void nop() {
        writeByte(Opcode.NOP);
        currentOffset++;
        duplicateFrame();
    }

    public void pop() {
        writeByte(Opcode.POP);
        currentOffset++;
        advanceFrame(currentFrame.pop());
    }

    public void pop2() {
        writeByte(Opcode.POP2);
        currentOffset++;
        advanceFrame(currentFrame.pop2());
    }

    public void putfield(String className, String field, Class<?> fieldType) {
        putfield(className, field, DescriptorUtils.makeDescriptor(fieldType));
    }

    public void putfield(String className, String field, String descriptor) {
        if (!getStack().isOnTop(descriptor)) {
            throw new InvalidBytecodeException("Attempting to put wrong type into  field. Field:" + className + "."
                    + field + " (" + descriptor + "). Stack State: " + getStack().toString());
        }
        if (getStack().top_1().getType() != StackEntryType.UNINITIALIZED_THIS) {
            assertTypeOnStack(1, StackEntryType.OBJECT, "expected object in position 2 on stack");
        }
        int index = constPool.addFieldEntry(className, field, descriptor);
        writeByte(Opcode.PUTFIELD);
        writeShort(index);
        currentOffset += 3;
        advanceFrame(currentFrame.pop2());
    }

    public void putstatic(String className, String field, Class<?> fieldType) {
        putstatic(className, field, DescriptorUtils.makeDescriptor(fieldType));
    }

    public void putstatic(String className, String field, String descriptor) {
        if (!getStack().isOnTop(descriptor)) {
            throw new InvalidBytecodeException("Attempting to put wrong type into static field. Field:" + className + "."
                    + field + " (" + descriptor + "). Stack State: " + getStack().toString());
        }
        int index = constPool.addFieldEntry(className, field, descriptor);
        writeByte(Opcode.PUTSTATIC);
        writeShort(index);
        currentOffset += 3;
        advanceFrame(currentFrame.pop());
    }

    /**
     * Adds the appropriate return instruction for the methods return type.
     */
    public void returnInstruction() {
        String returnType = method.getReturnType();
        if (!returnType.equals("V")) {
            if (!getStack().isOnTop(returnType)) {
                throw new InvalidBytecodeException(returnType + " is not on top of stack. " + getStack().toString());
            }
        }

        // all these instructions are one byte
        currentOffset++;

        // return instructions do not create stack map entries

        if (returnType.length() > 1) {
            writeByte(Opcode.ARETURN);
        } else {
            char ret = method.getReturnType().charAt(0);
            switch (ret) {
                case 'V':
                    writeByte(Opcode.RETURN);
                    break;
                case 'I':
                case 'Z':
                case 'S':
                case 'B':
                case 'C':
                    writeByte(Opcode.IRETURN);
                    break;
                case 'F':
                    writeByte(Opcode.FRETURN);
                    break;
                case 'D':
                    writeByte(Opcode.DRETURN);
                    break;
                case 'J':
                    writeByte(Opcode.LRETURN);
            }
        }
        currentFrame = null;
    }

    public void saload() {
        assertTypeOnStack(StackEntryType.INT, "saload requires an int on top of the stack");
        assertTypeOnStack(1, StackEntryType.OBJECT, "saload requires an array in position 2 on the stack");
        writeByte(Opcode.SALOAD);
        currentOffset++;
        advanceFrame(currentFrame.pop2push1("I"));
    }

    public void sastore() {
        assertTypeOnStack(StackEntryType.INT, "sastore requires an int on top of the stack");
        assertTypeOnStack(1, StackEntryType.INT, "sastore requires an int in position 2 on the stack");
        assertTypeOnStack(2, StackEntryType.OBJECT, "sastore requires an array reference in position 3 on the stack");
        writeByte(Opcode.SASTORE);
        currentOffset++;
        advanceFrame(currentFrame.pop3());
    }

    public void sipush(short value) {
        writeByte(Opcode.SIPUSH);
        writeShort(value);
        currentOffset += 3;
        advanceFrame(currentFrame.push("S"));
    }

    public void swap() {
        assertNotWideOnStack("swap cannot be used when wide type is on top of stack");
        assertNotWideOnStack(1, "swap cannot be used when wide type is on position 1 of the stack");
        writeByte(Opcode.SWAP);
        currentOffset++;
        advanceFrame(currentFrame.swap());
    }

    /**
     * loads all parameters onto the stack.
     * <p>
     * If this method is non-static then the parameter at location 0 (i.e. this object) is not pushed.
     */
    public void loadMethodParameters() {
        int index = method.isStatic() ? 0 : 1;
        for (String type : method.getParameters()) {
            if (type.length() > 1) {
                // object or array
                aload(index);
            } else if (type.equals("D")) {
                dload(index);
                index++;
            } else if (type.equals("J")) {
                lload(index);
                index++;
            } else if (type.equals("F")) {
                fload(index);
            } else {
                iload(index);
            }
            index++;
        }
    }

    private void writeByte(int n) {
        try {
            data.writeByte(n);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void writeShort(int n) {
        try {
            data.writeShort(n);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * overwrites a 16 bit value in the already written bytecode data
     *
     */
    private void overwriteShort(byte[] bytecode, int offset, int value) {
        bytecode[offset] = (byte) (value >> 8);
        bytecode[offset + 1] = (byte) (value);
    }

    public LinkedHashMap<Integer, StackFrame> getStackFrames() {
        return new LinkedHashMap<Integer, StackFrame>(stackFrames);
    }

    /**
     * Adds a duplicate of the current frame to the current position.
     * <p>
     * currently this just puts the same frame into a different position
     */
    private void duplicateFrame() {
        stackFrames.put(currentOffset, currentFrame);
        updateMaxValues();
    }

    private void advanceFrame(StackFrame frame) {
        stackFrames.put(currentOffset, frame);
        currentFrame = frame;
        updateMaxValues();
    }

    private void updateMaxValues() {
        if (getStack().getContents().size() > maxStackDepth) {
            maxStackDepth = getStack().getContents().size();
        }
        if (getLocalVars().getContents().size() > maxLocals) {
            maxLocals = getLocalVars().getContents().size();
        }
    }

    private LocalVariableState getLocalVars() {
        return currentFrame.getLocalVariableState();
    }

    private StackState getStack() {
        return currentFrame.getStackState();
    }

    public void assertTypeOnStack(int position, StackEntryType type, String message) {
        if (getStack().size() <= position) {
            throw new InvalidBytecodeException(message + " Stack State: " + getStack().toString());
        }
        int index = getStack().getContents().size() - 1 - position;
        if (type == StackEntryType.DOUBLE || type == StackEntryType.LONG) {
            index -= 1;
        }
        StackEntryType stype = getStack().getContents().get(index).getType();
        if (stype != type) {
            if (!(type == StackEntryType.OBJECT && stype == StackEntryType.NULL)) {
                throw new InvalidBytecodeException(message + " Stack State: " + getStack().toString());
            }
        }
    }

    public void assertTypeOnStack(StackEntryType type, String message) {
        assertTypeOnStack(0, type, message);
    }

    public void assertNotWideOnStack(int position, String message) {
        if (getStack().size() <= position) {
            throw new InvalidBytecodeException(message + " Stack State: " + getStack().toString());
        }
        int index = getStack().getContents().size() - 1 - position;

        StackEntryType stype = getStack().getContents().get(index).getType();
        if (stype == StackEntryType.TOP) {
            throw new InvalidBytecodeException(message + " Stack State: " + getStack().toString());
        }
    }

    public void assertNotWideOnStack(String message) {
        assertNotWideOnStack(0, message);
    }

    /**
     * Merge the stack frames.
     * <p>
     * If the frames are incompatible then an {@link InvalidBytecodeException} is thrown. If the frames cannot be properly
     * merged then the stack map is marked as invalid
     *
     */
    private void mergeStackFrames(StackFrame stackFrame) {
        if (currentFrame == null) {
            currentFrame = stackFrame;
            stackFrames.put(currentOffset, currentFrame);
            return;
        }
        StackState currentStackState = getStack();
        StackState mergeStackState = stackFrame.getStackState();
        if (currentStackState.size() != mergeStackState.size()) {
            throw new InvalidBytecodeException("Cannot merge stack frames, different stack sizes");
        }
        for (int i = 0; i < mergeStackState.size(); ++i) {
            StackEntry currentEntry = currentStackState.getContents().get(i);
            StackEntry mergeEntry = mergeStackState.getContents().get(i);
            if (mergeEntry.getType() == currentEntry.getType()) {
                if (mergeEntry.getType() == StackEntryType.OBJECT) {
                    if (!mergeEntry.getDescriptor().equals(currentEntry.getDescriptor())) {
                        if (!mergeEntry.equals("Ljava/lang/Object;")) {
                            // we cannot reliably determine if closes common superclass at this point
                            // so we will just mark the stack map attribute as invalid
                            stackMapAttributeValid = false;
                        }
                    }
                }
            } else if (!((mergeEntry.getType() == StackEntryType.NULL && currentEntry.getType() == StackEntryType.OBJECT) || (mergeEntry
                    .getType() == StackEntryType.OBJECT && currentEntry.getType() == StackEntryType.NULL))) {
                throw new InvalidBytecodeException("Cannot merge stack frame " + currentStackState + " with frame "
                        + mergeStackState + " stack entry " + i + " is invalid");
            }
        }

        LocalVariableState currentLocalVariableState = getLocalVars();
        LocalVariableState mergeLocalVariableState = getLocalVars();
        if (currentLocalVariableState.size() < mergeLocalVariableState.size()) {
            throw new InvalidBytecodeException(
                    "Cannot merge stack frames, merge location has less locals than current location");
        }
        for (int i = 0; i < mergeLocalVariableState.size(); ++i) {
            StackEntry currentEntry = currentLocalVariableState.getContents().get(i);
            StackEntry mergeEntry = mergeLocalVariableState.getContents().get(i);
            if (mergeEntry.getType() == currentEntry.getType()) {
                if (mergeEntry.getType() == StackEntryType.OBJECT) {
                    if (!mergeEntry.getDescriptor().equals(currentEntry.getDescriptor())) {
                        if (!mergeEntry.equals("Ljava/lang/Object;")) {
                            // we cannot reliably determine if closes common superclass at this point
                            // so we will just mark the stack map attribute as invalid
                            stackMapAttributeValid = false;
                        }
                    }
                }
            } else if (!((mergeEntry.getType() == StackEntryType.NULL && currentEntry.getType() == StackEntryType.OBJECT) || (mergeEntry
                    .getType() == StackEntryType.OBJECT && currentEntry.getType() == StackEntryType.NULL))) {
                throw new InvalidBytecodeException("Cannot merge stack frame " + currentLocalVariableState + " with frame "
                        + currentLocalVariableState + " local variable entry " + i + " is invalid");
            }
        }
    }

    private void addIfIcmp(CodeLocation location, int opcode, String name) {
        assertTypeOnStack(StackEntryType.INT, name + " requires int on stack");
        assertTypeOnStack(1, StackEntryType.INT, name + " requires int in position 2 on stack");
        writeByte(opcode);
        writeShort(location.getLocation() - currentOffset);
        currentOffset += 3;
        advanceFrame(currentFrame.pop2());
        mergeStackFrames(location.getStackFrame());
    }

    private BranchEnd addIfIcmp(int opcode, String name) {
        assertTypeOnStack(StackEntryType.INT, name + " requires int on stack");
        assertTypeOnStack(1, StackEntryType.INT, name + " requires int int position 2 on stack");
        writeByte(opcode);
        writeShort(0);
        currentOffset += 3;
        advanceFrame(currentFrame.pop2());
        BranchEnd ret = new BranchEnd(currentOffset - 3, currentFrame);
        return ret;
    }

    private void addIf(CodeLocation location, int opcode, String name) {
        assertTypeOnStack(StackEntryType.INT, name + " requires int on stack");
        writeByte(opcode);
        writeShort(location.getLocation() - currentOffset);
        currentOffset += 3;
        advanceFrame(currentFrame.pop());
        mergeStackFrames(location.getStackFrame());
    }

    private BranchEnd addIf(int opcode, String name) {
        assertTypeOnStack(StackEntryType.INT, name + " requires int on stack");
        writeByte(opcode);
        writeShort(0);
        currentOffset += 3;
        advanceFrame(currentFrame.pop());
        BranchEnd ret = new BranchEnd(currentOffset - 3, currentFrame);
        return ret;
    }

    private void addNullComparison(CodeLocation location, int opcode, String name) {
        assertTypeOnStack(StackEntryType.OBJECT, name + " requires reference type on stack");
        writeByte(opcode);
        writeShort(location.getLocation() - currentOffset);
        currentOffset += 3;
        advanceFrame(currentFrame.pop());
        mergeStackFrames(location.getStackFrame());
    }

    private BranchEnd addNullComparison(int opcode, String name) {
        assertTypeOnStack(StackEntryType.OBJECT, name + " requires reference type on stack");
        writeByte(opcode);
        writeShort(0);
        currentOffset += 3;
        advanceFrame(currentFrame.pop());
        BranchEnd ret = new BranchEnd(currentOffset - 3, currentFrame);
        return ret;
    }

}
TOP

Related Classes of org.jboss.classfilewriter.code.CodeAttribute

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.