public void visitWildcard(JCWildcard tree) {
                throw new AssertionError(this.getClass().getName());
            }
        }
        ComplexityScanner scanner = new ComplexityScanner();
        tree.accept(scanner);
        return scanner.complexity;
    }

    public void visitIf(JCIf tree) {
        int limit = code.nextreg;
        Chain thenExit = null;
        CondItem c = genCond(TreeInfo.skipParens(tree.cond),
                             CRT_FLOW_CONTROLLER);
        Chain elseChain = c.jumpFalse();
        if (!c.isFalse()) {
            code.resolve(c.trueJumps);
            genStat(tree.thenpart, env, CRT_STATEMENT | CRT_FLOW_TARGET);
            thenExit = code.branch(goto_);
        }
        if (elseChain != null) {
            code.resolve(elseChain);
            if (tree.elsepart != null)
                genStat(tree.elsepart, env,CRT_STATEMENT | CRT_FLOW_TARGET);
        }
        code.resolve(thenExit);
        code.endScopes(limit);
    }

    public void visitExec(JCExpressionStatement tree) {
        // Optimize x++ to ++x and x-- to --x.
        JCExpression e = tree.expr;
        switch (e.getTag()) {
            case JCTree.POSTINC:
                ((JCUnary) e).setTag(JCTree.PREINC);
                break;
            case JCTree.POSTDEC:
                ((JCUnary) e).setTag(JCTree.PREDEC);
                break;
        }
        genExpr(tree.expr, tree.expr.type).drop();
    }

    public void visitBreak(JCBreak tree) {
        Env<GenContext> targetEnv = unwind(tree.target, env);
        Assert.check(code.state.stacksize == 0);
        targetEnv.info.addExit(code.branch(goto_));
        endFinalizerGaps(env, targetEnv);
    }

    public void visitContinue(JCContinue tree) {
        Env<GenContext> targetEnv = unwind(tree.target, env);
        Assert.check(code.state.stacksize == 0);
        targetEnv.info.addCont(code.branch(goto_));
        endFinalizerGaps(env, targetEnv);
    }

    public void visitReturn(JCReturn tree) {
        int limit = code.nextreg;
        final Env<GenContext> targetEnv;
        if (tree.expr != null) {
            Item r = genExpr(tree.expr, pt).load();
            if (hasFinally(env.enclMethod, env)) {
                r = makeTemp(pt);
                r.store();
            }
            targetEnv = unwind(env.enclMethod, env);
            r.load();
            code.emitop0(ireturn + Code.truncate(Code.typecode(pt)));
        } else {
            targetEnv = unwind(env.enclMethod, env);
            code.emitop0(return_);
        }
        endFinalizerGaps(env, targetEnv);
        code.endScopes(limit);
    }

    public void visitThrow(JCThrow tree) {
        genExpr(tree.expr, tree.expr.type).load();
        code.emitop0(athrow);
    }

/* ************************************************************************
 * Visitor methods for expressions
 *************************************************************************/

    public void visitApply(JCMethodInvocation tree) {
        // Generate code for method.
        Item m = genExpr(tree.meth, methodType);
        // Generate code for all arguments, where the expected types are
        // the parameters of the method's external type (that is, any implicit
        // outer instance of a super(...) call appears as first parameter).
        genArgs(tree.args,
                TreeInfo.symbol(tree.meth).externalType(types).getParameterTypes());
        result = m.invoke();
    }

    public void visitConditional(JCConditional tree) {
        Chain thenExit = null;
        CondItem c = genCond(tree.cond, CRT_FLOW_CONTROLLER);
        Chain elseChain = c.jumpFalse();
        if (!c.isFalse()) {
            code.resolve(c.trueJumps);
            int startpc = genCrt ? code.curPc() : 0;
            genExpr(tree.truepart, pt).load();
            code.state.forceStackTop(tree.type);
            if (genCrt) code.crt.put(tree.truepart, CRT_FLOW_TARGET,
                                     startpc, code.curPc());
            thenExit = code.branch(goto_);
        }
        if (elseChain != null) {
            code.resolve(elseChain);
            int startpc = genCrt ? code.curPc() : 0;
            genExpr(tree.falsepart, pt).load();
            code.state.forceStackTop(tree.type);
            if (genCrt) code.crt.put(tree.falsepart, CRT_FLOW_TARGET,
                                     startpc, code.curPc());
        }
        code.resolve(thenExit);
        result = items.makeStackItem(pt);
    }

    public void visitNewClass(JCNewClass tree) {
        // Enclosing instances or anonymous classes should have been eliminated
        // by now.
        Assert.check(tree.encl == null && tree.def == null);

        code.emitop2(new_, makeRef(tree.pos(), tree.type));
        code.emitop0(dup);

        // Generate code for all arguments, where the expected types are
        // the parameters of the constructor's external type (that is,
        // any implicit outer instance appears as first parameter).
        genArgs(tree.args, tree.constructor.externalType(types).getParameterTypes());

        items.makeMemberItem(tree.constructor, true).invoke();
        result = items.makeStackItem(tree.type);
    }

    public void visitNewArray(JCNewArray tree) {

        if (tree.elems != null) {
            Type elemtype = types.elemtype(tree.type);
            loadIntConst(tree.elems.length());
            Item arr = makeNewArray(tree.pos(), tree.type, 1);
            int i = 0;
            for (List<JCExpression> l = tree.elems; l.nonEmpty(); l = l.tail) {
                arr.duplicate();
                loadIntConst(i);
                i++;
                genExpr(l.head, elemtype).load();
                items.makeIndexedItem(elemtype).store();
            }
            result = arr;
        } else {
            for (List<JCExpression> l = tree.dims; l.nonEmpty(); l = l.tail) {
                genExpr(l.head, syms.intType).load();
            }
            result = makeNewArray(tree.pos(), tree.type, tree.dims.length());
        }
    }
//where
        /** Generate code to create an array with given element type and number
         *  of dimensions.
         */
        Item makeNewArray(DiagnosticPosition pos, Type type, int ndims) {
            Type elemtype = types.elemtype(type);
            if (types.dimensions(type) > ClassFile.MAX_DIMENSIONS) {
                log.error(pos, "limit.dimensions");
                nerrs++;
            }
            int elemcode = Code.arraycode(elemtype);
            if (elemcode == 0 || (elemcode == 1 && ndims == 1)) {
                code.emitAnewarray(makeRef(pos, elemtype), type);
            } else if (elemcode == 1) {
                code.emitMultianewarray(ndims, makeRef(pos, type), type);
            } else {
                code.emitNewarray(elemcode, type);
            }
            return items.makeStackItem(type);
        }

    public void visitParens(JCParens tree) {
        result = genExpr(tree.expr, tree.expr.type);
    }

    public void visitAssign(JCAssign tree) {
        Item l = genExpr(tree.lhs, tree.lhs.type);
        genExpr(tree.rhs, tree.lhs.type).load();
        result = items.makeAssignItem(l);
    }

    public void visitAssignop(JCAssignOp tree) {
        OperatorSymbol operator = (OperatorSymbol) tree.operator;
        Item l;
        if (operator.opcode == string_add) {
            // Generate code to make a string buffer
            makeStringBuffer(tree.pos());

            // Generate code for first string, possibly save one
            // copy under buffer
            l = genExpr(tree.lhs, tree.lhs.type);
            if (l.width() > 0) {
                code.emitop0(dup_x1 + 3 * (l.width() - 1));
            }

            // Load first string and append to buffer.
            l.load();
            appendString(tree.lhs);

            // Append all other strings to buffer.
            appendStrings(tree.rhs);

            // Convert buffer to string.
            bufferToString(tree.pos());
        } else {
            // Generate code for first expression
            l = genExpr(tree.lhs, tree.lhs.type);

            // If we have an increment of -32768 to +32767 of a local
            // int variable we can use an incr instruction instead of
            // proceeding further.
            if ((tree.getTag() == JCTree.PLUS_ASG || tree.getTag() == JCTree.MINUS_ASG) &&
                l instanceof LocalItem &&
                tree.lhs.type.tag <= INT &&
                tree.rhs.type.tag <= INT &&
                tree.rhs.type.constValue() != null) {
                int ival = ((Number) tree.rhs.type.constValue()).intValue();
                if (tree.getTag() == JCTree.MINUS_ASG) ival = -ival;
                ((LocalItem)l).incr(ival);
                result = l;
                return;
            }
            // Otherwise, duplicate expression, load one copy
            // and complete binary operation.
            l.duplicate();
            l.coerce(operator.type.getParameterTypes().head).load();
            completeBinop(tree.lhs, tree.rhs, operator).coerce(tree.lhs.type);
        }
        result = items.makeAssignItem(l);
    }

    public void visitUnary(JCUnary tree) {
        OperatorSymbol operator = (OperatorSymbol)tree.operator;
        if (tree.getTag() == JCTree.NOT) {
            CondItem od = genCond(tree.arg, false);
            result = od.negate();
        } else {
            Item od = genExpr(tree.arg, operator.type.getParameterTypes().head);
            switch (tree.getTag()) {
            case JCTree.POS:
                result = od.load();
                break;
            case JCTree.NEG:
                result = od.load();
                code.emitop0(operator.opcode);
                break;
            case JCTree.COMPL:
                result = od.load();
                emitMinusOne(od.typecode);
                code.emitop0(operator.opcode);
                break;
            case JCTree.PREINC: case JCTree.PREDEC:
                od.duplicate();
                if (od instanceof LocalItem &&
                    (operator.opcode == iadd || operator.opcode == isub)) {
                    ((LocalItem)od).incr(tree.getTag() == JCTree.PREINC ? 1 : -1);
                    result = od;
                } else {
                    od.load();
                    code.emitop0(one(od.typecode));
                    code.emitop0(operator.opcode);
                    // Perform narrowing primitive conversion if byte,
                    // char, or short.  Fix for 4304655.
                    if (od.typecode != INTcode &&
                        Code.truncate(od.typecode) == INTcode)
                      code.emitop0(int2byte + od.typecode - BYTEcode);
                    result = items.makeAssignItem(od);
                }
                break;
            case JCTree.POSTINC: case JCTree.POSTDEC:
                od.duplicate();
                if (od instanceof LocalItem &&
                    (operator.opcode == iadd || operator.opcode == isub)) {
                    Item res = od.load();
                    ((LocalItem)od).incr(tree.getTag() == JCTree.POSTINC ? 1 : -1);
                    result = res;
                } else {
                    Item res = od.load();
                    od.stash(od.typecode);
                    code.emitop0(one(od.typecode));
                    code.emitop0(operator.opcode);
                    // Perform narrowing primitive conversion if byte,
                    // char, or short.  Fix for 4304655.
                    if (od.typecode != INTcode &&
                        Code.truncate(od.typecode) == INTcode)
                      code.emitop0(int2byte + od.typecode - BYTEcode);
                    od.store();
                    result = res;
                }
                break;
            case JCTree.NULLCHK:
                result = od.load();
                code.emitop0(dup);
                genNullCheck(tree.pos());
                break;
            default:
                Assert.error();
            }
        }
    }

    /** Generate a null check from the object value at stack top. */
    private void genNullCheck(DiagnosticPosition pos) {
        callMethod(pos, syms.objectType, names.getClass,
                   List.<Type>nil(), false);
        code.emitop0(pop);
    }

    public void visitBinary(JCBinary tree) {
        OperatorSymbol operator = (OperatorSymbol)tree.operator;
        if (operator.opcode == string_add) {
            // Create a string buffer.
            makeStringBuffer(tree.pos());
            // Append all strings to buffer.
            appendStrings(tree);
            // Convert buffer to string.
            bufferToString(tree.pos());
            result = items.makeStackItem(syms.stringType);
        } else if (tree.getTag() == JCTree.AND) {
            CondItem lcond = genCond(tree.lhs, CRT_FLOW_CONTROLLER);
            if (!lcond.isFalse()) {
                Chain falseJumps = lcond.jumpFalse();
                code.resolve(lcond.trueJumps);
                CondItem rcond = genCond(tree.rhs, CRT_FLOW_TARGET);
                result = items.
                    makeCondItem(rcond.opcode,
                                 rcond.trueJumps,
                                 Code.mergeChains(falseJumps,
                                                  rcond.falseJumps));
            } else {
                result = lcond;
            }
        } else if (tree.getTag() == JCTree.OR) {
            CondItem lcond = genCond(tree.lhs, CRT_FLOW_CONTROLLER);
            if (!lcond.isTrue()) {
                Chain trueJumps = lcond.jumpTrue();
                code.resolve(lcond.falseJumps);
                CondItem rcond = genCond(tree.rhs, CRT_FLOW_TARGET);
                result = items.
                    makeCondItem(rcond.opcode,
                                 Code.mergeChains(trueJumps, rcond.trueJumps),
                                 rcond.falseJumps);
            } else {
                result = lcond;
            }
        } else {
            Item od = genExpr(tree.lhs, operator.type.getParameterTypes().head);
            od.load();
            result = completeBinop(tree.lhs, tree.rhs, operator);
        }
    }
//where
        /** Make a new string buffer.
         */
        void makeStringBuffer(DiagnosticPosition pos) {
            code.emitop2(new_, makeRef(pos, stringBufferType));
            code.emitop0(dup);
            callMethod(
                pos, stringBufferType, names.init, List.<Type>nil(), false);
        }

        /** Append value (on tos) to string buffer (on tos - 1).
         */
        void appendString(JCTree tree) {
            Type t = tree.type.baseType();
            if (t.tag > lastBaseTag && t.tsym != syms.stringType.tsym) {
                t = syms.objectType;
            }
            items.makeMemberItem(getStringBufferAppend(tree, t), false).invoke();
        }
        Symbol getStringBufferAppend(JCTree tree, Type t) {
            Assert.checkNull(t.constValue());
            Symbol method = stringBufferAppend.get(t);
            if (method == null) {
                method = rs.resolveInternalMethod(tree.pos(),
                                                  attrEnv,
                                                  stringBufferType,
                                                  names.append,
                                                  List.of(t),
                                                  null);
                stringBufferAppend.put(t, method);
            }
            return method;
        }

        /** Add all strings in tree to string buffer.
         */
        void appendStrings(JCTree tree) {
            tree = TreeInfo.skipParens(tree);
            if (tree.getTag() == JCTree.PLUS && tree.type.constValue() == null) {
                JCBinary op = (JCBinary) tree;
                if (op.operator.kind == MTH &&
                    ((OperatorSymbol) op.operator).opcode == string_add) {
                    appendStrings(op.lhs);
                    appendStrings(op.rhs);
                    return;
                }
            }
            genExpr(tree, tree.type).load();
            appendString(tree);
        }

        /** Convert string buffer on tos to string.
         */
        void bufferToString(DiagnosticPosition pos) {
            callMethod(
                pos,
                stringBufferType,
                names.toString,
                List.<Type>nil(),
                false);
        }

        /** Complete generating code for operation, with left operand
         *  already on stack.
         *  @param lhs       The tree representing the left operand.
         *  @param rhs       The tree representing the right operand.
         *  @param operator  The operator symbol.
         */
        Item completeBinop(JCTree lhs, JCTree rhs, OperatorSymbol operator) {
            MethodType optype = (MethodType)operator.type;
            int opcode = operator.opcode;
            if (opcode >= if_icmpeq && opcode <= if_icmple &&
                rhs.type.constValue() instanceof Number &&
                ((Number) rhs.type.constValue()).intValue() == 0) {
                opcode = opcode + (ifeq - if_icmpeq);
            } else if (opcode >= if_acmpeq && opcode <= if_acmpne &&
                       TreeInfo.isNull(rhs)) {
                opcode = opcode + (if_acmp_null - if_acmpeq);
            } else {
                // The expected type of the right operand is
                // the second parameter type of the operator, except for
                // shifts with long shiftcount, where we convert the opcode
                // to a short shift and the expected type to int.
                Type rtype = operator.erasure(types).getParameterTypes().tail.head;
                if (opcode >= ishll && opcode <= lushrl) {
                    opcode = opcode + (ishl - ishll);
                    rtype = syms.intType;
                }
                // Generate code for right operand and load.
                genExpr(rhs, rtype).load();
                // If there are two consecutive opcode instructions,
                // emit the first now.
                if (opcode >= (1 << preShift)) {
                    code.emitop0(opcode >> preShift);
                    opcode = opcode & 0xFF;
                }
            }
            if (opcode >= ifeq && opcode <= if_acmpne ||
                opcode == if_acmp_null || opcode == if_acmp_nonnull) {
                return items.makeCondItem(opcode);
            } else {
                code.emitop0(opcode);
                return items.makeStackItem(optype.restype);
            }
        }

    public void visitTypeCast(JCTypeCast tree) {
        result = genExpr(tree.expr, tree.clazz.type).load();
        // Additional code is only needed if we cast to a reference type
        // which is not statically a supertype of the expression's type.
        // For basic types, the coerce(...) in genExpr(...) will do
        // the conversion.
        if (tree.clazz.type.tag > lastBaseTag &&
            types.asSuper(tree.expr.type, tree.clazz.type.tsym) == null) {
            code.emitop2(checkcast, makeRef(tree.pos(), tree.clazz.type));
        }
    }

    public void visitWildcard(JCWildcard tree) {
        throw new AssertionError(this.getClass().getName());
    }

    public void visitTypeTest(JCInstanceOf tree) {
        genExpr(tree.expr, tree.expr.type).load();
        code.emitop2(instanceof_, makeRef(tree.pos(), tree.clazz.type));
        result = items.makeStackItem(syms.booleanType);
    }

    public void visitIndexed(JCArrayAccess tree) {
        genExpr(tree.indexed, tree.indexed.type).load();
        genExpr(tree.index, syms.intType).load();
        result = items.makeIndexedItem(tree.type);
    }

    public void visitIdent(JCIdent tree) {
        Symbol sym = tree.sym;
        if (tree.name == names._this || tree.name == names._super) {
            Item res = tree.name == names._this
                ? items.makeThisItem()
                : items.makeSuperItem();
            if (sym.kind == MTH) {
                // Generate code to address the constructor.
                res.load();
                res = items.makeMemberItem(sym, true);
            }
            result = res;
        } else if (sym.kind == VAR && sym.owner.kind == MTH) {
            result = items.makeLocalItem((VarSymbol)sym);
        } else if ((sym.flags() & STATIC) != 0) {
            if (!isAccessSuper(env.enclMethod))
                sym = binaryQualifier(sym, env.enclClass.type);
            result = items.makeStaticItem(sym);
        } else {
            items.makeThisItem().load();
            sym = binaryQualifier(sym, env.enclClass.type);
            result = items.makeMemberItem(sym, (sym.flags() & PRIVATE) != 0);
        }
    }

    public void visitSelect(JCFieldAccess tree) {
        Symbol sym = tree.sym;

        if (tree.name == names._class) {
            Assert.check(target.hasClassLiterals());
            code.emitop2(ldc2, makeRef(tree.pos(), tree.selected.type));
            result = items.makeStackItem(pt);
            return;
        }

        Symbol ssym = TreeInfo.symbol(tree.selected);

        // Are we selecting via super?
        boolean selectSuper =
            ssym != null && (ssym.kind == TYP || ssym.name == names._super);

        // Are we accessing a member of the superclass in an access method
        // resulting from a qualified super?
        boolean accessSuper = isAccessSuper(env.enclMethod);

        Item base = (selectSuper)
            ? items.makeSuperItem()
            : genExpr(tree.selected, tree.selected.type);

        if (sym.kind == VAR && ((VarSymbol) sym).getConstValue() != null) {
            // We are seeing a variable that is constant but its selecting
            // expression is not.
            if ((sym.flags() & STATIC) != 0) {
                if (!selectSuper && (ssym == null || ssym.kind != TYP))
                    base = base.load();
                base.drop();
            } else {
                base.load();
                genNullCheck(tree.selected.pos());
            }
            result = items.
                makeImmediateItem(sym.type, ((VarSymbol) sym).getConstValue());
        } else {
            if (!accessSuper)
                sym = binaryQualifier(sym, tree.selected.type);
            if ((sym.flags() & STATIC) != 0) {
                if (!selectSuper && (ssym == null || ssym.kind != TYP))
                    base = base.load();
                base.drop();
                result = items.makeStaticItem(sym);
            } else {
                base.load();
                if (sym == syms.lengthVar) {
                    code.emitop0(arraylength);
                    result = items.makeStackItem(syms.intType);
                } else {
                    result = items.
                        makeMemberItem(sym,
                                       (sym.flags() & PRIVATE) != 0 ||
                                       selectSuper || accessSuper);
                }
            }
        }
    }

    public void visitLiteral(JCLiteral tree) {
        if (tree.type.tag == TypeTags.BOT) {
            code.emitop0(aconst_null);
            if (types.dimensions(pt) > 1) {
                code.emitop2(checkcast, makeRef(tree.pos(), pt));
                result = items.makeStackItem(pt);
            } else {
                result = items.makeStackItem(tree.type);
            }
        }
        else
            result = items.makeImmediateItem(tree.type, tree.value);
    }

    public void visitLetExpr(LetExpr tree) {
        int limit = code.nextreg;
        genStats(tree.defs, env);
        result = genExpr(tree.expr, tree.expr.type).load();
        code.endScopes(limit);
    }

/* ************************************************************************
 * main method
 *************************************************************************/

    /** Generate code for a class definition.
     *  @param env   The attribution environment that belongs to the
     *               outermost class containing this class definition.
     *               We need this for resolving some additional symbols.
     *  @param cdef  The tree representing the class definition.
     *  @return      True if code is generated with no errors.
     */
    public boolean genClass(Env<AttrContext> env, JCClassDecl cdef) {
        try {
            attrEnv = env;
            ClassSymbol c = cdef.sym;
            this.toplevel = env.toplevel;
            this.endPositions = toplevel.endPositions;
            // If this is a class definition requiring Miranda methods,
            // add them.
            if (generateIproxies &&
                (c.flags() & (INTERFACE|ABSTRACT)) == ABSTRACT
                && !allowGenerics // no Miranda methods available with generics
                )
                implementInterfaceMethods(c);
            cdef.defs = normalizeDefs(cdef.defs, c);
            c.pool = pool;
            pool.reset();
            Env<GenContext> localEnv =
                new Env<GenContext>(cdef, new GenContext());
            localEnv.toplevel = env.toplevel;
            localEnv.enclClass = cdef;
            for (List<JCTree> l = cdef.defs; l.nonEmpty(); l = l.tail) {
                genDef(l.head, localEnv);
            }
            if (pool.numEntries() > Pool.MAX_ENTRIES) {
                log.error(cdef.pos(), "limit.pool");
                nerrs++;
            }
            if (nerrs != 0) {
                // if errors, discard code
                for (List<JCTree> l = cdef.defs; l.nonEmpty(); l = l.tail) {
                    if (l.head.getTag() == JCTree.METHODDEF)
                        ((JCMethodDecl) l.head).sym.code = null;
                }
            }
            cdef.defs = List.nil(); // discard trees
            return nerrs == 0;
        } finally {
            // note: this method does NOT support recursion.
            attrEnv = null;
            this.env = null;
            toplevel = null;
            endPositions = null;
            nerrs = 0;
        }
    }

/* ************************************************************************
 * Auxiliary classes
 *************************************************************************/

    /** An abstract class for finalizer generation.
     */
    abstract class GenFinalizer {
        /** Generate code to clean up when unwinding. */
        abstract void gen();

        /** Generate code to clean up at last. */
        abstract void genLast();

        /** Does this finalizer have some nontrivial cleanup to perform? */
        boolean hasFinalizer() { return true; }
    }

    /** code generation contexts,
     *  to be used as type parameter for environments.
     */
    static class GenContext {

        /** A chain for all unresolved jumps that exit the current environment.
         */
        Chain exit = null;

        /** A chain for all unresolved jumps that continue in the
         *  current environment.
         */
        Chain cont = null;

        /** A closure that generates the finalizer of the current environment.
         *  Only set for Synchronized and Try contexts.
         */
        GenFinalizer finalize = null;

        /** Is this a switch statement?  If so, allocate registers
         * even when the variable declaration is unreachable.
         */
        boolean isSwitch = false;

        /** A list buffer containing all gaps in the finalizer range,
         *  where a catch all exception should not apply.
         */
        ListBuffer<Integer> gaps = null;

        /** Add given chain to exit chain.
         */
        void addExit(Chain c)  {
            exit = Code.mergeChains(c, exit);
        }