/*
 * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package sun.tools.asm;

import sun.tools.java.*;
import java.util.Enumeration;
import java.io.IOException;
import java.io.DataOutputStream;

/**
 * An Java instruction
 *
 * WARNING: The contents of this source file are not part of any
 * supported API.  Code that depends on them does so at its own risk:
 * they are subject to change or removal without notice.
 */
public
class Instruction implements Constants {
    long where;
    int pc;
    int opc;
    Object value;
    Instruction next;
//JCOV
    boolean flagCondInverted;        /* if true, the condition  is reversed
                                   relatively of source code */
    boolean flagNoCovered = false; /* if true, the command will
                                   ignored for coverage */


    /**
     * Constructor
     */
    public Instruction(long where, int opc, Object value, boolean flagCondInverted) {
        this.where = where;
        this.opc = opc;
        this.value = value;
        this.flagCondInverted = flagCondInverted;
    }

    /**
     * Constructor
     */
    public Instruction(boolean flagNoCovered, long where, int opc, Object value) {
        this.where = where;
        this.opc = opc;
        this.value = value;
        this.flagNoCovered = flagNoCovered;
    }

    /**
     * Constructor
     */
    public Instruction(long where, int opc, boolean flagNoCovered) {
        this.where = where;
        this.opc = opc;
        this.flagNoCovered = flagNoCovered;
    }
//end JCOV

    /**
     * Constructor
     */
    public Instruction(long where, int opc, Object value) {
        this.where = where;
        this.opc = opc;
        this.value = value;
    }

    /**
     * When deciding between a lookupswitch and a tableswitch, this
     * value is used in determining how much size increase is
     * acceptable.
     */
    public static final double SWITCHRATIO;

    static {
        // Set SWITCHRATIO from the property javac.switchratio
        // if it exists and is reasonable.  Otherwise, set
        // SWITCHRATIO to 1.5, meaning that we will accept a 1.5x
        // blowup (for the instruction) to use a tableswitch instead
        // of a lookupswitch.
        double ratio = 1.5;
        String valStr = System.getProperty("javac.switchratio");
        if (valStr != null) {
            try {
                double temp = Double.valueOf(valStr).doubleValue();
                if (!(Double.isNaN(temp) || temp < 0.0)) {
                    ratio = temp;
                }
            } catch (NumberFormatException ee) {}
        }
        SWITCHRATIO = ratio;
    }

    /**
     * Accessor
     */
    public int getOpcode() {
        return pc;
     }

    public Object getValue() {
        return value;
     }

    public void setValue(Object value) {
        this.value = value;
     }


    /**
     * Optimize
     */
    void optimize(Environment env) {
        switch (opc) {
          case opc_istore: case opc_lstore: case opc_fstore:
          case opc_dstore: case opc_astore:
            // Don't keep the LocalVariable info around, unless we
            // are actually going to generate a local variable table.
            if ((value instanceof LocalVariable) && !env.debug_vars()) {
                value = ((LocalVariable)value).slot;
            }
            break;

          case opc_goto: {
            Label lbl = (Label)value;
            value = lbl = lbl.getDestination();
            if (lbl == next) {
                // goto to the next instruction, obsolete
                opc = opc_dead;
                break;
            }

            // We optimize
            //
            //          goto Tag
            //          ...
            //    Tag:
            //          return
            //
            // except when we're generating debuggable code.  When
            // we're generating debuggable code, we leave it alone,
            // in order to provide better stepping behavior.  Consider
            // a method the end of which looks like this:
            //
            //          ...
            //          break;
            //      }   // end of loop
            //  }   // end of method
            //
            // If we optimize the goto away, we'll be left with a
            // single instruction (return) and the need to ascribe that
            // instruction to two source lines (the break statement and
            // the method's right curly).  Can't get there from here.
            // Depending on which line-number ascription we choose, the
            // stepping user will step directly from the break statement
            // back into the caller of the method (case 1) or from the
            // statement that precedes the break statement to the method's
            // right curly (case 2).  Similarly, he'll be able to set a
            // breakpoint on the break statement (case 1) or the method's
            // right curly (case 2), but not on both.  Neither case 1 nor
            // case 2 is desirable.  .We want him to see both the break
            // statement and the method's right curly when stepping,
            // and we want him to be able to set a breakpoint on either or
            // both.  So we suppress the optimization when generating
            // debuggable code.
            // (Above notes from brucek@eng in JDK1.0.2, copied here
            //  by kelly.ohair@eng for JDK1.1)
            //
            // With the changes to allow -O and -g at the same time,
            // I've changed the condition to be whether optimization is
            // on instead of the debugging flag being off.
            //     - david.stoutamire@eng for 1.2

            if (lbl.next != null && env.opt()) {
                switch (lbl.next.opc) {
                  case opc_return:  case opc_ireturn: case opc_lreturn:
                  case opc_freturn: case opc_dreturn: case opc_areturn:
                    // goto to return
                    opc = lbl.next.opc;
                    value = lbl.next.value;
                    break;
                }
            }
            break;
          }

          case opc_ifeq:   case opc_ifne:   case opc_ifgt:
          case opc_ifge:   case opc_iflt:   case opc_ifle:
          case opc_ifnull: case opc_ifnonnull:
            value = ((Label)value).getDestination();
            if (value == next) {
                // branch to next instruction, obsolete
                opc = opc_pop;
                break;
            }
            if ((next.opc == opc_goto) && (value == next.next)) {
                // Conditional branch over goto, invert
                // Note that you can't invert all conditions, condition
                // results for float/double compares are not invertable.
                switch (opc) {
                  case opc_ifeq:      opc = opc_ifne; break;
                  case opc_ifne:      opc = opc_ifeq; break;
                  case opc_iflt:      opc = opc_ifge; break;
                  case opc_ifle:      opc = opc_ifgt; break;
                  case opc_ifgt:      opc = opc_ifle; break;
                  case opc_ifge:      opc = opc_iflt; break;
                  case opc_ifnull:    opc = opc_ifnonnull; break;
                  case opc_ifnonnull: opc = opc_ifnull; break;
                }
//JCOV
                flagCondInverted = !flagCondInverted;
//end JCOV
                value = next.value;
                next.opc = opc_dead;
            }
            break;

          case opc_if_acmpeq:   case opc_if_acmpne:
          case opc_if_icmpeq:   case opc_if_icmpne:
          case opc_if_icmpgt:   case opc_if_icmpge:
          case opc_if_icmplt:   case opc_if_icmple:
            value = ((Label)value).getDestination();
            if (value == next) {
                // branch to next instruction, obsolete
                opc = opc_pop2;
                break;
            }
            if ((next.opc == opc_goto) && (value == next.next)) {
                // Conditional branch over goto, invert
                switch (opc) {
                  case opc_if_acmpeq: opc = opc_if_acmpne; break;
                  case opc_if_acmpne: opc = opc_if_acmpeq; break;
                  case opc_if_icmpeq: opc = opc_if_icmpne; break;
                  case opc_if_icmpne: opc = opc_if_icmpeq; break;
                  case opc_if_icmpgt: opc = opc_if_icmple; break;
                  case opc_if_icmpge: opc = opc_if_icmplt; break;
                  case opc_if_icmplt: opc = opc_if_icmpge; break;
                  case opc_if_icmple: opc = opc_if_icmpgt; break;
                }
//JCOV
                flagCondInverted = !flagCondInverted;
//end JCOV
                value = next.value;
                next.opc = opc_dead;
            }
            break;

          case opc_tableswitch:
          case opc_lookupswitch: {
            SwitchData sw = (SwitchData)value;
            sw.defaultLabel = sw.defaultLabel.getDestination();
            for (Enumeration<Integer> e = sw.tab.keys() ; e.hasMoreElements() ; ) {
                Integer k = e.nextElement();
                Label lbl = sw.tab.get(k);
                sw.tab.put(k, lbl.getDestination());
            }

            // Compute the approximate sizes of a tableswitch and a
            // lookupswitch.  Decide which one we want to generate.

            long range = (long)sw.maxValue - (long)sw.minValue + 1;
            long entries = sw.tab.size();

            long tableSize = 4 + range;
            long lookupSize = 3 + 2 * entries;

            if (tableSize <= lookupSize * SWITCHRATIO) {
                opc = opc_tableswitch;
            } else {
                opc = opc_lookupswitch;
            }
            break;
          }

        }
    }

    /**
     * Collect constants into the constant table
     */
    void collect(ConstantPool tab) {
        switch (opc) {
          case opc_istore:      case opc_lstore:        case opc_fstore:
          case opc_dstore:      case opc_astore:
            if (value instanceof LocalVariable) {
                MemberDefinition field = ((LocalVariable)value).field;
                tab.put(field.getName().toString());
                tab.put(field.getType().getTypeSignature());
            }
            return;

          case opc_new:                 case opc_putfield:
          case opc_putstatic:           case opc_getfield:
          case opc_getstatic:           case opc_invokevirtual:
          case opc_invokespecial:       case opc_invokestatic:
          case opc_invokeinterface:     case opc_instanceof:
          case opc_checkcast:
            tab.put(value);
            return;

          case opc_anewarray:
            tab.put(value);
            return;

          case opc_multianewarray:
            tab.put(((ArrayData)value).type);
            return;

          case opc_ldc:
          case opc_ldc_w:
            if (value instanceof Integer) {
                int v = ((Integer)value).intValue();
                if ((v >= -1) && (v <= 5)) {
                    opc = opc_iconst_0 + v;
                    return;
                } else if ((v >= -(1 << 7)) && (v < (1 << 7))) {
                    opc = opc_bipush;
                    return;
                } else if ((v >= -(1 << 15)) && (v < (1 << 15))) {
                    opc = opc_sipush;
                    return;
                }
            } else if (value instanceof Float) {
                float v = ((Float)value).floatValue();
                if (v == 0) {
                    if (Float.floatToIntBits(v) == 0) {
                        opc = opc_fconst_0;
                        return;
                    }
                } else if (v == 1) {
                    opc = opc_fconst_1;
                    return;
                } else if (v == 2) {
                    opc = opc_fconst_2;
                    return;
                }
            }
            tab.put(value);
            return;

          case opc_ldc2_w:
            if (value instanceof Long) {
                long v = ((Long)value).longValue();
                if (v == 0) {
                    opc = opc_lconst_0;
                    return;
                } else if (v == 1) {
                    opc = opc_lconst_1;
                    return;
                }
            } else if (value instanceof Double) {
                double v = ((Double)value).doubleValue();
                if (v == 0) {
                    if (Double.doubleToLongBits(v) == 0) {
                        opc = opc_dconst_0;
                        return;
                    }
                } else if (v == 1) {
                    opc = opc_dconst_1;
                    return;
                }
            }
            tab.put(value);
            return;

          case opc_try:
            for (Enumeration<CatchData> e = ((TryData)value).catches.elements() ; e.hasMoreElements() ;) {
                CatchData cd = e.nextElement();
                if (cd.getType() != null) {
                    tab.put(cd.getType());
                }
            }
            return;

          case opc_nop:
            if ((value != null) && (value instanceof ClassDeclaration))
                tab.put(value);
                return;
        }
    }

    /**
     * Balance the stack
     */
    int balance() {
        switch (opc) {
          case opc_dead:        case opc_label:         case opc_iinc:
          case opc_arraylength: case opc_laload:        case opc_daload:
          case opc_nop:         case opc_ineg:          case opc_fneg:
          case opc_lneg:        case opc_dneg:          case opc_i2f:
          case opc_f2i:         case opc_l2d:           case opc_d2l:
          case opc_i2b:         case opc_i2c:           case opc_i2s:
          case opc_jsr:         case opc_goto:          case opc_jsr_w:
          case opc_goto_w:      case opc_return:        case opc_ret:
          case opc_instanceof:  case opc_checkcast:     case opc_newarray:
          case opc_anewarray:   case opc_try:           case opc_swap:
            return 0;

          case opc_ldc:         case opc_ldc_w:         case opc_bipush:
          case opc_sipush:      case opc_aconst_null:   case opc_iconst_m1:
          case opc_iconst_0:    case opc_iconst_1:      case opc_iconst_2:
          case opc_iconst_3:    case opc_iconst_4:      case opc_iconst_5:
          case opc_fconst_0:    case opc_fconst_1:      case opc_fconst_2:
          case opc_iload:       case opc_fload:         case opc_aload:
          case opc_dup:         case opc_dup_x1:        case opc_dup_x2:
          case opc_i2l:         case opc_i2d:           case opc_f2l:
          case opc_f2d:         case opc_new:
            return 1;

          case opc_lload:       case opc_dload:         case opc_dup2:
          case opc_dup2_x1:     case opc_dup2_x2:       case opc_ldc2_w:
          case opc_lconst_0:    case opc_lconst_1:      case opc_dconst_0:
          case opc_dconst_1:
            return 2;

          case opc_istore:      case opc_fstore:        case opc_astore:
          case opc_iaload:      case opc_faload:        case opc_aaload:
          case opc_baload:      case opc_caload:        case opc_saload:
          case opc_pop:         case opc_iadd:          case opc_fadd:
          case opc_isub:        case opc_fsub:          case opc_imul:
          case opc_fmul:        case opc_idiv:          case opc_fdiv:
          case opc_irem:        case opc_frem:          case opc_ishl:
          case opc_ishr:        case opc_iushr:         case opc_lshl:
          case opc_lshr:        case opc_lushr:         case opc_iand:
          case opc_ior:         case opc_ixor:          case opc_l2i:
          case opc_l2f:         case opc_d2i:           case opc_d2f:
          case opc_ifeq:        case opc_ifne:          case opc_iflt:
          case opc_ifle:        case opc_ifgt:          case opc_ifge:
          case opc_ifnull:      case opc_ifnonnull:     case opc_fcmpl:
          case opc_fcmpg:       case opc_ireturn:       case opc_freturn:
          case opc_areturn:     case opc_tableswitch:   case opc_lookupswitch:
          case opc_athrow:      case opc_monitorenter:  case opc_monitorexit:
            return -1;

          case opc_lstore:      case opc_dstore:        case opc_pop2:
          case opc_ladd:        case opc_dadd:          case opc_lsub:
          case opc_dsub:        case opc_lmul:          case opc_dmul:
          case opc_ldiv:        case opc_ddiv:          case opc_lrem:
          case opc_drem:        case opc_land:          case opc_lor:
          case opc_lxor:        case opc_if_acmpeq:     case opc_if_acmpne:
          case opc_if_icmpeq:   case opc_if_icmpne:     case opc_if_icmplt:
          case opc_if_icmple:   case opc_if_icmpgt:     case opc_if_icmpge:
          case opc_lreturn:     case opc_dreturn:
            return -2;

          case opc_iastore:     case opc_fastore:       case opc_aastore:
          case opc_bastore:     case opc_castore:       case opc_sastore:
          case opc_lcmp:        case opc_dcmpl:         case opc_dcmpg:
            return -3;

          case opc_lastore:     case opc_dastore:
            return -4;

          case opc_multianewarray:
            return 1 - ((ArrayData)value).nargs;

          case opc_getfield:
            return ((MemberDefinition)value).getType().stackSize() - 1;

          case opc_putfield:
            return -1 - ((MemberDefinition)value).getType().stackSize();

          case opc_getstatic:
            return ((MemberDefinition)value).getType().stackSize();

          case opc_putstatic:
            return -((MemberDefinition)value).getType().stackSize();

          case opc_invokevirtual:
          case opc_invokespecial:
          case opc_invokeinterface:
            return ((MemberDefinition)value).getType().getReturnType().stackSize() -
                   (((MemberDefinition)value).getType().stackSize() + 1);

          case opc_invokestatic:
            return ((MemberDefinition)value).getType().getReturnType().stackSize() -
                   (((MemberDefinition)value).getType().stackSize());
        }
        throw new CompilerError("invalid opcode: " + toString());
    }

    /**
     * Return the size of the instruction
     */
    int size(ConstantPool tab) {
        switch (opc) {
          case opc_try:         case opc_label:         case opc_dead:
            return 0;

          case opc_bipush:      case opc_newarray:
            return 2;

          case opc_sipush:      case opc_goto:          case opc_jsr:
          case opc_ifeq:        case opc_ifne:          case opc_ifgt:
          case opc_ifge:        case opc_iflt:          case opc_ifle:
          case opc_ifnull:      case opc_ifnonnull:     case opc_if_acmpeq:
          case opc_if_acmpne:   case opc_if_icmpeq:     case opc_if_icmpne:
          case opc_if_icmpgt:   case opc_if_icmpge:     case opc_if_icmplt:
          case opc_if_icmple:
            return 3;

          case opc_ldc:
          case opc_ldc_w:
            if (tab.index(value) < 256) {
                opc = opc_ldc;
                return 2;
            } else {
                opc = opc_ldc_w;
                return 3;
            }

          case opc_iload:       case opc_lload:         case opc_fload:
          case opc_dload:       case opc_aload: {
            int v = ((Number)value).intValue();
            if (v < 4) {
                if (v < 0) {
                    throw new CompilerError("invalid slot: " + toString()
                        + "\nThis error possibly resulted from poorly constructed class paths.");
                }
                opc = opc_iload_0 + (opc - opc_iload) * 4 + v;
                return 1;
            } else if (v <= 255) {
                return 2;
            } else {
                opc += 256;     // indicate wide variant
                return 4;
            }
          }

           case opc_iinc: {
               int register = ((int[])value)[0];
               int increment = ((int[])value)[1];
               if (register < 0) {
                   throw new CompilerError("invalid slot: " + toString());
               }
               if (register <= 255 && (((byte)increment) == increment)) {
                   return 3;
               } else {
                   opc += 256;          // indicate wide variant
                   return 6;
               }
           }

          case opc_istore:      case opc_lstore:        case opc_fstore:
          case opc_dstore:      case opc_astore: {
            int v = (value instanceof Number) ?
                ((Number)value).intValue() : ((LocalVariable)value).slot;
            if (v < 4) {
                if (v < 0) {
                    throw new CompilerError("invalid slot: " + toString());
                }
                opc = opc_istore_0 + (opc - opc_istore) * 4 + v;
                return 1;
            } else if (v <= 255) {
                return 2;
            } else {
                opc += 256;     // indicate wide variant
                return 4;
            }
          }

          case opc_ret: {
              int v = ((Number)value).intValue();
              if (v <= 255) {
                  if (v < 0) {
                      throw new CompilerError("invalid slot: " + toString());
                  }
                  return 2;
              } else {
                  opc += 256;   // indicate wide variant
                  return 4;
              }
          }

          case opc_ldc2_w:              case opc_new:
          case opc_putstatic:           case opc_getstatic:
          case opc_putfield:            case opc_getfield:
          case opc_invokevirtual:       case opc_invokespecial:
          case opc_invokestatic:        case opc_instanceof:
          case opc_checkcast:           case opc_anewarray:
            return 3;

          case opc_multianewarray:
            return 4;

          case opc_invokeinterface:
          case opc_goto_w:
          case opc_jsr_w:
            return 5;

          case opc_tableswitch: {
            SwitchData sw = (SwitchData)value;
            int n = 1;
            for(; ((pc + n) % 4) != 0 ; n++);
            return n + 16 + (sw.maxValue - sw.minValue) * 4;
          }

          case opc_lookupswitch: {
            SwitchData sw = (SwitchData)value;
            int n = 1;
            for(; ((pc + n) % 4) != 0 ; n++);
            return n + 8 + sw.tab.size() * 8;
          }

          case opc_nop:
            if ((value != null) && !(value instanceof Integer))
                return 2;
            else
                return 1;
        }

        // most opcodes are only 1 byte long
        return 1;
    }

    /**
     * Generate code
     */
    @SuppressWarnings("fallthrough")
    void write(DataOutputStream out, ConstantPool tab) throws IOException {
        switch (opc) {
          case opc_try:         case opc_label:         case opc_dead:
            break;

          case opc_bipush:      case opc_newarray:
          case opc_iload:       case opc_lload:         case opc_fload:
          case opc_dload:       case opc_aload:         case opc_ret:
            out.writeByte(opc);
            out.writeByte(((Number)value).intValue());
            break;

          case opc_iload + 256:         case opc_lload + 256:
          case opc_fload + 256:         case opc_dload + 256:
          case opc_aload + 256:         case opc_ret   + 256:
            out.writeByte(opc_wide);
            out.writeByte(opc - 256);
            out.writeShort(((Number)value).intValue());
            break;

          case opc_istore:      case opc_lstore:        case opc_fstore:
          case opc_dstore:      case opc_astore:
            out.writeByte(opc);
            out.writeByte((value instanceof Number) ?
                          ((Number)value).intValue() : ((LocalVariable)value).slot);
            break;

          case opc_istore + 256:        case opc_lstore + 256:
          case opc_fstore + 256:        case opc_dstore + 256:
          case opc_astore + 256:
            out.writeByte(opc_wide);
            out.writeByte(opc - 256);
            out.writeShort((value instanceof Number) ?
                      ((Number)value).intValue() : ((LocalVariable)value).slot);
            break;

          case opc_sipush:
            out.writeByte(opc);
            out.writeShort(((Number)value).intValue());
            break;

          case opc_ldc:
            out.writeByte(opc);
            out.writeByte(tab.index(value));
            break;

          case opc_ldc_w:               case opc_ldc2_w:
          case opc_new:                 case opc_putstatic:
          case opc_getstatic:           case opc_putfield:
          case opc_getfield:            case opc_invokevirtual:
          case opc_invokespecial:       case opc_invokestatic:
          case opc_instanceof:          case opc_checkcast:
            out.writeByte(opc);
            out.writeShort(tab.index(value));
            break;

          case opc_iinc:
            out.writeByte(opc);
            out.writeByte(((int[])value)[0]); // register
            out.writeByte(((int[])value)[1]); // increment
            break;

          case opc_iinc + 256:
            out.writeByte(opc_wide);
            out.writeByte(opc - 256);
            out.writeShort(((int[])value)[0]); // register
            out.writeShort(((int[])value)[1]); // increment
            break;

          case opc_anewarray:
            out.writeByte(opc);
            out.writeShort(tab.index(value));
            break;

          case opc_multianewarray:
            out.writeByte(opc);
            out.writeShort(tab.index(((ArrayData)value).type));
            out.writeByte(((ArrayData)value).nargs);
            break;

          case opc_invokeinterface:
            out.writeByte(opc);
            out.writeShort(tab.index(value));
            out.writeByte(((MemberDefinition)value).getType().stackSize() + 1);
            out.writeByte(0);
            break;

          case opc_goto:        case opc_jsr:           case opc_ifeq:
          case opc_ifne:        case opc_ifgt:          case opc_ifge:
          case opc_iflt:        case opc_ifle:          case opc_ifnull:
          case opc_ifnonnull:   case opc_if_acmpeq:     case opc_if_acmpne:
          case opc_if_icmpeq:   case opc_if_icmpne:     case opc_if_icmpgt:
          case opc_if_icmpge:   case opc_if_icmplt:     case opc_if_icmple:
            out.writeByte(opc);
            out.writeShort(((Instruction)value).pc - pc);
            break;

          case opc_goto_w:
          case opc_jsr_w:
            out.writeByte(opc);
            out.writeLong(((Instruction)value).pc - pc);
            break;

          case opc_tableswitch: {
            SwitchData sw = (SwitchData)value;
            out.writeByte(opc);
            for(int n = 1 ; ((pc + n) % 4) != 0 ; n++) {
                out.writeByte(0);
            }
            out.writeInt(sw.defaultLabel.pc - pc);
            out.writeInt(sw.minValue);
            out.writeInt(sw.maxValue);
            for (int n = sw.minValue ; n <= sw.maxValue ; n++) {
                Label lbl = sw.get(n);
                int target_pc = (lbl != null) ? lbl.pc : sw.defaultLabel.pc;
                out.writeInt(target_pc - pc);
            }
            break;
          }

          case opc_lookupswitch: {
            SwitchData sw = (SwitchData)value;
            out.writeByte(opc);
            int n = pc + 1;
            for(; (n % 4) != 0 ; n++) {
                out.writeByte(0);
            }
            out.writeInt(sw.defaultLabel.pc - pc);
            out.writeInt(sw.tab.size());
            for (Enumeration<Integer> e = sw.sortedKeys(); e.hasMoreElements() ; ) {
                Integer v = e.nextElement();
                out.writeInt(v.intValue());
                out.writeInt(sw.get(v).pc - pc);
            }
            break;
          }

          case opc_nop:
            if (value != null) {
                if (value instanceof Integer)
                    out.writeByte(((Integer)value).intValue());
                else
                    out.writeShort(tab.index(value));
                return;
            }
            // fall through

          default:
            out.writeByte(opc);
            break;
        }
    }

    /**
     * toString
     */
    public String toString() {
        String prefix = (where >> WHEREOFFSETBITS) + ":\t";
        switch (opc) {
          case opc_try:
            return prefix + "try " + ((TryData)value).getEndLabel().hashCode();

          case opc_dead:
            return prefix + "dead";

          case opc_iinc: {
            int register = ((int[])value)[0];
            int increment = ((int[])value)[1];
            return prefix + opcNames[opc] + " " + register + ", " + increment;
          }

          default:
            if (value != null) {
                if (value instanceof Label) {
                    return prefix + opcNames[opc] + " " + value.toString();
                } else if (value instanceof Instruction) {
                    return prefix + opcNames[opc] + " " + value.hashCode();
                } else if (value instanceof String) {
                    return prefix + opcNames[opc] + " \"" + value + "\"";
                } else {
                    return prefix + opcNames[opc] + " " + value;
                }
            } else {
              return prefix + opcNames[opc];
            }
        }
    }
}
