public void visitClassDef(JCClassDecl tree) {}
            }
            new Patcher().scan(body);
        }
        /**
         * A statement of the form
         *
         * <pre>
         *     for ( T v : coll ) stmt ;
         * </pre>
         *
         * (where coll implements Iterable<? extends T>) gets translated to
         *
         * <pre>
         *     for ( Iterator<? extends T> #i = coll.iterator(); #i.hasNext(); ) {
         *         T v = (T) #i.next();
         *         stmt;
         *     }
         * </pre>
         *
         * where #i is a freshly named synthetic local variable.
         */
        private void visitIterableForeachLoop(JCEnhancedForLoop tree) {
            make_at(tree.expr.pos());
            Type iteratorTarget = syms.objectType;
            Type iterableType = types.asSuper(types.upperBound(tree.expr.type),
                                              syms.iterableType.tsym);
            if (iterableType.getTypeArguments().nonEmpty())
                iteratorTarget = types.erasure(iterableType.getTypeArguments().head);
            Type eType = tree.expr.type;
            tree.expr.type = types.erasure(eType);
            if (eType.tag == TYPEVAR && eType.getUpperBound().isCompound())
                tree.expr = make.TypeCast(types.erasure(iterableType), tree.expr);
            Symbol iterator = lookupMethod(tree.expr.pos(),
                                           names.iterator,
                                           types.erasure(syms.iterableType),
                                           List.<Type>nil());
            VarSymbol itvar = new VarSymbol(0, names.fromString("i" + target.syntheticNameChar()),
                                            types.erasure(iterator.type.getReturnType()),
                                            currentMethodSym);
            JCStatement init = make.
                VarDef(itvar,
                       make.App(make.Select(tree.expr, iterator)));
            Symbol hasNext = lookupMethod(tree.expr.pos(),
                                          names.hasNext,
                                          itvar.type,
                                          List.<Type>nil());
            JCMethodInvocation cond = make.App(make.Select(make.Ident(itvar), hasNext));
            Symbol next = lookupMethod(tree.expr.pos(),
                                       names.next,
                                       itvar.type,
                                       List.<Type>nil());
            JCExpression vardefinit = make.App(make.Select(make.Ident(itvar), next));
            if (tree.var.type.isPrimitive())
                vardefinit = make.TypeCast(types.upperBound(iteratorTarget), vardefinit);
            else
                vardefinit = make.TypeCast(tree.var.type, vardefinit);
            JCVariableDecl indexDef = (JCVariableDecl)make.VarDef(tree.var.mods,
                                                  tree.var.name,
                                                  tree.var.vartype,
                                                  vardefinit).setType(tree.var.type);
            indexDef.sym = tree.var.sym;
            JCBlock body = make.Block(0, List.of(indexDef, tree.body));
            body.endpos = TreeInfo.endPos(tree.body);
            result = translate(make.
                ForLoop(List.of(init),
                        cond,
                        List.<JCExpressionStatement>nil(),
                        body));
            patchTargets(body, tree, result);
        }

    public void visitVarDef(JCVariableDecl tree) {
        MethodSymbol oldMethodSym = currentMethodSym;
        tree.mods = translate(tree.mods);
        tree.vartype = translate(tree.vartype);
        if (currentMethodSym == null) {
            // A class or instance field initializer.
            currentMethodSym =
                new MethodSymbol((tree.mods.flags&STATIC) | BLOCK,
                                 names.empty, null,
                                 currentClass);
        }
        if (tree.init != null) tree.init = translate(tree.init, tree.type);
        result = tree;
        currentMethodSym = oldMethodSym;
    }

    public void visitBlock(JCBlock tree) {
        MethodSymbol oldMethodSym = currentMethodSym;
        if (currentMethodSym == null) {
            // Block is a static or instance initializer.
            currentMethodSym =
                new MethodSymbol(tree.flags | BLOCK,
                                 names.empty, null,
                                 currentClass);
        }
        super.visitBlock(tree);
        currentMethodSym = oldMethodSym;
    }

    public void visitDoLoop(JCDoWhileLoop tree) {
        tree.body = translate(tree.body);
        tree.cond = translate(tree.cond, syms.booleanType);
        result = tree;
    }

    public void visitWhileLoop(JCWhileLoop tree) {
        tree.cond = translate(tree.cond, syms.booleanType);
        tree.body = translate(tree.body);
        result = tree;
    }

    public void visitForLoop(JCForLoop tree) {
        tree.init = translate(tree.init);
        if (tree.cond != null)
            tree.cond = translate(tree.cond, syms.booleanType);
        tree.step = translate(tree.step);
        tree.body = translate(tree.body);
        result = tree;
    }

    public void visitReturn(JCReturn tree) {
        if (tree.expr != null)
            tree.expr = translate(tree.expr,
                                  types.erasure(currentMethodDef
                                                .restype.type));
        result = tree;
    }

    public void visitSwitch(JCSwitch tree) {
        Type selsuper = types.supertype(tree.selector.type);
        boolean enumSwitch = selsuper != null &&
            (tree.selector.type.tsym.flags() & ENUM) != 0;
        boolean stringSwitch = selsuper != null &&
            types.isSameType(tree.selector.type, syms.stringType);
        Type target = enumSwitch ? tree.selector.type :
            (stringSwitch? syms.stringType : syms.intType);
        tree.selector = translate(tree.selector, target);
        tree.cases = translateCases(tree.cases);
        if (enumSwitch) {
            result = visitEnumSwitch(tree);
        } else if (stringSwitch) {
            result = visitStringSwitch(tree);
        } else {
            result = tree;
        }
    }

    public JCTree visitEnumSwitch(JCSwitch tree) {
        TypeSymbol enumSym = tree.selector.type.tsym;
        EnumMapping map = mapForEnum(tree.pos(), enumSym);
        make_at(tree.pos());
        Symbol ordinalMethod = lookupMethod(tree.pos(),
                                            names.ordinal,
                                            tree.selector.type,
                                            List.<Type>nil());
        JCArrayAccess selector = make.Indexed(map.mapVar,
                                        make.App(make.Select(tree.selector,
                                                             ordinalMethod)));
        ListBuffer<JCCase> cases = new ListBuffer<JCCase>();
        for (JCCase c : tree.cases) {
            if (c.pat != null) {
                VarSymbol label = (VarSymbol)TreeInfo.symbol(c.pat);
                JCLiteral pat = map.forConstant(label);
                cases.append(make.Case(pat, c.stats));
            } else {
                cases.append(c);
            }
        }
        JCSwitch enumSwitch = make.Switch(selector, cases.toList());
        patchTargets(enumSwitch, tree, enumSwitch);
        return enumSwitch;
    }

    public JCTree visitStringSwitch(JCSwitch tree) {
        List<JCCase> caseList = tree.getCases();
        int alternatives = caseList.size();

        if (alternatives == 0) { // Strange but legal possibility
            return make.at(tree.pos()).Exec(attr.makeNullCheck(tree.getExpression()));
        } else {
            /*
             * The general approach used is to translate a single
             * string switch statement into a series of two chained
             * switch statements: the first a synthesized statement
             * switching on the argument string's hash value and
             * computing a string's position in the list of original
             * case labels, if any, followed by a second switch on the
             * computed integer value.  The second switch has the same
             * code structure as the original string switch statement
             * except that the string case labels are replaced with
             * positional integer constants starting at 0.
             *
             * The first switch statement can be thought of as an
             * inlined map from strings to their position in the case
             * label list.  An alternate implementation would use an
             * actual Map for this purpose, as done for enum switches.
             *
             * With some additional effort, it would be possible to
             * use a single switch statement on the hash code of the
             * argument, but care would need to be taken to preserve
             * the proper control flow in the presence of hash
             * collisions and other complications, such as
             * fallthroughs.  Switch statements with one or two
             * alternatives could also be specially translated into
             * if-then statements to omit the computation of the hash
             * code.
             *
             * The generated code assumes that the hashing algorithm
             * of String is the same in the compilation environment as
             * in the environment the code will run in.  The string
             * hashing algorithm in the SE JDK has been unchanged
             * since at least JDK 1.2.  Since the algorithm has been
             * specified since that release as well, it is very
             * unlikely to be changed in the future.
             *
             * Different hashing algorithms, such as the length of the
             * strings or a perfect hashing algorithm over the
             * particular set of case labels, could potentially be
             * used instead of String.hashCode.
             */

            ListBuffer<JCStatement> stmtList = new ListBuffer<JCStatement>();

            // Map from String case labels to their original position in
            // the list of case labels.
            Map<String, Integer> caseLabelToPosition =
                new LinkedHashMap<String, Integer>(alternatives + 1, 1.0f);

            // Map of hash codes to the string case labels having that hashCode.
            Map<Integer, Set<String>> hashToString =
                new LinkedHashMap<Integer, Set<String>>(alternatives + 1, 1.0f);

            int casePosition = 0;
            for(JCCase oneCase : caseList) {
                JCExpression expression = oneCase.getExpression();

                if (expression != null) { // expression for a "default" case is null
                    expression = TreeInfo.skipParens(expression);
                    String labelExpr = (String) expression.type.constValue();
                    Integer mapping = caseLabelToPosition.put(labelExpr, casePosition);
                    Assert.checkNull(mapping);
                    int hashCode = labelExpr.hashCode();

                    Set<String> stringSet = hashToString.get(hashCode);
                    if (stringSet == null) {
                        stringSet = new LinkedHashSet<String>(1, 1.0f);
                        stringSet.add(labelExpr);
                        hashToString.put(hashCode, stringSet);
                    } else {
                        boolean added = stringSet.add(labelExpr);
                        Assert.check(added);
                    }
                }
                casePosition++;
            }

            // Synthesize a switch statement that has the effect of
            // mapping from a string to the integer position of that
            // string in the list of case labels.  This is done by
            // switching on the hashCode of the string followed by an
            // if-then-else chain comparing the input for equality
            // with all the case labels having that hash value.

            /*
             * s$ = top of stack;
             * tmp$ = -1;
             * switch($s.hashCode()) {
             *     case caseLabel.hashCode:
             *         if (s$.equals("caseLabel_1")
             *           tmp$ = caseLabelToPosition("caseLabel_1");
             *         else if (s$.equals("caseLabel_2"))
             *           tmp$ = caseLabelToPosition("caseLabel_2");
             *         ...
             *         break;
             * ...
             * }
             */

            VarSymbol dollar_s = new VarSymbol(FINAL|SYNTHETIC,
                                               names.fromString("s" + tree.pos + target.syntheticNameChar()),
                                               syms.stringType,
                                               currentMethodSym);
            stmtList.append(make.at(tree.pos()).VarDef(dollar_s, tree.getExpression()).setType(dollar_s.type));

            VarSymbol dollar_tmp = new VarSymbol(SYNTHETIC,
                                                 names.fromString("tmp" + tree.pos + target.syntheticNameChar()),
                                                 syms.intType,
                                                 currentMethodSym);
            JCVariableDecl dollar_tmp_def =
                (JCVariableDecl)make.VarDef(dollar_tmp, make.Literal(INT, -1)).setType(dollar_tmp.type);
            dollar_tmp_def.init.type = dollar_tmp.type = syms.intType;
            stmtList.append(dollar_tmp_def);
            ListBuffer<JCCase> caseBuffer = ListBuffer.lb();
            // hashCode will trigger nullcheck on original switch expression
            JCMethodInvocation hashCodeCall = makeCall(make.Ident(dollar_s),
                                                       names.hashCode,
                                                       List.<JCExpression>nil()).setType(syms.intType);
            JCSwitch switch1 = make.Switch(hashCodeCall,
                                        caseBuffer.toList());
            for(Map.Entry<Integer, Set<String>> entry : hashToString.entrySet()) {
                int hashCode = entry.getKey();
                Set<String> stringsWithHashCode = entry.getValue();
                Assert.check(stringsWithHashCode.size() >= 1);

                JCStatement elsepart = null;
                for(String caseLabel : stringsWithHashCode ) {
                    JCMethodInvocation stringEqualsCall = makeCall(make.Ident(dollar_s),
                                                                   names.equals,
                                                                   List.<JCExpression>of(make.Literal(caseLabel)));
                    elsepart = make.If(stringEqualsCall,
                                       make.Exec(make.Assign(make.Ident(dollar_tmp),
                                                             make.Literal(caseLabelToPosition.get(caseLabel))).
                                                 setType(dollar_tmp.type)),
                                       elsepart);
                }

                ListBuffer<JCStatement> lb = ListBuffer.lb();
                JCBreak breakStmt = make.Break(null);
                breakStmt.target = switch1;
                lb.append(elsepart).append(breakStmt);

                caseBuffer.append(make.Case(make.Literal(hashCode), lb.toList()));
            }

            switch1.cases = caseBuffer.toList();
            stmtList.append(switch1);

            // Make isomorphic switch tree replacing string labels
            // with corresponding integer ones from the label to
            // position map.

            ListBuffer<JCCase> lb = ListBuffer.lb();
            JCSwitch switch2 = make.Switch(make.Ident(dollar_tmp), lb.toList());
            for(JCCase oneCase : caseList ) {
                // Rewire up old unlabeled break statements to the
                // replacement switch being created.
                patchTargets(oneCase, tree, switch2);

                boolean isDefault = (oneCase.getExpression() == null);
                JCExpression caseExpr;
                if (isDefault)
                    caseExpr = null;
                else {
                    caseExpr = make.Literal(caseLabelToPosition.get((String)TreeInfo.skipParens(oneCase.
                                                                                                getExpression()).
                                                                    type.constValue()));
                }

                lb.append(make.Case(caseExpr,
                                    oneCase.getStatements()));
            }

            switch2.cases = lb.toList();
            stmtList.append(switch2);

            return make.Block(0L, stmtList.toList());
        }
    }

    public void visitNewArray(JCNewArray tree) {
        tree.elemtype = translate(tree.elemtype);
        for (List<JCExpression> t = tree.dims; t.tail != null; t = t.tail)
            if (t.head != null) t.head = translate(t.head, syms.intType);
        tree.elems = translate(tree.elems, types.elemtype(tree.type));
        result = tree;
    }

    public void visitSelect(JCFieldAccess tree) {
        // need to special case-access of the form C.super.x
        // these will always need an access method.
        boolean qualifiedSuperAccess =
            tree.selected.getTag() == JCTree.SELECT &&
            TreeInfo.name(tree.selected) == names._super;
        tree.selected = translate(tree.selected);
        if (tree.name == names._class)
            result = classOf(tree.selected);
        else if (tree.name == names._this || tree.name == names._super)
            result = makeThis(tree.pos(), tree.selected.type.tsym);
        else
            result = access(tree.sym, tree, enclOp, qualifiedSuperAccess);
    }

    public void visitLetExpr(LetExpr tree) {
        tree.defs = translateVarDefs(tree.defs);
        tree.expr = translate(tree.expr, tree.type);
        result = tree;
    }

    // There ought to be nothing to rewrite here;
    // we don't generate code.
    public void visitAnnotation(JCAnnotation tree) {
        result = tree;
    }

    @Override
    public void visitTry(JCTry tree) {
        if (tree.resources.isEmpty()) {
            super.visitTry(tree);
        } else {
            result = makeTwrTry(tree);
        }
    }

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

    /** Translate a toplevel class and return a list consisting of
     *  the translated class and translated versions of all inner classes.
     *  @param env   The attribution environment current at the class definition.
     *               We need this for resolving some additional symbols.
     *  @param cdef  The tree representing the class definition.
     */
    public List<JCTree> translateTopLevelClass(Env<AttrContext> env, JCTree cdef, TreeMaker make) {
        ListBuffer<JCTree> translated = null;
        try {
            attrEnv = env;
            this.make = make;
            endPositions = env.toplevel.endPositions;
            currentClass = null;
            currentMethodDef = null;
            outermostClassDef = (cdef.getTag() == JCTree.CLASSDEF) ? (JCClassDecl)cdef : null;
            outermostMemberDef = null;
            this.translated = new ListBuffer<JCTree>();
            classdefs = new HashMap<ClassSymbol,JCClassDecl>();
            actualSymbols = new HashMap<Symbol,Symbol>();
            freevarCache = new HashMap<ClassSymbol,List<VarSymbol>>();
            proxies = new Scope(syms.noSymbol);
            twrVars = new Scope(syms.noSymbol);
            outerThisStack = List.nil();
            accessNums = new HashMap<Symbol,Integer>();
            accessSyms = new HashMap<Symbol,MethodSymbol[]>();
            accessConstrs = new HashMap<Symbol,MethodSymbol>();
            accessConstrTags = List.nil();
            accessed = new ListBuffer<Symbol>();
            translate(cdef, (JCExpression)null);
            for (List<Symbol> l = accessed.toList(); l.nonEmpty(); l = l.tail)
                makeAccessible(l.head);
            for (EnumMapping map : enumSwitchMap.values())
                map.translate();
            checkConflicts(this.translated.toList());
            checkAccessConstructorTags();
            translated = this.translated;
        } finally {
            // note that recursive invocations of this method fail hard
            attrEnv = null;
            this.make = null;
            endPositions = null;
            currentClass = null;
            currentMethodDef = null;
            outermostClassDef = null;
            outermostMemberDef = null;
            this.translated = null;
            classdefs = null;
            actualSymbols = null;
            freevarCache = null;
            proxies = null;
            outerThisStack = null;
            accessNums = null;
            accessSyms = null;
            accessConstrs = null;
            accessConstrTags = null;
            accessed = null;
            enumSwitchMap.clear();
        }
        return translated.toList();
    }

    //////////////////////////////////////////////////////////////
    // The following contributed by Borland for bootstrapping purposes
    //////////////////////////////////////////////////////////////
    private void addEnumCompatibleMembers(JCClassDecl cdef) {
        make_at(null);

        // Add the special enum fields
        VarSymbol ordinalFieldSym = addEnumOrdinalField(cdef);
        VarSymbol nameFieldSym = addEnumNameField(cdef);

        // Add the accessor methods for name and ordinal
        MethodSymbol ordinalMethodSym = addEnumFieldOrdinalMethod(cdef, ordinalFieldSym);
        MethodSymbol nameMethodSym = addEnumFieldNameMethod(cdef, nameFieldSym);

        // Add the toString method
        addEnumToString(cdef, nameFieldSym);

        // Add the compareTo method
        addEnumCompareTo(cdef, ordinalFieldSym);
    }

    private VarSymbol addEnumOrdinalField(JCClassDecl cdef) {
        VarSymbol ordinal = new VarSymbol(PRIVATE|FINAL|SYNTHETIC,
                                          names.fromString("$ordinal"),
                                          syms.intType,
                                          cdef.sym);
        cdef.sym.members().enter(ordinal);
        cdef.defs = cdef.defs.prepend(make.VarDef(ordinal, null));
        return ordinal;
    }

    private VarSymbol addEnumNameField(JCClassDecl cdef) {
        VarSymbol name = new VarSymbol(PRIVATE|FINAL|SYNTHETIC,
                                          names.fromString("$name"),
                                          syms.stringType,
                                          cdef.sym);
        cdef.sym.members().enter(name);
        cdef.defs = cdef.defs.prepend(make.VarDef(name, null));
        return name;
    }

    private MethodSymbol addEnumFieldOrdinalMethod(JCClassDecl cdef, VarSymbol ordinalSymbol) {
        // Add the accessor methods for ordinal
        Symbol ordinalSym = lookupMethod(cdef.pos(),
                                         names.ordinal,
                                         cdef.type,
                                         List.<Type>nil());

        Assert.check(ordinalSym instanceof MethodSymbol);

        JCStatement ret = make.Return(make.Ident(ordinalSymbol));
        cdef.defs = cdef.defs.append(make.MethodDef((MethodSymbol)ordinalSym,
                                                    make.Block(0L, List.of(ret))));

        return (MethodSymbol)ordinalSym;
    }

    private MethodSymbol addEnumFieldNameMethod(JCClassDecl cdef, VarSymbol nameSymbol) {
        // Add the accessor methods for name
        Symbol nameSym = lookupMethod(cdef.pos(),
                                   names._name,
                                   cdef.type,
                                   List.<Type>nil());

        Assert.check(nameSym instanceof MethodSymbol);

        JCStatement ret = make.Return(make.Ident(nameSymbol));

        cdef.defs = cdef.defs.append(make.MethodDef((MethodSymbol)nameSym,
                                                    make.Block(0L, List.of(ret))));

        return (MethodSymbol)nameSym;
    }