/**
 * A (bytecode) cursor.
 * @file ovm/services/bytecode/editor/Cursor.java 
 **/
package ovm.services.bytecode.editor;

import ovm.core.OVMBase;
import ovm.core.repository.Selector;
import ovm.core.repository.TypeName;
import ovm.core.domain.ResolutionInfo;
import ovm.core.repository.Binder;
import ovm.core.repository.ConstantsEditor;
import ovm.core.repository.Descriptor;
import ovm.core.repository.RepositoryString;
import ovm.core.repository.TypeCodes;
import ovm.core.repository.ConstantFieldref;
import ovm.core.repository.ConstantMethodref;
import ovm.core.repository.ConstantClass;
import ovm.services.bytecode.JVMConstants;
import ovm.util.ByteBuffer;
import ovm.util.NumberRanges;

/**
 * Implementation of a bytecode cursor.  A Cursor is a class that
 * allows the user to generate bytecode specifying the opcode and its
 * arguments in a high-level fashion. The Cursor will then generate
 * pieces of code and take care of updates to contant pools and jump
 * targets.<p> The naming conventions for the add-methods are
 * full-capitalized instruction names if exactly the given instruction
 * will be generated or partial capitalization if the add-method
 * represents a group of instructions and the Cursor may choose which
 * exact instruction (sequence) fits the requirements.  <p>
 *
 * @author Christian Grothoff
 **/
public final class Cursor
    extends OVMBase
    implements JVMConstants.Opcodes {

    /**
     * The Editor that we belong to.
     **/
    final CodeFragmentEditor editor_;

    /**
     * The pc in the editor that we're inserting at.
     **/
    private final int pc_;

    /**
     * The current offset we're at (relative to the
     * beginning of the code generated by this Cursor).
     **/
    int offset_;

    /**
     * The inserted instructions (linked list)
     **/
    private InstructionBuilder instructionBuilder_;

    /**
     * Marker that marks the beginning of this Cursor.
     **/
    private final Marker markerZero_;

    private final ConstantsEditor cPoolBuilder_;

    /**
     * Create an Cursor that inserts code
     * into the given editor at the given pc.
     * @param editor the editor
     * @param pc the pc
     **/
    Cursor(CodeFragmentEditor editor,
	   ConstantsEditor cPoolBuilder,
	   int pc) {
        this.offset_ = 0;
        this.editor_ = editor;
        this.cPoolBuilder_ = cPoolBuilder;
        this.pc_ = pc;
        this.markerZero_ = new Marker(this);
        this.instructionBuilder_ = markerZero_;
    }

    public String toString() {
	String prefix = "";
	StringBuffer sb = new StringBuffer();
	for (InstructionBuilder ib = instructionBuilder_;
	     ib != null;
	     ib = ib.next_) {
	    sb.append(prefix);
	    prefix = " ";
	    sb.append(ib);
	}
	return sb.toString();
    }

    /**
     * Return the PC that this Cursor produces code for.
     */
    public int getPC() {  return pc_;  }

    /** 
     * Predict the size that the code generated by this cursor will have.
     **/
    int predictSize(int pos) {
        InstructionBuilder ib = instructionBuilder_;
        offset_ = pos;
        while (ib != null) {
            //d("Cursor.predictSize(): "+ib+" increases by "+ ib.predictSize());
            offset_ += ib.predictSize();
            ib = ib.next();
        }
        return offset_ - pos;
    }

    /**
     * Dump the code generated by this cursor into the
     * given array.
     * @param buffer byte buffer to write the generated code to
     **/
    void write(ByteBuffer buffer) {
        InstructionBuilder ib = instructionBuilder_;
        for (; ib != null; ib = ib.next()) {
            offset_ = buffer.position();
            ib.write(buffer);
        }
    }

    /**
     * Obtain the marker at the beginning of this Cursor.
     **/
    public Marker getMarkerAtZero() {
        return markerZero_;
    }

    /**
     * Obtain a CursorPosition used to set a jump target to the next opcode
     * that is added to this Cursor (or the next opcode that will follow
     * this Cursor in the main method if there is no other code added).
     **/
    public Marker addMarker() {
        Marker m = new Marker(this);
        addInstruction(m);
        return m;
    }

    /**
     * Obtain a marker that can be bound later (for forward-jumps).
     **/
    public Marker makeUnboundMarker() {
        return new Marker(this); // this could be null - as we don't necessarily have to bind to the same cursor-jv
    }

    /**
     * Bind a marker to the current position.
     **/
    public void bindMarker(Marker m) {
	m.setCursor(this);
        addInstruction(m);
    }

    /**
     * Create an IInc instruction.
     **/
    public void addIInc(char index, short value) {
        addInstruction(new IIncBuilder(value, index));
    }

    /**
     * Add an ILOAD_N to the codestream.
     **/
    public void addILoad(char index) {
        if (index < 4)
            addInstruction(new SimpleOpBuilder(ILOAD_0 + index));
        else if (NumberRanges.isUnsignedByte(index))
            addInstruction(new ByteIndexedBuilder((byte) ILOAD, (byte) index));
        else
            addInstruction(new Wide4Builder((byte) ILOAD, index));
    }

    /**
     * Add an FLOAD_N to the codestream.
     **/
    public void addFLoad(char index) {
        if (index < 4)
            addInstruction(new SimpleOpBuilder(FLOAD_0 + index));
        else if (NumberRanges.isUnsignedByte(index))
            addInstruction(new ByteIndexedBuilder((byte) FLOAD, (byte) index));
        else
            addInstruction(new Wide4Builder((byte) FLOAD, index));
    }

    /**
     * Add an DLOAD_N to the codestream.
     **/
    public void addDLoad(char index) {
        if (index < 4)
            addInstruction(new SimpleOpBuilder(DLOAD_0 + index));
        else if (NumberRanges.isUnsignedByte(index))
            addInstruction(new ByteIndexedBuilder((byte) DLOAD, (byte) index));
        else
            addInstruction(new Wide4Builder((byte) DLOAD, index));
    }

    /**
    * Add an LLOAD_N to the codestream.
    **/
    public void addLLoad(char index) {
        if (index < 4)
            addInstruction(new SimpleOpBuilder(LLOAD_0 + index));
        else if (NumberRanges.isUnsignedByte(index))
            addInstruction(new ByteIndexedBuilder((byte) LLOAD, (byte) index));
        else
            addInstruction(new Wide4Builder((byte) LLOAD, index));
    }

    /**
     * Add an ALOAD_N to the codestream.
     **/
    public void addALoad(char index) {
        if (index < 4)
            addInstruction(new SimpleOpBuilder(ALOAD_0 + index));
        else if (NumberRanges.isUnsignedByte(index))
            addInstruction(new ByteIndexedBuilder((byte) ALOAD, (byte) index));
        else
            addInstruction(new Wide4Builder((byte) ALOAD, index));
    }

    /**
     * Add an ISTORE_N to the codestream.
     **/
    public void addIStore(char index) {
        if (index < 4)
            addInstruction(new SimpleOpBuilder(ISTORE_0 + index));
        else if (NumberRanges.isUnsignedByte(index))
            addInstruction(new ByteIndexedBuilder((byte) ISTORE, (byte) index));
        else
            addInstruction(new Wide4Builder((byte) ISTORE, index));
    }

    /**
     * Add an FSTORE_N to the codestream.
     **/
    public void addFStore(char index) {
        if (index < 4)
            addInstruction(new SimpleOpBuilder(FSTORE_0 + index));
        else if (NumberRanges.isUnsignedByte(index))
            addInstruction(new ByteIndexedBuilder((byte) FSTORE, (byte) index));
        else
            addInstruction(new Wide4Builder((byte) FSTORE, index));
    }

    /**
    * Add an DSTORE_N to the codestream.
    **/
    public void addDStore(char index) {
        if (index < 4)
            addInstruction(new SimpleOpBuilder(DSTORE_0 + index));
        else if (NumberRanges.isUnsignedByte(index))
            addInstruction(new ByteIndexedBuilder((byte) DSTORE, (byte) index));
        else
            addInstruction(new Wide4Builder((byte) DSTORE, index));
    }

    /**
     * Add an LSTORE_N to the codestream.
     **/
    public void addLStore(char index) {
        if (index < 4)
            addInstruction(new SimpleOpBuilder(LSTORE_0 + index));
        else if (NumberRanges.isUnsignedByte(index))
            addInstruction(new ByteIndexedBuilder((byte) LSTORE, (byte) index));
        else
            addInstruction(new Wide4Builder((byte) LSTORE, index));
    }

    /**
     * Add an ASTORE_N to the codestream.
     **/
    public void addAStore(char index) {
        if (index < 4)
            addInstruction(new SimpleOpBuilder(ASTORE_0 + index));
        else if (NumberRanges.isUnsignedByte(index))
            addInstruction(new ByteIndexedBuilder((byte) ASTORE, (byte) index));
        else
            addInstruction(new Wide4Builder((byte) ASTORE, index));
    }

    /**
     * Rotate the top stack elements with {@link
     * ovm.services.bytecode.Instruction.SWAP} or 
     * {@link ovm.services.bytecode.Instruction.ROLL}.
     **/
    public void addRoll(char span, byte count) {
        if (count == 1 && span == 1) {
            return;
        }
        if (count == 1 && span == 2) {
            addSimpleInstruction(SWAP);
            return;
        }
        if (count != -1) {
            addROLL(span, count);
            return;
        }
        switch (span) {
            case 1 :
                break;
            case 2 :
                addSimpleInstruction(SWAP);
                break;
            default :
                addROLL(span, (byte) - 1);
                break;
        }
    }

    /**
     * Add a {@link ovm.services.bytecode.Instruction.LABEL} at this
     * position.
     **/ 
    public void addLABEL(final int value) {
	addInstruction(new InstructionBuilder() {
		public int predictSize() { return 5; }
		public void write(ByteBuffer buf) {
		    buf.put((byte) LABEL).putInt(value);
		}
	    });
    }

    /**
     * Add a {@link ovm.services.bytecode.Instruction.ROLL} at this
     * position.
     **/ 
    public void addROLL(final char span, final byte count) {
        addInstruction(new InstructionBuilder() {
            public int predictSize() {
                return 4;
            }
            public void write(ByteBuffer buf) {
                buf.put((byte) ROLL).putChar(span).put(count);
            }
        });
    }
    
    /** Add a FIAT instruction to the codestream.
     *  Once implemented, this method will do exactly that.
     * @param tn the TypeName to be unconditionally ascribed to the value
     * on ToS.
     */
    public void addFiat(final TypeName tn) {
	OVMBase.OVMassert(!tn.isPrimitive()
			  || tn.getTypeTag() != 'V');
        addInstruction( tn.isPrimitive() ?
            (InstructionBuilder)new InstructionBuilder() {
                public int predictSize() { return 1; }
                public void write(ByteBuffer buf) {
                    int opcode;
                    switch ( tn.getTypeTag() ) {
                        case TypeCodes.INT:
                        case TypeCodes.CHAR:
                        case TypeCodes.SHORT:
                        case TypeCodes.BYTE:
                        case TypeCodes.BOOLEAN:
                            opcode = (byte)IFIAT;
                            break;
                        case TypeCodes.FLOAT:
                            opcode = (byte)FFIAT;
                            break;
                        case TypeCodes.LONG:
                            opcode = (byte)LFIAT;
                            break;
                        case TypeCodes.DOUBLE:
                            opcode = (byte)DFIAT;
                            break;
                        default:
                            throw failure( "Fiat " + tn +"?");
                    }
                    buf.put( (byte)opcode);
                }
            }
            :
            new OpClassRefBuilder( tn.asCompound(), (byte)AFIAT));
    }

    public void addPOLLCHECK() {
	addInstruction(new InstructionBuilder(){
		public int predictSize() {
		    return 1;
		}
		public void write(ByteBuffer buf) {
		    buf.put((byte)POLLCHECK);
		}
	    });
    }

    /**
     * Add a GETFIELD instruction to the codestream.
     **/
    public void addGETFIELD(ConstantFieldref fieldref) {
        addInstruction(new FieldBuilder(fieldref, (byte) GETFIELD));
    }

    /**
     * Add a GETFIELD instruction to the codestream.
     **/
    public void addPUTFIELD_WITH_BARRIER_REF(ConstantFieldref fieldref) {
        addInstruction(new FieldBuilder(fieldref, 
					(byte) PUTFIELD_WITH_BARRIER_REF));
    }

    /**
     * Add a PUTFIELD instruction to the codestream.
     **/
    public void addPUTFIELD(ConstantFieldref fieldref) {
        addInstruction(new FieldBuilder(fieldref, (byte) PUTFIELD));
    }

    /**
     * Add a GETSTATIC instruction to the codestream.
     **/
    public void addGETSTATIC(ConstantFieldref fieldref) {
        addInstruction(new FieldBuilder(fieldref, (byte) GETSTATIC));
    }

    /**
     * Add a PUTSTATIC instruction to the codestream.
     **/
    public void addPUTSTATIC(ConstantFieldref fieldref) {
        addInstruction(new FieldBuilder(fieldref, (byte) PUTSTATIC));
    }

    /**
     * Add a PUTSTATIC_WITH_BARRIER_REF instruction to the codestream.
     **/
    public void addPUTSTATIC_WITH_BARRIER_REF(ConstantFieldref fieldref) {
	addInstruction(new FieldBuilder(fieldref, (byte) PUTSTATIC_WITH_BARRIER_REF));
    }

    /**
     * Add a QUICK instruction to the codestream.
     **/
    public void addQuickOpcode(int opcode, char index) {
        addInstruction(new QuickFieldBuilder(index, (byte) opcode));
    }

    /**
     * Add an invokevirtual to the codestream.
     **/
    public void addINVOKEVIRTUAL(ConstantMethodref mref) {
        addInstruction(new InvokeBuilder(mref, (byte) INVOKEVIRTUAL));
    }

    public void addINVOKEVIRTUAL(char cpindex) {
	addInstruction(new InvokeBuilderWithGivenCPIndex
		       (cpindex,
			(byte) INVOKEVIRTUAL));
    }

    /**
     * Add an invokestatic to the codestream.
     **/
    public void addINVOKESTATIC(ConstantMethodref mref) {
        addInstruction(new InvokeBuilder(mref, (byte) INVOKESTATIC));
    }

    /**
     * Add an invokespecial to the codestream.
     **/
    public void addINVOKESPECIAL(ConstantMethodref mref) {
        addInstruction(new InvokeBuilder(mref, (byte) INVOKESPECIAL));
    }

    /**
     * Add an INVOKEINTERFACE to the codestream.
     **/
    public void addINVOKEINTERFACE(ConstantMethodref mref) {
        addInstruction(new InvokeBuilder(mref, (byte) INVOKEINTERFACE) {
            public int predictSize() {
                return super.predictSize() + 2;
            }
            void write(ByteBuffer buf) {
                char sel =
                    (char) cPoolBuilder_.addInterfaceMethodref(methodref_);
                buf.put(opcode_).putChar(sel);
                buf.put(
		(byte) ((methodref_.asSelector().getDescriptor().getArgumentLength() / 4)
                        + 1));
                // or getArgumentCount? Spec is not too clear here!
                buf.put((byte) 0);
            }
        });
    }

    /**
     * invokesystem
     **/
    public void addINVOKESYSTEM(byte method) {
        addInstruction(
            new InvokeSystemBuilder(method, (byte) INVOKE_SYSTEM));
    }

    /**
     * invokesystem
     **/
    public void addINVOKESYSTEM(byte method, byte sub) {
        addInstruction(
            new InvokeSystemBuilder(method, sub, (byte) INVOKE_SYSTEM));
    }

    /**
     * Add an arbitrary special sequence of opcodes to the stream.
     **/
    public void addSpecialSequence(byte[] sequence) {
        addInstruction(new SequenceBuilder(sequence));
    }

    /**
     * Add an INVOKEVIRTUAL_QUICK to the codestream.
     **/
    public void addINVOKEVIRTUAL_QUICK(
        ResolutionInfo rInfo,
        Selector.Method sel) {
        addInstruction(new INVOKEVIRTUAL_QUICKBuilder(rInfo, sel));
    }

    /**
     * Add an INVOKEINTERFACE to the codestream.
     **/
    public void addINVOKEINTERFACE_QUICK(
        ResolutionInfo rInfo,
        Selector.Method sel) {
        addInstruction(new INVOKEINTERFACE_QUICKBuilder(rInfo, sel));
    }

    /**
     * Add an INVOKENONVIRTUAL_QUICK to the codestream.
     **/
    public void addINVOKENONVIRTUAL_QUICK(
        ResolutionInfo rInfo,
        Selector.Method sel) {
        addInstruction(new INVOKENONVIRTUAL_QUICKBuilder(rInfo, sel));
    }

    /**
     * Add an INVOKENONVIRTUAL_QUICK to the codestream.
     **/
    public void addINVOKENONVIRTUAL2_QUICK(
        ResolutionInfo rInfo,
        int cpIndex,
        Selector.Method sel) {
        addInstruction(
            new INVOKENONVIRTUAL_QUICK2Builder(
                rInfo,
                cpIndex,
                sel,
                false));
    }

    /**
     * Add an INVOKENONVIRTUAL_QUICK to the codestream.
     **/
    public void addINVOKESUPER_QUICK(
        ResolutionInfo rInfo,
        int cpIndex,
        Selector.Method sel) {
        addInstruction(
            new INVOKENONVIRTUAL_QUICK2Builder(
                rInfo,
                cpIndex,
                sel,
                true));
    }

    /**
     * Adds an instruction of length 1.
     * @param opcode the opcode of the instruction (use  from JVMConstants)
     **/
    public void addSimpleInstruction(int opcode) {
        addInstruction(new SimpleOpBuilder(opcode));
    }

    /**
     * Add a NEW instruction.
     **/
    public void addNEW(ConstantClass name) {
        addInstruction(new OpClassRefBuilder(name, (byte) NEW));
    }

    /**
     * Add a CheckCast instruction.
     **/
    public void addCHECKCAST(ConstantClass name) {
        addInstruction(new OpClassRefBuilder(name, (byte) CHECKCAST));
    }

    /**
     * Add a INSTANCEOF instruction.
     **/
    public void addINSTANCEOF(ConstantClass name) {
        addInstruction(new OpClassRefBuilder(name, (byte) INSTANCEOF));
    }

    /**
     * Add a Goto to a marker.
     **/
    public void addGoto(Marker m) {
        addInstruction(new GotoBuilder(m));
    }

    /**
     * Add a branch-statement.
     * @param op the opcode specifying the exact operation
     *        (IFEQ, IFNE, IFNULL, ...)
     * @param trueTarget where to jump if the condition is true?
     **/
    public void addIf(int op, Marker trueTarget) {
        addInstruction(new IfBuilder(op, trueTarget));
    }

    /**
     * Add an instruction pushing a constant on the stack.
     **/
    public void addLoadConstant(RepositoryString s) {
        if (s == null)
            throw new NullPointerException();
	int index = cPoolBuilder_.addUnresolvedString(s);
	addLDC(index);       
    }

   /**
     * Add an instruction pushing a RESOLVED String/SharedState/Singleton
     * constant on the stack.  Will ONLY work when editing LINKED domain
     * code, not Repository code!
     *
     * @param o the resolved object
     * @throws a runtime exception if the current type of code edited is not linked
     **/
    public void addResolvedRefLoadConstant(Object o) {
	if (o == null)
	    throw new NullPointerException();
	int cpIdx = cPoolBuilder_.addResolvedConstant(o);
	addLDC_REF_QUICK(cpIdx);
    }

    public void addLOAD_SHST_FIELD(ConstantFieldref fref) {
        addInstruction(new LoadShstFieldBuilder(fref));
    }

    public void addLOAD_SHST_METHOD(ConstantMethodref mref) {
        addInstruction(new LoadShstMethodBuilder(mref));
    }

    public void addLOAD_SHST_FIELD_QUICK(ConstantFieldref fref) {
        addInstruction(new LoadShstFieldQuickBuilder(fref));
    }

    public void addLOAD_SHST_METHOD_QUICK(ConstantMethodref mref) {
        addInstruction(new LoadShstMethodQuickBuilder(mref));
    }

    public void addLOAD_SHST_FIELD(int cpindex) {
        if (cpindex <= 0)
            throw new Error("LOAD_SHST_FIELD may not load from index " + cpindex);
	// check that the cp index points to a
	// ConstantResolvedStaticField entry
        addInstruction(new LoadShstFieldBuilder(cpindex));
    }

    public void addLOAD_SHST_METHOD(int cpindex) {
        if (cpindex <= 0)
            throw new Error("LOAD_SHST_METHOD may not load from index " + cpindex);
	// check that the cp index points to a
	// ConstantResolvedStaticMethod entry
        addInstruction(new LoadShstMethodBuilder(cpindex));
    }

    public void addLOAD_SHST_FIELD_QUICK(int cpindex) {
        if (cpindex <= 0)
            throw new Error("LOAD_SHST_FIELD_QUICK may not load from index " + cpindex);
	// check that the cp index points to a
	// ConstantResolvedStaticField entry
        addInstruction(new LoadShstFieldQuickBuilder(cpindex));
    }

    public void addLOAD_SHST_METHOD_QUICK(int cpindex) {
        if (cpindex <= 0)
            throw new Error("LOAD_SHST_METHOD_QUICK may not load from index " + cpindex);
	// check that the cp index points to a
	// ConstantResolvedStaticMethod entry
        addInstruction(new LoadShstMethodQuickBuilder(cpindex));
    }

    /**
     * Add an LDC for an UNRESOLVED Shared State to the instruction stream.
     * This corresponds to the "addLoadConstant(String)" for unresolved
     * Strings.  If you want to add a resolved String or SharedState,
     * use "addResolvedLoadConstant".
     */
    public void addUnresolvedSharedStateLoadConstant(TypeName.Gemeinsam tn) {
	if (tn == null)
	    throw new NullPointerException();
	addLDC(cPoolBuilder_.addUnresolvedSharedState(tn));
    }

    public void addUnresolvedBinderLoadConstant(Binder b) {
	if (b == null)
	    throw new NullPointerException();
	addLDC(cPoolBuilder_.addUnresolvedBinder(b));
    }

    public void addLdc(Object o) {
	if (o == null)
	    throw new NullPointerException();
	if ((o instanceof RepositoryString) || (o == null))
	    addLoadConstant((RepositoryString) o);
	else if (o instanceof Float)
	    addLoadConstant(((Float) o).floatValue());
	else if (o instanceof Double)
	    addLoadConstant(((Double) o).doubleValue());
	else if (o instanceof Long)
	    addLoadConstant(((Long) o).longValue());
	else if (o instanceof Integer)
	    addLoadConstant(((Integer) o).intValue());
	else if (o instanceof TypeName.Gemeinsam)
	    addUnresolvedSharedStateLoadConstant((TypeName.Gemeinsam)o);
	else if (o instanceof Binder)
	    addUnresolvedBinderLoadConstant((Binder)o);	
	else
	    addResolvedRefLoadConstant(o);
    }

    public void addQuickLdc(Object o) {
	if (o == null)
	    throw new NullPointerException();
	if ((o instanceof RepositoryString) || (o == null))
	    addLoadConstant((RepositoryString) o);
	else if (o instanceof Float)
	    addQuickLoadConstant(((Float) o).floatValue());
	else if (o instanceof Double)
	    addQuickLoadConstant(((Double) o).doubleValue());
	else if (o instanceof Long)
	    addQuickLoadConstant(((Long) o).longValue());
	else if (o instanceof Integer)
	    addQuickLoadConstant(((Integer) o).intValue());
	else 
	    addResolvedRefLoadConstant(o);
    }

    /**
     * Add an instruction pushing a constant on the stack.
     **/
    public void addLoadConstant(int i) {
        if ((i >= -1) && (i < 6)) {
            addSimpleInstruction(ICONST_0 + i);
            return;
        }
        if (NumberRanges.isByte(i)) { // BIPUSH
            addInstruction(new BIPUSHBuilder((byte) i));
            return;
        }
        if (NumberRanges.isShort(i)) { // SIPUSH
            addInstruction(new SIPUSHBuilder((short) i));
            return;
        }
        int index = cPoolBuilder_.addConstantInt(i);
        addLDC(index);
    }

    /**
     * Add an instruction pushing a constant on the stack.
     **/
    public void addQuickLoadConstant(int i) {
        if ((i >= -1) && (i < 6)) {
            addSimpleInstruction(ICONST_0 + i);
            return;
        }
        if (NumberRanges.isByte(i)) { // BIPUSH
            addInstruction(new BIPUSHBuilder((byte) i));
            return;
        }
        if (NumberRanges.isShort(i)) { // SIPUSH
            addInstruction(new SIPUSHBuilder((short) i));
            return;
        }
        addLDC_INT_QUICK(i);
    }

    /**
     * Add an instruction pushing a constant on the stack.
     **/
    public void addQuickLoadConstant(float f) {
        if (f == 0.0f)
            addSimpleInstruction(FCONST_0);
        else if (f == 1.0f)
            addSimpleInstruction(FCONST_1);
        else if (f == 2.0f)
            addSimpleInstruction(FCONST_2);
        else 
            addLDC_FLOAT_QUICK(f);        
    }

    /**
     * Add an instruction pushing a constant on the stack.
     **/
    public void addLoadConstant(float f) {
        if (f == 0.0f)
            addSimpleInstruction(FCONST_0);
        else if (f == 1.0f)
            addSimpleInstruction(FCONST_1);
        else if (f == 2.0f)
            addSimpleInstruction(FCONST_2);
        else 
            addLDC(cPoolBuilder_.addConstantFloat(f));        
    }

    /**
     * Add an instruction pushing a constant on the stack.
     **/
    public void addQuickLoadConstant(double d) {
        if (d == 0.0d)
            addSimpleInstruction(DCONST_0);
        else if (d == 1.0d)
            addSimpleInstruction(DCONST_1);
        else 
	    addLDC_DOUBLE_QUICK(d);
    }

    /**
     * Add an instruction pushing a constant on the stack.
     **/
    public void addLoadConstant(double d) {
        if (d == 0.0d)
            addSimpleInstruction(DCONST_0);
        else if (d == 1.0d)
            addSimpleInstruction(DCONST_1);
        else 
            addLDC2_W(cPoolBuilder_.addConstantDouble(d));        
    }

    /**
     * Add an instruction pushing a constant on the stack.  For
     * quick-longs, we save an extra opcode by pushing two ints (both
     * inlined).  But it costs 1 byte of instruction stream and one
     * operation in this (rare?) case...
     **/
    public void addQuickLoadConstant(long l) {
        if (l == 0L)
            addSimpleInstruction(LCONST_0);
        else if (l == 1L)
            addSimpleInstruction(LCONST_1);
        else 
	    addLDC_LONG_QUICK(l);
    }

    /**
     * Add an instruction pushing a constant on the stack.
     **/
    public void addLoadConstant(long l) {
        if (l == 0L)
            addSimpleInstruction(LCONST_0);
        else if (l == 1L)
            addSimpleInstruction(LCONST_1);
        else 
            addLDC2_W(cPoolBuilder_.addConstantLong(l));        
    }

    /**
     * Add MULTIANEWARRAY_QUICK.
     * @param dim the number of dimensions (must be > 1, <= 255)
     **/
    public void addMULTIANEWARRAY_QUICK(char quick, final int dim) {
        if ((dim < 1) || !NumberRanges.isUnsignedByte(dim))
            throw failure("Invalid number of dimensions: " + dim);

        addInstruction(new QuickCharBuilder(
            quick,
            (byte) MULTIANEWARRAY_QUICK) {
            int predictSize() {
                return 4;
            }
            void write(ByteBuffer buf) {
                super.write(buf);
                buf.put((byte) dim);
            }
        });
    }

    private static byte getPrimitiveNEWARRAYtag(TypeName.Primitive t) {
        switch (t.getTypeTag()) {
            case TypeCodes.BOOLEAN :
                return 4;
            case TypeCodes.CHAR :
                return 5;
            case TypeCodes.FLOAT :
                return 6;
            case TypeCodes.DOUBLE :
                return 7;
            case TypeCodes.BYTE :
                return 8;
            case TypeCodes.SHORT :
                return 9;
            case TypeCodes.INT :
                return 10;
            case TypeCodes.LONG :
                return 11;
            default :
                throw new Error(
                    "TYPE TAG NOT SUPPORTED!\n" + (int) t.getTypeTag());
        }
    }

    public void addSINGLEANEWARRAY(final ConstantClass arraytype) {
	addInstruction(new OpClassRefBuilder(arraytype, (byte) SINGLEANEWARRAY));
    }

    /**
     * Add array creation. Chooses automatically between MULTIANEWARRAY and
     * ANEWARRAY.
     * @param name the type of the array to create (may not be primitive)
     * @param dim the number of dimensions to allocate (must be >= 1, <= 255)
     **/
    public void addNewArray(final ConstantClass name, final int dim) {
        if (dim == 1) {
            TypeName tn = name.asTypeName()
		.asArray().getComponentTypeName();
            if (tn.isPrimitive())
                addNewArray(getPrimitiveNEWARRAYtag(tn.asPrimitive()));
            else
                addInstruction(
                    new OpClassRefBuilder(tn.asCompound(), (byte) ANEWARRAY));
            return;
        }
        if ((dim < 1) || !NumberRanges.isUnsignedByte(dim))
            throw failure("Invalid number of dimensions: " + dim);

        addInstruction(new OpClassRefBuilder(name, (byte) MULTIANEWARRAY) {
            int predictSize() {
                return 4;
            }
            void write(ByteBuffer buf) {
                super.write(buf);
                buf.put((byte) dim);
            }
        });
    }

    /**
     * Create primitive array.
     * @param type the primitive type of the array, use T_XXXX constants.
     **/
    public void addNewArray(byte type) {
        addInstruction(new ByteIndexedBuilder((byte) NEWARRAY, type));
    }

    /**
     * Add an LDC instruction that loads from the given index.
     **/
    public void addJsr(Marker target) {
        addInstruction(new JsrBuilder(target));
    }

    /**
     * Add an LDC instruction that loads from the given index.
     **/
    public void addRet(char local) {
        addInstruction(new RetBuilder(local));
    }

    /**
     * Add an Switch instruction.
     * @param defaultTarget the default target
     * @param indices the indices for the non-default targets
     * @param targets the addresses of the non-default targets
     * @param preference, TABLESWITCH, LOOKUPSWITCH 
     *  or 0 for automatic; if non-null, targets and indices must be sorted appropriately
     **/
    public void addSwitch(
        Marker defaultTarget,
        int[] indices,
        Marker[] targets,
        int preference) {
        if (preference == TABLESWITCH) {
            addInstruction(
                new TABLESWITCHBuilder(defaultTarget, indices, targets));
        } else if (preference == LOOKUPSWITCH) {
            addInstruction(
                new LOOKUPSWITCHBuilder(defaultTarget, indices, targets));
        } else
            throw failure("Auto-switch NOT implemented!");
    }

    /* ****************** factored out methods ******************** */

    void removeFirst() {
	instructionBuilder_.next_ = instructionBuilder_.next_.next_;
    }
    
    /**
     * Add an instruction (att current position, grow as needed,
     * increase offset).
     **/
    private void addInstruction(InstructionBuilder ib) {
        offset_ += ib.predictSize();
        instructionBuilder_.append(ib);
        if (!(ib instanceof Marker))
            editor_.dirty();
    }

    /**
     * Add an LDC instruction that loads from the given index.
     **/
    private void addLDC(int index) {
        if (index <= 0)
            throw new Error("LDC may not load from index " + index);
        addInstruction(new LdcBuilder(index));
    }

    /**
     * Add an LDC instruction that loads from the given index.
     **/
    private void addLDC_INT_QUICK(int value) {
        addInstruction(new LdcIQuickBuilder(value));
    }
    /**
     * Add an LDC instruction that loads from the given index.
     **/
    private void addLDC_FLOAT_QUICK(float value) {
        addInstruction(new LdcFQuickBuilder(value));
    }
    /**
     * Add an LDC instruction that loads from the given index.
     **/
    private void addLDC_LONG_QUICK(long value) {
        addInstruction(new LdcLQuickBuilder(value));
    }
    /**
     * Add an LDC instruction that loads from the given index.
     **/
    private void addLDC_DOUBLE_QUICK(double value) {
        addInstruction(new LdcDQuickBuilder(value));
    }

    /**
     * Add an LDC instruction that loads from the given index.
     **/
    private void addLDC_REF_QUICK(int index) {
        if (index <= 0)
            throw new Error("LDC may not load from index " + index);
        addInstruction(new LdcRefQuickBuilder(index));
    }

    /**
     * Add an LDC2 instruction that loads from the given index.
     **/
    private void addLDC2_W(int index) {
        addInstruction(new LDC2_WBuilder(index));
    }

    /* **************** InstructionBuilders ********************** */

    /**
     * All Instructions generated by the Cursor are first
     * stored using this interface. When finally written back
     * to bytecode, the linked list of InstructionBuilder objects
     * is used to create the bytestream.
     **/
    abstract static class InstructionBuilder implements JVMConstants.Opcodes {

        /**
         * The next Builder.
         **/
        private InstructionBuilder next_;

        InstructionBuilder() {}

        /**
         * Obtain the next InstructionBuilder object in the linked list.
         * @return the next object or null if none.
         **/
        InstructionBuilder next() {
            return next_;
        }

        /**
         * Append an instruction to the sequence.
         **/
        void append(InstructionBuilder ib) {
            if (next_ == null) {
                next_ = ib;
            } else {
                next_.append(ib);
            }
        }

        /**
         * Generate code. Writes the code of this
         * instruction into the buffer.
         **/
        abstract void write(ByteBuffer buffer);

        /**
         * Returns the estimated size of the instruction.
         * This method may query the enclosing Cursor
         * for the editor to obtain information about the
         * location of other instructions.
         * <p>
         * As the result of this instruction may vary depending
         * on the location of other instructions, the editor
         * must perform a fix-point iteration before the final
         * sizes can be determined.
         * <p>
         * predictSize is usually called before write.
         **/
        abstract int predictSize();

    } // end of InstructionBuilder

    static class SimpleOpBuilder extends InstructionBuilder {
        private final int opcode_;
        SimpleOpBuilder(int op) {
            this.opcode_ = op;
        }
        int predictSize() {
            return 1;
        }

        void write(ByteBuffer buf) {
            buf.put((byte) opcode_);
        }

    }

    static class SequenceBuilder extends InstructionBuilder {
        private final byte[] sequence_;
        SequenceBuilder(byte[] sequence) {
            this.sequence_ = sequence;
        }
        int predictSize() {
            return sequence_.length;
        }
        void write(ByteBuffer buf) {
            buf.put(sequence_);
        }
    }

    abstract static class SwitchBuilder extends InstructionBuilder {
        final Marker defaultTarget_;
        final int[] indices_;
        final Marker[] targets_;

        SwitchBuilder(Marker defaultTarget, int[] indices, Marker[] targets) {
            this.indices_ = indices;
            this.defaultTarget_ = defaultTarget;
            this.targets_ = targets;
        }
        /**
         * Return the number of bytes padding.
         **/
        int getArgumentsStartPosition(int offset) {
            // this code was optimized, it can now do without
            // any division/modulo operation. If you insist to
            // change it, please profile the new code first!
            // (and make sure it is correct...)
            int pc = offset + 1;
            while ((pc & 3) != 0)
                pc++;
            return pc;
        }
        static protected void padWithZeroes(ByteBuffer buf) {
            while ((buf.position() & 3) != 0) {
                buf.put((byte) 0); // padding
            }
        }
    }

    class InvokeBuilderWithGivenCPIndex
	extends InstructionBuilder {
        protected final char cpindex_;
	protected final byte opcode_;
        InvokeBuilderWithGivenCPIndex(char cpindex, byte opcode) {
	    this.cpindex_ = cpindex;
	    this.opcode_ = opcode;
        }
        int predictSize() {
            return 3;
        }
        void write(ByteBuffer buf) {
            buf.put(opcode_).putChar(cpindex_);
        }
    }

    class InvokeBuilder extends InstructionBuilder {
        protected final ConstantMethodref methodref_;
        protected final byte opcode_;
        InvokeBuilder(ConstantMethodref mref, byte opcode) {
            this.methodref_ = mref;
            this.opcode_ = opcode;
        }
        int predictSize() {
            return 3;
        }
        void write(ByteBuffer buf) {
            char sel = (char) cPoolBuilder_.addMethodref(methodref_);
            buf.put(opcode_).putChar(sel);
            /* pln(selector_ + "@" + (int)sel 
               + " // " + cPoolBuilder_.getClass());*/
        }
    }

    static class InvokeSystemBuilder extends InstructionBuilder {
        private final byte opcode_;
        private final byte methodTag_;
        private final byte subTag_;
        InvokeSystemBuilder(byte method, byte opcode) {
            this.methodTag_ = method;
            this.subTag_ = 0;
            this.opcode_ = opcode;
        }
        InvokeSystemBuilder(byte method, byte sub, byte opcode) {
            this.methodTag_ = method;
            this.subTag_ = sub;
            this.opcode_ = opcode;
        }

        int predictSize() {
            return 3;
        }

        void write(ByteBuffer buf) {
            buf.put(opcode_).put(methodTag_).put(subTag_);
        }
    }

    static class InvokeQuickBuilder extends InstructionBuilder {
        private final byte opcode_;
        private byte paramCount_;
        private byte vtblIndex_;

        InvokeQuickBuilder(int vtblIndex, int paramCount, byte opcode) {
            try {
                //d("INVOKEVIRTUAL_QUICK("+paramCount+","+vtblIndex+")");
                this.vtblIndex_ = NumberRanges.checkByte(vtblIndex);
                this.paramCount_ = NumberRanges.checkByte(paramCount);
            } catch (NumberRanges.NumberRangeException e) {
                // we need growing rewriting
                this.vtblIndex_ = (byte) vtblIndex;
                this.paramCount_ = (byte) paramCount;
            }
            this.opcode_ = opcode;
        }
        int predictSize() {
            return 3;
        }
        void write(ByteBuffer buf) {
            buf.put(opcode_).put(paramCount_).put(vtblIndex_);
        }
    }

    // for NEW, CheckCast, InstanceOf (opcode + classname-index into cp)
    class OpClassRefBuilder extends InstructionBuilder {
        private final ConstantClass class_;
        private final byte opcode_;
        OpClassRefBuilder(ConstantClass cl, byte opcode) {
            this.class_ = cl;
            this.opcode_ = opcode;
        }
        int predictSize() {
            return 3;
        }
        void write(ByteBuffer buf) {
            char index = (char) cPoolBuilder_.addClass(class_);
            buf.put(opcode_).putChar(index);
        }
    }

    static class QuickCharBuilder extends InstructionBuilder {
        private final char quick_;
        private final byte opcode_;
        QuickCharBuilder(char quick, byte opcode) {
            this.quick_ = quick;
            this.opcode_ = opcode;
        }
        int predictSize() {
            return 3;
        }
        void write(ByteBuffer buf) {
            buf.put(opcode_).putChar(quick_);
        }
    }

    static class BIPUSHBuilder extends InstructionBuilder {
        private final byte value_;
        BIPUSHBuilder(byte value) {
            this.value_ = value;
        }
        int predictSize() {
            return 2;
        }

        void write(ByteBuffer buf) {
            buf.put((byte) BIPUSH).put(value_);
        }
    }

    static class ByteIndexedBuilder extends InstructionBuilder {
        private final byte opcode_;
        private final byte value_;
        ByteIndexedBuilder(byte opcode, byte value) {
            this.opcode_ = opcode;
            this.value_ = value;
        }
        int predictSize() {
            return 2;
        }
        void write(ByteBuffer buf) {
            buf.put(opcode_).put(value_);
        }
    }

    static class Wide4Builder extends InstructionBuilder {
        private final byte opcode_;
        private final char value_;
        Wide4Builder(byte opcode, char value) {
            this.opcode_ = opcode;
            this.value_ = value;
        }
        int predictSize() {
            return 4;
        }
        void write(ByteBuffer buf) {
            buf.put((byte) WIDE).put(opcode_).putChar(value_);
        }
    }

    static class IIncBuilder extends InstructionBuilder {
        private final short value_;
        private final char index_;
        IIncBuilder(short value, char index) {
            this.value_ = value;
            this.index_ = index;
        }
        int predictSize() {
            if (!NumberRanges.isByte(value_)
                || !NumberRanges.isUnsignedByte(index_)) {
                return 6; // wide-iinc
            } else {
                return 3; // IINC
            }
        }

        void write(ByteBuffer buf) {
            if (!NumberRanges.isByte(value_)
                || !NumberRanges.isUnsignedByte(index_)) {
                buf.put((byte) WIDE).put((byte) IINC).putChar(index_).putShort(
                    value_);
            } else {
                buf.put((byte) IINC).put((byte) index_).put((byte) value_);
            }
        }

    }

    static class SIPUSHBuilder extends InstructionBuilder {
        private final short value_;
        SIPUSHBuilder(short value) {
            this.value_ = value;
        }
        int predictSize() {
            return 3;
        }
        void write(ByteBuffer buf) {
            buf.put((byte) SIPUSH).putShort(value_);
        }
    }

    class FieldBuilder extends InstructionBuilder {
        private final ConstantFieldref fieldref_;
        private final byte opcode_;
        FieldBuilder(ConstantFieldref cfr, byte opcode) {
            this.fieldref_ = cfr;
            this.opcode_ = opcode;
        }
        int predictSize() {
            return 3;
        }
        void write(ByteBuffer buf) {
            char index = (char) cPoolBuilder_.addFieldref(fieldref_);
            buf.put(opcode_).putChar(index);
        }
    }

    public static class QuickFieldBuilder extends InstructionBuilder {
        /**
         * QUICK builders MUST have a SIZE field which is reflectively
         * read by the Quickifier to 
         **/
        public static final int SIZE = 3;
        private final char off_;
        private final byte opcode_;
        QuickFieldBuilder(char off, byte opcode) {
            this.off_ = off;
            this.opcode_ = opcode;
        }
        int predictSize() {
            return SIZE;
        }

        void write(ByteBuffer buf) {
            buf.put(opcode_).putChar(off_);
        }
    }

    abstract class QuickInvokeBuilder extends InstructionBuilder {
        protected final byte argCount;
        protected final byte wideArgCount;
        protected final char index;
	protected final Selector.Method selector;

        QuickInvokeBuilder(
            ResolutionInfo rInfo,
            Selector.Method sel) {
	    selector = sel;
	    Descriptor.Method desc = sel.getDescriptor();
            argCount = NumberRanges.checkByte(desc.getArgumentCount());
            wideArgCount = NumberRanges.checkByte(desc.getWideArgumentCount());
            index = NumberRanges.checkUnsignedShort(rInfo.getOffset());
        }
    }

    public class INVOKEVIRTUAL_QUICKBuilder extends QuickInvokeBuilder {
        public static final int SIZE = 7;
        INVOKEVIRTUAL_QUICKBuilder(
            ResolutionInfo rInfo,
            Selector.Method sel) {
            super(rInfo, sel);
        }
        int predictSize() {
            return SIZE;
        }

        void write(ByteBuffer buf) {
	    char sel_index = (char)cPoolBuilder_.addMethodref(selector);
            buf.put((byte) INVOKEVIRTUAL_QUICK)
		.putChar(index)
		.put(argCount)
		.put(wideArgCount)
		.putChar(sel_index);
        }
    }

    public class INVOKENONVIRTUAL_QUICK2Builder
        extends QuickInvokeBuilder {
        public static final int SIZE = 9;
        private final char cpIndex;
        private final boolean isSuper;
        INVOKENONVIRTUAL_QUICK2Builder(
            ResolutionInfo rInfo,
            int cpIndex,
            Selector.Method sel,
            boolean isSuper) {
            super(rInfo, sel);
            this.cpIndex = NumberRanges.checkUnsignedShort(cpIndex);
            this.isSuper = isSuper;
        }

        int predictSize() {
            return SIZE;
        }

        void write(ByteBuffer buf) {
	    char sel_index = (char)cPoolBuilder_.addMethodref(selector);
            if (isSuper) {
                buf.put((byte) INVOKESUPER_QUICK);
            } else {
                buf.put((byte) INVOKENONVIRTUAL2_QUICK);
            }
            buf.putChar(cpIndex);
            buf.putChar(index).put(argCount).put(wideArgCount).putChar(sel_index);
        }
    }

    public class INVOKENONVIRTUAL_QUICKBuilder
        extends QuickInvokeBuilder {
        public static final int SIZE = 7;
        INVOKENONVIRTUAL_QUICKBuilder(
            ResolutionInfo rInfo,
            Selector.Method sel) {
            super(rInfo, sel);
        }
        int predictSize() {
            return SIZE;
        }
        void write(ByteBuffer buf) {
	    char sel_index = (char)cPoolBuilder_.addMethodref(selector);
            buf.put((byte) INVOKENONVIRTUAL_QUICK);
            buf.putChar(index).put(argCount).put(wideArgCount).putChar(sel_index);
        }
    }

    public class INVOKEINTERFACE_QUICKBuilder
        extends QuickInvokeBuilder {
        public static final int SIZE = 7;
        INVOKEINTERFACE_QUICKBuilder(
            ResolutionInfo rInfo,
            Selector.Method sel) {
            super(rInfo, sel);
        }

        int predictSize() {
            return SIZE;
        }

        void write(ByteBuffer buf) {
	    char sel_index = (char)cPoolBuilder_.addMethodref(selector);
            buf.put((byte) INVOKEINTERFACE_QUICK).putChar(index).put(
                argCount).put(
                wideArgCount).putChar(sel_index);
        }
    }

    class GotoBuilder extends InstructionBuilder {
        private final Marker marker_;
        GotoBuilder(Marker m) {
            this.marker_ = m;
        }
        int predictSize() {
            int delta = marker_.getOffset() - offset_;
            if (!NumberRanges.isShort(delta)) {
                return 5;
            } else {
                return 3;
            }
        }

        void write(ByteBuffer buf) {
            int delta = marker_.getOffset() - buf.position();
            if (!NumberRanges.isShort(delta)) {
                buf.put((byte) GOTO_W).putInt(delta);
            } else {
                buf.put((byte) GOTO).putShort((short) delta);
            }
        }

    }
    static class LdcRefQuickBuilder extends InstructionBuilder {
        private final int index_;
        LdcRefQuickBuilder(int index) {
            this.index_ = index;
        }
        int predictSize() {
            if (!NumberRanges.isUnsignedByte(index_))
                return 3;
            else
                return 2;
        }

        void write(ByteBuffer buf) {
            //System.err.println("Writing LDC QUICK at " + buf.position());
            if (!NumberRanges.isUnsignedByte(index_)) {
                buf.put((byte) LDC_W_REF_QUICK).putChar((char) index_);
            } else {
                buf.put((byte) LDC_REF_QUICK).put((byte) index_);
            }
        }

    }
    static class LdcIQuickBuilder extends InstructionBuilder {
        private final int value_;
        LdcIQuickBuilder(int value) {
            this.value_ = value;
        }
        int predictSize() {
	    return 5;
        }
        void write(ByteBuffer buf) {
            //System.err.println("Writing LDC QUICK at " + buf.position());
	    buf.put((byte) LDC_INT_QUICK).putInt(value_);
        }

    }
    static class LdcFQuickBuilder extends InstructionBuilder {
        private final float value_;
        LdcFQuickBuilder(float value) {
            this.value_ = value;
        }
        int predictSize() {
	    return 5;
        }
        void write(ByteBuffer buf) {
	    buf.put((byte) LDC_FLOAT_QUICK).putFloat(value_);
        }

    }    
    static class LdcLQuickBuilder extends InstructionBuilder {
        private final long value_;
        LdcLQuickBuilder(long value) {
            this.value_ = value;
        }
        int predictSize() {
	    return 9;
        }
        void write(ByteBuffer buf) {
            //System.err.println("Writing LDC QUICK at " + buf.position());
	    buf.put((byte) LDC_LONG_QUICK).putLong(value_);
        }

    } 
    static class LdcDQuickBuilder extends InstructionBuilder {
        private final double value_;
        LdcDQuickBuilder(double value) {
            this.value_ = value;
        }
        int predictSize() {
	    return 9;
        }
        void write(ByteBuffer buf) {
            //System.err.println("Writing LDC QUICK at " + buf.position());
	    buf.put((byte) LDC_DOUBLE_QUICK).putDouble(value_);
        }

    }
    static class LdcBuilder extends InstructionBuilder {
        private final int index_;
        LdcBuilder(int index) {
            this.index_ = index;
        }
        int predictSize() {
            if (!NumberRanges.isUnsignedByte(index_))
                return 3;
            else
                return 2;
        }

        void write(ByteBuffer buf) {
            //System.err.println("Writing LDC at " + buf.position());
            if (!NumberRanges.isUnsignedByte(index_)) {
                buf.put((byte) LDC_W).putChar((char) index_);
            } else {
                buf.put((byte) LDC).put((byte) index_);
            }
        }

    }
    class LoadShstFieldBuilder extends InstructionBuilder {
        private final int index_;
	private final ConstantFieldref fref;
        LoadShstFieldBuilder(int index) {
            this.index_ = index;
	    this.fref = null;
        }
	LoadShstFieldBuilder(ConstantFieldref fref) {
	    this.fref = fref;
	    this.index_ = -1;
	}
        int predictSize() {
	    return 3;
        }

        void write(ByteBuffer buf) {
	    if (fref == null)
		buf.put((byte) LOAD_SHST_FIELD).putChar((char) index_);
	    else {
		int idx = cPoolBuilder_.addFieldref(fref);
		buf.put((byte) LOAD_SHST_FIELD).putChar((char) idx);
	    }
        }
    }
    class LoadShstMethodBuilder extends InstructionBuilder {
        private final int index_;
	private final ConstantMethodref mref;
        LoadShstMethodBuilder(int index) {
            this.index_ = index;
	    this.mref = null;
        }
	LoadShstMethodBuilder(ConstantMethodref mref) {
	    this.mref = mref;
	    this.index_ = -1;
	}
        int predictSize() {
	    return 3;
        }

        void write(ByteBuffer buf) {
	    if (mref == null)
		buf.put((byte) LOAD_SHST_METHOD).putChar((char) index_);
	    else {
		int idx = cPoolBuilder_.addMethodref(mref);
		buf.put((byte) LOAD_SHST_METHOD).putChar((char) idx);
	    }
        }
    }


    class LoadShstFieldQuickBuilder extends InstructionBuilder {
        private final int index_;
	private final ConstantFieldref fref;
        LoadShstFieldQuickBuilder(int index) {
            this.index_ = index;
	    this.fref = null;
        }
	LoadShstFieldQuickBuilder(ConstantFieldref fref) {
	    this.fref = fref;
	    this.index_ = -1;
	}
        int predictSize() {
	    return 3;
        }

        void write(ByteBuffer buf) {
	    if (fref == null)
		buf.put((byte) LOAD_SHST_FIELD_QUICK).putChar((char) index_);
	    else {
		int idx = cPoolBuilder_.addFieldref(fref);
		buf.put((byte) LOAD_SHST_FIELD_QUICK).putChar((char) idx);
	    }
        }
    }
    class LoadShstMethodQuickBuilder extends InstructionBuilder {
        private final int index_;
	private final ConstantMethodref mref;
        LoadShstMethodQuickBuilder(int index) {
            this.index_ = index;
	    this.mref = null;
        }
	LoadShstMethodQuickBuilder(ConstantMethodref mref) {
	    this.mref = mref;
	    this.index_ = -1;
	}
        int predictSize() {
	    return 3;
        }

        void write(ByteBuffer buf) {
	    if (mref == null)
		buf.put((byte) LOAD_SHST_METHOD_QUICK).putChar((char) index_);
	    else {
		int idx = cPoolBuilder_.addMethodref(mref);
		buf.put((byte) LOAD_SHST_METHOD_QUICK).putChar((char) idx);
	    }
        }
    }

    static class LDC2_WBuilder extends InstructionBuilder {
        private final int index_;
        LDC2_WBuilder(int index) {
            this.index_ = index;
        }
        int predictSize() {
            return 3;
        }
        void write(ByteBuffer buf) {
            buf.put((byte) LDC2_W).putChar((char) index_);
        }
    }

    class JsrBuilder extends InstructionBuilder {
        private final Marker marker_;
        JsrBuilder(Marker m) {
            this.marker_ = m;
        }
        int predictSize() {
            int delta = marker_.getOffset() - offset_;
            if (!NumberRanges.isShort(delta)) {
                return 5;
            } else {
                return 3;
            }
        }
        void write(ByteBuffer buf) {
            int delta = marker_.getOffset() - buf.position();
            if (!NumberRanges.isShort(delta)) {
                buf.put((byte) JSR_W).putInt(delta);
            } else {
                buf.put((byte) JSR).putShort((short) delta);
            }
        }
    }

    static class RetBuilder extends InstructionBuilder {
        private final char index_;
        RetBuilder(char index) {
            this.index_ = index;
        }
        int predictSize() {
            if (!NumberRanges.isUnsignedByte(index_))
                return 4; // wide-ret
            else
                return 2; // IINC
        }

        void write(ByteBuffer buf) {
            if (!NumberRanges.isUnsignedByte(index_)) {
                buf.put((byte) WIDE).put((byte) RET).putChar(index_);
            } else {
                buf.put((byte) RET).put((byte) index_);
            }
        }
    }

    class IfBuilder extends InstructionBuilder {
        private final Marker marker_;
        private final int opcode_;
        IfBuilder(int op, Marker m) {
            this.marker_ = m;
            this.opcode_ = op;
        }
        int predictSize() {
            int delta = marker_.getOffset() - offset_;
            if (!NumberRanges.isShort(delta)) {
                return 11;
            } else {
                return 3;
            }
        }
        void write(ByteBuffer buf) {
            int delta = marker_.getOffset() - buf.position();
            if (NumberRanges.isShort(delta)) {
                buf.put((byte) opcode_).putShort((short) delta);
            } else {
                /*
                 * new sequence:
                 *   short-jump-specialized-instruction
                 *   code_1
                 * falltrough:
                 *   short jump code_2
                 * code_1:
                 *   long jump real code_
                 * code_2:
                 *   NEXT INSTRUCTION
                 * <p>
                 * same in bytecodes:
                 * 0 - encoding[0]
                 * 1 - short: 6 [code_ 1]
                 * falltrough:
                 * 3 - goto (0xa7)
                 * 4 - short: 8 [code_ 2]
                 * code_1:
                 * 6 - goto_w (0xc8)
                 * 7 - int: newDistance - 6
                 * code_2:
                 * 11 - NEXT INSTRUCTION
                 **/

                buf.put((byte) opcode_).putShort((short) 6);
                buf.put((byte) GOTO).putShort((short) 8);
                buf.put((byte) GOTO_W).putInt(delta - 6);
            }
        }
    }

    class TABLESWITCHBuilder extends SwitchBuilder {
        TABLESWITCHBuilder(
            Marker defaultTarget,
            int[] indices,
            Marker[] targets) {
            super(defaultTarget, indices, targets);
        }
        int predictSize() {
            int start = offset_ + 1;
            while ((start & 3) != 0)
                start++;
            start += 0 + 4 // default target
            +8 // low index, high index
            +4 * targets_.length; // targets	    
            return start - offset_;
        }

        void write(ByteBuffer buf) {
            int pos = buf.position();
            buf.put((byte) TABLESWITCH);
            padWithZeroes(buf);
            buf.putInt(defaultTarget_.getOffset() - pos);
            buf.putInt(indices_[0]);
            buf.putInt(indices_[indices_.length - 1]);
            for (int i = 0; i < targets_.length; i++) {
                buf.putInt(targets_[i].getOffset() - pos);
            }
        }
    }

    class LOOKUPSWITCHBuilder extends SwitchBuilder {
        LOOKUPSWITCHBuilder(
            Marker defaultTarget,
            int[] indices,
            Marker[] targets) {
            super(defaultTarget, indices, targets);
        }
        int predictSize() {
            int start = offset_ + 1;
            while ((start & 3) != 0)
                start++;
            start += 0 + 4 // default target
            +4 // size
            +8 * targets_.length; // targets	and indices
            return start - offset_;
        }
        void write(ByteBuffer buf) {
            int pos = buf.position();
            buf.put((byte) LOOKUPSWITCH);
            padWithZeroes(buf);
            buf.putInt(defaultTarget_.getOffset() - pos);
            buf.putInt(indices_.length);
            for (int i = 0; i < targets_.length; i++) {
                buf.putInt(indices_[i]);
                buf.putInt(targets_[i].getOffset() - pos);
            }
        }
    }

} // end of Cursor
