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

com.tangosol.dev.assembler.Op Maven / Gradle / Ivy

There is a newer version: 24.09
Show newest version
/*
 * Copyright (c) 2000, 2020, Oracle and/or its affiliates.
 *
 * Licensed under the Universal Permissive License v 1.0 as shown at
 * http://oss.oracle.com/licenses/upl.
 */


package com.tangosol.dev.assembler;


import java.io.IOException;
import java.io.EOFException;
import java.io.DataInput;
import java.io.DataOutput;

import java.util.Enumeration;
import java.util.Vector;

import com.tangosol.io.ByteArrayReadBuffer;
import com.tangosol.io.ReadBuffer;

import com.tangosol.util.NullImplementation;


/**
* Represents a Java Virtual Machine assembly (JASM) operation, which is
* disassembled from and/or assembled to a Java byte code.
*
* @version 0.50, 05/20/98, assembler/dis-assembler
* @author  Cameron Purdy
*/
public abstract class Op extends VMStructure implements Constants
    {
    // ----- constructors ---------------------------------------------------

    /**
    * Construct the specified JASM op.
    *
    * @param iOp    the JASM op value
    */
    protected Op(int iOp)
        {
        m_iOp   = iOp;
        m_iHash = sm_iLastHash = (int) (((long) sm_iLastHash + BIGPRIME) % INTLIMIT);
        }


    // ----- Op operations --------------------------------------------------

    /**
    * Determine if the passed value is a legal JVM byte code.
    *
    * @param ub  the unsigned byte code value
    *
    * @return true if the value is a byte code defined by the JVM spec
    */
    public static boolean isByteCode(int ub)
        {
        // 0x00 (NOP) through 0xc9 (JSR_W) except for 0xba
        return ub >= NOP && ub <= JSR_W && ub != 0xba;
        }

    /**
    * For disassembly of ops, provide the requested variable.
    *
    * @param  iOp   the variable declaration op value (IVAR, ...)
    * @param  iVar  the variable index
    *
    * @return the requested variable
    */
    protected static OpDeclare getVariable(int iOp, int iVar, OpDeclare[][] aavar)
        {
        int       iType = iOp - IVAR;
        OpDeclare decl  = aavar[iType][iVar];

        if (decl == null)
            {
            switch (iOp)
                {
                case IVAR:
                    decl = new Ivar(iVar);
                    break;
                case LVAR:
                    decl = new Lvar(iVar);
                    break;
                case FVAR:
                    decl = new Fvar(iVar);
                    break;
                case DVAR:
                    decl = new Dvar(iVar);
                    break;
                case AVAR:
                    decl = new Avar(iVar);
                    break;
                case RVAR:
                    decl = new Rvar(iVar);
                    break;
                }

            aavar[iType][iVar] = decl;
            }

        return decl;
        }

    /**
    * For disassembly of ops, get the requested label.
    *
    * @param of      byte code offset
    * @param alabel  an array of labels per byte code offset
    *
    * @exception IOException
    */
    private static Label getLabel(int of, Op[] alabel)
            throws IOException
        {
        try
            {
            Label label = (Label) alabel[of];
            if (label == null)
                {
                label = new Label(String.valueOf(of));
                label.setOffset(of);
                alabel[of] = label;
                }
            return label;
            }
        catch (ArrayIndexOutOfBoundsException e)
            {
            throw new IOException(CLASS + ".getLabel:  Illegal label offset -- " + of);
            }
        }

    /**
    * For disassembly of ops, add the try at the specified offset.
    *
    * @param of        byte code offset
    * @param opTry     the try op
    * @param aopDefer  the deferred ops, by offset
    *
    * @exception IOException
    */
    private static void addTry(int of, Try opTry, Op[] aopDefer)
            throws IOException
        {
        // store the offset of the op
        opTry.setOffset(of);

        // see if any deferred ops are already at that offset
        Op op;
        try
            {
            op = aopDefer[of];
            }
        catch (ArrayIndexOutOfBoundsException e)
            {
            throw new IOException(CLASS + ".addTry:  Illegal Try offset!");
            }

        // place the try after any instances of catch and after any labels
        // but before any instances of try
        if (op == null || op instanceof Try)
            {
            // link the try in at the beginning
            aopDefer[of] = opTry;
            opTry.setNext(op);
            }
        else
            {
            // link the try in immediately after the catch's and label's
            Op opPrev = op;
            Op opNext = opPrev.getNext();
            while (opNext instanceof Catch || opNext instanceof Label)
                {
                opPrev = opNext;
                opNext = opPrev.getNext();
                }
            opPrev.setNext(opTry );
            opTry .setNext(opNext);
            }
        }

    /**
    * For disassembly of ops, add the catch at the specified offset.
    *
    * @param of        byte code offset
    * @param opCatch   the catch op
    * @param aopDefer  the deferred ops, by offset
    *
    * @exception IOException
    */
    private static void addCatch(int of, Catch opCatch, Op[] aopDefer)
            throws IOException
        {
        // store the offset of the op
        opCatch.setOffset(of);

        // see if any deferred ops are already at that offset
        Op op;
        try
            {
            op = aopDefer[of];
            }
        catch (ArrayIndexOutOfBoundsException e)
            {
            throw new IOException(CLASS + ".addCatch:  Illegal Catch offset!");
            }

        // see if the deferred ops are instances of Catch
        if (op instanceof Catch)
            {
            // link the try in after the catch's
            Op opPrev = op;
            Op opNext = opPrev.getNext();
            while (opNext instanceof Catch)
                {
                opPrev = opNext;
                opNext = opPrev.getNext();
                }
            opPrev .setNext(opCatch);
            opCatch.setNext(opNext );
            }
        else
            {
            // link the catch in at the beginning
            aopDefer[of] = opCatch;
            opCatch.setNext(op);
            }
        }


    /**
    * For disassembly of ops, update the LocalVariable/LocalVariableType
    * tables to point to the disassembled ops.
    *
    * @param attrTable  the local variable table attribute
    * @param aopLabel   labels by op offset
    */
    private static void disassembleVarTable(AbstractLocalVariableTableAttribute attrTable, Op[] aopLabel)
            throws IOException
        {
        for (Enumeration enmr = attrTable.ranges(); enmr.hasMoreElements(); )
            {
            AbstractLocalVariableTableAttribute.Range range =
                (AbstractLocalVariableTableAttribute.Range) enmr.nextElement();
            
            // determine byte code offsets of the range
            int ofInit = range.getOffset();
            int ofStop = ofInit + range.getLength();
            
            // translate offsets to ops
            range.setInit(getLabel(ofInit, aopLabel));
            range.setStop(getLabel(ofStop, aopLabel));
            }
        }

    
    /**
    * Based on the byte code, which is encountered first in the stream,
    * construct the correct Op class and disassemble it.
    *
    * @param abCode       the byte array containing the byte code portion of
    *                     the "Code" attribute
    * @param cVars        the number of local variable words
    * @param asParam      an array of JVM types for the method parameters
    * @param vectCatch    the table of try/catch data
    * @param attrLine     the line number table, if available, null otherwise
    * @param attrVar      the local variable table, if available
    * @param attrVarType  the local variable type table, if available
    * @param pool         the constant pool for the class
    * @param aopBounds    an array to return the first and last op in
    *
    * @exception IOException
    */
    protected static void disassembleOps(byte[] abCode, int cVars, String[] asParam, Vector vectCatch, LineNumberTableAttribute attrLine, LocalVariableTableAttribute attrVar, LocalVariableTypeTableAttribute attrVarType, ConstantPool pool, Op[] aopBounds)
            throws IOException
        {
        ReadBuffer.BufferInput stream = new ByteArrayReadBuffer(abCode).getBufferInput();

        // prime the line number enumeration
        Enumeration enmrLines = (attrLine == null ? NullImplementation.getEnumeration()
                                                  : attrLine.entries()         );
        LineNumberTableAttribute.Entry line =
                (LineNumberTableAttribute.Entry)
                (enmrLines.hasMoreElements() ? enmrLines.nextElement() : null);
        int iLine = 0;

        // build a table of variables by type/by slot
        OpDeclare[][] aavar = new OpDeclare[6][cVars];

        // labels by op offset (collected as necessary)
        Op[] aopDefer = new Op[abCode.length + 1];

        // support for switch parsing
        boolean  fSwitchOp = false;
        OpSwitch opSwitch  = null;
        int      iOpSwitch = 0;
        int      cCases    = 0;
        int      iCase     = 0;

        Op   opFirst  = null;
        Op   opLast   = null;
        while (true)
            {
            // get the offset of the byte code instruction
            int ofOp = stream.getOffset();
            Op  op   = null;

            if (fSwitchOp)
                {
                // lookupswitch cases have case values in the byte code
                // (tableswitch cases are ordered; their values are implied)
                if (iOpSwitch == LOOKUPSWITCH)
                    {
                    iCase = stream.readInt();
                    }

                // case branches to a label
                Label label = getLabel(((Op) opSwitch).m_of + stream.readInt(), aopDefer);

                // only create a case op if the case label is not the default
                if (label != opSwitch.getLabel())
                    {
                    op = new Case(iCase, label);
                    }

                ++iCase;
                if (--cCases <= 0)
                    {
                    fSwitchOp = false;
                    }
                }
            else
                {
                // check for line change
                if (line != null && ofOp >= line.getOffset())
                    {
                    iLine = line.getLine();
                    line  = (LineNumberTableAttribute.Entry)
                            (enmrLines.hasMoreElements() ? enmrLines.nextElement() : null);
                    }

                // read the byte code instruction
                int iOp;
                try
                    {
                    // read and validate the op
                    iOp = stream.readUnsignedByte();
                    }
                catch (EOFException e)
                    {
                    break;
                    }

                // create the op for the byte code
                switch (iOp)
                    {
                    case NOP:
                        op = new Nop();
                        break;

                    case ACONST_NULL:
                        op = new Aconst();
                        break;

                    case ICONST_M1:
                        op = new Iconst(CONSTANT_ICONST_M1);
                        break;

                    case ICONST_0:
                        op = new Iconst(CONSTANT_ICONST_0);
                        break;

                    case ICONST_1:
                        op = new Iconst(CONSTANT_ICONST_1);
                        break;

                    case ICONST_2:
                        op = new Iconst(CONSTANT_ICONST_2);
                        break;

                    case ICONST_3:
                        op = new Iconst(CONSTANT_ICONST_3);
                        break;

                    case ICONST_4:
                        op = new Iconst(CONSTANT_ICONST_4);
                        break;

                    case ICONST_5:
                        op = new Iconst(CONSTANT_ICONST_5);
                        break;

                    case LCONST_0:
                        op = new Lconst(CONSTANT_LCONST_0);
                        break;

                    case LCONST_1:
                        op = new Lconst(CONSTANT_LCONST_1);
                        break;

                    case FCONST_0:
                        op = new Fconst(CONSTANT_FCONST_0);
                        break;

                    case FCONST_1:
                        op = new Fconst(CONSTANT_FCONST_1);
                        break;

                    case FCONST_2:
                        op = new Fconst(CONSTANT_FCONST_2);
                        break;

                    case DCONST_0:
                        op = new Dconst(CONSTANT_DCONST_0);
                        break;

                    case DCONST_1:
                        op = new Dconst(CONSTANT_DCONST_1);
                        break;

                    case BIPUSH:
                        op = new Iconst(new IntConstant(stream.readByte()));
                        break;

                    case SIPUSH:
                        op = new Iconst(new IntConstant(stream.readShort()));
                        break;

                    case LDC:
                    case LDC_W:
                        {
                        int iConst = (iOp == LDC ? stream.readUnsignedByte()
                                                 : stream.readUnsignedShort());
                        Constant constant = pool.getConstant(iConst);
                        if (constant instanceof StringConstant)
                            {
                            op = new Aconst((StringConstant) constant);
                            }
                        else if (constant instanceof IntConstant)
                            {
                            op = new Iconst((IntConstant) constant);
                            }
                        else if (constant instanceof FloatConstant)
                            {
                            op = new Fconst((FloatConstant) constant);
                            }
                        else if (constant instanceof ClassConstant)
                            {
                            op = new Aconst((ClassConstant) constant);
                            }
                        else
                            {
                            throw new IOException(CLASS + ".disassembleOps:  " +
                                    "Invalid LDC/LDC_W constant type!");
                            }
                        }
                        break;

                    case LDC2_W:
                        {
                        Constant constant = pool.getConstant(stream.readUnsignedShort());
                        if (constant instanceof LongConstant)
                            {
                            op = new Lconst((LongConstant) constant);
                            }
                        else if (constant instanceof DoubleConstant)
                            {
                            op = new Dconst((DoubleConstant) constant);
                            }
                        else
                            {
                            throw new IOException(CLASS + ".disassembleOps:  " +
                                    "Invalid LDC2_W constant type!");
                            }
                        }
                        break;

                    case ILOAD:
                        op = new Iload((Ivar) getVariable(IVAR, stream.readUnsignedByte(), aavar));
                        break;

                    case LLOAD:
                        op = new Lload((Lvar) getVariable(LVAR, stream.readUnsignedByte(), aavar));
                        break;

                    case FLOAD:
                        op = new Fload((Fvar) getVariable(FVAR, stream.readUnsignedByte(), aavar));
                        break;

                    case DLOAD:
                        op = new Dload((Dvar) getVariable(DVAR, stream.readUnsignedByte(), aavar));
                        break;

                    case ALOAD:
                        op = new Aload((Avar) getVariable(AVAR, stream.readUnsignedByte(), aavar));
                        break;

                    case ILOAD_0:
                    case ILOAD_1:
                    case ILOAD_2:
                    case ILOAD_3:
                        op = new Iload((Ivar) getVariable(IVAR, iOp - ILOAD_0, aavar));
                        break;

                    case LLOAD_0:
                    case LLOAD_1:
                    case LLOAD_2:
                    case LLOAD_3:
                        op = new Lload((Lvar) getVariable(LVAR, iOp - LLOAD_0, aavar));
                        break;

                    case FLOAD_0:
                    case FLOAD_1:
                    case FLOAD_2:
                    case FLOAD_3:
                        op = new Fload((Fvar) getVariable(FVAR, iOp - FLOAD_0, aavar));
                        break;

                    case DLOAD_0:
                    case DLOAD_1:
                    case DLOAD_2:
                    case DLOAD_3:
                        op = new Dload((Dvar) getVariable(DVAR, iOp - DLOAD_0, aavar));
                        break;

                    case ALOAD_0:
                    case ALOAD_1:
                    case ALOAD_2:
                    case ALOAD_3:
                        op = new Aload((Avar) getVariable(AVAR, iOp - ALOAD_0, aavar));
                        break;

                    case IALOAD:
                        op = new Iaload();
                        break;

                    case LALOAD:
                        op = new Laload();
                        break;

                    case FALOAD:
                        op = new Faload();
                        break;

                    case DALOAD:
                        op = new Daload();
                        break;

                    case AALOAD:
                        op = new Aaload();
                        break;

                    case BALOAD:
                        op = new Baload();
                        break;

                    case CALOAD:
                        op = new Caload();
                        break;

                    case SALOAD:
                        op = new Saload();
                        break;

                    case ISTORE:
                        op = new Istore((Ivar) getVariable(IVAR, stream.readUnsignedByte(), aavar));
                        break;

                    case LSTORE:
                        op = new Lstore((Lvar) getVariable(LVAR, stream.readUnsignedByte(), aavar));
                        break;

                    case FSTORE:
                        op = new Fstore((Fvar) getVariable(FVAR, stream.readUnsignedByte(), aavar));
                        break;

                    case DSTORE:
                        op = new Dstore((Dvar) getVariable(DVAR, stream.readUnsignedByte(), aavar));
                        break;

                    case ASTORE:
                        op = new Astore((Avar) getVariable(AVAR, stream.readUnsignedByte(), aavar));
                        break;

                    case ISTORE_0:
                    case ISTORE_1:
                    case ISTORE_2:
                    case ISTORE_3:
                        op = new Istore((Ivar) getVariable(IVAR, iOp - ISTORE_0, aavar));
                        break;

                    case LSTORE_0:
                    case LSTORE_1:
                    case LSTORE_2:
                    case LSTORE_3:
                        op = new Lstore((Lvar) getVariable(LVAR, iOp - LSTORE_0, aavar));
                        break;

                    case FSTORE_0:
                    case FSTORE_1:
                    case FSTORE_2:
                    case FSTORE_3:
                        op = new Fstore((Fvar) getVariable(FVAR, iOp - FSTORE_0, aavar));
                        break;

                    case DSTORE_0:
                    case DSTORE_1:
                    case DSTORE_2:
                    case DSTORE_3:
                        op = new Dstore((Dvar) getVariable(DVAR, iOp - DSTORE_0, aavar));
                        break;

                    case ASTORE_0:
                    case ASTORE_1:
                    case ASTORE_2:
                    case ASTORE_3:
                        op = new Astore((Avar) getVariable(AVAR, iOp - ASTORE_0, aavar));
                        break;

                    case IASTORE:
                        op = new Iastore();
                        break;

                    case LASTORE:
                        op = new Lastore();
                        break;

                    case FASTORE:
                        op = new Fastore();
                        break;

                    case DASTORE:
                        op = new Dastore();
                        break;

                    case AASTORE:
                        op = new Aastore();
                        break;

                    case BASTORE:
                        op = new Bastore();
                        break;

                    case CASTORE:
                        op = new Castore();
                        break;

                    case SASTORE:
                        op = new Sastore();
                        break;

                    case POP:
                        op = new Pop();
                        break;

                    case POP2:
                        op = new Pop2();
                        break;

                    case DUP:
                        op = new Dup();
                        break;

                    case DUP_X1:
                        op = new Dup_x1();
                        break;

                    case DUP_X2:
                        op = new Dup_x2();
                        break;

                    case DUP2:
                        op = new Dup2();
                        break;

                    case DUP2_X1:
                        op = new Dup2_x1();
                        break;

                    case DUP2_X2:
                        op = new Dup2_x2();
                        break;

                    case SWAP:
                        op = new Swap();
                        break;

                    case IADD:
                        op = new Iadd();
                        break;

                    case LADD:
                        op = new Ladd();
                        break;

                    case FADD:
                        op = new Fadd();
                        break;

                    case DADD:
                        op = new Dadd();
                        break;

                    case ISUB:
                        op = new Isub();
                        break;

                    case LSUB:
                        op = new Lsub();
                        break;

                    case FSUB:
                        op = new Fsub();
                        break;

                    case DSUB:
                        op = new Dsub();
                        break;

                    case IMUL:
                        op = new Imul();
                        break;

                    case LMUL:
                        op = new Lmul();
                        break;

                    case FMUL:
                        op = new Fmul();
                        break;

                    case DMUL:
                        op = new Dmul();
                        break;

                    case IDIV:
                        op = new Idiv();
                        break;

                    case LDIV:
                        op = new Ldiv();
                        break;

                    case FDIV:
                        op = new Fdiv();
                        break;

                    case DDIV:
                        op = new Ddiv();
                        break;

                    case IREM:
                        op = new Irem();
                        break;

                    case LREM:
                        op = new Lrem();
                        break;

                    case FREM:
                        op = new Frem();
                        break;

                    case DREM:
                        op = new Drem();
                        break;

                    case INEG:
                        op = new Ineg();
                        break;

                    case LNEG:
                        op = new Lneg();
                        break;

                    case FNEG:
                        op = new Fneg();
                        break;

                    case DNEG:
                        op = new Dneg();
                        break;

                    case ISHL:
                        op = new Ishl();
                        break;

                    case LSHL:
                        op = new Lshl();
                        break;

                    case ISHR:
                        op = new Ishr();
                        break;

                    case LSHR:
                        op = new Lshr();
                        break;

                    case IUSHR:
                        op = new Iushr();
                        break;

                    case LUSHR:
                        op = new Lushr();
                        break;

                    case IAND:
                        op = new Iand();
                        break;

                    case LAND:
                        op = new Land();
                        break;

                    case IOR:
                        op = new Ior();
                        break;

                    case LOR:
                        op = new Lor();
                        break;

                    case IXOR:
                        op = new Ixor();
                        break;

                    case LXOR:
                        op = new Lxor();
                        break;

                    case IINC:
                        {
                        int      iVar = stream.readUnsignedByte();
                        Ivar     var  = (Ivar) getVariable(IVAR, iVar, aavar);
                        short    sInc = (short) stream.readByte();
                        op = new Iinc(var, sInc);
                        }
                        break;

                    case I2L:
                        op = new I2l();
                        break;

                    case I2F:
                        op = new I2f();
                        break;

                    case I2D:
                        op = new I2d();
                        break;

                    case L2I:
                        op = new L2i();
                        break;

                    case L2F:
                        op = new L2f();
                        break;

                    case L2D:
                        op = new L2d();
                        break;

                    case F2I:
                        op = new F2i();
                        break;

                    case F2L:
                        op = new F2l();
                        break;

                    case F2D:
                        op = new F2d();
                        break;

                    case D2I:
                        op = new D2i();
                        break;

                    case D2L:
                        op = new D2l();
                        break;

                    case D2F:
                        op = new D2f();
                        break;

                    case I2B:
                        op = new I2b();
                        break;

                    case I2C:
                        op = new I2c();
                        break;

                    case I2S:
                        op = new I2s();
                        break;

                    case LCMP:
                        op = new Lcmp();
                        break;

                    case FCMPL:
                        op = new Fcmpl();
                        break;

                    case FCMPG:
                        op = new Fcmpg();
                        break;

                    case DCMPL:
                        op = new Dcmpl();
                        break;

                    case DCMPG:
                        op = new Dcmpg();
                        break;

                    case IFEQ:
                        op = new Ifeq(getLabel(ofOp + stream.readShort(), aopDefer));
                        break;

                    case IFNE:
                        op = new Ifne(getLabel(ofOp + stream.readShort(), aopDefer));
                        break;

                    case IFLT:
                        op = new Iflt(getLabel(ofOp + stream.readShort(), aopDefer));
                        break;

                    case IFGE:
                        op = new Ifge(getLabel(ofOp + stream.readShort(), aopDefer));
                        break;

                    case IFGT:
                        op = new Ifgt(getLabel(ofOp + stream.readShort(), aopDefer));
                        break;

                    case IFLE:
                        op = new Ifle(getLabel(ofOp + stream.readShort(), aopDefer));
                        break;

                    case IF_ICMPEQ:
                        op = new If_icmpeq(getLabel(ofOp + stream.readShort(), aopDefer));
                        break;

                    case IF_ICMPNE:
                        op = new If_icmpne(getLabel(ofOp + stream.readShort(), aopDefer));
                        break;

                    case IF_ICMPLT:
                        op = new If_icmplt(getLabel(ofOp + stream.readShort(), aopDefer));
                        break;

                    case IF_ICMPGE:
                        op = new If_icmpge(getLabel(ofOp + stream.readShort(), aopDefer));
                        break;

                    case IF_ICMPGT:
                        op = new If_icmpgt(getLabel(ofOp + stream.readShort(), aopDefer));
                        break;

                    case IF_ICMPLE:
                        op = new If_icmple(getLabel(ofOp + stream.readShort(), aopDefer));
                        break;

                    case IF_ACMPEQ:
                        op = new If_acmpeq(getLabel(ofOp + stream.readShort(), aopDefer));
                        break;

                    case IF_ACMPNE:
                        op = new If_acmpne(getLabel(ofOp + stream.readShort(), aopDefer));
                        break;

                    case GOTO:
                        op = new Goto(getLabel(ofOp + stream.readShort(), aopDefer));
                        break;

                    case JSR:
                        op = new Jsr(getLabel(ofOp + stream.readShort(), aopDefer));
                        break;

                    case RET:
                        op = new Ret((Rvar) getVariable(RVAR, stream.readUnsignedByte(), aavar));
                        break;

                    case TABLESWITCH:
                    case LOOKUPSWITCH:
                        {
                        // skip allignment padding
                        int cbPad = 3 - ofOp % 4;
                        for (int i = 0; i < cbPad; ++i)
                            {
                            int iPad = stream.readUnsignedByte();
                            if (iPad != 0x00)
                                {
                                throw new IOException(CLASS + ".disassembleOps:  " +
                                    "Illegal padding! (" + iPad + ")");
                                }
                            }

                        // read the "default:" label
                        Label labelDefault = getLabel(ofOp + stream.readInt(), aopDefer);

                        // determine the number of cases
                        if (iOp == TABLESWITCH)
                            {
                            int iLow   = stream.readInt();
                            int iHigh  = stream.readInt();

                            cCases = iHigh - iLow + 1;
                            iCase  = iLow;
                            }
                        else
                            {
                            cCases = stream.readInt();
                            }

                        // create a generic switch op
                        op = opSwitch = new Switch(labelDefault);

                        // remember the switch type
                        iOpSwitch = iOp;

                        // change the parsing mode to parse the case ops
                        // 2001.05.03 cp  switch can have 0 cases, in which
                        //                case we are already done parsing it
                        fSwitchOp = cCases > 0;
                        }
                        break;

                    case IRETURN:
                        op = new Ireturn();
                        break;

                    case LRETURN:
                        op = new Lreturn();
                        break;

                    case FRETURN:
                        op = new Freturn();
                        break;

                    case DRETURN:
                        op = new Dreturn();
                        break;

                    case ARETURN:
                        op = new Areturn();
                        break;

                    case RETURN:
                        op = new Return();
                        break;

                    case GETSTATIC:
                        op = new Getstatic((FieldConstant)pool.getConstant(
                                stream.readUnsignedShort()));
                        break;

                    case PUTSTATIC:
                        op = new Putstatic((FieldConstant)pool.getConstant(
                                stream.readUnsignedShort()));
                        break;

                    case GETFIELD:
                        op = new Getfield((FieldConstant)pool.getConstant(
                                stream.readUnsignedShort()));
                        break;

                    case PUTFIELD:
                        op = new Putfield((FieldConstant)pool.getConstant(
                                stream.readUnsignedShort()));
                        break;

                    case INVOKEVIRTUAL:
                        op = new Invokevirtual((MethodConstant)pool.getConstant(
                                stream.readUnsignedShort()));
                        break;

                    case INVOKESPECIAL:
                        {
                        op = new Invokespecial((MethodConstant)pool.getConstant(
                                stream.readUnsignedShort()));
                        }
                        break;

                    case INVOKESTATIC:
                        op = new Invokestatic((MethodConstant)pool.getConstant(
                                stream.readUnsignedShort()));
                        break;

                    case INVOKEINTERFACE:
                        // the invoke interface construct has the byte code
                        // followed by a two-byte constant index, a single-byte
                        // "number of arguments" value, and a zero; the number
                        // of arguments is redundant and the zero is meaningless
                        op = new Invokeinterface((InterfaceConstant)pool.getConstant(
                                stream.readUnsignedShort()));
                        stream.readUnsignedByte(); // nargs is redundant
                        stream.readUnsignedByte(); // zero
                        break;

                    case INVOKEDYNAMIC:
                        op = new Invokedynamic((InvokeDynamicConstant)
                                pool.getConstant(stream.readUnsignedShort()));
                        stream.readUnsignedByte(); // zero
                        stream.readUnsignedByte(); // zero
                        break;

                    default:
                        throw new IOException(CLASS + ".disassembleOps:  " +
                            "Illegal byte code! (" + iOp + ")");

                    case NEW:
                        op = new New((ClassConstant)pool.getConstant(
                                stream.readUnsignedShort()));
                        break;

                    case NEWARRAY:
                        {
                        int iType = stream.readUnsignedByte();
                        switch (iType)
                            {
                            case 4:
                                op = new Znewarray();
                                break;
                            case 5:
                                op = new Cnewarray();
                                break;
                            case 6:
                                op = new Fnewarray();
                                break;
                            case 7:
                                op = new Dnewarray();
                                break;
                            case 8:
                                op = new Bnewarray();
                                break;
                            case 9:
                                op = new Snewarray();
                                break;
                            case 10:
                                op = new Inewarray();
                                break;
                            case 11:
                                op = new Lnewarray();
                                break;
                            default:
                                throw new IOException(CLASS + ".disassembleOps:  Unexpected NEWARRAY type (" + iType + ")");
                            }
                        }
                        break;

                    case ANEWARRAY:
                        op = new Anewarray((ClassConstant)pool.getConstant(
                                stream.readUnsignedShort()));
                        break;

                    case ARRAYLENGTH:
                        op = new Arraylength();
                        break;

                    case ATHROW:
                        op = new Athrow();
                        break;

                    case CHECKCAST:
                        op = new Checkcast((ClassConstant)pool.getConstant(
                                stream.readUnsignedShort()));
                        break;

                    case INSTANCEOF:
                        op = new Instanceof((ClassConstant)pool.getConstant(
                                stream.readUnsignedShort()));
                        break;

                    case MONITORENTER:
                        op = new Monitorenter();
                        break;

                    case MONITOREXIT:
                        op = new Monitorexit();
                        break;

                    case WIDE:
                        iOp = stream.readUnsignedByte();
                        switch (iOp)
                            {
                            case IINC:
                                {
                                int      iVar = stream.readUnsignedShort();
                                Ivar     var  = (Ivar) getVariable(IVAR, iVar, aavar);
                                short    sInc = (short) stream.readShort();
                                op = new Iinc(var, sInc);
                                }
                                break;

                            case ILOAD:
                                op = new Iload((Ivar) getVariable(IVAR, stream.readUnsignedShort(), aavar));
                                break;

                            case LLOAD:
                                op = new Lload((Lvar) getVariable(LVAR, stream.readUnsignedShort(), aavar));
                                break;

                            case FLOAD:
                                op = new Fload((Fvar) getVariable(FVAR, stream.readUnsignedShort(), aavar));
                                break;

                            case DLOAD:
                                op = new Dload((Dvar) getVariable(DVAR, stream.readUnsignedShort(), aavar));
                                break;

                            case ALOAD:
                                op = new Aload((Avar) getVariable(AVAR, stream.readUnsignedShort(), aavar));
                                break;

                            case ISTORE:
                                op = new Istore((Ivar) getVariable(IVAR, stream.readUnsignedShort(), aavar));
                                break;

                            case LSTORE:
                                op = new Lstore((Lvar) getVariable(LVAR, stream.readUnsignedShort(), aavar));
                                break;

                            case FSTORE:
                                op = new Fstore((Fvar) getVariable(FVAR, stream.readUnsignedShort(), aavar));
                                break;

                            case DSTORE:
                                op = new Dstore((Dvar) getVariable(DVAR, stream.readUnsignedShort(), aavar));
                                break;

                            case ASTORE:
                                op = new Astore((Avar) getVariable(AVAR, stream.readUnsignedShort(), aavar));
                                break;

                            case RET:
                                op = new Ret((Rvar) getVariable(RVAR, stream.readUnsignedShort(), aavar));
                                break;

                            default:
                                throw new IOException(CLASS + ".disassembleOps:  " +
                                    "Illegal byte code modified by WIDE! (" + iOp + ")");
                            }
                        break;

                    case MULTIANEWARRAY:
                        {
                        int iConst = stream.readUnsignedShort();
                        int cDims  = stream.readUnsignedByte();
                        ClassConstant constant = (ClassConstant) pool.getConstant(iConst);
                        op = new Multianewarray(constant, cDims);
                        }
                        break;

                    case IFNULL:
                        op = new Ifnull(getLabel(ofOp + stream.readShort(), aopDefer));
                        break;

                    case IFNONNULL:
                        op = new Ifnonnull(getLabel(ofOp + stream.readShort(), aopDefer));
                        break;

                    case GOTO_W:
                        op = new Goto(getLabel(ofOp + stream.readShort(), aopDefer));
                        break;

                    case JSR_W:
                        op = new Jsr(getLabel(ofOp + stream.readShort(), aopDefer));
                        break;
                    }
                }

            // determine how much was read from the stream (i.e. size of op)
            int cbOp = stream.getOffset() - ofOp;

            if (op == null)
                {
                // give this op's size to the previous op
                // (e.g. a case op which is the same as the switch default)
                opLast.m_cb += cbOp;
                }
            else
                {
                // op line number, offset and length
                op.m_iLine = iLine;
                op.m_of    = ofOp;
                op.m_cb    = cbOp;

                // add the op to the linked list of ops
                if (opFirst == null)
                    {
                    opFirst = op;
                    opLast  = op;
                    }
                else
                    {
                    // append op
                    opLast.m_opNext = op;
                    opLast = op;
                    }
                }
            }

        // the local variable table offsets/lengths need to be turned into ops;
        // the easiest way is to use labels since we keep labels by offset
        if (attrVar != null)
            {
            disassembleVarTable(attrVar, aopDefer);
            }
        if (attrVarType != null)
            {
            disassembleVarTable(attrVarType, aopDefer);
            }


        // the labels encounted in the code are already arranged by offset
        // (aopDefer) but the labels implied by guarded sections are not
        for (Enumeration enmr = vectCatch.elements(); enmr.hasMoreElements(); )
            {
            GuardedSection section = (GuardedSection) enmr.nextElement();
            section.setHandler(getLabel(section.getHandlerOffset(), aopDefer));
            }

        // using the guarded section informtion, add try and catch ops;
        // for each try, there is one or more catch;
        // for each guarded section, there is one catch;
        // multiple catch ops share a try op if and only if the guarded
        // section start and end PC's are identical;
        // guarded sections are arranged in order of the catch ops
        int ofTryPrev   = -1;
        int ofCatchPrev = -1;
        Try opTryPrev   = null;
        for (Enumeration enmr = vectCatch.elements(); enmr.hasMoreElements(); )
            {
            GuardedSection section = (GuardedSection) enmr.nextElement();
            int ofTry   = section.getTryOffset();
            int ofCatch = section.getCatchOffset();
            if (ofTry == ofTryPrev && ofCatch == ofCatchPrev)
                {
                // use previous try but create a new catch
                Try   opTry   = opTryPrev;
                Catch opCatch = new Catch(opTry, section.getException(), section.getHandler());
                // add the catch to the defered ops
                addCatch(ofCatch, opCatch, aopDefer);
                }
            else
                {
                // create a new try and catch
                Try   opTry   = new Try();
                Catch opCatch = new Catch(opTry, section.getException(), section.getHandler());
                // add the try and catch to the defered ops
                addTry  (ofTry  , opTry  , aopDefer);
                addCatch(ofCatch, opCatch, aopDefer);
                // remember the bounds of the guarded section in case the
                // next one is based on the same try
                ofTryPrev   = ofTry;
                ofCatchPrev = ofCatch;
                opTryPrev   = opTry;
                }
            }

        // ensure that all parameters are declared
        for (int iParam = 0, cParams = asParam.length, iVar = 0; iParam < cParams; ++iParam)
            {
            // determine the parameter type
            int  nOp;
            char chType = asParam[iParam].charAt(0);
            switch (chType)
                {
                case 'Z':
                case 'B':
                case 'C':
                case 'S':
                case 'I':
                    nOp = IVAR;
                    break;

                case 'J':
                    nOp = LVAR;
                    break;

                case 'F':
                    nOp = FVAR;
                    break;
            
                case 'D':
                    nOp = DVAR;
                    break;

                case '[':
                case 'L':
                    nOp = AVAR;
                    break;

                case 'V':
                    throw new IllegalStateException("Parameter cannot be void");

                default:
                    throw new IllegalStateException("JVM Type Signature cannot start with '"
                            + chType + "'");
                }

            // make sure that the parameter is declared
            getVariable(nOp, iVar, aavar);

            // iVar is the slot number, which reflects the width (1 or 2) of
            // the parameter
            iVar += OpDeclare.getJavaWidth(chType);
            }

        // preface:  open the global scope, add variable declarations
        // (including parameters)
        Op opPreInit = new Begin();
        Op opPreStop = opPreInit;
        for (int iVar = 0; iVar < cVars; ++iVar)
            {
            for (int iType = 0; iType < 6; ++iType)
                {
                OpDeclare decl = aavar[iType][iVar];
                if (decl != null)
                    {
                    opPreStop.m_opNext = decl;
                    opPreStop = decl;
                    }
                }
            }

        // postlogue:  close the global scope
        Op opPost = new End();
        opPost.m_of     = opLast.m_of + opLast.m_cb;
        opPost.m_iLine  = opLast.m_iLine;
        opLast.m_opNext = opPost;
        opLast = opPost;

        // insert labels, try's, catch's
        int ofPrev = -1;
        for (Op opPrev = null, op = opFirst; op != null; op = (opPrev = op).m_opNext)
            {
            int of = op.m_of;
            if (of > ofPrev)
                {
                // verify that no ops are being missed
                for (int ofSkip = ofPrev + 1; ofSkip < of; ++ofSkip)
                    {
                    if (aopDefer[ofSkip] != null)
                        {
                        Op opLabel = aopDefer[ofSkip];
                        StringBuffer sb = new StringBuffer();
                        while (opLabel != null)
                            {
                            sb.append("\n  ")
                              .append(opLabel.getClass().getName())
                              .append(' ')
                              .append(opLabel);
                            opLabel = opLabel.m_opNext;
                            }
                        throw new IOException(CLASS + ".disassembleOps:  " +
                                "Non-alligned label, try, or catch at offset " + ofSkip + ":" + sb
                                + "\n  opPrev=" + opPrev.getClass().getName() + " " + opPrev + " @" + opPrev.m_of
                                + "\n  op=" + op.getClass().getName() + " " + op + " @" + op.m_of);
                        }
                    }
                ofPrev = of;

                // check if any ops are present to insert
                Op opInsert = aopDefer[of];
                if (opInsert != null)
                    {
                    // find the last op in the linked list of ops to insert
                    // (also set offset for each op)
                    opInsert.m_of = of;
                    opInsert.m_iLine = op.m_iLine;
                    Op opLastInsert = opInsert;
                    while (opLastInsert.m_opNext != null)
                        {
                        opLastInsert = opLastInsert.m_opNext;
                        opLastInsert.m_of = of;
                        opLastInsert.m_iLine = op.m_iLine;
                        }

                    // link in the deferred ops
                    if (opPrev == null)
                        {
                        // insert at the front of the entire set of ops
                        opFirst = opInsert;
                        }
                    else
                        {
                        // insert before the current op
                        opPrev.m_opNext = opInsert;
                        }
                    opLastInsert.m_opNext = op;
                    }
                }
            }

        // link the preface code into the list of ops
        opPreStop.m_opNext = opFirst;
        opFirst = opPreInit;

        aopBounds[0] = opFirst;
        aopBounds[1] = opLast;
        }


    // ----- VMStructure operations -----------------------------------------

    /**
    * The disassemble method for an op is not used.  The logic is centralized
    * in the disassembleOps method.
    *
    * @param stream  the stream implementing java.io.DataInput from which
    *                to read the assembled VM structure
    * @param pool    the constant pool for the class which contains any
    *                constants referenced by this VM structure
    */
    protected void disassemble(DataInput stream, ConstantPool pool)
            throws IOException
        {
        }

    /**
    * The pre-assembly step collects the necessary entries for the constant
    * pool.  During this step, all constants used by this VM structure and
    * any sub-structures are registered with (but not yet bound by position
    * in) the constant pool.
    *
    * The implementation provided here is for a simple op.  A simple op is
    * a single-byte op that exists as both a JASM instruction and a Java
    * byte code.
    *
    * @param pool  the constant pool for the class which needs to be
    *              populated with the constants required to build this
    *              VM structure
    */
    protected void preassemble(ConstantPool pool)
        {
        }

    /**
    * The assembly process assembles and writes the structure to the passed
    * output stream, resolving any dependencies using the passed constant
    * pool.
    *
    * The implementation provided here is for simple ops and simple pseudo-
    * ops.  A simple op is a single-byte op that exists as both a JASM
    * instruction and a Java byte code.  A simple pseudo-op assembles to
    * nothing.
    *
    * @param stream  the stream implementing java.io.DataOutput to which to
    *                write the assembled VM structure
    * @param pool    the constant pool for the class which by this point
    *                contains the entire set of constants required to build
    *                this VM structure
    */
    protected void assemble(DataOutput stream, ConstantPool pool)
            throws IOException
        {
        // JSR_W is the last byte code; assume everything higher is a zero
        // length pseudo op and everything below is a one length byte code
        if (m_iOp <= JSR_W)
            {
            stream.write(m_iOp);
            }
        }


    // ----- Object operations ----------------------------------------------

    /**
    * Produce a human-readable string describing the byte code.
    *
    * @return a string describing the byte code
    */
    public String toString()
        {
        return format(null, OPNAME[m_iOp], null);
        }

    /**
    * Format a line of assembly.
    *
    * @return a formatted 3-column string (label, instruction, comment)
    */
    protected static String format(String sLabel, String sInstruction, String sComment)
        {
        StringBuffer sb = new StringBuffer();

        if (sLabel == null)
            {
            sLabel = BLANK;
            }
        if (sInstruction == null)
            {
            sInstruction = BLANK;
            }
        if (sComment == null)
            {
            sComment = BLANK;
            }

        boolean fLabel       = (sLabel      .length() > 0);
        boolean fInstruction = (sInstruction.length() > 0);
        boolean fComment     = (sComment    .length() > 0);

        boolean fOverflow = false;
        if (fLabel)
            {
            sb.append(sLabel)
              .append(':');
            fOverflow = (sb.length() > BLANK_LABEL.length());
            }

        if (fInstruction || fComment)
            {
            if (fOverflow)
                {
                sb.append('\n')
                  .append(BLANK_LABEL)
                  .append(SEPARATOR);
                }
            else
                {
                sb.append(BLANK_LABEL.substring(sb.length()))
                  .append(SEPARATOR);
                }

            fOverflow = false;
            if (fInstruction)
                {
                sb.append(sInstruction);
                fOverflow = (sInstruction.length() >= BLANK_INSTRUCTION.length());
                }

            if (fComment)
                {
                if (fOverflow)
                    {
                    sb.append('\n')
                      .append(BLANK_LABEL)
                      .append(SEPARATOR)
                      .append(BLANK_INSTRUCTION)
                      .append(SEPARATOR);
                    }
                else
                    {
                    sb.append(BLANK_INSTRUCTION.substring(sInstruction.length()))
                      .append(SEPARATOR);
                    }

                sb.append("// ")
                  .append(sComment);
                }
            }

        return sb.toString();
        }

    /**
    * Produce a human-readable string describing the byte code.
    *
    * @return a string describing the byte code
    */
    public String toJasm()
        {
        return OPNAME[m_iOp];
        }

    /**
    * Produce a fairly unique hash code.
    *
    * @return the hash code for this object
    */
    public int hashCode()
        {
        return m_iHash;
        }


    // ----- accessors ------------------------------------------------------

    /**
    * Access the JASM op value.
    *
    * @return the JASM op value
    */
    public int getValue()
        {
        return m_iOp;
        }

    /**
    * Set the JASM op value.
    *
    * @param iOp  the JASM op value
    */
    protected void setValue(int iOp)
        {
        m_iOp = iOp;
        }

    /**
    * Returns the offset of the byte code.  If the op was disassembled, the
    * offset reflects the location that the byte code was read from the
    * stream.  If the op was assembled, the offset is the location assigned
    * for the op to assemble to.  Otherwise the offset is meaningless.
    *
    * @return the op's byte code offset
    */
    public int getOffset()
        {
        return m_of;
        }

    /**
    * Sets the byte code offset for the op.
    *
    * @param  of  the op's new byte code offset
    */
    protected void setOffset(int of)
        {
        m_of = of;
        }

    /**
    * Determines if the op results in one or more byte codes.
    *
    * @return true if the op has a size when assembled
    */
    public boolean hasSize()
        {
        // JSR_W is the last byte code
        if (m_iOp <= JSR_W)
            {
            return true;
            }

        // some pseudo-ops don't produce code
        switch (m_iOp)
            {
            case BEGIN:
            case END:
            case IVAR:
            case LVAR:
            case FVAR:
            case DVAR:
            case AVAR:
            case RVAR:
            case CASE:
            case LABEL:
            case TRY:
            case CATCH:
                return false;

            default:
                return true;
            }
        }

    /**
    * Returns the size of the byte code.  If the op was disassembled, the
    * size is the number of bytes read from the stream.  If the op was
    * assembled, the size is the number of bytes written to the stream.
    * Otherwise, the size is meaningless.
    *
    * @return the size of the op
    */
    public int getSize()
        {
        return m_cb;
        }

    /**
    * Sets the byte code length for the op.
    *
    * @param  cb  the op's new byte code length
    */
    protected void setSize(int cb)
        {
        m_cb = cb;
        }

    /**
    * Calculate and set the size of the assembled op based on the offset of
    * the op and the constant pool which is passed.
    *
    * The implementation provided here supports simple ops and zero-length
    * pseudo-ops.  A simple op is a single-byte op that exists as both a
    * JASM instruction and a Java byte code.  A pseudo-op is an op which
    * does not have a one-to-one correlation to any Java byte code; it
    * either assembles to one of several byte codes or is used to build
    * non-byte-code structures contained within a Code attribute.
    *
    * @param pool    the constant pool for the class which by this point
    *                contains the entire set of constants required to build
    *                this VM structure
    */
    protected void calculateSize(ConstantPool pool)
        {
        // JSR_W is the last byte code; assume everything higher is a zero
        // length pseudo op and everything below is a one length byte code
        setSize(m_iOp > JSR_W ? 0 : 1);
        }

    /**
    * Returns the effect of the byte code on the height of the stack.  This
    * method is overridden by ops which dynamically calculate their effect
    * on the stack.
    *
    * @return the number of words pushed (if positive) or popped (if
    *         negative) from the stack by the op
    */
    public int getStackChange()
        {
        return OPEFFECT[m_iOp];
        }

    /**
    * Get the expected stack height for this op.  If the expected stack size
    * is still not calculated (UNKNOWN) after calculating the max stack,
    * then the op is considered "not reachable".
    */
    protected int getStackHeight()
        {
        return m_cwStack;
        }

    /**
    * Set the expected stack height for this op.
    *
    * @param cwStack the number of words in the stack when this op is
    *                executed
    */
    protected void setStackHeight(int cwStack)
        {
        // verify that this op always has the same stack size when it is
        // executed
        int cwStackPrev = m_cwStack;
        if (cwStackPrev != UNKNOWN && cwStackPrev != cwStack)
            {
            throw new IllegalStateException(CLASS + ".setStackHeight:  " +
                    "Height mismatch (" + cwStack + " vs " + cwStackPrev +
                    ") on " + toString() + "!");
            }

        // store the stack size
        m_cwStack = cwStack;
        }

    /**
    * Determine if the op is reachable; this is only valid after calculating
    * the max stack.
    *
    * @return true if the op was reached by the stack size calculating
    *         algorithm
    */
    protected boolean isReachable()
        {
        return m_cwStack != UNKNOWN;
        }

    /**
    * Determine if the op is discardable; by default, this is dependent on
    * determining if the op is reachable.
    *
    * @return true if the op can be discarded from assembly
    */
    protected boolean isDiscardable()
        {
        return !isReachable();
        }

    /**
    * Returns the line number of the source code which produced the op.
    *
    * @return the source code line number of the op
    */
    public int getLine()
        {
        return m_iLine;
        }

    /**
    * Sets the source code line number for the op.
    *
    * @param  iLine  the op's new source code line number
    */
    protected void setLine(int iLine)
        {
        m_iLine = iLine;
        }

    /**
    * Returns the name of the op.  This is for descriptive purposes only.
    *
    * @return the name of the op
    */
    public String getName()
        {
        return OPNAME[m_iOp];
        }

    /**
    * Get the op that follows this one.
    *
    * @return the next op
    */
    public Op getNext()
        {
        return m_opNext;
        }

    /**
    * Set the op that follows this one.  This is used internally by the
    * assembler.
    *
    * @param op  the next op
    */
    protected void setNext(Op op)
        {
        m_opNext = op;
        }

    /**
    * For debugging or listing purposes, print the op details.
    */
    public void print()
        {
        out('[' + String.valueOf(getOffset()) + "] (" + getLine() + ") " + toString());
        }


    // ----- data members ---------------------------------------------------

    /**
    * The name of this class.
    */
    private static final String CLASS = "Op";

    /**
    * An empty byte array.
    */
    private static final byte[] NO_BYTES = new byte[0];

    /**
    * A big prime number.
    * @see 
    * http://www.utm.edu/research/primes/lists/small/small.html
    */
    private static final long BIGPRIME = 1500450271L;

    /**
    * A number just a little too big to be an int.
    */
    private static final long INTLIMIT = 0x80000000L;

    /**
    * A blank string.
    */
    private static final String BLANK               = "";

    /**
    * The formatted space reserved for a label.
    */
    private static final String BLANK_LABEL         = "                  ";

    /**
    * The formatted space reserved for an instruction.
    */
    private static final String BLANK_INSTRUCTION   = "                        ";

    /**
    * The formatted space between columns (label, instruction, comment).
    */
    private static final String SEPARATOR           = "  ";

    /**
    * The last hash code given out.
    */
    private static int sm_iLastHash;

    /**
    * The JASM op value.
    */
    private int m_iOp;

    /**
    * The offset of the byte code.  If the op is disassembled from byte code,
    * this value is set to the offset within the disassembled byte code of
    * the byte code instruction which disassembled to this op.  When the op
    * is assembled, this value is set to the expected offset where the op
    * will produce byte code.
    */
    private int m_of;

    /**
    * The length of the byte code.  If the op is disassembled from byte code,
    * this value is set to the number of bytes that were disassembled to make
    * this op.  When the op is assembled, this value is set to the expected
    * length of byte code that the op will produce.
    */
    private int m_cb;

    /**
    * The source code line number.
    */
    private int m_iLine;

    /**
    * The next op.
    */
    private Op m_opNext;

    /**
    * Count of words on the stack when this op is reached.
    */
    private int m_cwStack = UNKNOWN;

    /**
    * Hash code.
    */
    private int m_iHash;
    }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy