langtools/src/share/classes/com/sun/tools/javac/jvm/Code.java
changeset 10 06bc494ca11e
child 1260 a772ba9ba43d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/com/sun/tools/javac/jvm/Code.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,2178 @@
+/*
+ * Copyright 1999-2007 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.tools.javac.jvm;
+
+import com.sun.tools.javac.code.*;
+import com.sun.tools.javac.code.Symbol.*;
+import com.sun.tools.javac.util.*;
+import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
+
+import static com.sun.tools.javac.code.TypeTags.*;
+import static com.sun.tools.javac.jvm.ByteCodes.*;
+import static com.sun.tools.javac.jvm.UninitializedType.*;
+import static com.sun.tools.javac.jvm.ClassWriter.StackMapTableFrame;
+
+/** An internal structure that corresponds to the code attribute of
+ *  methods in a classfile. The class also provides some utility operations to
+ *  generate bytecode instructions.
+ *
+ *  <p><b>This is NOT part of any API supported by Sun Microsystems.  If
+ *  you write code that depends on this, you do so at your own risk.
+ *  This code and its internal interfaces are subject to change or
+ *  deletion without notice.</b>
+ */
+public class Code {
+
+    public final boolean debugCode;
+    public final boolean needStackMap;
+
+    public enum StackMapFormat {
+        NONE,
+        CLDC {
+            Name getAttributeName(Name.Table names) {
+                return names.StackMap;
+            }
+        },
+        JSR202 {
+            Name getAttributeName(Name.Table names) {
+                return names.StackMapTable;
+            }
+        };
+        Name getAttributeName(Name.Table names) {
+            return names.empty;
+        }
+    }
+
+    final Types types;
+    final Symtab syms;
+
+/*---------- classfile fields: --------------- */
+
+    /** The maximum stack size.
+     */
+    public int max_stack = 0;
+
+    /** The maximum number of local variable slots.
+     */
+    public int max_locals = 0;
+
+    /** The code buffer.
+     */
+    public byte[] code = new byte[64];
+
+    /** the current code pointer.
+     */
+    public int cp = 0;
+
+    /** Check the code against VM spec limits; if
+     *  problems report them and return true.
+     */
+    public boolean checkLimits(DiagnosticPosition pos, Log log) {
+        if (cp > ClassFile.MAX_CODE) {
+            log.error(pos, "limit.code");
+            return true;
+        }
+        if (max_locals > ClassFile.MAX_LOCALS) {
+            log.error(pos, "limit.locals");
+            return true;
+        }
+        if (max_stack > ClassFile.MAX_STACK) {
+            log.error(pos, "limit.stack");
+            return true;
+        }
+        return false;
+    }
+
+    /** A buffer for expression catch data. Each enter is a vector
+     *  of four unsigned shorts.
+     */
+    ListBuffer<char[]> catchInfo = new ListBuffer<char[]>();
+
+    /** A buffer for line number information. Each entry is a vector
+     *  of two unsigned shorts.
+     */
+    List<char[]> lineInfo = List.nil(); // handled in stack fashion
+
+    /** The CharacterRangeTable
+     */
+    public CRTable crt;
+
+/*---------- internal fields: --------------- */
+
+    /** Are we generating code with jumps >= 32K?
+     */
+    public boolean fatcode;
+
+    /** Code generation enabled?
+     */
+    private boolean alive = true;
+
+    /** The current machine state (registers and stack).
+     */
+    State state;
+
+    /** Is it forbidden to compactify code, because something is
+     *  pointing to current location?
+     */
+    private boolean fixedPc = false;
+
+    /** The next available register.
+     */
+    public int nextreg = 0;
+
+    /** A chain for jumps to be resolved before the next opcode is emitted.
+     *  We do this lazily to avoid jumps to jumps.
+     */
+    Chain pendingJumps = null;
+
+    /** The position of the currently statement, if we are at the
+     *  start of this statement, NOPOS otherwise.
+     *  We need this to emit line numbers lazily, which we need to do
+     *  because of jump-to-jump optimization.
+     */
+    int pendingStatPos = Position.NOPOS;
+
+    /** Set true when a stackMap is needed at the current PC. */
+    boolean pendingStackMap = false;
+
+    /** The stack map format to be generated. */
+    StackMapFormat stackMap;
+
+    /** Switch: emit variable debug info.
+     */
+    boolean varDebugInfo;
+
+    /** Switch: emit line number info.
+     */
+    boolean lineDebugInfo;
+
+    /** Emit line number info if map supplied
+     */
+    Position.LineMap lineMap;
+
+    /** The constant pool of the current class.
+     */
+    final Pool pool;
+
+    final MethodSymbol meth;
+
+    /** Construct a code object, given the settings of the fatcode,
+     *  debugging info switches and the CharacterRangeTable.
+     */
+    public Code(MethodSymbol meth,
+                boolean fatcode,
+                Position.LineMap lineMap,
+                boolean varDebugInfo,
+                StackMapFormat stackMap,
+                boolean debugCode,
+                CRTable crt,
+                Symtab syms,
+                Types types,
+                Pool pool) {
+        this.meth = meth;
+        this.fatcode = fatcode;
+        this.lineMap = lineMap;
+        this.lineDebugInfo = lineMap != null;
+        this.varDebugInfo = varDebugInfo;
+        this.crt = crt;
+        this.syms = syms;
+        this.types = types;
+        this.debugCode = debugCode;
+        this.stackMap = stackMap;
+        switch (stackMap) {
+        case CLDC:
+        case JSR202:
+            this.needStackMap = true;
+            break;
+        default:
+            this.needStackMap = false;
+        }
+        state = new State();
+        lvar = new LocalVar[20];
+        this.pool = pool;
+    }
+
+
+/* **************************************************************************
+ * Typecodes & related stuff
+ ****************************************************************************/
+
+    /** Given a type, return its type code (used implicitly in the
+     *  JVM architecture).
+     */
+    public static int typecode(Type type) {
+        switch (type.tag) {
+        case BYTE: return BYTEcode;
+        case SHORT: return SHORTcode;
+        case CHAR: return CHARcode;
+        case INT: return INTcode;
+        case LONG: return LONGcode;
+        case FLOAT: return FLOATcode;
+        case DOUBLE: return DOUBLEcode;
+        case BOOLEAN: return BYTEcode;
+        case VOID: return VOIDcode;
+        case CLASS:
+        case ARRAY:
+        case METHOD:
+        case BOT:
+        case TYPEVAR:
+        case UNINITIALIZED_THIS:
+        case UNINITIALIZED_OBJECT:
+            return OBJECTcode;
+        default: throw new AssertionError("typecode " + type.tag);
+        }
+    }
+
+    /** Collapse type code for subtypes of int to INTcode.
+     */
+    public static int truncate(int tc) {
+        switch (tc) {
+        case BYTEcode: case SHORTcode: case CHARcode: return INTcode;
+        default: return tc;
+        }
+    }
+
+    /** The width in bytes of objects of the type.
+     */
+    public static int width(int typecode) {
+        switch (typecode) {
+        case LONGcode: case DOUBLEcode: return 2;
+        case VOIDcode: return 0;
+        default: return 1;
+        }
+    }
+
+    public static int width(Type type) {
+        return type == null ? 1 : width(typecode(type));
+    }
+
+    /** The total width taken up by a vector of objects.
+     */
+    public static int width(List<Type> types) {
+        int w = 0;
+        for (List<Type> l = types; l.nonEmpty(); l = l.tail)
+            w = w + width(l.head);
+        return w;
+    }
+
+    /** Given a type, return its code for allocating arrays of that type.
+     */
+    public static int arraycode(Type type) {
+        switch (type.tag) {
+        case BYTE: return 8;
+        case BOOLEAN: return 4;
+        case SHORT: return 9;
+        case CHAR: return 5;
+        case INT: return 10;
+        case LONG: return 11;
+        case FLOAT: return 6;
+        case DOUBLE: return 7;
+        case CLASS: return 0;
+        case ARRAY: return 1;
+        default: throw new AssertionError("arraycode " + type);
+        }
+    }
+
+
+/* **************************************************************************
+ * Emit code
+ ****************************************************************************/
+
+    /** The current output code pointer.
+     */
+    public int curPc() {
+        if (pendingJumps != null) resolvePending();
+        if (pendingStatPos != Position.NOPOS) markStatBegin();
+        fixedPc = true;
+        return cp;
+    }
+
+    /** Emit a byte of code.
+     */
+    private  void emit1(int od) {
+        if (!alive) return;
+        if (cp == code.length) {
+            byte[] newcode = new byte[cp * 2];
+            System.arraycopy(code, 0, newcode, 0, cp);
+            code = newcode;
+        }
+        code[cp++] = (byte)od;
+    }
+
+    /** Emit two bytes of code.
+     */
+    private void emit2(int od) {
+        if (!alive) return;
+        if (cp + 2 > code.length) {
+            emit1(od >> 8);
+            emit1(od);
+        } else {
+            code[cp++] = (byte)(od >> 8);
+            code[cp++] = (byte)od;
+        }
+    }
+
+    /** Emit four bytes of code.
+     */
+    public void emit4(int od) {
+        if (!alive) return;
+        if (cp + 4 > code.length) {
+            emit1(od >> 24);
+            emit1(od >> 16);
+            emit1(od >> 8);
+            emit1(od);
+        } else {
+            code[cp++] = (byte)(od >> 24);
+            code[cp++] = (byte)(od >> 16);
+            code[cp++] = (byte)(od >> 8);
+            code[cp++] = (byte)od;
+        }
+    }
+
+    /** Emit an opcode.
+     */
+    private void emitop(int op) {
+        if (pendingJumps != null) resolvePending();
+        if (alive) {
+            if (pendingStatPos != Position.NOPOS)
+                markStatBegin();
+            if (pendingStackMap) {
+                pendingStackMap = false;
+                emitStackMap();
+            }
+            if (debugCode)
+                System.err.println("emit@" + cp + " stack=" +
+                                   state.stacksize + ": " +
+                                   mnem(op));
+            emit1(op);
+        }
+    }
+
+    void postop() {
+        assert alive || state.stacksize == 0;
+    }
+
+    /** Emit a multinewarray instruction.
+     */
+    public void emitMultianewarray(int ndims, int type, Type arrayType) {
+        emitop(multianewarray);
+        if (!alive) return;
+        emit2(type);
+        emit1(ndims);
+        state.pop(ndims);
+        state.push(arrayType);
+    }
+
+    /** Emit newarray.
+     */
+    public void emitNewarray(int elemcode, Type arrayType) {
+        emitop(newarray);
+        if (!alive) return;
+        emit1(elemcode);
+        state.pop(1); // count
+        state.push(arrayType);
+    }
+
+    /** Emit anewarray.
+     */
+    public void emitAnewarray(int od, Type arrayType) {
+        emitop(anewarray);
+        if (!alive) return;
+        emit2(od);
+        state.pop(1);
+        state.push(arrayType);
+    }
+
+    /** Emit an invokeinterface instruction.
+     */
+    public void emitInvokeinterface(int meth, Type mtype) {
+        int argsize = width(mtype.getParameterTypes());
+        emitop(invokeinterface);
+        if (!alive) return;
+        emit2(meth);
+        emit1(argsize + 1);
+        emit1(0);
+        state.pop(argsize + 1);
+        state.push(mtype.getReturnType());
+    }
+
+    /** Emit an invokespecial instruction.
+     */
+    public void emitInvokespecial(int meth, Type mtype) {
+        int argsize = width(mtype.getParameterTypes());
+        emitop(invokespecial);
+        if (!alive) return;
+        emit2(meth);
+        Symbol sym = (Symbol)pool.pool[meth];
+        state.pop(argsize);
+        if (sym.isConstructor())
+            state.markInitialized((UninitializedType)state.peek());
+        state.pop(1);
+        state.push(mtype.getReturnType());
+    }
+
+    /** Emit an invokestatic instruction.
+     */
+    public void emitInvokestatic(int meth, Type mtype) {
+        int argsize = width(mtype.getParameterTypes());
+        emitop(invokestatic);
+        if (!alive) return;
+        emit2(meth);
+        state.pop(argsize);
+        state.push(mtype.getReturnType());
+    }
+
+    /** Emit an invokevirtual instruction.
+     */
+    public void emitInvokevirtual(int meth, Type mtype) {
+        int argsize = width(mtype.getParameterTypes());
+        emitop(invokevirtual);
+        if (!alive) return;
+        emit2(meth);
+        state.pop(argsize + 1);
+        state.push(mtype.getReturnType());
+    }
+
+    /** Emit an opcode with no operand field.
+     */
+    public void emitop0(int op) {
+        emitop(op);
+        if (!alive) return;
+        switch (op) {
+        case aaload: {
+            state.pop(1);// index
+            Type a = state.stack[state.stacksize-1];
+            state.pop(1);
+            state.push(types.erasure(types.elemtype(a))); }
+            break;
+        case goto_:
+            markDead();
+            break;
+        case nop:
+        case ineg:
+        case lneg:
+        case fneg:
+        case dneg:
+            break;
+        case aconst_null:
+            state.push(syms.botType);
+            break;
+        case iconst_m1:
+        case iconst_0:
+        case iconst_1:
+        case iconst_2:
+        case iconst_3:
+        case iconst_4:
+        case iconst_5:
+        case iload_0:
+        case iload_1:
+        case iload_2:
+        case iload_3:
+            state.push(syms.intType);
+            break;
+        case lconst_0:
+        case lconst_1:
+        case lload_0:
+        case lload_1:
+        case lload_2:
+        case lload_3:
+            state.push(syms.longType);
+            break;
+        case fconst_0:
+        case fconst_1:
+        case fconst_2:
+        case fload_0:
+        case fload_1:
+        case fload_2:
+        case fload_3:
+            state.push(syms.floatType);
+            break;
+        case dconst_0:
+        case dconst_1:
+        case dload_0:
+        case dload_1:
+        case dload_2:
+        case dload_3:
+            state.push(syms.doubleType);
+            break;
+        case aload_0:
+            state.push(lvar[0].sym.type);
+            break;
+        case aload_1:
+            state.push(lvar[1].sym.type);
+            break;
+        case aload_2:
+            state.push(lvar[2].sym.type);
+            break;
+        case aload_3:
+            state.push(lvar[3].sym.type);
+            break;
+        case iaload:
+        case baload:
+        case caload:
+        case saload:
+            state.pop(2);
+            state.push(syms.intType);
+            break;
+        case laload:
+            state.pop(2);
+            state.push(syms.longType);
+            break;
+        case faload:
+            state.pop(2);
+            state.push(syms.floatType);
+            break;
+        case daload:
+            state.pop(2);
+            state.push(syms.doubleType);
+            break;
+        case istore_0:
+        case istore_1:
+        case istore_2:
+        case istore_3:
+        case fstore_0:
+        case fstore_1:
+        case fstore_2:
+        case fstore_3:
+        case astore_0:
+        case astore_1:
+        case astore_2:
+        case astore_3:
+        case pop:
+        case lshr:
+        case lshl:
+        case lushr:
+            state.pop(1);
+            break;
+        case areturn:
+        case ireturn:
+        case freturn:
+            assert state.nlocks == 0;
+            state.pop(1);
+            markDead();
+            break;
+        case athrow:
+            state.pop(1);
+            markDead();
+            break;
+        case lstore_0:
+        case lstore_1:
+        case lstore_2:
+        case lstore_3:
+        case dstore_0:
+        case dstore_1:
+        case dstore_2:
+        case dstore_3:
+        case pop2:
+            state.pop(2);
+            break;
+        case lreturn:
+        case dreturn:
+            assert state.nlocks == 0;
+            state.pop(2);
+            markDead();
+            break;
+        case dup:
+            state.push(state.stack[state.stacksize-1]);
+            break;
+        case return_:
+            assert state.nlocks == 0;
+            markDead();
+            break;
+        case arraylength:
+            state.pop(1);
+            state.push(syms.intType);
+            break;
+        case isub:
+        case iadd:
+        case imul:
+        case idiv:
+        case imod:
+        case ishl:
+        case ishr:
+        case iushr:
+        case iand:
+        case ior:
+        case ixor:
+            state.pop(1);
+            // state.pop(1);
+            // state.push(syms.intType);
+            break;
+        case aastore:
+            state.pop(3);
+            break;
+        case land:
+        case lor:
+        case lxor:
+        case lmod:
+        case ldiv:
+        case lmul:
+        case lsub:
+        case ladd:
+            state.pop(2);
+            break;
+        case lcmp:
+            state.pop(4);
+            state.push(syms.intType);
+            break;
+        case l2i:
+            state.pop(2);
+            state.push(syms.intType);
+            break;
+        case i2l:
+            state.pop(1);
+            state.push(syms.longType);
+            break;
+        case i2f:
+            state.pop(1);
+            state.push(syms.floatType);
+            break;
+        case i2d:
+            state.pop(1);
+            state.push(syms.doubleType);
+            break;
+        case l2f:
+            state.pop(2);
+            state.push(syms.floatType);
+            break;
+        case l2d:
+            state.pop(2);
+            state.push(syms.doubleType);
+            break;
+        case f2i:
+            state.pop(1);
+            state.push(syms.intType);
+            break;
+        case f2l:
+            state.pop(1);
+            state.push(syms.longType);
+            break;
+        case f2d:
+            state.pop(1);
+            state.push(syms.doubleType);
+            break;
+        case d2i:
+            state.pop(2);
+            state.push(syms.intType);
+            break;
+        case d2l:
+            state.pop(2);
+            state.push(syms.longType);
+            break;
+        case d2f:
+            state.pop(2);
+            state.push(syms.floatType);
+            break;
+        case tableswitch:
+        case lookupswitch:
+            state.pop(1);
+            // the caller is responsible for patching up the state
+            break;
+        case dup_x1: {
+            Type val1 = state.pop1();
+            Type val2 = state.pop1();
+            state.push(val1);
+            state.push(val2);
+            state.push(val1);
+            break;
+        }
+        case bastore:
+            state.pop(3);
+            break;
+        case int2byte:
+        case int2char:
+        case int2short:
+            break;
+        case fmul:
+        case fadd:
+        case fsub:
+        case fdiv:
+        case fmod:
+            state.pop(1);
+            break;
+        case castore:
+        case iastore:
+        case fastore:
+        case sastore:
+            state.pop(3);
+            break;
+        case lastore:
+        case dastore:
+            state.pop(4);
+            break;
+        case dup2:
+            if (state.stack[state.stacksize-1] != null) {
+                Type value1 = state.pop1();
+                Type value2 = state.pop1();
+                state.push(value2);
+                state.push(value1);
+                state.push(value2);
+                state.push(value1);
+            } else {
+                Type value = state.pop2();
+                state.push(value);
+                state.push(value);
+            }
+            break;
+        case dup2_x1:
+            if (state.stack[state.stacksize-1] != null) {
+                Type value1 = state.pop1();
+                Type value2 = state.pop1();
+                Type value3 = state.pop1();
+                state.push(value2);
+                state.push(value1);
+                state.push(value3);
+                state.push(value2);
+                state.push(value1);
+            } else {
+                Type value1 = state.pop2();
+                Type value2 = state.pop1();
+                state.push(value1);
+                state.push(value2);
+                state.push(value1);
+            }
+            break;
+        case dup2_x2:
+            if (state.stack[state.stacksize-1] != null) {
+                Type value1 = state.pop1();
+                Type value2 = state.pop1();
+                if (state.stack[state.stacksize-1] != null) {
+                    // form 1
+                    Type value3 = state.pop1();
+                    Type value4 = state.pop1();
+                    state.push(value2);
+                    state.push(value1);
+                    state.push(value4);
+                    state.push(value3);
+                    state.push(value2);
+                    state.push(value1);
+                } else {
+                    // form 3
+                    Type value3 = state.pop2();
+                    state.push(value2);
+                    state.push(value1);
+                    state.push(value3);
+                    state.push(value2);
+                    state.push(value1);
+                }
+            } else {
+                Type value1 = state.pop2();
+                if (state.stack[state.stacksize-1] != null) {
+                    // form 2
+                    Type value2 = state.pop1();
+                    Type value3 = state.pop1();
+                    state.push(value1);
+                    state.push(value3);
+                    state.push(value2);
+                    state.push(value1);
+                } else {
+                    // form 4
+                    Type value2 = state.pop2();
+                    state.push(value1);
+                    state.push(value2);
+                    state.push(value1);
+                }
+            }
+            break;
+        case dup_x2: {
+            Type value1 = state.pop1();
+            if (state.stack[state.stacksize-1] != null) {
+                // form 1
+                Type value2 = state.pop1();
+                Type value3 = state.pop1();
+                state.push(value1);
+                state.push(value3);
+                state.push(value2);
+                state.push(value1);
+            } else {
+                // form 2
+                Type value2 = state.pop2();
+                state.push(value1);
+                state.push(value2);
+                state.push(value1);
+            }
+        }
+            break;
+        case fcmpl:
+        case fcmpg:
+            state.pop(2);
+            state.push(syms.intType);
+            break;
+        case dcmpl:
+        case dcmpg:
+            state.pop(4);
+            state.push(syms.intType);
+            break;
+        case swap: {
+            Type value1 = state.pop1();
+            Type value2 = state.pop1();
+            state.push(value1);
+            state.push(value2);
+            break;
+        }
+        case dadd:
+        case dsub:
+        case dmul:
+        case ddiv:
+        case dmod:
+            state.pop(2);
+            break;
+        case ret:
+            markDead();
+            break;
+        case wide:
+            // must be handled by the caller.
+            return;
+        case monitorenter:
+        case monitorexit:
+            state.pop(1);
+            break;
+
+        default:
+            throw new AssertionError(mnem(op));
+        }
+        postop();
+    }
+
+    /** Emit an opcode with a one-byte operand field.
+     */
+    public void emitop1(int op, int od) {
+        emitop(op);
+        if (!alive) return;
+        emit1(od);
+        switch (op) {
+        case bipush:
+            state.push(syms.intType);
+            break;
+        case ldc1:
+            state.push(typeForPool(pool.pool[od]));
+            break;
+        default:
+            throw new AssertionError(mnem(op));
+        }
+        postop();
+    }
+
+    /** The type of a constant pool entry. */
+    private Type typeForPool(Object o) {
+        if (o instanceof Integer) return syms.intType;
+        if (o instanceof Float) return syms.floatType;
+        if (o instanceof String) return syms.stringType;
+        if (o instanceof Long) return syms.longType;
+        if (o instanceof Double) return syms.doubleType;
+        if (o instanceof ClassSymbol) return syms.classType;
+        if (o instanceof Type.ArrayType) return syms.classType;
+        throw new AssertionError(o);
+    }
+
+    /** Emit an opcode with a one-byte operand field;
+     *  widen if field does not fit in a byte.
+     */
+    public void emitop1w(int op, int od) {
+        if (od > 0xFF) {
+            emitop(wide);
+            emitop(op);
+            emit2(od);
+        } else {
+            emitop(op);
+            emit1(od);
+        }
+        if (!alive) return;
+        switch (op) {
+        case iload:
+            state.push(syms.intType);
+            break;
+        case lload:
+            state.push(syms.longType);
+            break;
+        case fload:
+            state.push(syms.floatType);
+            break;
+        case dload:
+            state.push(syms.doubleType);
+            break;
+        case aload:
+            state.push(lvar[od].sym.type);
+            break;
+        case lstore:
+        case dstore:
+            state.pop(2);
+            break;
+        case istore:
+        case fstore:
+        case astore:
+            state.pop(1);
+            break;
+        case ret:
+            markDead();
+            break;
+        default:
+            throw new AssertionError(mnem(op));
+        }
+        postop();
+    }
+
+    /** Emit an opcode with two one-byte operand fields;
+     *  widen if either field does not fit in a byte.
+     */
+    public void emitop1w(int op, int od1, int od2) {
+        if (od1 > 0xFF || od2 < -128 || od2 > 127) {
+            emitop(wide);
+            emitop(op);
+            emit2(od1);
+            emit2(od2);
+        } else {
+            emitop(op);
+            emit1(od1);
+            emit1(od2);
+        }
+        if (!alive) return;
+        switch (op) {
+        case iinc:
+            break;
+        default:
+            throw new AssertionError(mnem(op));
+        }
+    }
+
+    /** Emit an opcode with a two-byte operand field.
+     */
+    public void emitop2(int op, int od) {
+        emitop(op);
+        if (!alive) return;
+        emit2(od);
+        switch (op) {
+        case getstatic:
+            state.push(((Symbol)(pool.pool[od])).erasure(types));
+            break;
+        case putstatic:
+            state.pop(((Symbol)(pool.pool[od])).erasure(types));
+            break;
+        case new_:
+            state.push(uninitializedObject(((Symbol)(pool.pool[od])).erasure(types), cp-3));
+            break;
+        case sipush:
+            state.push(syms.intType);
+            break;
+        case if_acmp_null:
+        case if_acmp_nonnull:
+        case ifeq:
+        case ifne:
+        case iflt:
+        case ifge:
+        case ifgt:
+        case ifle:
+            state.pop(1);
+            break;
+        case if_icmpeq:
+        case if_icmpne:
+        case if_icmplt:
+        case if_icmpge:
+        case if_icmpgt:
+        case if_icmple:
+        case if_acmpeq:
+        case if_acmpne:
+            state.pop(2);
+            break;
+        case goto_:
+            markDead();
+            break;
+        case putfield:
+            state.pop(((Symbol)(pool.pool[od])).erasure(types));
+            state.pop(1); // object ref
+            break;
+        case getfield:
+            state.pop(1); // object ref
+            state.push(((Symbol)(pool.pool[od])).erasure(types));
+            break;
+        case checkcast: {
+            state.pop(1); // object ref
+            Object o = pool.pool[od];
+            Type t = (o instanceof Symbol)
+                ? ((Symbol)o).erasure(types)
+                : types.erasure(((Type)o));
+            state.push(t);
+            break; }
+        case ldc2w:
+            state.push(typeForPool(pool.pool[od]));
+            break;
+        case instanceof_:
+            state.pop(1);
+            state.push(syms.intType);
+            break;
+        case ldc2:
+            state.push(typeForPool(pool.pool[od]));
+            break;
+        case jsr:
+            break;
+        default:
+            throw new AssertionError(mnem(op));
+        }
+        // postop();
+    }
+
+    /** Emit an opcode with a four-byte operand field.
+     */
+    public void emitop4(int op, int od) {
+        emitop(op);
+        if (!alive) return;
+        emit4(od);
+        switch (op) {
+        case goto_w:
+            markDead();
+            break;
+        case jsr_w:
+            break;
+        default:
+            throw new AssertionError(mnem(op));
+        }
+        // postop();
+    }
+
+    /** Align code pointer to next `incr' boundary.
+     */
+    public void align(int incr) {
+        if (alive)
+            while (cp % incr != 0) emitop0(nop);
+    }
+
+    /** Place a byte into code at address pc. Pre: pc + 1 <= cp.
+     */
+    private void put1(int pc, int op) {
+        code[pc] = (byte)op;
+    }
+
+    /** Place two bytes into code at address pc. Pre: pc + 2 <= cp.
+     */
+    private void put2(int pc, int od) {
+        // pre: pc + 2 <= cp
+        put1(pc, od >> 8);
+        put1(pc+1, od);
+    }
+
+    /** Place four  bytes into code at address pc. Pre: pc + 4 <= cp.
+     */
+    public void put4(int pc, int od) {
+        // pre: pc + 4 <= cp
+        put1(pc  , od >> 24);
+        put1(pc+1, od >> 16);
+        put1(pc+2, od >> 8);
+        put1(pc+3, od);
+    }
+
+    /** Return code byte at position pc as an unsigned int.
+     */
+    private int get1(int pc) {
+        return code[pc] & 0xFF;
+    }
+
+    /** Return two code bytes at position pc as an unsigned int.
+     */
+    private int get2(int pc) {
+        return (get1(pc) << 8) | get1(pc+1);
+    }
+
+    /** Return four code bytes at position pc as an int.
+     */
+    public int get4(int pc) {
+        // pre: pc + 4 <= cp
+        return
+            (get1(pc) << 24) |
+            (get1(pc+1) << 16) |
+            (get1(pc+2) << 8) |
+            (get1(pc+3));
+    }
+
+    /** Is code generation currently enabled?
+     */
+    public boolean isAlive() {
+        return alive || pendingJumps != null;
+    }
+
+    /** Switch code generation on/off.
+     */
+    public void markDead() {
+        alive = false;
+    }
+
+    /** Declare an entry point; return current code pointer
+     */
+    public int entryPoint() {
+        int pc = curPc();
+        alive = true;
+        pendingStackMap = needStackMap;
+        return pc;
+    }
+
+    /** Declare an entry point with initial state;
+     *  return current code pointer
+     */
+    public int entryPoint(State state) {
+        int pc = curPc();
+        alive = true;
+        this.state = state.dup();
+        assert state.stacksize <= max_stack;
+        if (debugCode) System.err.println("entry point " + state);
+        pendingStackMap = needStackMap;
+        return pc;
+    }
+
+    /** Declare an entry point with initial state plus a pushed value;
+     *  return current code pointer
+     */
+    public int entryPoint(State state, Type pushed) {
+        int pc = curPc();
+        alive = true;
+        this.state = state.dup();
+        assert state.stacksize <= max_stack;
+        this.state.push(pushed);
+        if (debugCode) System.err.println("entry point " + state);
+        pendingStackMap = needStackMap;
+        return pc;
+    }
+
+
+/**************************************************************************
+ * Stack map generation
+ *************************************************************************/
+
+    /** An entry in the stack map. */
+    static class StackMapFrame {
+        int pc;
+        Type[] locals;
+        Type[] stack;
+    }
+
+    /** A buffer of cldc stack map entries. */
+    StackMapFrame[] stackMapBuffer = null;
+
+    /** A buffer of compressed StackMapTable entries. */
+    StackMapTableFrame[] stackMapTableBuffer = null;
+    int stackMapBufferSize = 0;
+
+    /** The last PC at which we generated a stack map. */
+    int lastStackMapPC = -1;
+
+    /** The last stack map frame in StackMapTable. */
+    StackMapFrame lastFrame = null;
+
+    /** The stack map frame before the last one. */
+    StackMapFrame frameBeforeLast = null;
+
+    /** Emit a stack map entry.  */
+    public void emitStackMap() {
+        int pc = curPc();
+        if (!needStackMap) return;
+
+
+
+        switch (stackMap) {
+            case CLDC:
+                emitCLDCStackMap(pc, getLocalsSize());
+                break;
+            case JSR202:
+                emitStackMapFrame(pc, getLocalsSize());
+                break;
+            default:
+                throw new AssertionError("Should have chosen a stackmap format");
+        }
+        // DEBUG code follows
+        if (debugCode) state.dump(pc);
+    }
+
+    private int getLocalsSize() {
+        int nextLocal = 0;
+        for (int i=max_locals-1; i>=0; i--) {
+            if (state.defined.isMember(i) && lvar[i] != null) {
+                nextLocal = i + width(lvar[i].sym.erasure(types));
+                break;
+            }
+        }
+        return nextLocal;
+    }
+
+    /** Emit a CLDC stack map frame. */
+    void emitCLDCStackMap(int pc, int localsSize) {
+        if (lastStackMapPC == pc) {
+            // drop existing stackmap at this offset
+            stackMapBuffer[--stackMapBufferSize] = null;
+        }
+        lastStackMapPC = pc;
+
+        if (stackMapBuffer == null) {
+            stackMapBuffer = new StackMapFrame[20];
+        } else if (stackMapBuffer.length == stackMapBufferSize) {
+            StackMapFrame[] newStackMapBuffer =
+                new StackMapFrame[stackMapBufferSize << 1];
+            System.arraycopy(stackMapBuffer, 0, newStackMapBuffer,
+                             0, stackMapBufferSize);
+            stackMapBuffer = newStackMapBuffer;
+        }
+        StackMapFrame frame =
+            stackMapBuffer[stackMapBufferSize++] = new StackMapFrame();
+        frame.pc = pc;
+
+        frame.locals = new Type[localsSize];
+        for (int i=0; i<localsSize; i++) {
+            if (state.defined.isMember(i) && lvar[i] != null) {
+                Type vtype = lvar[i].sym.type;
+                if (!(vtype instanceof UninitializedType))
+                    vtype = types.erasure(vtype);
+                frame.locals[i] = vtype;
+            }
+        }
+        frame.stack = new Type[state.stacksize];
+        for (int i=0; i<state.stacksize; i++)
+            frame.stack[i] = state.stack[i];
+    }
+
+    void emitStackMapFrame(int pc, int localsSize) {
+        if (lastFrame == null) {
+            // first frame
+            lastFrame = getInitialFrame();
+        } else if (lastFrame.pc == pc) {
+            // drop existing stackmap at this offset
+            stackMapTableBuffer[--stackMapBufferSize] = null;
+            lastFrame = frameBeforeLast;
+            frameBeforeLast = null;
+        }
+
+        StackMapFrame frame = new StackMapFrame();
+        frame.pc = pc;
+
+        int localCount = 0;
+        Type[] locals = new Type[localsSize];
+        for (int i=0; i<localsSize; i++, localCount++) {
+            if (state.defined.isMember(i) && lvar[i] != null) {
+                Type vtype = lvar[i].sym.type;
+                if (!(vtype instanceof UninitializedType))
+                    vtype = types.erasure(vtype);
+                locals[i] = vtype;
+                if (width(vtype) > 1) i++;
+            }
+        }
+        frame.locals = new Type[localCount];
+        for (int i=0, j=0; i<localsSize; i++, j++) {
+            assert(j < localCount);
+            frame.locals[j] = locals[i];
+            if (width(locals[i]) > 1) i++;
+        }
+
+        int stackCount = 0;
+        for (int i=0; i<state.stacksize; i++) {
+            if (state.stack[i] != null) {
+                stackCount++;
+            }
+        }
+        frame.stack = new Type[stackCount];
+        stackCount = 0;
+        for (int i=0; i<state.stacksize; i++) {
+            if (state.stack[i] != null) {
+                frame.stack[stackCount++] = state.stack[i];
+            }
+        }
+
+        if (stackMapTableBuffer == null) {
+            stackMapTableBuffer = new StackMapTableFrame[20];
+        } else if (stackMapTableBuffer.length == stackMapBufferSize) {
+            StackMapTableFrame[] newStackMapTableBuffer =
+                new StackMapTableFrame[stackMapBufferSize << 1];
+            System.arraycopy(stackMapTableBuffer, 0, newStackMapTableBuffer,
+                             0, stackMapBufferSize);
+            stackMapTableBuffer = newStackMapTableBuffer;
+        }
+        stackMapTableBuffer[stackMapBufferSize++] =
+                StackMapTableFrame.getInstance(frame, lastFrame.pc, lastFrame.locals, types);
+
+        frameBeforeLast = lastFrame;
+        lastFrame = frame;
+    }
+
+    StackMapFrame getInitialFrame() {
+        StackMapFrame frame = new StackMapFrame();
+        List<Type> arg_types = ((MethodType)meth.externalType(types)).argtypes;
+        int len = arg_types.length();
+        int count = 0;
+        if (!meth.isStatic()) {
+            Type thisType = meth.owner.type;
+            frame.locals = new Type[len+1];
+            if (meth.isConstructor() && thisType != syms.objectType) {
+                frame.locals[count++] = UninitializedType.uninitializedThis(thisType);
+            } else {
+                frame.locals[count++] = types.erasure(thisType);
+            }
+        } else {
+            frame.locals = new Type[len];
+        }
+        for (Type arg_type : arg_types) {
+            frame.locals[count++] = types.erasure(arg_type);
+        }
+        frame.pc = -1;
+        frame.stack = null;
+        return frame;
+    }
+
+
+/**************************************************************************
+ * Operations having to do with jumps
+ *************************************************************************/
+
+    /** A chain represents a list of unresolved jumps. Jump locations
+     *  are sorted in decreasing order.
+     */
+    public static class Chain {
+
+        /** The position of the jump instruction.
+         */
+        public final int pc;
+
+        /** The machine state after the jump instruction.
+         *  Invariant: all elements of a chain list have the same stacksize
+         *  and compatible stack and register contents.
+         */
+        Code.State state;
+
+        /** The next jump in the list.
+         */
+        public final Chain next;
+
+        /** Construct a chain from its jump position, stacksize, previous
+         *  chain, and machine state.
+         */
+        public Chain(int pc, Chain next, Code.State state) {
+            this.pc = pc;
+            this.next = next;
+            this.state = state;
+        }
+    }
+
+    /** Negate a branch opcode.
+     */
+    public static int negate(int opcode) {
+        if (opcode == if_acmp_null) return if_acmp_nonnull;
+        else if (opcode == if_acmp_nonnull) return if_acmp_null;
+        else return ((opcode + 1) ^ 1) - 1;
+    }
+
+    /** Emit a jump instruction.
+     *  Return code pointer of instruction to be patched.
+     */
+    public int emitJump(int opcode) {
+        if (fatcode) {
+            if (opcode == goto_ || opcode == jsr) {
+                emitop4(opcode + goto_w - goto_, 0);
+            } else {
+                emitop2(negate(opcode), 8);
+                emitop4(goto_w, 0);
+                alive = true;
+                pendingStackMap = needStackMap;
+            }
+            return cp - 5;
+        } else {
+            emitop2(opcode, 0);
+            return cp - 3;
+        }
+    }
+
+    /** Emit a branch with given opcode; return its chain.
+     *  branch differs from jump in that jsr is treated as no-op.
+     */
+    public Chain branch(int opcode) {
+        Chain result = null;
+        if (opcode == goto_) {
+            result = pendingJumps;
+            pendingJumps = null;
+        }
+        if (opcode != dontgoto && isAlive()) {
+            result = new Chain(emitJump(opcode),
+                               result,
+                               state.dup());
+            fixedPc = fatcode;
+            if (opcode == goto_) alive = false;
+        }
+        return result;
+    }
+
+    /** Resolve chain to point to given target.
+     */
+    public void resolve(Chain chain, int target) {
+        boolean changed = false;
+        State newState = state;
+        for (; chain != null; chain = chain.next) {
+            assert state != chain.state;
+            assert target > chain.pc || state.stacksize == 0;
+            if (target >= cp) {
+                target = cp;
+            } else if (get1(target) == goto_) {
+                if (fatcode) target = target + get4(target + 1);
+                else target = target + get2(target + 1);
+            }
+            if (get1(chain.pc) == goto_ &&
+                chain.pc + 3 == target && target == cp && !fixedPc) {
+                // If goto the next instruction, the jump is not needed:
+                // compact the code.
+                cp = cp - 3;
+                target = target - 3;
+                if (chain.next == null) {
+                    // This is the only jump to the target. Exit the loop
+                    // without setting new state. The code is reachable
+                    // from the instruction before goto_.
+                    alive = true;
+                    break;
+                }
+            } else {
+                if (fatcode)
+                    put4(chain.pc + 1, target - chain.pc);
+                else if (target - chain.pc < Short.MIN_VALUE ||
+                         target - chain.pc > Short.MAX_VALUE)
+                    fatcode = true;
+                else
+                    put2(chain.pc + 1, target - chain.pc);
+                assert !alive ||
+                    chain.state.stacksize == newState.stacksize &&
+                    chain.state.nlocks == newState.nlocks;
+            }
+            fixedPc = true;
+            if (cp == target) {
+                changed = true;
+                if (debugCode)
+                    System.err.println("resolving chain state=" + chain.state);
+                if (alive) {
+                    newState = chain.state.join(newState);
+                } else {
+                    newState = chain.state;
+                    alive = true;
+                }
+            }
+        }
+        assert !changed || state != newState;
+        if (state != newState) {
+            setDefined(newState.defined);
+            state = newState;
+            pendingStackMap = needStackMap;
+        }
+    }
+
+    /** Resolve chain to point to current code pointer.
+     */
+    public void resolve(Chain chain) {
+        assert
+            !alive ||
+            chain==null ||
+            state.stacksize == chain.state.stacksize &&
+            state.nlocks == chain.state.nlocks;
+        pendingJumps = mergeChains(chain, pendingJumps);
+    }
+
+    /** Resolve any pending jumps.
+     */
+    public void resolvePending() {
+        Chain x = pendingJumps;
+        pendingJumps = null;
+        resolve(x, cp);
+    }
+
+    /** Merge the jumps in of two chains into one.
+     */
+    public static Chain mergeChains(Chain chain1, Chain chain2) {
+        // recursive merge sort
+        if (chain2 == null) return chain1;
+        if (chain1 == null) return chain2;
+        assert
+            chain1.state.stacksize == chain2.state.stacksize &&
+            chain1.state.nlocks == chain2.state.nlocks;
+        if (chain1.pc < chain2.pc)
+            return new Chain(
+                chain2.pc,
+                mergeChains(chain1, chain2.next),
+                chain2.state);
+        return new Chain(
+                chain1.pc,
+                mergeChains(chain1.next, chain2),
+                chain1.state);
+    }
+
+
+/* **************************************************************************
+ * Catch clauses
+ ****************************************************************************/
+
+    /** Add a catch clause to code.
+     */
+    public void addCatch(
+        char startPc, char endPc, char handlerPc, char catchType) {
+        catchInfo.append(new char[]{startPc, endPc, handlerPc, catchType});
+    }
+
+
+/* **************************************************************************
+ * Line numbers
+ ****************************************************************************/
+
+    /** Add a line number entry.
+     */
+    public void addLineNumber(char startPc, char lineNumber) {
+        if (lineDebugInfo) {
+            if (lineInfo.nonEmpty() && lineInfo.head[0] == startPc)
+                lineInfo = lineInfo.tail;
+            if (lineInfo.isEmpty() || lineInfo.head[1] != lineNumber)
+                lineInfo = lineInfo.prepend(new char[]{startPc, lineNumber});
+        }
+    }
+
+    /** Mark beginning of statement.
+     */
+    public void statBegin(int pos) {
+        if (pos != Position.NOPOS) {
+            pendingStatPos = pos;
+        }
+    }
+
+    /** Force stat begin eagerly
+     */
+    public void markStatBegin() {
+        if (alive && lineDebugInfo) {
+            int line = lineMap.getLineNumber(pendingStatPos);
+            char cp1 = (char)cp;
+            char line1 = (char)line;
+            if (cp1 == cp && line1 == line)
+                addLineNumber(cp1, line1);
+        }
+        pendingStatPos = Position.NOPOS;
+    }
+
+
+/* **************************************************************************
+ * Simulated VM machine state
+ ****************************************************************************/
+
+    class State implements Cloneable {
+        /** The set of registers containing values. */
+        Bits defined;
+
+        /** The (types of the) contents of the machine stack. */
+        Type[] stack;
+
+        /** The first stack position currently unused. */
+        int stacksize;
+
+        /** The numbers of registers containing locked monitors. */
+        int[] locks;
+        int nlocks;
+
+        State() {
+            defined = new Bits();
+            stack = new Type[16];
+        }
+
+        State dup() {
+            try {
+                State state = (State)super.clone();
+                state.defined = defined.dup();
+                state.stack = stack.clone();
+                if (locks != null) state.locks = locks.clone();
+                if (debugCode) {
+                    System.err.println("duping state " + this);
+                    dump();
+                }
+                return state;
+            } catch (CloneNotSupportedException ex) {
+                throw new AssertionError(ex);
+            }
+        }
+
+        void lock(int register) {
+            if (locks == null) {
+                locks = new int[20];
+            } else if (locks.length == nlocks) {
+                int[] newLocks = new int[locks.length << 1];
+                System.arraycopy(locks, 0, newLocks, 0, locks.length);
+                locks = newLocks;
+            }
+            locks[nlocks] = register;
+            nlocks++;
+        }
+
+        void unlock(int register) {
+            nlocks--;
+            assert locks[nlocks] == register;
+            locks[nlocks] = -1;
+        }
+
+        void push(Type t) {
+            if (debugCode) System.err.println("   pushing " + t);
+            switch (t.tag) {
+            case TypeTags.VOID:
+                return;
+            case TypeTags.BYTE:
+            case TypeTags.CHAR:
+            case TypeTags.SHORT:
+            case TypeTags.BOOLEAN:
+                t = syms.intType;
+                break;
+            default:
+                break;
+            }
+            if (stacksize+2 >= stack.length) {
+                Type[] newstack = new Type[2*stack.length];
+                System.arraycopy(stack, 0, newstack, 0, stack.length);
+                stack = newstack;
+            }
+            stack[stacksize++] = t;
+            switch (width(t)) {
+            case 1:
+                break;
+            case 2:
+                stack[stacksize++] = null;
+                break;
+            default:
+                throw new AssertionError(t);
+            }
+            if (stacksize > max_stack)
+                max_stack = stacksize;
+        }
+
+        Type pop1() {
+            if (debugCode) System.err.println("   popping " + 1);
+            stacksize--;
+            Type result = stack[stacksize];
+            stack[stacksize] = null;
+            assert result != null && width(result) == 1;
+            return result;
+        }
+
+        Type peek() {
+            return stack[stacksize-1];
+        }
+
+        Type pop2() {
+            if (debugCode) System.err.println("   popping " + 2);
+            stacksize -= 2;
+            Type result = stack[stacksize];
+            stack[stacksize] = null;
+            assert stack[stacksize+1] == null;
+            assert result != null && width(result) == 2;
+            return result;
+        }
+
+        void pop(int n) {
+            if (debugCode) System.err.println("   popping " + n);
+            while (n > 0) {
+                stack[--stacksize] = null;
+                n--;
+            }
+        }
+
+        void pop(Type t) {
+            pop(width(t));
+        }
+
+        /** Force the top of the stack to be treated as this supertype
+         *  of its current type. */
+        void forceStackTop(Type t) {
+            if (!alive) return;
+            switch (t.tag) {
+            case CLASS:
+            case ARRAY:
+                int width = width(t);
+                Type old = stack[stacksize-width];
+                assert types.isSubtype(types.erasure(old),
+                                       types.erasure(t));
+                stack[stacksize-width] = t;
+                break;
+            default:
+            }
+        }
+
+        void markInitialized(UninitializedType old) {
+            Type newtype = old.initializedType();
+            for (int i=0; i<stacksize; i++)
+                if (stack[i] == old) stack[i] = newtype;
+            for (int i=0; i<lvar.length; i++) {
+                LocalVar lv = lvar[i];
+                if (lv != null && lv.sym.type == old) {
+                    VarSymbol sym = lv.sym;
+                    sym = sym.clone(sym.owner);
+                    sym.type = newtype;
+                    LocalVar newlv = lvar[i] = new LocalVar(sym);
+                    // should the following be initialized to cp?
+                    newlv.start_pc = lv.start_pc;
+                }
+            }
+        }
+
+        State join(State other) {
+            defined = defined.andSet(other.defined);
+            assert stacksize == other.stacksize;
+            assert nlocks == other.nlocks;
+            for (int i=0; i<stacksize; ) {
+                Type t = stack[i];
+                Type tother = other.stack[i];
+                Type result =
+                    t==tother ? t :
+                    types.isSubtype(t, tother) ? tother :
+                    types.isSubtype(tother, t) ? t :
+                    error();
+                int w = width(result);
+                stack[i] = result;
+                if (w == 2) assert stack[i+1] == null;
+                i += w;
+            }
+            return this;
+        }
+
+        Type error() {
+            throw new AssertionError("inconsistent stack types at join point");
+        }
+
+        void dump() {
+            dump(-1);
+        }
+
+        void dump(int pc) {
+            System.err.print("stackMap for " + meth.owner + "." + meth);
+            if (pc == -1)
+                System.out.println();
+            else
+                System.out.println(" at " + pc);
+            System.err.println(" stack (from bottom):");
+            for (int i=0; i<stacksize; i++)
+                System.err.println("  " + i + ": " + stack[i]);
+
+            int lastLocal = 0;
+            for (int i=max_locals-1; i>=0; i--) {
+                if (defined.isMember(i)) {
+                    lastLocal = i;
+                    break;
+                }
+            }
+            if (lastLocal >= 0)
+                System.err.println(" locals:");
+            for (int i=0; i<=lastLocal; i++) {
+                System.err.print("  " + i + ": ");
+                if (defined.isMember(i)) {
+                    LocalVar var = lvar[i];
+                    if (var == null) {
+                        System.err.println("(none)");
+                    } else if (var.sym == null)
+                        System.err.println("UNKNOWN!");
+                    else
+                        System.err.println("" + var.sym + " of type " +
+                                           var.sym.erasure(types));
+                } else {
+                    System.err.println("undefined");
+                }
+            }
+            if (nlocks != 0) {
+                System.err.print(" locks:");
+                for (int i=0; i<nlocks; i++) {
+                    System.err.print(" " + locks[i]);
+                }
+                System.err.println();
+            }
+        }
+    }
+
+    static Type jsrReturnValue = new Type(TypeTags.INT, null);
+
+
+/* **************************************************************************
+ * Local variables
+ ****************************************************************************/
+
+    /** A live range of a local variable. */
+    static class LocalVar {
+        final VarSymbol sym;
+        final char reg;
+        char start_pc = Character.MAX_VALUE;
+        char length = Character.MAX_VALUE;
+        LocalVar(VarSymbol v) {
+            this.sym = v;
+            this.reg = (char)v.adr;
+        }
+        public LocalVar dup() {
+            return new LocalVar(sym);
+        }
+        public String toString() {
+            return "" + sym + " in register " + ((int)reg) + " starts at pc=" + ((int)start_pc) + " length=" + ((int)length);
+        }
+    };
+
+    /** Local variables, indexed by register. */
+    LocalVar[] lvar;
+
+    /** Add a new local variable. */
+    private void addLocalVar(VarSymbol v) {
+        int adr = v.adr;
+        if (adr+1 >= lvar.length) {
+            int newlength = lvar.length << 1;
+            if (newlength <= adr) newlength = adr + 10;
+            LocalVar[] new_lvar = new LocalVar[newlength];
+            System.arraycopy(lvar, 0, new_lvar, 0, lvar.length);
+            lvar = new_lvar;
+        }
+        assert lvar[adr] == null;
+        if (pendingJumps != null) resolvePending();
+        lvar[adr] = new LocalVar(v);
+        state.defined.excl(adr);
+    }
+
+    /** Set the current variable defined state. */
+    public void setDefined(Bits newDefined) {
+        if (alive && newDefined != state.defined) {
+            Bits diff = state.defined.dup().xorSet(newDefined);
+            for (int adr = diff.nextBit(0);
+                 adr >= 0;
+                 adr = diff.nextBit(adr+1)) {
+                if (adr >= nextreg)
+                    state.defined.excl(adr);
+                else if (state.defined.isMember(adr))
+                    setUndefined(adr);
+                else
+                    setDefined(adr);
+            }
+        }
+    }
+
+    /** Mark a register as being (possibly) defined. */
+    public void setDefined(int adr) {
+        LocalVar v = lvar[adr];
+        if (v == null) {
+            state.defined.excl(adr);
+        } else {
+            state.defined.incl(adr);
+            if (cp < Character.MAX_VALUE) {
+                if (v.start_pc == Character.MAX_VALUE)
+                    v.start_pc = (char)cp;
+            }
+        }
+    }
+
+    /** Mark a register as being undefined. */
+    public void setUndefined(int adr) {
+        state.defined.excl(adr);
+        if (adr < lvar.length &&
+            lvar[adr] != null &&
+            lvar[adr].start_pc != Character.MAX_VALUE) {
+            LocalVar v = lvar[adr];
+            char length = (char)(curPc() - v.start_pc);
+            if (length > 0 && length < Character.MAX_VALUE) {
+                lvar[adr] = v.dup();
+                v.length = length;
+                putVar(v);
+            } else {
+                v.start_pc = Character.MAX_VALUE;
+            }
+        }
+    }
+
+    /** End the scope of a variable. */
+    private void endScope(int adr) {
+        LocalVar v = lvar[adr];
+        if (v != null) {
+            lvar[adr] = null;
+            if (v.start_pc != Character.MAX_VALUE) {
+                char length = (char)(curPc() - v.start_pc);
+                if (length < Character.MAX_VALUE) {
+                    v.length = length;
+                    putVar(v);
+                }
+            }
+        }
+        state.defined.excl(adr);
+    }
+
+    /** Put a live variable range into the buffer to be output to the
+     *  class file.
+     */
+    void putVar(LocalVar var) {
+        if (!varDebugInfo) return;
+        if ((var.sym.flags() & Flags.SYNTHETIC) != 0) return;
+        if (varBuffer == null)
+            varBuffer = new LocalVar[20];
+        else if (varBufferSize >= varBuffer.length) {
+            LocalVar[] newVarBuffer = new LocalVar[varBufferSize*2];
+            System.arraycopy(varBuffer, 0, newVarBuffer, 0, varBuffer.length);
+            varBuffer = newVarBuffer;
+        }
+        varBuffer[varBufferSize++] = var;
+    }
+
+    /** Previously live local variables, to be put into the variable table. */
+    LocalVar[] varBuffer;
+    int varBufferSize;
+
+    /** Create a new local variable address and return it.
+     */
+    private int newLocal(int typecode) {
+        int reg = nextreg;
+        int w = width(typecode);
+        nextreg = reg + w;
+        if (nextreg > max_locals) max_locals = nextreg;
+        return reg;
+    }
+
+    private int newLocal(Type type) {
+        return newLocal(typecode(type));
+    }
+
+    public int newLocal(VarSymbol v) {
+        int reg = v.adr = newLocal(v.erasure(types));
+        addLocalVar(v);
+        return reg;
+    }
+
+    /** Start a set of fresh registers.
+     */
+    public void newRegSegment() {
+        nextreg = max_locals;
+    }
+
+    /** End scopes of all variables with registers >= first.
+     */
+    public void endScopes(int first) {
+        int prevNextReg = nextreg;
+        nextreg = first;
+        for (int i = nextreg; i < prevNextReg; i++) endScope(i);
+    }
+
+/**************************************************************************
+ * static tables
+ *************************************************************************/
+
+    public static String mnem(int opcode) {
+        return Mneumonics.mnem[opcode];
+    }
+
+    private static class Mneumonics {
+        private final static String[] mnem = new String[ByteCodeCount];
+        static {
+            mnem[nop] = "nop";
+            mnem[aconst_null] = "aconst_null";
+            mnem[iconst_m1] = "iconst_m1";
+            mnem[iconst_0] = "iconst_0";
+            mnem[iconst_1] = "iconst_1";
+            mnem[iconst_2] = "iconst_2";
+            mnem[iconst_3] = "iconst_3";
+            mnem[iconst_4] = "iconst_4";
+            mnem[iconst_5] = "iconst_5";
+            mnem[lconst_0] = "lconst_0";
+            mnem[lconst_1] = "lconst_1";
+            mnem[fconst_0] = "fconst_0";
+            mnem[fconst_1] = "fconst_1";
+            mnem[fconst_2] = "fconst_2";
+            mnem[dconst_0] = "dconst_0";
+            mnem[dconst_1] = "dconst_1";
+            mnem[bipush] = "bipush";
+            mnem[sipush] = "sipush";
+            mnem[ldc1] = "ldc1";
+            mnem[ldc2] = "ldc2";
+            mnem[ldc2w] = "ldc2w";
+            mnem[iload] = "iload";
+            mnem[lload] = "lload";
+            mnem[fload] = "fload";
+            mnem[dload] = "dload";
+            mnem[aload] = "aload";
+            mnem[iload_0] = "iload_0";
+            mnem[lload_0] = "lload_0";
+            mnem[fload_0] = "fload_0";
+            mnem[dload_0] = "dload_0";
+            mnem[aload_0] = "aload_0";
+            mnem[iload_1] = "iload_1";
+            mnem[lload_1] = "lload_1";
+            mnem[fload_1] = "fload_1";
+            mnem[dload_1] = "dload_1";
+            mnem[aload_1] = "aload_1";
+            mnem[iload_2] = "iload_2";
+            mnem[lload_2] = "lload_2";
+            mnem[fload_2] = "fload_2";
+            mnem[dload_2] = "dload_2";
+            mnem[aload_2] = "aload_2";
+            mnem[iload_3] = "iload_3";
+            mnem[lload_3] = "lload_3";
+            mnem[fload_3] = "fload_3";
+            mnem[dload_3] = "dload_3";
+            mnem[aload_3] = "aload_3";
+            mnem[iaload] = "iaload";
+            mnem[laload] = "laload";
+            mnem[faload] = "faload";
+            mnem[daload] = "daload";
+            mnem[aaload] = "aaload";
+            mnem[baload] = "baload";
+            mnem[caload] = "caload";
+            mnem[saload] = "saload";
+            mnem[istore] = "istore";
+            mnem[lstore] = "lstore";
+            mnem[fstore] = "fstore";
+            mnem[dstore] = "dstore";
+            mnem[astore] = "astore";
+            mnem[istore_0] = "istore_0";
+            mnem[lstore_0] = "lstore_0";
+            mnem[fstore_0] = "fstore_0";
+            mnem[dstore_0] = "dstore_0";
+            mnem[astore_0] = "astore_0";
+            mnem[istore_1] = "istore_1";
+            mnem[lstore_1] = "lstore_1";
+            mnem[fstore_1] = "fstore_1";
+            mnem[dstore_1] = "dstore_1";
+            mnem[astore_1] = "astore_1";
+            mnem[istore_2] = "istore_2";
+            mnem[lstore_2] = "lstore_2";
+            mnem[fstore_2] = "fstore_2";
+            mnem[dstore_2] = "dstore_2";
+            mnem[astore_2] = "astore_2";
+            mnem[istore_3] = "istore_3";
+            mnem[lstore_3] = "lstore_3";
+            mnem[fstore_3] = "fstore_3";
+            mnem[dstore_3] = "dstore_3";
+            mnem[astore_3] = "astore_3";
+            mnem[iastore] = "iastore";
+            mnem[lastore] = "lastore";
+            mnem[fastore] = "fastore";
+            mnem[dastore] = "dastore";
+            mnem[aastore] = "aastore";
+            mnem[bastore] = "bastore";
+            mnem[castore] = "castore";
+            mnem[sastore] = "sastore";
+            mnem[pop] = "pop";
+            mnem[pop2] = "pop2";
+            mnem[dup] = "dup";
+            mnem[dup_x1] = "dup_x1";
+            mnem[dup_x2] = "dup_x2";
+            mnem[dup2] = "dup2";
+            mnem[dup2_x1] = "dup2_x1";
+            mnem[dup2_x2] = "dup2_x2";
+            mnem[swap] = "swap";
+            mnem[iadd] = "iadd";
+            mnem[ladd] = "ladd";
+            mnem[fadd] = "fadd";
+            mnem[dadd] = "dadd";
+            mnem[isub] = "isub";
+            mnem[lsub] = "lsub";
+            mnem[fsub] = "fsub";
+            mnem[dsub] = "dsub";
+            mnem[imul] = "imul";
+            mnem[lmul] = "lmul";
+            mnem[fmul] = "fmul";
+            mnem[dmul] = "dmul";
+            mnem[idiv] = "idiv";
+            mnem[ldiv] = "ldiv";
+            mnem[fdiv] = "fdiv";
+            mnem[ddiv] = "ddiv";
+            mnem[imod] = "imod";
+            mnem[lmod] = "lmod";
+            mnem[fmod] = "fmod";
+            mnem[dmod] = "dmod";
+            mnem[ineg] = "ineg";
+            mnem[lneg] = "lneg";
+            mnem[fneg] = "fneg";
+            mnem[dneg] = "dneg";
+            mnem[ishl] = "ishl";
+            mnem[lshl] = "lshl";
+            mnem[ishr] = "ishr";
+            mnem[lshr] = "lshr";
+            mnem[iushr] = "iushr";
+            mnem[lushr] = "lushr";
+            mnem[iand] = "iand";
+            mnem[land] = "land";
+            mnem[ior] = "ior";
+            mnem[lor] = "lor";
+            mnem[ixor] = "ixor";
+            mnem[lxor] = "lxor";
+            mnem[iinc] = "iinc";
+            mnem[i2l] = "i2l";
+            mnem[i2f] = "i2f";
+            mnem[i2d] = "i2d";
+            mnem[l2i] = "l2i";
+            mnem[l2f] = "l2f";
+            mnem[l2d] = "l2d";
+            mnem[f2i] = "f2i";
+            mnem[f2l] = "f2l";
+            mnem[f2d] = "f2d";
+            mnem[d2i] = "d2i";
+            mnem[d2l] = "d2l";
+            mnem[d2f] = "d2f";
+            mnem[int2byte] = "int2byte";
+            mnem[int2char] = "int2char";
+            mnem[int2short] = "int2short";
+            mnem[lcmp] = "lcmp";
+            mnem[fcmpl] = "fcmpl";
+            mnem[fcmpg] = "fcmpg";
+            mnem[dcmpl] = "dcmpl";
+            mnem[dcmpg] = "dcmpg";
+            mnem[ifeq] = "ifeq";
+            mnem[ifne] = "ifne";
+            mnem[iflt] = "iflt";
+            mnem[ifge] = "ifge";
+            mnem[ifgt] = "ifgt";
+            mnem[ifle] = "ifle";
+            mnem[if_icmpeq] = "if_icmpeq";
+            mnem[if_icmpne] = "if_icmpne";
+            mnem[if_icmplt] = "if_icmplt";
+            mnem[if_icmpge] = "if_icmpge";
+            mnem[if_icmpgt] = "if_icmpgt";
+            mnem[if_icmple] = "if_icmple";
+            mnem[if_acmpeq] = "if_acmpeq";
+            mnem[if_acmpne] = "if_acmpne";
+            mnem[goto_] = "goto_";
+            mnem[jsr] = "jsr";
+            mnem[ret] = "ret";
+            mnem[tableswitch] = "tableswitch";
+            mnem[lookupswitch] = "lookupswitch";
+            mnem[ireturn] = "ireturn";
+            mnem[lreturn] = "lreturn";
+            mnem[freturn] = "freturn";
+            mnem[dreturn] = "dreturn";
+            mnem[areturn] = "areturn";
+            mnem[return_] = "return_";
+            mnem[getstatic] = "getstatic";
+            mnem[putstatic] = "putstatic";
+            mnem[getfield] = "getfield";
+            mnem[putfield] = "putfield";
+            mnem[invokevirtual] = "invokevirtual";
+            mnem[invokespecial] = "invokespecial";
+            mnem[invokestatic] = "invokestatic";
+            mnem[invokeinterface] = "invokeinterface";
+            // mnem[___unused___] = "___unused___";
+            mnem[new_] = "new_";
+            mnem[newarray] = "newarray";
+            mnem[anewarray] = "anewarray";
+            mnem[arraylength] = "arraylength";
+            mnem[athrow] = "athrow";
+            mnem[checkcast] = "checkcast";
+            mnem[instanceof_] = "instanceof_";
+            mnem[monitorenter] = "monitorenter";
+            mnem[monitorexit] = "monitorexit";
+            mnem[wide] = "wide";
+            mnem[multianewarray] = "multianewarray";
+            mnem[if_acmp_null] = "if_acmp_null";
+            mnem[if_acmp_nonnull] = "if_acmp_nonnull";
+            mnem[goto_w] = "goto_w";
+            mnem[jsr_w] = "jsr_w";
+            mnem[breakpoint] = "breakpoint";
+        }
+    }
+}