Logo Search packages:      
Sourcecode: kaffe version File versions  Download package

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.bytecodecvssnap;
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
  */

00021 public class CodeAttr extends Attribute implements AttrContainer
{
  Attribute attributes;
00024   public final Attribute getAttributes () { return attributes; }
00025   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.  */
00042   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. */
00055   static final int FIXUP_NONE = 0;
  /** The definition of a label. */
00057   static final int FIXUP_DEFINE = 1;
  /** The offset points to a tableswitch/lookupswitch - handle padding. */
00059   static final int FIXUP_SWITCH = 2;
  /** The offset contains a label relative to the previous FIXUP_SWITCH. */
00061   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. */
00065   static final int FIXUP_GOTO = 4;
  /** The offset points to a jsr instruction. */
00067   static final int FIXUP_JSR = 5;
  /** The offset points to a conditional transfer (if_xxx) instruction. */
00069   static final int FIXUP_TRANSFER = 6;
  /** A FIXUP_GOTO_, FIXUP_JSR, or FIXUP_TRANSFER that uses a 2-byte offset. */
00071   static final int FIXUP_TRANSFER2 = 7;
  /** The offsets points to 3 bytes that should be deleted. */
00073   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. */
00080   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.) */
00086   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. */
00090   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. */
00094   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. */
00097   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. */
00100   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 codet a target comes here, nstead of the following instructions. */
00107   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. */
00116   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;
  }

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

  /** The stack of currently active try statements. */
00158   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. */
00175   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. */
00180   public int getMaxStack() { return max_stack; }
  /** Get the maximum number of local variable words in this method. */
00182   public int getMaxLocals() { return max_locals; }

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

  /** Get the code (instruction bytes) of this method.
    * Does not make a copy. */
00191   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). */
00195   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.) */
00200   public void setCodeLength(int len) { PC = len;}
  /** Set the current lengthof the code (instruction bytes) of this method. */
00202   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). */
00224   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
   */
00236   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
   */
00246   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
   */
00257   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
   */
00323   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().
   */
00350   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. */
00383   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. */
00394   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]
   */
00416   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
   */
00484   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. */
00524   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).
   */
00534   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. */
00549   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. */
00558   public Variable addLocal (Type type, String name)
  {
    return locals.current_scope.addVariable (this, type, name);
  }

  /** Call addLocal for parameters (as implied by method type). */
00564   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.
   */
00726   public 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.
   */
00754   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. */
00808   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.
   */
00832   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 => ...
   */
00971   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 */
00982   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
   */
00995   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
   */
01007   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 */
01094   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 */
01099   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.
   */
01164   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 */

01242   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 */

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

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

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

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

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

  /** Comptes the number of stack words taken by a list of types. */
01278   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
   */
01330   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
   */
01344   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).
   */
01364   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 */
01434   public final void emitGotoIfEq (Label label, boolean invert)
  {
    emitGotoIfCompare2(label, invert ? 154 : 153);
  }

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

  /** Compile conditional transfer if 2 top stack elements are not equal. */
01446   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. */
01464   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) ... */
01476   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) ... */
01483   public final void emitIfIntEqZero()
  {
    emitIfCompare1(154); // ifne
  }

  /** Compile start of conditional:  <tt>if (x <= 0)</tt>. */
01489   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. */
01497   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) */
01508   public final void emitIfNotNull()
  {
    emitIfRefCompare1(198); // ifnull
  }

  /** Compile start of conditional:  if (x == null) */
01514   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. */
01521   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. */
01539   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. */
01548   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. */
01557   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. */
01566   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. */
01575   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. */
01584   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 */
01593   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. */
01622   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. */
01650   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 void emitCheckcast (Type type)
  {
    if (type instanceof ClassType)
      {
      Type top = topType();
      if (top instanceof ClassType
          && ((ClassType) top).isSubclass((ClassType) type))
        return;
      }
    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.
   */
01818   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.
   */
01858   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. */
01872   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. */
01894   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>.
   */
01914   public void emitWithCleanupStart ()
  {
    new TryState(this);
  }

  /** 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.
   */
01922   public void emitWithCleanupCatch (Variable catchVar)
  {
    emitTryEnd();
    try_stack.saved_result = catchVar;
    int save_SP = SP;
    emitCatchStart(catchVar);
    // Don't trash stack_types, and set things up so the SP has the
    // right value after emitWithCleanupDone (assuming the handler leaves
    // the stack empty after the throw).  The + 1 for the incoming exception.
    SP = save_SP + 1;
  }

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

01936   public void emitWithCleanupDone ()
  {
    Variable catchVar = try_stack.saved_result;
    try_stack.saved_result = null;
    if (catchVar != null)
      emitLoad(catchVar);
    emitThrow();
    emitCatchEnd();
    emitTryCatchEnd();
  }


  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. */
02092   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;
  }

02384   public void assignConstants (ClassType cl)
  {
    super.assignConstants(cl);
    if (locals != null && locals.container == null && ! locals.isEmpty())
      locals.addToFrontOf(this);
    processFixups();
    Attribute.assignConstants(this, cl);
  }

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

02399   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. */
02853   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. */
02865   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.
   */
02887   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);
  }
}

Generated by  Doxygen 1.6.0   Back to index