CodeAttr.java :  » Scripting » Kawa » gnu » bytecode » Java Open Source

Java Open Source » Scripting » Kawa 
Kawa » gnu » bytecode » CodeAttr.java
// Copyright (c) 1997, 1998, 1999, 2001, 2003, 2004  Per M.A. Bothner.
// This is free software;  for terms and warranty disclaimer see ./COPYING.

package gnu.bytecode;
import java.io.*;

/**
  * Represents the contents of a standard "Code" attribute.
  * <p>
  * Most of the actual methods that generate bytecode operation
  * are in this class (typically with names starting with <code>emit</code>),
  * though there are also some in <code>Method</code>.
  * <p>
  * Note that a <code>CodeAttr</code> is an <code>Attribute</code>
  * of a <code>Method</code>, and can in turn contain other
  * <code>Attribute</code>s, such as a <code>LineNumbersAttr</code>.
  *
  * @author      Per Bothner
  */

public class CodeAttr extends Attribute implements AttrContainer
{
  Attribute attributes;
  public final Attribute getAttributes () { return attributes; }
  public final void setAttributes (Attribute attributes)
  { this.attributes = attributes; }
  LineNumbersAttr lines;
  public LocalVarsAttr locals;

  SourceDebugExtAttr sourceDbgExt;

  // In hindsight, maintaining stack_types is more hassle than it is worth.
  // Instead, better to just keep track of SP, which should catch most
  // stack errors, while being more general and less hassle.  FIXME.
  Type[] stack_types;

  int SP;  // Current stack size (in "words")
  private int max_stack;
  private int max_locals;
  /** Current active length of code array.
   * Note that processFixups may expand/contract the code array.  */
  int PC;
  byte[] code;

  /* The exception handler table, as a vector of quadruples
     (start_pc, end_pc, handler_pc, catch_type).
     Only the first exception_table_length quadruples are defined. */
  short[] exception_table;

  /* The number of (defined) exception handlers (i.e. quadruples)
     in exception_table. */
  int exception_table_length;

  /** Not a fixup - a no-op. */
  static final int FIXUP_NONE = 0;
  /** The definition of a label. */
  static final int FIXUP_DEFINE = 1;
  /** The offset points to a tableswitch/lookupswitch - handle padding. */
  static final int FIXUP_SWITCH = 2;
  /** The offset contains a label relative to the previous FIXUP_SWITCH. */
  static final int FIXUP_CASE = 3;
  /** The offset points to a goto instruction.
   * This case up to FIXUP_TRANSFER2 must be contiguous
   * - see the jump-to-jump optimization in processFixups. */
  static final int FIXUP_GOTO = 4;
  /** The offset points to a jsr instruction. */
  static final int FIXUP_JSR = 5;
  /** The offset points to a conditional transfer (if_xxx) instruction. */
  static final int FIXUP_TRANSFER = 6;
  /** A FIXUP_GOTO_, FIXUP_JSR, or FIXUP_TRANSFER that uses a 2-byte offset. */
  static final int FIXUP_TRANSFER2 = 7;
  /** The offsets points to 3 bytes that should be deleted. */
  static final int FIXUP_DELETE3 = 8;
  /** The following instructions are moved to later in the code stream.
   * Instead the instructions starting at the fixup label are patched here.
   * (If the fixup label is null, we're done.)
   * This allows re-arranging code to avoid unneeded gotos.
   * The following instruction is the target of a later FIXUP_MOVE,
   * and we'll insert then when we get to it. */
  static final int FIXUP_MOVE = 9;
  /** The following instructions are moved to the end of the code stream.
   * Same as FIXUP_MOVE, but there is no explicit later FIXUP_MOVE that
   * refers to the following instructions.  Created by beginFragment.
   * The fixup_offset points to the end of the fragment.
   * (The first processFixups patches these to FIXUP_MOVE.) */
  static final int FIXUP_MOVE_TO_END = 10;
  /** FIXUP_TRY with the following FIXUP_CATCH marks an exception handler.
   * The label is the start of the try clause;
   * the current offset marks the exception handler. */
  static final int FIXUP_TRY = 11;
  /** Second half of a FIXUP_TRY/FIXUP_CATCH pair.
   * The label is the ed of the try clause;
   * the current offset is the exception type as a constant pool index. */
  static final int FIXUP_CATCH = 12;
  /** With following FIXUP_LINE_NUMBER associates an offset with a line number.
   * The fixup_offset is the code location; the fixup_label is null. */
  static final int FIXUP_LINE_PC = 13;
  /** With preceding FIXUP_LINE_PC associates an offset with a line number.
   * The fixup_offset is the line number; the fixup_label is null. */
  static final int FIXUP_LINE_NUMBER = 14;
  int[] fixup_offsets;
  Label[] fixup_labels;
  int fixup_count;

  /** This causes a later processFixup to rearrange the code.
   * The code at target comes here, instead of the following instructions.
   * Fuctionally equivalent to: <code>goto target; here:</code>,
   * but implemented by code re-arranging.  Therefore there should be
   * at some later point a <code>goto here; target:</code>.
   */
  public final void fixupChain (Label here, Label target)
  {
    fixupAdd(CodeAttr.FIXUP_MOVE, 0, target);
    here.define(this);
  }

  /** Add a fixup at this location.
   * @param kind one of the FIXUP_xxx codes.
   * @param label varies - typically the target of jump. */
  public final void fixupAdd (int kind, Label label)
  {
    fixupAdd(kind, PC, label);
  }

  final void fixupAdd (int kind, int offset, Label label)
  {
    int count = fixup_count;
    if (count == 0)
      {
  fixup_offsets = new int[30];
  fixup_labels = new Label[30];
      }
    else if (fixup_count == fixup_offsets.length)
      {
  int new_length = 2 * count;
  Label[] new_labels = new Label[new_length];
  System.arraycopy (fixup_labels, 0, new_labels, 0, count);
  fixup_labels = new_labels;
  int[] new_offsets = new int[new_length];
  System.arraycopy (fixup_offsets, 0, new_offsets, 0, count);
  fixup_offsets = new_offsets;
      }
    fixup_offsets[count] = (offset << 4) | kind;
    fixup_labels[count] = label;
    fixup_count = count + 1;
  }

  private final int fixupOffset(int index)
  {
    return fixup_offsets[index] >> 4;
  }

  private final int fixupKind(int index)
  {
    return fixup_offsets[index] & 15;
  }

  /** If true we get a line number entry for each instruction.
   * Normally false, but can be a convenient hack to allow instruction-level
   * stepping/debugging and stacktraces.  In this case {@code LINE==PC}. */
  public static boolean instructionLineMode = false;

  /** The stack of currently active conditionals. */
  IfState if_stack;

  /** The stack of currently active try statements. */
  TryState try_stack;

  public final Method getMethod() { return (Method) getContainer(); }

  public final int getPC() { return PC; }

  public final int getSP() { return SP; }

  public final ConstantPool getConstants ()
  {
    return getMethod().classfile.constants;
  }

  /* True if we cannot fall through to bytes[PC] -
     the previous instruction was an uncondition control transfer.  */
  private boolean unreachable_here;
  /** True if control could reach here. */
  public final boolean reachableHere () { return !unreachable_here; }
  public final void setReachable(boolean val) { unreachable_here = !val; }
  public final void setUnreachable() { unreachable_here = true; }

  /** Get the maximum number of words on the operand stack in this method. */
  public int getMaxStack() { return max_stack; }
  /** Get the maximum number of local variable words in this method. */
  public int getMaxLocals() { return max_locals; }

  /** Set the maximum number of words on the operand stack in this method. */
  public void setMaxStack(int n) { max_stack = n; }
  /** Set the maximum number of local variable words in this method. */
  public void setMaxLocals(int n) { max_locals = n; }

  /** Get the code (instruction bytes) of this method.
    * Does not make a copy. */
  public byte[] getCode() { return code; }
  /** Set the code (instruction bytes) of this method.
    * @param code the code bytes (which are not copied).
    * Implicitly calls setCodeLength(code.length). */
  public void setCode(byte[] code) {
    this.code = code; this.PC = code.length; }
  /** Set the length the the code (instruction bytes) of this method.
    * That is the number of current used bytes in getCode().
    * (Any remaing bytes provide for future growth.) */
  public void setCodeLength(int len) { PC = len;}
  /** Set the current lengthof the code (instruction bytes) of this method. */
  public int getCodeLength() { return PC; }

  public CodeAttr (Method meth)
  {
    super ("Code");
    addToFrontOf(meth);
    meth.code = this;
  }

  public final void reserve (int bytes)
  {
    if (code == null)
      code = new byte[100+bytes];
    else if (PC + bytes > code.length)
      {
  byte[] new_code = new byte[2 * code.length + bytes];
  System.arraycopy (code, 0, new_code, 0, PC);
  code = new_code;
      }
  }

  /** Get opcode that implements NOT (x OPCODE y). */
  byte invert_opcode (byte opcode)
  {
    if ((opcode >= 153 && opcode <= 166)
  || (opcode >= 198 && opcode <= 199))
      return (byte) (opcode ^ 1);
    throw new Error("unknown opcode to invert_opcode");
  }

  /**
   * Write an 8-bit byte to the current code-stream.
   * @param i the byte to write
   */
  public final void put1(int i)
  {
    code[PC++] = (byte) i;
    unreachable_here = false;
  }

  /**
   * Write a 16-bit short to the current code-stream
   * @param i the value to write
   */
  public final void put2(int i)
  {
    code[PC++] = (byte) (i >> 8);
    code[PC++] = (byte) (i);
    unreachable_here = false;
  }

  /**
   * Write a 32-bit int to the current code-stream
   * @param i the value to write
   */
  public final void put4(int i)
  {
    code[PC++] = (byte) (i >> 24);
    code[PC++] = (byte) (i >> 16);
    code[PC++] = (byte) (i >> 8);

    code[PC++] = (byte) (i);
    unreachable_here = false;
  }

  public final void putIndex2 (CpoolEntry cnst)
  {
    put2(cnst.index);
  }

  public final void putLineNumber (String filename, int linenumber)
  {
    getMethod().classfile.setSourceFile(filename);
    putLineNumber(linenumber);
  }

  public final void putLineNumber (int linenumber)
  {
    if (sourceDbgExt != null)
      linenumber = sourceDbgExt.fixLine(linenumber);
    fixupAdd(FIXUP_LINE_PC, null);
    fixupAdd(FIXUP_LINE_NUMBER, linenumber, null);
  }

  public final void pushType(Type type)
  {
    if (type.size == 0)
      throw new Error ("pushing void type onto stack");
    if (stack_types == null)
      stack_types = new Type[20];
    else if (SP + 1 >= stack_types.length) {
      Type[] new_array = new Type[2 * stack_types.length];
      System.arraycopy (stack_types, 0, new_array, 0, SP);
      stack_types = new_array;
    }
    if (type.size == 8)
      stack_types[SP++] = Type.void_type;
    stack_types[SP++] = type;
    if (SP > max_stack)
      max_stack = SP;
  }

  public final Type popType ()
  {
    if (SP <= 0)
      throw new Error("popType called with empty stack "+getMethod());
    Type type = stack_types[--SP];
    if (type.size == 8)
      if (! popType().isVoid())
  throw new Error("missing void type on stack");
    return type;
  }

  public final Type topType ()
  {
    return stack_types[SP - 1];
  }

  /** Compile code to pop values off the stack (and ignore them).
   * @param nvalues the number of values (not words) to pop
   */
  public void emitPop (int nvalues)
  {
    for ( ; nvalues > 0;  --nvalues)
      {
        reserve(1);
  Type type = popType();
  if (type.size > 4)
    put1(88);  // pop2
  else if (nvalues > 1)
    { // optimization:  can we pop 2 4-byte words using a pop2
      Type type2 = popType();
      if (type2.size > 4)
        {
    put1(87);  // pop
    reserve(1);
        }
      put1(88);  // pop2
      --nvalues;
    }
  else
    put1(87); // pop
      }
  }

  /** Get a new Label for the current location.
   * Unlike Label.define, Does not change reachableHere().
   */
  public Label getLabel ()
  {
    boolean  unreachable = unreachable_here;
    Label label = new Label();
    label.define(this);
    unreachable_here = unreachable;
    return label;
  }

  public void emitSwap ()
  {
    reserve(1);
    Type type1 = popType();
    Type type2 = popType();

    if (type1.size > 4 || type2.size > 4)
      {
  // There is no swap instruction in the JVM for this case.
  // Fall back to a more convoluted way.
  pushType(type2);
  pushType(type1);
  emitDupX();
  emitPop(1);
      }
    else
      {
  pushType(type1);
  put1(95);  // swap
  pushType(type2);
      }
  }

  /** Emit code to duplicate the top element of the stack. */
  public void emitDup ()
  {
    reserve(1);

    Type type = topType();
    put1 (type.size <= 4 ? 89 : 92); // dup or dup2
    pushType (type);
  }

  /** Emit code to duplicate the top element of the stack
      and place the copy before the previous element. */
  public void emitDupX ()
  {
    reserve(1);

    Type type = popType();
    Type skipedType = popType();

    if (skipedType.size <= 4)
      put1 (type.size <= 4 ? 90 : 93); // dup_x1 or dup2_x1
    else
      put1 (type.size <= 4 ? 91 : 94); // dup_x2 or dup2_x2

    pushType (type);
    pushType (skipedType);
    pushType (type);
  }

  /** Compile code to duplicate with offset.
   * @param size the size of the stack item to duplicate (1 or 2)
   * @param offset where to insert the result (must be 0, 1, or 2)
   * The new words get inserted at stack[SP-size-offset]
   */
  public void emitDup (int size, int offset)
  {
    if (size == 0)
      return;
    reserve(1);
    // copied1 and (optionally copied2) are the types of the duplicated words
    Type copied1 = popType ();
    Type copied2 = null;
    if (size == 1)
      {
  if (copied1.size > 4)
    throw new Error ("using dup for 2-word type");
      }
    else if (size != 2)
      throw new Error ("invalid size to emitDup");
    else if (copied1.size <= 4)
      {
  copied2 = popType();
  if (copied2.size > 4)
    throw new Error ("dup will cause invalid types on stack");
      }

    int kind;
    // These are the types of the words (in any) that are "skipped":
    Type skipped1 = null;
    Type skipped2 = null;
    if (offset == 0)
      {
  kind = size == 1 ? 89 : 92;  // dup or dup2
      }
    else if (offset == 1)
      {
  kind = size == 1 ? 90 : 93; // dup_x1 or dup2_x1
  skipped1 = popType ();
  if (skipped1.size > 4)
    throw new Error ("dup will cause invalid types on stack");
      }
    else if (offset == 2)
      {
  kind = size == 1 ? 91 : 94; // dup_x2 or dup2_x2
  skipped1 = popType();
  if (skipped1.size <= 4)
    {
      skipped2 = popType();
      if (skipped2.size > 4)
        throw new Error ("dup will cause invalid types on stack");
    }
      }
    else
      throw new Error ("emitDup:  invalid offset");

    put1(kind);
    if (copied2 != null)
      pushType(copied2);
    pushType(copied1);
    if (skipped2 != null)
      pushType(skipped2);
    if (skipped1 != null)
      pushType(skipped1);
    if (copied2 != null)
      pushType(copied2);
    pushType(copied1);
  }

  /**
   * Compile code to duplicate the top 1 or 2 words.
   * @param size number of words to duplicate
   */
  public void emitDup (int size)
  {
    emitDup(size, 0);
  }

  public void emitDup (Type type)
  {
    emitDup(type.size > 4 ? 2 : 1, 0);
  }

  public void enterScope (Scope scope)
  {
    scope.setStartPC(this);
    locals.enterScope(scope);
  }

  public Scope pushScope () {
    Scope scope = new Scope ();
    if (locals == null)
      locals = new LocalVarsAttr(getMethod());
    enterScope(scope);
    if (locals.parameter_scope == null) 
      locals.parameter_scope = scope;
    return scope;
  }

  public Scope getCurrentScope()
  {
    return locals.current_scope;
  }

  public Scope popScope () {
    Scope scope = locals.current_scope;
    locals.current_scope = scope.parent;
    scope.freeLocals(this);
    scope.end = getLabel();
    return scope;
  }

  /** Get the index'th parameter. */
  public Variable getArg (int index)
  {
    return locals.parameter_scope.getVariable(index);
  }

  /**
   * Search by name for a Variable
   * @param name name to search for
   * @return the Variable, or null if not found (in any scope of this Method).
   */
  public Variable lookup (String name)
  {
    Scope scope = locals.current_scope;
    for (; scope != null;  scope = scope.parent)
      {
  Variable var = scope.lookup (name);
  if (var != null)
    return var;
      }
    return null;
  }

  /** Add a new local variable (in the current scope).
   * @param type type of the new Variable.
   * @return the new Variable. */
  public Variable addLocal (Type type)
  {
    return locals.current_scope.addVariable(this, type, null);
  }

  /** Add a new local variable (in the current scope).
   * @param type type of the new Variable.
   * @param name name of the new Variable.
   * @return the new Variable. */
  public Variable addLocal (Type type, String name)
  {
    return locals.current_scope.addVariable (this, type, name);
  }

  /** Call addLocal for parameters (as implied by method type). */
  public void addParamLocals()
  {
    Method method = getMethod();
    if ((method.access_flags & Access.STATIC) == 0)
      addLocal(method.classfile).setParameter(true);
    int arg_count = method.arg_types.length;
    for (int i = 0;  i < arg_count;  i++)
      addLocal(method.arg_types[i]).setParameter(true);
  }

  public final void emitPushConstant(int val, Type type)
  {
    switch (type.getSignature().charAt(0))
      {
      case 'B':  case 'C':  case 'I':  case 'Z':  case 'S':
  emitPushInt(val);  break;
      case 'J':
  emitPushLong((long)val);  break;
      case 'F':
  emitPushFloat((float)val);  break;
      case 'D':
  emitPushDouble((double)val);  break;
      default:
  throw new Error("bad type to emitPushConstant");
      }
  }

  /* Low-level method to pust a ConstantPool entry.
   * Does not do the appropriatre <code>pushType</code>. */
  public final void emitPushConstant (CpoolEntry cnst)
  {
    reserve(3);
    int index = cnst.index;
    if (cnst instanceof CpoolValue2)
      {
        put1 (20); // ldc2_w
  put2 (index);
      }
    else if (index < 256)
      {
  put1(18); // ldc
  put1(index);
      }
    else
      {
  put1(19); // ldc_w
  put2(index);
      }
  }

  public final void emitPushInt(int i)
  {
    reserve(3);
    if (i >= -1 && i <= 5)
      put1(i + 3);  // iconst_m1 .. iconst_5
    else if (i >= -128 && i < 128)
      {
  put1(16); // bipush
  put1(i);
      }
    else if (i >= -32768 && i < 32768)
      {
  put1(17); // sipush
  put2(i);
      }
    else
      {
  emitPushConstant(getConstants().addInt(i));
      }
    pushType(Type.int_type);
  }

  public void emitPushLong (long i)
  {
    if (i == 0 || i == 1)
      {
  reserve(1);
  put1 (9 + (int) i);  // lconst_0 .. lconst_1
      }
    else if ((long) (int) i == i)
      {
  emitPushInt ((int) i);
  reserve(1);
  popType();
  put1 (133); // i2l
      }
    else
      {
  emitPushConstant(getConstants().addLong(i));
      }
    pushType(Type.long_type);
  }

  public void emitPushFloat (float x)
  {
    int xi = (int) x;
    if ((float) xi == x && xi >= -128 && xi < 128)
      {
  if (xi >= 0 && xi <= 2)
    {
      reserve(1);
      put1(11 + xi);  // fconst_0 .. fconst_2
      if (xi == 0 && Float.floatToIntBits(x) != 0) // x == -0.0
        {
    reserve(1);
    put1(118);  // fneg
        }
    }
  else
    {
      // Saves space in the constant pool
      // Probably faster, at least on modern CPUs.
      emitPushInt (xi);
      reserve(1);
      popType();
      put1 (134); // i2f
    }
      }
    else
      {
  emitPushConstant(getConstants().addFloat(x));
      }
    pushType(Type.float_type);
  }

  public void emitPushDouble (double x)
  {
    int xi = (int) x;
    if ((double) xi == x && xi >= -128 && xi < 128)
      {
  if (xi == 0 || xi == 1)
    {
      reserve(1);
      put1(14+xi);  // dconst_0 or dconst_1
      if (xi == 0 && Double.doubleToLongBits(x) != 0L) // x == -0.0
        {
    reserve(1);
    put1(119);  // dneg
        }
    }
  else
    {
      // Saves space in the constant pool
      // Probably faster, at least on modern CPUs.
      emitPushInt (xi);
      reserve(1);
      popType();
      put1 (135); // i2d
    }
      }
    else
      {
  emitPushConstant(getConstants().addDouble(x));
      }
    pushType(Type.double_type);
  }

  /** Calculate how many CONSTANT_String constants we need for a string.
   * Each CONSTANT_String can be at most 0xFFFF bytes (as a UTF8 string).
   * Returns a String, where each char, coerced to an int, is the length
   * of a substring of the input that is at most 0xFFFF bytes.
   */
  public static final String calculateSplit (String str)
  {
    int strLength = str.length();
    StringBuffer sbuf = new StringBuffer(20);
    // Where the current segments starts, as an index in 'str':
    int segmentStart = 0;
    int byteLength = 0; // Length in bytes of current segment so far.
    for (int i = 0;  i < strLength; i++)
      {
  char ch = str.charAt(i);
  int bytes = ch >= 0x0800 ? 3 : ch >= 0x0080 || ch == 0 ? 2 : 1;
  if (byteLength + bytes > 0xFFFF)
    {
      sbuf.append((char) (i - segmentStart));
      segmentStart = i;
      byteLength = 0;
    }
  byteLength += bytes;
      }
    sbuf.append((char) (strLength - segmentStart));
    return sbuf.toString();
  }

  /** Emit code to push the value of a constant String.
   * Uses CONSTANT_String and CONSTANT_Utf8 constant pool entries as needed.
   * Can handle Strings whose UTF8 length is greates than 0xFFFF bytes
   * (the limit of a CONSTANT_Utf8) by generating String concatenation.
   */
  public final void emitPushString (String str)
  {
    if (str == null)
      emitPushNull();
    else
      {
  int length = str.length();
  String segments = calculateSplit(str);
  int numSegments = segments.length();
  if (numSegments <= 1)
    emitPushConstant(getConstants().addString(str));
  else
    {
      if (numSegments == 2)
        {
    int firstSegment = (int) segments.charAt(0);
    emitPushString(str.substring(0, firstSegment));
    emitPushString(str.substring(firstSegment));
    Method concatMethod
      = Type.string_type.getDeclaredMethod("concat", 1);
    emitInvokeVirtual(concatMethod);
        }
      else
        {
    ClassType sbufType = ClassType.make("java.lang.StringBuffer");
    emitNew(sbufType);
    emitDup(sbufType);
    emitPushInt(length);
    Type[] args1 = { Type.int_type };
    emitInvokeSpecial(sbufType.getDeclaredMethod("<init>", args1));
    Type[] args2 = { Type.string_type };
    Method appendMethod
      = sbufType.getDeclaredMethod("append", args2);
    int segStart = 0;
    for (int seg = 0;  seg < numSegments;  seg++)
      {
        emitDup(sbufType);
        int segEnd = segStart + (int) segments.charAt(seg);
        emitPushString(str.substring(segStart, segEnd));
        emitInvokeVirtual(appendMethod);
        segStart = segEnd;
      }
    emitInvokeVirtual(Type.toString_method);
        }
      if (str == str.intern())
        emitInvokeVirtual(Type.string_type.getDeclaredMethod("intern", 0));
      return;
    }
  pushType(Type.string_type);
      }
  }

  /** Push a class constant pool entry.
   * This is only supported by JDK 1.5 and later. */
  public final void emitPushClass (String name)
  {
    emitPushConstant(getConstants().addClass(name));
    pushType(Type.java_lang_Class_type);
  }

  public void emitPushNull ()
  {
    reserve(1);
    put1(1);  // aconst_null
    pushType(Type.pointer_type);
  }

  public final void emitPushThis()
  {
    reserve(1);
    put1(42);  // aload_0
    pushType(getMethod().getDeclaringClass());
  }

  /** Emit code to push a constant primitive array.
   * @param value The array value that we want the emitted code to re-create.
   * @param arrayType The ArrayType that matches value.
   */
  public final void emitPushPrimArray(Object value, ArrayType arrayType)
  {
    Type elementType = arrayType.getComponentType();
    int len = java.lang.reflect.Array.getLength(value);
    emitPushInt(len);
    emitNewArray(elementType);
    char sig = elementType.getSignature().charAt(0);
    for (int i = 0;  i < len;  i++)
      {
  long ival = 0;  float fval = 0;  double dval = 0;
  switch (sig)
    {
    case 'J':
      ival = ((long[]) value)[i];
      if (ival == 0)
        continue;
      break;
    case 'I':
      ival = ((int[]) value)[i];
      if (ival == 0)
        continue;
      break;
    case 'S':
      ival = ((short[]) value)[i];
      if (ival == 0)
        continue;
      break;
    case 'C':
      ival = ((char[]) value)[i];
      if (ival == 0)
        continue;
      break;
    case 'B':
      ival = ((byte[]) value)[i];
      if (ival == 0)
        continue;
      break;
    case 'Z':
      ival = ((boolean[]) value)[i] ? 1 : 0;
      if (ival == 0)
        continue;
      break;
    case 'F':
      fval = ((float[]) value)[i];
      if (fval == 0.0)
        continue;
      break;
    case 'D':
      dval = ((double[]) value)[i];
      if (dval == 0.0)
        continue;
      break;
    }
  emitDup(arrayType);
  emitPushInt(i);
  switch (sig)
    {
    case 'Z':
    case 'C':
    case 'B':
    case 'S':
    case 'I':
      emitPushInt((int) ival);
      break;
    case 'J':
      emitPushLong(ival);
      break;
    case 'F':
      emitPushFloat(fval);
      break;
    case 'D':
      emitPushDouble(dval);
      break;
    }
  emitArrayStore(elementType);
      }
  }



  void emitNewArray (int type_code)
  {
    reserve(2);
    put1(188);  // newarray
    put1(type_code);
  }

  public final void emitArrayLength ()
  {
    if (! (popType() instanceof ArrayType))
      throw new Error( "non-array type in emitArrayLength" );
    
    reserve(1);
    put1(190);  // arraylength
    pushType(Type.int_type);
  }

  /* Returns an integer in the range 0 (for 'int') through 4 (for object
     reference) to 7 (for 'short') which matches the pattern of how JVM
     opcodes typically depend on the operand type. */

  private int adjustTypedOp  (char sig)
  {
    switch (sig)
      {
      case 'I':  return 0;  // int
      case 'J':  return 1;  // long
      case 'F':  return 2;  // float
      case 'D':  return 3;  // double
      default:   return 4;  // object
      case 'B':
      case 'Z':  return 5;  // byte or boolean
      case 'C':  return 6;  // char
      case 'S':  return 7;  // short
      }
  }

  private int adjustTypedOp  (Type type)
  {
    return adjustTypedOp(type.getSignature().charAt(0));
  }

  private void emitTypedOp (int op, Type type)
  {
    reserve(1);
    put1(op + adjustTypedOp(type));
  }

  private void emitTypedOp (int op, char sig)
  {
    reserve(1);
    put1(op + adjustTypedOp(sig));
  }

  /** Store into an element of an array.
   * Must already have pushed the array reference, the index,
   * and the new value (in that order).
   * Stack:  ..., array, index, value => ...
   */
  public void emitArrayStore (Type element_type)
  {
    popType();  // Pop new value
    popType();  // Pop index
    popType();  // Pop array reference
    emitTypedOp(79, element_type);
  }

  /** Load an element from an array.
   * Must already have pushed the array and the index (in that order):
   * Stack:  ..., array, index => ..., value */
  public void emitArrayLoad (Type element_type)
  {
    popType();  // Pop index
    popType();  // Pop array reference
    emitTypedOp(46, element_type);
    pushType(element_type);
  }

  /**
   * Invoke new on a class type.
   * Does not call the constructor!
   * @param type the desired new object type
   */
  public void emitNew (ClassType type)
  {
    reserve(3);
    put1(187); // new
    putIndex2(getConstants().addClass(type));
    pushType(type);
  }

  /** Compile code to allocate a new array.
   * The size should have been already pushed on the stack.
   * @param element_type type of the array elements
   */
  public void emitNewArray (Type element_type, int dims)
  {
    if (popType ().promote () != Type.int_type)
      throw new Error ("non-int dim. spec. in emitNewArray");

    if (element_type instanceof PrimType)
      {
  int code;
  switch (element_type.getSignature().charAt(0))
    {
    case 'B':  code =  8;  break;
    case 'S':  code =  9;  break;
    case 'I':  code = 10;  break;
    case 'J':  code = 11;  break;
    case 'F':  code =  6;  break;
    case 'D':  code =  7;  break;
    case 'Z':  code =  4;  break;
    case 'C':  code =  5;  break;
    default:   throw new Error("bad PrimType in emitNewArray");
    }
  emitNewArray(code);
      }
    else if (element_type instanceof ObjectType)
      {
  reserve(3);
  put1(189); // anewarray
  putIndex2(getConstants().addClass((ObjectType) element_type));
      }
    else if (element_type instanceof ArrayType)
    {
      reserve(4);
      put1(197); // multianewarray
      putIndex2 (getConstants ().addClass (new ArrayType (element_type)));
      if (dims < 1 || dims > 255)
  throw new Error ("dims out of range in emitNewArray");
      put1(dims);
      while (-- dims > 0) // first dim already popped
  if (popType ().promote () != Type.int_type)
    throw new Error ("non-int dim. spec. in emitNewArray");
    }
    else
      throw new Error ("unimplemented type in emitNewArray");

    pushType (new ArrayType (element_type));
  }

  public void emitNewArray (Type element_type)
  {
    emitNewArray (element_type, 1);
  }

  // We may want to deprecate this, because it depends on popType.
  private void emitBinop (int base_code)
  {
    Type type2 = popType().promote();
    Type type1_raw = popType();
    Type type1 = type1_raw.promote();
    if (type1 != type2 || ! (type1 instanceof PrimType))
      throw new Error ("non-matching or bad types in binary operation");
    emitTypedOp(base_code, type1);
    pushType(type1_raw);
  }

  private void emitBinop (int base_code, char sig)
  {
    popType();
    popType();
    emitTypedOp(base_code, sig);
    pushType(Type.signatureToPrimitive(sig));
  }

  private void emitBinop (int base_code, Type type)
  {
    popType();
    popType();
    emitTypedOp(base_code, type);
    pushType(type);
  }

  // public final void emitIntAdd () { put1(96); popType();}
  // public final void emitLongAdd () { put1(97); popType();}
  // public final void emitFloatAdd () { put1(98); popType();}
  // public final void emitDoubleAdd () { put1(99); popType();}

  public final void emitAdd(char sig) { emitBinop (96, sig); }
  public final void emitAdd(PrimType type) { emitBinop (96, type); }
  /** @deprecated */
  public final void emitAdd () { emitBinop (96); }

  public final void emitSub(char sig) { emitBinop (100, sig); }
  public final void emitSub(PrimType type) { emitBinop (100, type); }
  /** @deprecated */
  public final void emitSub () { emitBinop (100); }

  public final void emitMul () { emitBinop (104); }
  public final void emitDiv () { emitBinop (108); }
  public final void emitRem () { emitBinop (112); }
  public final void emitAnd () { emitBinop (126); }
  public final void emitIOr () { emitBinop (128); }
  public final void emitXOr () { emitBinop (130); }

  public final void emitShl () { emitShift (120); }
  public final void emitShr () { emitShift (122); }
  public final void emitUshr() { emitShift (124); }

  private void emitShift (int base_code)
  {
    Type type2 = popType().promote();
    Type type1_raw = popType();
    Type type1 = type1_raw.promote();

    if (type1 != Type.int_type && type1 != Type.long_type)
      throw new Error ("the value shifted must be an int or a long");

    if (type2 != Type.int_type)
      throw new Error ("the amount of shift must be an int");

    emitTypedOp(base_code, type1);
    pushType(type1_raw);
  }

  public final void emitNot(Type type)
  {
    emitPushConstant(1, type);
    emitAdd();
    emitPushConstant(1, type);
    emitAnd();
  }

  public void emitPrimop (int opcode, int arg_count, Type retType)
  {
    reserve(1);
    while (-- arg_count >= 0)
      popType();
    put1(opcode);
    pushType(retType);
  }

  void emitMaybeWide (int opcode, int index)
  {
    if (index >= 256)
      {
  put1(196); // wide
  put1(opcode);
  put2(index);
      }
    else
      {
  put1(opcode);
  put1(index);
      }
  }

  /**
   * Compile code to push the contents of a local variable onto the statck.
   * @param var The variable whose contents we want to push.
   */
  public final void emitLoad (Variable var)
  {
    if (var.dead())
      throw new Error("attempting to push dead variable");
    int offset = var.offset;
    if (offset < 0 || !var.isSimple())
      throw new Error ("attempting to load from unassigned variable "+var
           +" simple:"+var.isSimple()+", offset: "+offset);
    Type type = var.getType().promote();
    reserve(4);
    int kind = adjustTypedOp(type);
    if (offset <= 3)
      put1(26 + 4 * kind + offset);  // [ilfda]load_[0123]
    else
      emitMaybeWide(21 + kind, offset); // [ilfda]load
    pushType(var.getType());
  }

  public void emitStore (Variable var)
  {
    if (var.dead ())
      throw new Error("attempting to push dead variable");
    int offset = var.offset;
    if (offset < 0 || !var.isSimple ())
      throw new Error ("attempting to store in unassigned "+var
           +" simple:"+var.isSimple()+", offset: "+offset);
    Type type = var.getType().promote ();
    reserve(4);
    popType();
    int kind = adjustTypedOp(type);
    if (offset <= 3)
      put1(59 + 4 * kind + offset);  // [ilfda]store_[0123]
    else
      emitMaybeWide(54 + kind, offset); // [ilfda]store
  }


  public void emitInc (Variable var, short inc)
  {
    if (var.dead ())
      throw new Error ("attempting to increment dead variable");
    int offset = var.offset;
    if (offset < 0 || !var.isSimple ())
      throw new Error ("attempting to increment unassigned variable"+var.getName()
           +" simple:"+var.isSimple()+", offset: "+offset);
    Type type = var.getType().promote ();
    reserve(6);
    if (type != Type.int_type)
      throw new Error("attempting to increment non-int variable");

    boolean wide = offset > 255 || inc > 255 || inc < -256;

    if (wide)
    {
      put1(196); // wide
      put1(132); // iinc
      put2(offset);
      put2(inc);
    }
    else
    {
      put1(132); // iinc
      put1(offset);
      put1(inc);
    }
  }
  

  private final void emitFieldop (Field field, int opcode)
  {
    reserve(3);
    put1(opcode);
    putIndex2(getConstants().addFieldRef(field));
  }

  /** Compile code to get a static field value.
   * Stack:  ... => ..., value */

  public final void emitGetStatic(Field field)
  {
    pushType(field.type);
    emitFieldop (field, 178);  // getstatic
  }

  /** Compile code to get a non-static field value.
   * Stack:  ..., objectref => ..., value */

  public final void emitGetField(Field field)
  {
    popType();
    pushType(field.type);
    emitFieldop(field, 180);  // getfield
  }

  /** Compile code to put a static field value.
   * Stack:  ..., value => ... */

  public final void emitPutStatic (Field field)
  {
    popType();
    emitFieldop(field, 179);  // putstatic
  }

  /** Compile code to put a non-static field value.
   * Stack:  ..., objectref, value => ... */

  public final void emitPutField (Field field)
  {
    popType();
    popType();
    emitFieldop(field, 181);  // putfield
  }

  /** Comptes the number of stack words taken by a list of types. */
  private int words(Type[] types)
  {
    int res = 0;
    for (int i=types.length; --i >= 0; )
      if (types[i].size > 4)
       res+=2;
      else
       res++;
    return res;
  }

  public void emitInvokeMethod (Method method, int opcode)
  {
    reserve(opcode == 185 ? 5 : 3);
    int arg_count = method.arg_types.length;
    boolean is_invokestatic = opcode == 184;
    if (is_invokestatic != ((method.access_flags & Access.STATIC) != 0))
      throw new Error
  ("emitInvokeXxx static flag mis-match method.flags="+method.access_flags);
    if (!is_invokestatic)
      arg_count++;
    put1(opcode);  // invokevirtual, invokespecial, or invokestatic
    putIndex2(getConstants().addMethodRef(method));
    if (opcode == 185)  // invokeinterface
      {
  put1(words(method.arg_types)+1); // 1 word for 'this'
  put1(0);
      }
    while (--arg_count >= 0)
      popType();
    if (method.return_type.size != 0)
      pushType(method.return_type);
  }

  public void emitInvoke (Method method)
  {
    int opcode;
    if ((method.access_flags & Access.STATIC) != 0)
      opcode = 184;   // invokestatic
    else if (method.classfile.isInterface())
      opcode = 185;   // invokeinterface
    else if ("<init>".equals(method.getName()))
      opcode = 183;   // invokespecial
    else
      opcode = 182;   // invokevirtual
    emitInvokeMethod(method, opcode);
  }

  /** Compile a virtual method call.
   * The stack contains the 'this' object, followed by the arguments in order.
   * @param method the method to invoke virtually
   */
  public void emitInvokeVirtual (Method method)
  {
    emitInvokeMethod(method, 182);  // invokevirtual
  }

  public void emitInvokeSpecial (Method method)
  {
    emitInvokeMethod(method, 183);  // invokespecial
  }

  /** Compile a static method call.
   * The stack contains the the arguments in order.
   * @param method the static method to invoke
   */
  public void emitInvokeStatic (Method method)
  {
    emitInvokeMethod(method, 184);  // invokestatic
  }

  public void emitInvokeInterface (Method method)
  {
    emitInvokeMethod(method, 185);  // invokeinterface
  }
  
  final void emitTransfer (Label label, int opcode)
  {
    fixupAdd(FIXUP_TRANSFER, label);
    put1(opcode);
    PC += 2;
  }

  /** Compile an unconditional branch (goto).
   * @param label target of the branch (must be in this method).
   */
  public final void emitGoto (Label label)
  {
    fixupAdd(FIXUP_GOTO, label);
    reserve(3);
    put1(167);
    PC += 2;
    setUnreachable();
  }

  public final void emitJsr (Label label)
  {
    fixupAdd(FIXUP_JSR, label);
    reserve(3);
    put1(168);
    PC += 2;
  }

  public final void emitGotoIfCompare1 (Label label, int opcode)
  {
    popType();
    reserve(3);
    emitTransfer (label, opcode);
  }

  public final void emitGotoIfIntEqZero(Label label)
  { emitGotoIfCompare1(label, 153); }
  public final void emitGotoIfIntNeZero(Label label)
  { emitGotoIfCompare1(label, 154); }
  public final void emitGotoIfIntLtZero(Label label)
  { emitGotoIfCompare1(label, 155); }
  public final void emitGotoIfIntGeZero(Label label)
  { emitGotoIfCompare1(label, 156); }
  public final void emitGotoIfIntGtZero(Label label)
  { emitGotoIfCompare1(label, 157); }
  public final void emitGotoIfIntLeZero(Label label)
  { emitGotoIfCompare1(label, 158); }

  public final void emitGotoIfCompare2 (Label label, int logop)
  { 
    if( logop < 153 || logop > 158 )
      throw new Error ("emitGotoIfCompare2: logop must be one of ifeq...ifle");
    
    Type type2 = popType().promote();
    Type type1 = popType().promote();
    reserve(4);
    char sig1 = type1.getSignature().charAt(0);
    char sig2 = type2.getSignature().charAt(0);

    boolean cmpg = (logop == 155 || logop == 158); // iflt,ifle

    if (sig1 == 'I' && sig2 == 'I')
      logop += 6;  // iflt -> if_icmplt etc.
    else if (sig1 == 'J' && sig2 == 'J')
      put1(148);   // lcmp
    else if (sig1 == 'F' && sig2 == 'F')
      put1(cmpg ? 149 : 150);   // fcmpl/fcmpg
    else if (sig1 == 'D' && sig2 == 'D')
      put1(cmpg ? 151 : 152);   // dcmpl/dcmpg
    else if ((sig1 == 'L' || sig1 == '[')
       && (sig2 == 'L' || sig2 == '[')
       && logop <= 154)
      logop += 12; // ifeq->if_acmpeq, ifne->if_acmpne
    else
      throw new Error ("invalid types to emitGotoIfCompare2");

    emitTransfer (label, logop);
  }

  // binary comparisons
  /** @deprecated */
  public final void emitGotoIfEq (Label label, boolean invert)
  {
    emitGotoIfCompare2(label, invert ? 154 : 153);
  }

  /** Compile a conditional transfer if 2 top stack elements are equal. */
  public final void emitGotoIfEq (Label label)
  {
    emitGotoIfCompare2(label, 153);
  }

  /** Compile conditional transfer if 2 top stack elements are not equal. */
  public final void emitGotoIfNE (Label label)
  {
    emitGotoIfCompare2(label, 154);
  }

  public final void emitGotoIfLt(Label label)
  { emitGotoIfCompare2(label, 155); }
  public final void emitGotoIfGe(Label label)
  { emitGotoIfCompare2(label, 156); }
  public final void emitGotoIfGt(Label label)
  { emitGotoIfCompare2(label, 157); }
  public final void emitGotoIfLe(Label label)
  { emitGotoIfCompare2(label, 158); }


  /** Compile start of a conditional:
   *   <tt>if (!(<var>x</var> opcode 0)) ...</tt>.
   * The value of <var>x</var> must already have been pushed. */
  public final void emitIfCompare1 (int opcode)
  {
    IfState new_if = new IfState(this);
    if (popType().promote() != Type.int_type)
      throw new Error ("non-int type to emitIfCompare1");
    reserve(3);
    emitTransfer (new_if.end_label, opcode);
    new_if.start_stack_size = SP;
  }

  /** Compile start of conditional:  <tt>if (x != 0) ...</tt>.
   * Also use this if you have pushed a boolean value:  if (b) ... */
  public final void emitIfIntNotZero()
  {
    emitIfCompare1(153); // ifeq
  }

  /** Compile start of conditional:  <tt>if (x == 0) ...</tt>.
   * Also use this if you have pushed a boolean value:  if (!b) ... */
  public final void emitIfIntEqZero()
  {
    emitIfCompare1(154); // ifne
  }

  /** Compile start of conditional:  <tt>if (x <= 0)</tt>. */
  public final void emitIfIntLEqZero()
  {
    emitIfCompare1(157); // ifgt
  }

  /** Compile start of a conditional:  <tt>if (!(x opcode null)) ...</tt>.
   * The value of <tt>x</tt> must already have been pushed and must be of
   * reference type. */
  public final void emitIfRefCompare1 (int opcode)
  {
    IfState new_if = new IfState(this);
    if (! (popType() instanceof ObjectType))
      throw new Error ("non-ref type to emitIfRefCompare1");
    reserve(3);
    emitTransfer (new_if.end_label, opcode);
    new_if.start_stack_size = SP;
  }  
  
  /** Compile start of conditional:  if (x != null) */
  public final void emitIfNotNull()
  {
    emitIfRefCompare1(198); // ifnull
  }

  /** Compile start of conditional:  if (x == null) */
  public final void emitIfNull()
  {
    emitIfRefCompare1(199); // ifnonnull
  }  
  
  /** Compile start of a conditional:  if (!(x OPCODE y)) ...
   * The value of x and y must already have been pushed. */
  public final void emitIfIntCompare(int opcode)
  {
    IfState new_if = new IfState(this);
    popType();
    popType();
    reserve(3);
    emitTransfer(new_if.end_label, opcode);
    new_if.start_stack_size = SP;
  }

  /* Compile start of a conditional:  if (x < y) ... */
  public final void emitIfIntLt()
  {
    emitIfIntCompare(162);  // if_icmpge
  }

  /** Compile start of a conditional:  if (x != y) ...
   * The values of x and y must already have been pushed. */
  public final void emitIfNEq ()
  {
    IfState new_if = new IfState (this);
    emitGotoIfEq(new_if.end_label);
    new_if.start_stack_size = SP;
  }

  /** Compile start of a conditional:  if (x == y) ...
   * The values of x and y must already have been pushed. */
  public final void emitIfEq ()
  {
    IfState new_if = new IfState (this);
    emitGotoIfNE(new_if.end_label);
    new_if.start_stack_size = SP;
  }

  /** Compile start of a conditional:  if (x < y) ...
   * The values of x and y must already have been pushed. */
  public final void emitIfLt ()
  {
    IfState new_if = new IfState (this);
    emitGotoIfGe(new_if.end_label);
    new_if.start_stack_size = SP;
  }

  /** Compile start of a conditional:  if (x >= y) ...
   * The values of x and y must already have been pushed. */
  public final void emitIfGe ()
  {
    IfState new_if = new IfState (this);
    emitGotoIfLt(new_if.end_label);
    new_if.start_stack_size = SP;
  }

  /** Compile start of a conditional:  if (x > y) ...
   * The values of x and y must already have been pushed. */
  public final void emitIfGt ()
  {
    IfState new_if = new IfState (this);
    emitGotoIfLe(new_if.end_label);
    new_if.start_stack_size = SP;
  }

  /** Compile start of a conditional:  if (x <= y) ...
   * The values of x and y must already have been pushed. */
  public final void emitIfLe ()
  {
    IfState new_if = new IfState (this);
    emitGotoIfGt(new_if.end_label);
    new_if.start_stack_size = SP;
  }

  /** Emit a 'ret' instruction.
    * @param var the variable containing the return address */
  public void emitRet (Variable var)
  {
    int offset = var.offset;
    if (offset < 256)
      {
  reserve(2);
  put1(169);  // ret
  put1(offset);
      }
    else
      {
  reserve(4);
  put1(196);  // wide
  put1(169);  // ret
  put2(offset);
      }
  }

  public final void emitThen()
  {
    if_stack.start_stack_size = SP;
  }

  public final void emitIfThen ()
  {
    new IfState(this, null);
  }

  /** Compile start of else clause. */
  public final void emitElse ()
  {
    Label else_label = if_stack.end_label;
    Label end_label = new Label (this);
    if_stack.end_label = end_label;
    if (reachableHere ())
      {
  int growth = SP-if_stack.start_stack_size;
  if_stack.stack_growth = growth;
  if (growth > 0)
    {
      if_stack.then_stacked_types = new Type[growth];
      System.arraycopy (stack_types, if_stack.start_stack_size,
            if_stack.then_stacked_types, 0, growth);
    }
  else
    if_stack.then_stacked_types = new Type[0];  // ???
  emitGoto (end_label);
      }
    while (SP > if_stack.start_stack_size)
      popType();
    SP = if_stack.start_stack_size;
    if (else_label != null)
      else_label.define (this);
    if_stack.doing_else = true;    
  }

  /** Compile end of conditional. */
  public final void emitFi ()
  {
    boolean make_unreachable = false;
    if (! if_stack.doing_else)
      { // There was no 'else' clause.
  if (reachableHere ()
      && SP != if_stack.start_stack_size)
    throw new Error("at PC "+PC+" then clause grows stack with no else clause");
      }
    else if (if_stack.then_stacked_types != null)
      {
  int then_clause_stack_size
    = if_stack.start_stack_size + if_stack.stack_growth;
  if (! reachableHere ())
    {
      if (if_stack.stack_growth > 0)
        System.arraycopy (if_stack.then_stacked_types, 0,
        stack_types, if_stack.start_stack_size,
        if_stack.stack_growth);
      SP = then_clause_stack_size;
    }
  else if (SP != then_clause_stack_size)
    throw new Error("at PC "+PC+": SP at end of 'then' was " +
        then_clause_stack_size
        + " while SP at end of 'else' was " + SP);
      }
    else if (unreachable_here)
      make_unreachable = true;

    if (if_stack.end_label != null)
      if_stack.end_label.define (this);
    if (make_unreachable)
      setUnreachable();
    // Pop the if_stack.
    if_stack = if_stack.previous;
  }

  public final void emitConvert (Type from, Type to)
  {
    String to_sig = to.getSignature();
    String from_sig = from.getSignature();
    int op = -1;
    if (to_sig.length() == 1 || from_sig.length() == 1)
      {
  char to_sig0 = to_sig.charAt(0);
  char from_sig0 = from_sig.charAt(0);
  if (from_sig0 == to_sig0)
    return;
  if (from.size < 4)
    from_sig0 = 'I';
  if (to.size < 4)
    {
      emitConvert(from, Type.int_type);
      from_sig0 = 'I';
    }
  if (from_sig0 == to_sig0)
    return;
  switch (from_sig0)
    {
    case 'I':
      switch (to_sig0)
        {
          case 'B':  op = 145;  break;  // i2b
          case 'C':  op = 146;  break;  // i2c
          case 'S':  op = 147;  break;  // i2s
    case 'J':  op = 133;  break;  // i2l
    case 'F':  op = 134;  break;  // i2f
    case 'D':  op = 135;  break;  // i2d
        }
      break;
    case 'J':
      switch (to_sig0)
        {
    case 'I':  op = 136;  break;  // l2i
    case 'F':  op = 137;  break;  // l2f
    case 'D':  op = 138;  break;  // l2d
        }
      break;
    case 'F':
      switch (to_sig0)
        {
    case 'I':  op = 139;  break;  // f2i
    case 'J':  op = 140;  break;  // f2l
    case 'D':  op = 141;  break;  // f2d
        }
      break;
    case 'D':
      switch (to_sig0)
        {
    case 'I':  op = 142;  break;  // d2i
    case 'J':  op = 143;  break;  // d2l
    case 'F':  op = 144;  break;  // d2f
        }
      break;
    }
      }
    if (op < 0)
      throw new Error ("unsupported CodeAttr.emitConvert");
    reserve(1);
    popType();
    put1(op);
    pushType(to);
  }

  private void emitCheckcast (Type type, int opcode)
  {
    reserve(3);
    popType();
    put1(opcode);
    if (type instanceof ArrayType)
      {
  ArrayType atype = (ArrayType) type;
  CpoolUtf8 name = getConstants().addUtf8(atype.signature);
  putIndex2(getConstants().addClass(name));
      }
    else if (type instanceof ClassType)
      {
  putIndex2(getConstants().addClass((ClassType) type));
      }
    else
      throw new Error ("unimplemented type " + type
           + " in emitCheckcast/emitInstanceof");
  } 

  public static boolean castNeeded (Type top, Type required)
  {
    for (;;)
      {
        if (required instanceof ClassType
            && top instanceof ClassType
            && ((ClassType) top).isSubclass((ClassType) required))
          return false;
        else if (required instanceof ArrayType
                 && top instanceof ArrayType)
          {
            required = ((ArrayType) required).getComponentType();
            top = ((ArrayType) top).getComponentType();
            continue;
          }
        return true;
      }
  }

  public void emitCheckcast (Type type)
  {
    if (castNeeded(topType(), type))
      {
        emitCheckcast(type, 192);
        pushType(type);
      }
  }

  public void emitInstanceof (Type type)
  {
    emitCheckcast(type, 193);
    pushType(Type.boolean_type);
  }

  public final void emitThrow ()
  {
    popType();
    reserve(1);
    put1 (191);  // athrow
    setUnreachable();
  }

  public final void emitMonitorEnter ()
  {
    popType();
    reserve(1);
    put1 (194);  // monitorenter
  }

  public final void emitMonitorExit ()
  {
    popType();
    reserve(1);
    put1 (195);  // monitorexit
  }

  /** Call pending finalizer functions.
   * @param limit Only call finalizers more recent than this.
   */
  public void doPendingFinalizers (TryState limit)
  {
    TryState stack = try_stack;

    /* If a value is returned, it must be saved to a local variable,
       to prevent a verification error because of inconsistent stack sizes.
    */
    boolean saveResult = ! getMethod().getReturnType().isVoid();
    Variable result = null;

    while (stack != limit)
      {
  if (stack.finally_subr != null         // there is a finally block
      && stack.finally_ret_addr == null) // 'return' is not inside it
    {
      if (saveResult && result == null)
        {
    result = addLocal(topType());
    emitStore(result);
        }
      emitJsr(stack.finally_subr);
    }

  stack = stack.previous;
      }

    if (result != null)
      emitLoad(result);
    // We'd like to do freeLocal on the result Variable, but then we risk
    // it being re-used in a finalizer, which would trash its value.  We
    // don't have any convenient way to to do that (the pending Scope
    // mechanism is over-kill), we for now we just leak the Variable.
  }

  /**
   * Compile a method return.
   * If inside a 'catch' clause, first call 'finally' clauses.
   * The return value (unless the return type is void) must be on the stack,
   * and have the correct type.
   */
  public final void emitReturn ()
  {
    doPendingFinalizers(null);
    if (getMethod().getReturnType().size == 0)
      {
  reserve(1);
  put1(177); // return
      }
    else
      emitTypedOp (172, popType().promote());
    setUnreachable();
  }

  /** Add an exception handler. */
  public void addHandler (int start_pc, int end_pc,
        int handler_pc, int catch_type)
  {
    int index = 4 * exception_table_length;
    if (exception_table == null)
      {
  exception_table = new short[20];
      }
    else if (exception_table.length <= index)
      {
  short[] new_table = new short[2 * exception_table.length];
  System.arraycopy(exception_table, 0, new_table, 0, index);
  exception_table = new_table;
      }
    exception_table[index++] = (short) start_pc;
    exception_table[index++] = (short) end_pc;
    exception_table[index++] = (short) handler_pc;
    exception_table[index++] = (short) catch_type;
    exception_table_length++;
  }

  /** Add an exception handler. */
  public void addHandler (Label start_try, Label end_try,
        ClassType catch_type)
  {
    ConstantPool constants = getConstants();
    int catch_type_index;
    if (catch_type == null)
      catch_type_index = 0;
    else
      catch_type_index = constants.addClass(catch_type).index;
    fixupAdd(FIXUP_TRY, start_try);
    fixupAdd(FIXUP_CATCH, catch_type_index, end_try);
  }

  /** Beginning of code that has a cleanup handler.
   * This is similar to a try-finally, but the cleanup is only
   * done in the case of an exception.  Alternatively, the try clause
   * has to manually do the cleanup with code duplication.
   * Equivalent to: <code>try <var>body</var> catch (Throwable ex) { <var>cleanup</var>; throw ex; }</code>
   * Call <code>emitWithCleanupStart</code> before the <code><var>body</var></code>.
   */
  public void emitWithCleanupStart ()
  {
    int savedSP = SP;
    SP = 0;
    emitTryStart(false, null);
    SP = savedSP;
  }

  /** Called after a <code><var>body</var></code> that has a <code><var>cleanup</var></code> clause.
   * Followed by the <code><var>cleanup</var></code> code.
   */
  public void emitWithCleanupCatch (Variable catchVar)
  {
    emitTryEnd();
    Type[] savedTypes;
    if (SP > 0)
      {
        savedTypes = new Type[SP];
        System.arraycopy(stack_types, 0, savedTypes, 0, SP);
        SP = 0;
      }
    else
      savedTypes = null;
    try_stack.savedTypes = savedTypes;

    try_stack.saved_result = catchVar;
    int save_SP = SP;
    emitCatchStart(catchVar);
  }

  /** Called after generating a <code><var>cleanup</var></code> handler. */

  public void emitWithCleanupDone ()
  {
    Variable catchVar = try_stack.saved_result;
    try_stack.saved_result = null;
    if (catchVar != null)
      emitLoad(catchVar);
    emitThrow();
    emitCatchEnd();
    Type[] savedTypes = try_stack.savedTypes;
    emitTryCatchEnd();
    if (savedTypes != null)
      {
        SP = savedTypes.length;
        if (SP >= stack_types.length)
          stack_types = savedTypes;
        else
          System.arraycopy(savedTypes, 0, stack_types, 0, SP);
      }
    else
      SP = 0;
  }


  public void emitTryStart(boolean has_finally, Type result_type)
  {
    if (result_type != null && result_type.isVoid())
      result_type = null;
    Variable[] savedStack = null;
    if (result_type != null || SP > 0)
      pushScope();
    if (SP > 0)
      {
  savedStack = new Variable[SP];
  int i = 0;
  while (SP > 0)
    {
      Variable var = addLocal(topType());
      emitStore(var);
      savedStack[i++] = var;
    }
      }
    TryState try_state = new TryState(this);
    try_state.savedStack = savedStack;
    if (result_type != null)
      try_state.saved_result = addLocal(result_type);
    if (has_finally)
      try_state.finally_subr = new Label();
  }

  public void emitTryEnd()
  {
    if (try_stack.end_label == null)
      {
  if (try_stack.saved_result != null && reachableHere())
    emitStore(try_stack.saved_result);
  try_stack.end_label = new Label();
  if (reachableHere())
    {
      if (try_stack.finally_subr != null)
        emitJsr(try_stack.finally_subr);
      emitGoto(try_stack.end_label);
    }
  try_stack.end_try = getLabel();
      }
  }

  public void emitCatchStart(Variable var)
  {
    emitTryEnd();
    SP = 0;
    if (try_stack.try_type != null)
      emitCatchEnd();
    ClassType type = var == null ? null : (ClassType) var.getType();
    try_stack.try_type = type;
    addHandler(try_stack.start_try, try_stack.end_try, type);
    if (var != null)
      {
  pushType(type);
  emitStore(var);
      }
    else
      pushType(Type.throwable_type);
  }

  public void emitCatchEnd()
  {
    if (reachableHere())
      {
  if (try_stack.saved_result != null)
    emitStore(try_stack.saved_result);
  if (try_stack.finally_subr != null)
    emitJsr(try_stack.finally_subr);
  emitGoto(try_stack.end_label);
      }
    try_stack.try_type = null;
  }

  public void emitFinallyStart()
  {
    emitTryEnd();
    if (try_stack.try_type != null)
      emitCatchEnd();
    SP = 0;
    try_stack.end_try = getLabel();

    pushScope();
    Type except_type = Type.pointer_type;
    Variable except = addLocal(except_type);
    emitCatchStart(null);
    emitStore(except);
    emitJsr(try_stack.finally_subr);
    emitLoad(except);
    emitThrow();
    
    try_stack.finally_subr.define(this);
    Type ret_addr_type = Type.pointer_type;
    try_stack.finally_ret_addr = addLocal(ret_addr_type);
    pushType(ret_addr_type);
    emitStore(try_stack.finally_ret_addr);
  }

  public void emitFinallyEnd()
  {
    emitRet(try_stack.finally_ret_addr);
    setUnreachable();
    popScope();
    try_stack.finally_subr = null;
  }

  public void emitTryCatchEnd()
  {
    if (try_stack.finally_subr != null)
      emitFinallyEnd();
    try_stack.end_label.define(this);
    Variable[] vars = try_stack.savedStack;
    if (vars != null)
      {
  for (int i = vars.length;  --i >= 0; )
    {
      Variable v = vars[i];
      if (v != null) {
        emitLoad(v);
      }
    }
      }
    if (try_stack.saved_result != null)
  emitLoad(try_stack.saved_result);
    if (try_stack.saved_result != null || vars != null)
  popScope();
    try_stack = try_stack.previous;
  }

  public final TryState getCurrentTry ()
  {
    return try_stack;
  }

  public final boolean isInTry()
  {
    // This also return true if we're in  a catch clause, but that is
    // good enough for now.
    return try_stack != null;
  }

  /** Compile a tail-call to position 0 of the current procedure.
   * @param pop_args if true, copy argument registers (except this) from stack.
   * @param scope Scope whose start we jump back to. */
  public void emitTailCall (boolean pop_args, Scope scope)
  {
    if (pop_args)
      {
  Method meth = getMethod();
  int arg_slots = ((meth.access_flags & Access.STATIC) != 0) ? 0 : 1;
  for (int i = meth.arg_types.length;  --i >= 0; )
    arg_slots += meth.arg_types[i].size > 4 ? 2 : 1;
  for (int i = meth.arg_types.length;  --i >= 0; )
    {
      arg_slots -= meth.arg_types[i].size > 4 ? 2 : 1;
      emitStore(locals.used [arg_slots]);
    }
      }
    emitGoto(scope.start);
  }

  public void processFixups ()
  {
    if (fixup_count == 0)
      return;

    // For each label, set it to its maximum limit, assuming all
    // fixups causes the code the be expanded.  We need a prepass
    // for this, since FIXUP_MOVEs can cause code to be reordered.
    // Also, convert each FIXUP_MOVE_TO_END to FIXUP_MOVE.

    int delta = 0;
    int instruction_tail = fixup_count;
    fixupAdd(CodeAttr.FIXUP_MOVE, 0, null);

  loop1:
   for (int i = 0;  ;  )
      {
  int offset = fixup_offsets[i];
  int kind = offset & 15;
  offset >>= 4;
  Label label = fixup_labels[i];
  switch (kind)
    {
    case FIXUP_TRY:
    case FIXUP_LINE_PC:
      i++;
    case FIXUP_NONE:
    case FIXUP_CASE:
    case FIXUP_DELETE3:
      break;
    case FIXUP_DEFINE:
      label.position += delta;
      break;
    case FIXUP_SWITCH:
      delta += 3;  // May need to add up to 3 padding bytes.
      break;
    case FIXUP_GOTO:
      // The first test fails in this case:  GOTO L2; L1: L2:  FIXME
      if (label.first_fixup == i + 1
    && fixupOffset(i+1) == offset + 3)
        {
    // Optimize: GOTO L; L:
    fixup_offsets[i] = (offset << 4) | FIXUP_DELETE3;
    fixup_labels[i] = null;
    delta -= 3;
    break;
        }
      // ... else fall through ...
    case FIXUP_JSR:
      if (PC >= 0x8000)
        delta += 2;  // May need to convert goto->goto_w, jsr->jsr_w.
      break;
    case FIXUP_TRANSFER:
      if (PC >= 0x8000)
        delta += 5;  // May need to add a goto_w.
      break;
    case FIXUP_MOVE_TO_END:
      fixup_labels[instruction_tail] = fixup_labels[i+1];
      instruction_tail = offset;
      // ... fall through ...
    case FIXUP_MOVE:
      int cur_pc = ((i+1) >= fixup_count ? PC
        : fixupOffset(fixup_labels[i+1].first_fixup));
      fixup_offsets[i] = (cur_pc << 4) | FIXUP_MOVE;
      if (label == null)
        break loop1;
      else
        {
    i = label.first_fixup;
    int next_pc = fixupOffset(i);
    delta = (cur_pc + delta) - next_pc;
    continue;
        }
    default:
      throw new Error("unexpected fixup");
    }
  i++;
      }
    // Next a loop to fix the position of each label, and calculate
    // the exact number of code bytes.

    // Number of bytes to be inserted or (if negative) removed, so far.
    int new_size = PC;
    // Current delta between final PC and offset in generate code array.
    delta = 0;
  loop2:
    for (int i = 0;  i < fixup_count;  )
      {
  int offset = fixup_offsets[i];
  int kind = offset & 15;
  Label label = fixup_labels[i];
  if (label != null && label.position < 0)
    throw new Error ("undefined label "+label);
  while (label != null
         && kind >= FIXUP_GOTO && kind <= FIXUP_TRANSFER2
         && label.first_fixup + 1 < fixup_count
         && (fixup_offsets[label.first_fixup + 1]
       == ((fixup_offsets[label.first_fixup] & 15) | FIXUP_GOTO)))
    {
      // Optimize  JUMP L; ... L:  GOTO X
      // (where the JUMP is any GOTO or other transfer)
      // by changing the JUMP L to JUMP X.
      label = fixup_labels[label.first_fixup + 1];
      fixup_labels[i] = label;
    }
  offset = offset >> 4;
  switch (kind)
    {
    case FIXUP_TRY:
    case FIXUP_LINE_PC:
      i++;
    case FIXUP_NONE:
    case FIXUP_CASE:
      break;
    case FIXUP_DELETE3:
      delta -= 3;
      new_size -= 3;
      break;
    case FIXUP_DEFINE:
      label.position = offset + delta;
      break;
    case FIXUP_SWITCH:
      int padding = 3 - (offset+delta) & 3;
      delta += padding;
      new_size += padding;
      break;
    case FIXUP_GOTO:
    case FIXUP_JSR:
    case FIXUP_TRANSFER:
      int rel = label.position - (offset+delta);
      if ((short) rel == rel)
        {
    fixup_offsets[i] = (offset << 4) | FIXUP_TRANSFER2;
        }
      else
        {
    delta += kind == FIXUP_TRANSFER ? 5 : 2;  // need goto_w
    new_size += kind == FIXUP_TRANSFER ? 5 : 2;  // need goto_w
        }
      break;
    case FIXUP_MOVE:
      if (label == null)
        break loop2;
      else
        {
    i = label.first_fixup;
    int next_pc = fixupOffset(i);
    delta = (offset + delta) - next_pc;
    continue;
        }
    default:
      throw new Error("unexpected fixup");
    }
  i++;
      }

    byte[] new_code = new byte[new_size];
    int new_pc = 0;
    int next_fixup_index = 0;
    int next_fixup_offset = fixupOffset(0);
  loop3:
    for (int old_pc = 0;  ;  )
      {
  if (old_pc < next_fixup_offset)
    {
    new_code[new_pc++] = code[old_pc++];
    }
  else
    {
      int kind = fixup_offsets[next_fixup_index] & 15;
      Label label = fixup_labels[next_fixup_index];
      switch (kind)
        {
        case FIXUP_NONE:
        case FIXUP_DEFINE:
    break;
        case FIXUP_DELETE3:
    old_pc += 3;
    break;
        case FIXUP_TRANSFER2:
    delta = label.position - new_pc;
    new_code[new_pc++] = code[old_pc];
    new_code[new_pc++] = (byte) (delta >> 8);
    new_code[new_pc++] = (byte) (delta & 0xFF);
    old_pc += 3;
    break;
        case FIXUP_GOTO:
        case FIXUP_JSR:
        case FIXUP_TRANSFER:
    delta = label.position - new_pc;
    byte opcode = code[old_pc];
    if (kind == FIXUP_TRANSFER)
      {
        // convert: IF_xxx L to IF_NOT_xxx Lt; GOTO L; Lt:
        opcode = invert_opcode(opcode);
        new_code[new_pc++] = opcode;
        new_code[new_pc++] = 0;
        new_code[new_pc++] = 8;  // 8 byte offset to Lt.
        opcode = (byte) 200;  // goto_w
      }
    else
      {
        // Change goto to goto_w; jsr to jsr_w:
        opcode = (byte) (opcode + (200-167));
      }
    new_code[new_pc++] = opcode;
    new_code[new_pc++] = (byte) (delta >> 24);
    new_code[new_pc++] = (byte) (delta >> 16);
    new_code[new_pc++] = (byte) (delta >> 8);
    new_code[new_pc++] = (byte) (delta & 0xFF);
    old_pc += 3;
    break;
        case FIXUP_SWITCH:
    int padding = 3 - new_pc & 3;
    int switch_start = new_pc;
    new_code[new_pc++] = code[old_pc++];
    while (--padding >= 0)
      new_code[new_pc++] = 0;
    while (next_fixup_index < fixup_count
           && fixupKind(next_fixup_index + 1) == FIXUP_CASE)
      {
        next_fixup_index++;
        int offset = fixupOffset(next_fixup_index);
        while (old_pc < offset)
          new_code[new_pc++] = code[old_pc++];
        delta = (fixup_labels[next_fixup_index].position
           - switch_start);
        new_code[new_pc++] = (byte) (delta >> 24);
        new_code[new_pc++] = (byte) (delta >> 16);
        new_code[new_pc++] = (byte) (delta >> 8);
        new_code[new_pc++] = (byte) (delta & 0xFF);
        old_pc += 4;
      }
    break;
        case FIXUP_TRY:
    addHandler(fixup_labels[next_fixup_index].position,
         fixup_labels[next_fixup_index+1].position,
         new_pc,
         fixupOffset(next_fixup_index+1));
    next_fixup_index++;
    break;
        case FIXUP_LINE_PC:
    if (lines == null)
      lines = new LineNumbersAttr(this);
    next_fixup_index++;
    lines.put(fixupOffset(next_fixup_index), new_pc);
    break;
        case FIXUP_MOVE:
    if (label == null)
      break loop3;
    else
      {
        next_fixup_index = label.first_fixup;
        old_pc = fixupOffset(next_fixup_index);
        next_fixup_offset = old_pc;
        if (label.position != new_pc)
          throw new Error("bad pc");
        continue;
      }
        default:
    throw new Error("unexpected fixup");
        }
      next_fixup_index++;
      next_fixup_offset = fixupOffset(next_fixup_index);
    }
      }
    if (new_size != new_pc)
      throw new Error("PC confusion new_pc:"+new_pc+" new_size:"+new_size);
    PC = new_size;
    code = new_code;
    fixup_count = 0;
    fixup_labels = null;
    fixup_offsets = null;
  }

  public void assignConstants (ClassType cl)
  {
    if (locals != null && locals.container == null && ! locals.isEmpty())
      locals.addToFrontOf(this);
    processFixups();
    if (instructionLineMode)
      {
        // A kludge to low-level debugging:
        // Define a "line number" for each instrction.
        if (lines == null)
          lines = new LineNumbersAttr(this);
        lines.linenumber_count = 0;
        int codeLen = getCodeLength();
        for (int i = 0;  i < codeLen;  i++)
          lines.put(i, i);
      }
    super.assignConstants(cl);
    Attribute.assignConstants(this, cl);
  }

  public final int getLength()
  {
    return 12 + getCodeLength() + 8 * exception_table_length
      + Attribute.getLengthAll(this);
  }

  public void write (DataOutputStream dstr) throws java.io.IOException
  {
    dstr.writeShort (max_stack);
    dstr.writeShort (max_locals);
    dstr.writeInt (PC);
    dstr.write (code, 0, PC);

    dstr.writeShort (exception_table_length);
    int count = exception_table_length;
    for (int i = 0;  --count >= 0;  i += 4)
      {
  dstr.writeShort(exception_table[i]);
  dstr.writeShort(exception_table[i+1]);
  dstr.writeShort(exception_table[i+2]);
  dstr.writeShort(exception_table[i+3]);
      }

    Attribute.writeAll(this, dstr);
  }

  public void print (ClassTypeWriter dst) 
  {
    dst.print("Attribute \"");
    dst.print(getName());
    dst.print("\", length:");
    dst.print(getLength());
    dst.print(", max_stack:");
    dst.print(max_stack);
    dst.print(", max_locals:");
    dst.print(max_locals);
    dst.print(", code_length:");
    int length = getCodeLength();
    dst.println(length);
    disAssemble(dst, 0, length);
    if (exception_table_length > 0)
      {
  dst.print("Exceptions (count: ");
  dst.print(exception_table_length);
  dst.println("):");
  int count = exception_table_length;
  for (int i = 0;  --count >= 0;  i += 4)
    {
      dst.print("  start: ");
      dst.print(exception_table[i] & 0xffff);
      dst.print(", end: ");
      dst.print(exception_table[i+1] & 0xffff);
      dst.print(", handler: ");
      dst.print(exception_table[i+2] & 0xffff);
      dst.print(", type: ");
      int catch_type_index = exception_table[i+3] & 0xffff;
      if (catch_type_index == 0)
        dst.print("0 /* finally */");
      else
        {
    dst.printOptionalIndex(catch_type_index);
    dst.printConstantTersely(catch_type_index, ConstantPool.CLASS);
        }
      dst.println();
    }
      }
    dst.printAttributes(this);
  }

  /* DEBUGGING:
  public void disAssembleWithFixups (ClassTypeWriter dst)
  {
    if (fixup_count == 0)
      {
  disAssemble(dst, 0, PC);
  return;
      }
    int prev_pc = 0;
    for (int i = 0;  i < fixup_count; )
      {
  int offset = fixup_offsets[i];
  int kind = offset & 15;
  Label label = fixup_labels[i];
  offset = offset >> 4;
  int pc = offset;
  if (kind == FIXUP_MOVE || kind == FIXUP_MOVE_TO_END)
    pc = (i+1 >= fixup_count) ? PC : fixup_offsets[i+1] >> 4;
  disAssemble(dst, prev_pc, offset);
  prev_pc = pc;
  dst.print("fixup#");  dst.print(i);
  dst.print(" @");  dst.print(offset);
  switch (kind)
    {
    case FIXUP_NONE:
      dst.println(" NONE");
      break;
    case FIXUP_DEFINE:
      dst.print(" DEFINE ");
      dst.println(label);
      break;
    case FIXUP_SWITCH:
      dst.println(" SWITCH");
      break;
    case FIXUP_CASE:
      dst.println(" CASE");
      break;
    case FIXUP_GOTO:
      dst.print(" GOTO ");
      dst.println(label);
      break;
    case FIXUP_TRANSFER:
      dst.print(" TRANSFER ");
      dst.println(label);
      break;
    case FIXUP_TRANSFER2:
      dst.print(" TRANSFER2 ");
      dst.println(label);
      break;
    case FIXUP_DELETE3:
      dst.println(" DELETE3");
      break;
    case FIXUP_MOVE:
      dst.print(" MOVE ");
      dst.println(label);
      break;
    case FIXUP_MOVE_TO_END:
      dst.print(" MOVE_TO_END ");
      dst.println(label);
      break;
    case FIXUP_TRY:
      dst.print(" TRY start: ");
      dst.println(label);
      i++;
      dst.print(" - end: ");
      dst.print(fixup_labels[i]);
      dst.print(" type: ");
      dst.println(fixup_offsets[i] >> 4);
      break;
    case FIXUP_LINE_PC:
      dst.print(" LINE ");
      i++;
      dst.println(fixup_offsets[i] >> 4);
      break;
    default:
      dst.println(" kind:"+fixupKind(i)+" offset:"+fixupOffset(i)+" "+fixup_labels[i]);
    }
  i++;
      }
  }
  */

  public void disAssemble (ClassTypeWriter dst, int start, int limit)
  {
    boolean wide = false;
    for (int i = start;  i < limit; )
      {
  int oldpc = i++;
  int op = code[oldpc] & 0xff;
  String str = Integer.toString(oldpc);
  int printConstant = 0;
  int j = str.length();
  while (++j <= 3) dst.print(' ');
  dst.print(str);
  dst.print(": ");
  // We do a rough binary partition of the opcodes.
  if (op < 120)
    {
      if (op < 87)
        {
    if (op < 3)  print("nop;aconst_null;iconst_m1;", op, dst);
    else if (op < 9) { dst.print("iconst_");  dst.print(op-3); }
    else if (op < 16) // op >= 9 [lconst_0] && op <= 15 [dconst_1]
      {
        char typ;
        if (op < 11) { typ = 'l';  op -= 9; }
        else if (op < 14) { typ = 'f';  op -= 11; }
        else { typ = 'd';  op -= 14; }
        dst.print(typ);  dst.print("const_");  dst.print(op);
      }
    else if (op < 21)
      {
        if (op < 18)  // op >= 16 [bipush] && op <= 17 [sipush]
          {
      print("bipush ;sipush ;", op-16, dst);
      int constant;
      if (op == 16)  constant = code[i++];
      else { constant = (short) readUnsignedShort(i);  i+=2;}
      dst.print(constant);
          }
        else // op >= 18 [ldc] && op <= 20 [ldc2_w]
          {
      printConstant = op == 18 ? 1 : 2;
      print("ldc;ldc_w;ldc2_w;", op-18, dst);
          }
      }
    else // op >= 21 && op < 87
      {
        String load_or_store;
        if (op < 54) { load_or_store = "load"; }
        else { load_or_store = "store"; op -=(54-21); }
        int index;  // -2 if array op;  -1 if index follows
        if (op < 26) { index = -1; op -= 21; }
        else if (op < 46) { op -= 26;  index = op % 4;  op >>= 2; }
        else { index = -2; op -= 46; }
        dst.print("ilfdabcs".charAt(op));
        if (index == -2) dst.write('a');
        dst.print(load_or_store);
        if (index >= 0) { dst.write('_');  dst.print(index); }
        else if (index == -1)
          {
      if (wide) { index = readUnsignedShort(i); i += 2; }
      else { index = code[i] & 0xff; i++; }
      wide = false;
      dst.print(' ');
      dst.print(index);
          }
      }
        }
      else // op >= 87 && op < 120
        {
    if (op < 96)
      print("pop;pop2;dup;dup_x1;dup_x2;dup2;dup2_x1;dup2_x2;swap;"
      , op-87, dst);
    else // op >= 96 [iadd] && op <= 119 [dneg]
      {
        dst.print("ilfda".charAt((op-96) % 4));
        print("add;sub;mul;div;rem;neg;", (op-96)>>2, dst);
      }
        }
    }
  else // op >= 120
    {
      if (op < 170)
        {
    if (op < 132) // op >= 120 [ishl] && op <= 131 [lxor]
      {
        dst.print((op & 1) == 0 ? 'i' : 'l');
        print("shl;shr;ushr;and;or;xor;", (op-120)>>1, dst);
      }
    else if (op == 132) // iinc
      {
        int var_index;
        int constant;
        dst.print("iinc");
        if (! wide)
          {
      var_index = 0xff & code[i++];
      constant = code[i++];
          }
        else
          {
      var_index = readUnsignedShort(i);
      i += 2;
      constant = (short) readUnsignedShort(i);
      i += 2;
      wide = false;
          }
        dst.print(' ');  dst.print(var_index);
        dst.print(' ');  dst.print(constant);
      }
    else if (op < 148) // op >= 133 [i2l] && op <= 147 [i2s]
      {
        dst.print("ilfdi".charAt((op-133) / 3));
        dst.print('2');
        dst.print("lfdifdildilfbcs".charAt(op-133));
      }
    else if (op < 153) // op >= 148 [lcmp] && op <= 152 [dcmpg]
      print("lcmp;fcmpl;fcmpg;dcmpl;dcmpg;", op-148, dst);
    else if (op < 169)
      {
        if (op < 159)
          {
      dst.print("if");
      print("eq;ne;lt;ge;gt;le;", op-153, dst);
          }
        else if (op < 167)
          {
      if (op < 165) { dst.print("if_icmp"); }
      else { dst.print("if_acmp"); op -= 165-159; }
      print("eq;ne;lt;ge;gt;le;", op-159, dst);
          }
        else
          print("goto;jsr;", op-167, dst);
        int delta = (short) readUnsignedShort(i);
        i += 2;
        dst.print(' ');  dst.print(oldpc+delta);
      }
    else
      {
        int index;
        dst.print("ret ");
        if (wide) { index = readUnsignedShort(i); index += 2; }
        else { index = code[i] & 0xff; i++; }
        wide = false;
        dst.print(index);
      }
        }
      else
        {
    if (op < 172) //  [tableswitch] or [lookupswitch]
      {
        if (fixup_count == 0)
          i = (i + 3) & ~3; // skip 0-3 byte padding.
        int code_offset = readInt(i);  i += 4;
        if (op == 170)
          {
      dst.print("tableswitch");
      int low = readInt(i);  i += 4;
      int high = readInt(i);  i += 4;
      dst.print(" low: "); dst.print(low);
      dst.print(" high: "); dst.print(high);
      dst.print(" default: "); dst.print(oldpc+code_offset);
      for (;  low <= high;  low++)
        {
          code_offset = readInt(i);  i += 4;
          dst.println();
          dst.print("  ");  dst.print(low);
          dst.print(": ");  dst.print(oldpc + code_offset); 
        }
          }
        else
          {
      dst.print("lookupswitch");
      int npairs = readInt(i);  i += 4;
      dst.print(" npairs: "); dst.print(npairs);
      dst.print(" default: "); dst.print(oldpc+code_offset);
      while (--npairs >= 0)
        {
          int match = readInt(i);  i += 4;
          code_offset = readInt(i);  i += 4;
          dst.println();
          dst.print("  ");  dst.print(match);
          dst.print(": ");  dst.print(oldpc + code_offset); 
        }
          }
      }
    else if (op < 178) // op >= 172 [ireturn] && op <= 177 [return]
      {
        if (op < 177) dst.print("ilfda".charAt(op-172));
        dst.print("return");
      }
    else if (op < 182) // op >= 178 [getstatic] && op <= 181 [putfield]
      {
        print("getstatic;putstatic;getfield;putfield;", op-178, dst);
        printConstant = 2;
      }
    else if (op < 185) // op >= 182 && op <= 185 [invoke*]
      {
        dst.print("invoke");
        print("virtual;special;static;", op-182, dst);
        printConstant = 2;
      }
    else if (op == 185) // invokeinterface
      {
        dst.print("invokeinterface (");
        int index = readUnsignedShort(i);
        i += 2;
        int args = 0xff & code[i];
        i += 2;
        dst.print(args + " args)");
        dst.printConstantOperand(index);
      }
    else if (op < 196)
      {
        print("186;new;newarray;anewarray;arraylength;athrow;checkcast;instanceof;monitorenter;monitorexit;", op-186, dst);
        if (op == 187 || op == 189 || op == 192 || op == 193)
          printConstant = 2;
        else if (op == 188)  // newarray
          {
      int type = code[i++];
      dst.print(' ');
      if (type >= 4 && type <= 11)
        print("boolean;char;float;double;byte;short;int;long;",
        type-4, dst);
      else
        dst.print(type);
          }
      
      }
    else if (op == 196) // wide
      {
        dst.print("wide");
        wide = true;
      }
    else if (op == 197)
      {
        dst.print("multianewarray");
        int index = readUnsignedShort(i);
        i += 2;
        dst.printConstantOperand(index);
        int dims = 0xff & code[i++];
        dst.print(' ');
        dst.print(dims);
      }
    else if (op < 200)
      {
        print("ifnull;ifnonnull;", op-198, dst);
        int delta = (short) readUnsignedShort(i);
        i += 2;
        dst.print(' ');  dst.print(oldpc+delta);
      }
    else if (op < 202)
      {
        print("goto_w;jsr_w;", op-200, dst);
        int delta = readInt(i);
        i += 4;
        dst.print(' ');  dst.print(oldpc+delta);
      }
    else
      dst.print(op);
        }
    }
  if (printConstant > 0)
    {
      int index;
      if (printConstant == 1) index = 0xff & code[i++];
      else { index = readUnsignedShort(i);  i += 2; }
      dst.printConstantOperand(index);
    }
  dst.println();
      }
  }

  private int readUnsignedShort(int offset)
  {
    return ((0xff & code[offset]) << 8) | (0xff & code[offset+1]);
  }

  private int readInt(int offset)
  {
    return (readUnsignedShort(offset) << 16) | readUnsignedShort(offset+2);
  }

  /*
  public saveStack (ClassType into)
  {
    Field[] flds = new Field[SP];
    while (SP > 0)
      {
  Field fld = ?;
  emitStore(fld);
  flds[SP...]
      }
  }
  */

  /* Print the i'th ';'-delimited substring of str on dst. */
  private void print (String str, int i, PrintWriter dst)
  {
    int last = 0;
    int pos = -1;
    for (; i >= 0; i--)
      {
  last = ++pos;
  pos = str.indexOf(';', last);
      }
    dst.write(str, last, pos-last);
  }

  /** Return an object encapsulating the type state of the JVM stack. */
  public Type[] saveStackTypeState(boolean clear)
  {
    if (SP == 0)
      return null;
    Type[] typeState = new Type[SP];
    System.arraycopy(stack_types, 0, typeState, 0, SP);
    if (clear)
      SP = 0;
    return typeState;
  }

  /** Restore a type state as saved by saveStackTypeState. */
  public void restoreStackTypeState (Type[] save)
  {
    if (save == null)
      SP = 0;
    else
      {
  SP = save.length;
  System.arraycopy(save, 0, stack_types, 0, SP);
      }
  }

  public int beginFragment (Label start, Label after)
  {
    int i = fixup_count;
    fixupAdd(FIXUP_MOVE_TO_END, after);
    start.define(this);
    return i;
  }

  /** End a fragment.
   * @param cookie the return value from the previous beginFragment.
   */
  public void endFragment (int cookie)
  {
    fixup_offsets[cookie] = (fixup_count << 4) | FIXUP_MOVE_TO_END;
    Label after = fixup_labels[cookie];
    fixupAdd(FIXUP_MOVE, 0, null);
    after.define(this);
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.