< prev index next >

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java

Print this page
rev 51258 : imported patch switch.diff


   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.tools.javac.comp;
  27 
  28 import java.util.*;



  29 

  30 import com.sun.tools.javac.code.*;
  31 import com.sun.tools.javac.code.Kinds.KindSelector;
  32 import com.sun.tools.javac.code.Scope.WriteableScope;
  33 import com.sun.tools.javac.jvm.*;
  34 import com.sun.tools.javac.main.Option.PkgInfo;
  35 import com.sun.tools.javac.resources.CompilerProperties.Fragments;
  36 import com.sun.tools.javac.tree.*;
  37 import com.sun.tools.javac.util.*;
  38 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
  39 import com.sun.tools.javac.util.List;
  40 
  41 import com.sun.tools.javac.code.Symbol.*;
  42 import com.sun.tools.javac.code.Symbol.OperatorSymbol.AccessCode;
  43 import com.sun.tools.javac.resources.CompilerProperties.Errors;
  44 import com.sun.tools.javac.tree.JCTree.*;
  45 import com.sun.tools.javac.code.Type.*;
  46 
  47 import com.sun.tools.javac.jvm.Target;
  48 import com.sun.tools.javac.tree.EndPosTable;
  49 
  50 import static com.sun.tools.javac.code.Flags.*;
  51 import static com.sun.tools.javac.code.Flags.BLOCK;
  52 import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE;
  53 import static com.sun.tools.javac.code.TypeTag.*;
  54 import static com.sun.tools.javac.code.Kinds.Kind.*;
  55 import static com.sun.tools.javac.code.Symbol.OperatorSymbol.AccessCode.DEREF;
  56 import static com.sun.tools.javac.jvm.ByteCodes.*;




  57 import static com.sun.tools.javac.tree.JCTree.JCOperatorExpression.OperandPos.LEFT;
  58 import static com.sun.tools.javac.tree.JCTree.Tag.*;
  59 
  60 /** This pass translates away some syntactic sugar: inner classes,
  61  *  class literals, assertions, foreach loops, etc.
  62  *
  63  *  <p><b>This is NOT part of any supported API.
  64  *  If you write code that depends on this, you do so at your own risk.
  65  *  This code and its internal interfaces are subject to change or
  66  *  deletion without notice.</b>
  67  */
  68 public class Lower extends TreeTranslator {
  69     protected static final Context.Key<Lower> lowerKey = new Context.Key<>();
  70 
  71     public static Lower instance(Context context) {
  72         Lower instance = context.get(lowerKey);
  73         if (instance == null)
  74             instance = new Lower(context);
  75         return instance;
  76     }


 246         abstract void visitSymbol(Symbol _sym);
 247 
 248         /** If tree refers to a class instance creation expression
 249          *  add all free variables of the freshly created class.
 250          */
 251         public void visitNewClass(JCNewClass tree) {
 252             ClassSymbol c = (ClassSymbol)tree.constructor.owner;
 253             addFreeVars(c);
 254             super.visitNewClass(tree);
 255         }
 256 
 257         /** If tree refers to a superclass constructor call,
 258          *  add all free variables of the superclass.
 259          */
 260         public void visitApply(JCMethodInvocation tree) {
 261             if (TreeInfo.name(tree.meth) == names._super) {
 262                 addFreeVars((ClassSymbol) TreeInfo.symbol(tree.meth).owner);
 263             }
 264             super.visitApply(tree);
 265         }







 266     }
 267 
 268     /**
 269      * Lower-specific subclass of {@code BasicFreeVarCollector}.
 270      */
 271     class FreeVarCollector extends BasicFreeVarCollector {
 272 
 273         /** The owner of the local class.
 274          */
 275         Symbol owner;
 276 
 277         /** The local class.
 278          */
 279         ClassSymbol clazz;
 280 
 281         /** The list of owner's variables accessed from within the local class,
 282          *  without any duplicates.
 283          */
 284         List<VarSymbol> fvs;
 285 


 347                 tree.selected.type.tsym != clazz &&
 348                 outerThisStack.head != null)
 349                 visitSymbol(outerThisStack.head);
 350             super.visitSelect(tree);
 351         }
 352 
 353         /** If tree refers to a superclass constructor call,
 354          *  add all free variables of the superclass.
 355          */
 356         public void visitApply(JCMethodInvocation tree) {
 357             if (TreeInfo.name(tree.meth) == names._super) {
 358                 Symbol constructor = TreeInfo.symbol(tree.meth);
 359                 ClassSymbol c = (ClassSymbol)constructor.owner;
 360                 if (c.hasOuterInstance() &&
 361                     !tree.meth.hasTag(SELECT) &&
 362                     outerThisStack.head != null)
 363                     visitSymbol(outerThisStack.head);
 364             }
 365             super.visitApply(tree);
 366         }

 367     }
 368 
 369     ClassSymbol ownerToCopyFreeVarsFrom(ClassSymbol c) {
 370         if (!c.isLocal()) {
 371             return null;
 372         }
 373         Symbol currentOwner = c.owner;
 374         while (currentOwner.owner.kind.matches(KindSelector.TYP) && currentOwner.isLocal()) {
 375             currentOwner = currentOwner.owner;
 376         }
 377         if (currentOwner.owner.kind.matches(KindSelector.VAL_MTH) && c.isSubClass(currentOwner, types)) {
 378             return (ClassSymbol)currentOwner;
 379         }
 380         return null;
 381     }
 382 
 383     /** Return the variables accessed from within a local class, which
 384      *  are declared in the local class' owner.
 385      *  (in reverse order of first access).
 386      */


3323     }
3324 
3325     public void visitForLoop(JCForLoop tree) {
3326         tree.init = translate(tree.init);
3327         if (tree.cond != null)
3328             tree.cond = translate(tree.cond, syms.booleanType);
3329         tree.step = translate(tree.step);
3330         tree.body = translate(tree.body);
3331         result = tree;
3332     }
3333 
3334     public void visitReturn(JCReturn tree) {
3335         if (tree.expr != null)
3336             tree.expr = translate(tree.expr,
3337                                   types.erasure(currentMethodDef
3338                                                 .restype.type));
3339         result = tree;
3340     }
3341 
3342     public void visitSwitch(JCSwitch tree) {







































3343         Type selsuper = types.supertype(tree.selector.type);
3344         boolean enumSwitch = selsuper != null &&
3345             (tree.selector.type.tsym.flags() & ENUM) != 0;
3346         boolean stringSwitch = selsuper != null &&
3347             types.isSameType(tree.selector.type, syms.stringType);
3348         Type target = enumSwitch ? tree.selector.type :
3349             (stringSwitch? syms.stringType : syms.intType);
3350         tree.selector = translate(tree.selector, target);
3351         tree.cases = translateCases(tree.cases);
3352         if (enumSwitch) {
3353             result = visitEnumSwitch(tree);
3354         } else if (stringSwitch) {
3355             result = visitStringSwitch(tree);
3356         } else {
3357             result = tree;
3358         }
3359     }
3360 
3361     public JCTree visitEnumSwitch(JCSwitch tree) {
3362         TypeSymbol enumSym = tree.selector.type.tsym;
3363         EnumMapping map = mapForEnum(tree.pos(), enumSym);
3364         make_at(tree.pos());
3365         Symbol ordinalMethod = lookupMethod(tree.pos(),
3366                                             names.ordinal,
3367                                             tree.selector.type,
3368                                             List.nil());
3369         JCArrayAccess selector = make.Indexed(map.mapVar,
3370                                         make.App(make.Select(tree.selector,
3371                                                              ordinalMethod)));
3372         ListBuffer<JCCase> cases = new ListBuffer<>();
3373         for (JCCase c : tree.cases) {
3374             if (c.pat != null) {
3375                 VarSymbol label = (VarSymbol)TreeInfo.symbol(c.pat);
3376                 JCLiteral pat = map.forConstant(label);
3377                 cases.append(make.Case(pat, c.stats));
3378             } else {
3379                 cases.append(c);
3380             }
3381         }
3382         JCSwitch enumSwitch = make.Switch(selector, cases.toList());
3383         patchTargets(enumSwitch, tree, enumSwitch);
3384         return enumSwitch;
3385     }
3386 
3387     public JCTree visitStringSwitch(JCSwitch tree) {
3388         List<JCCase> caseList = tree.getCases();
3389         int alternatives = caseList.size();
3390 
3391         if (alternatives == 0) { // Strange but legal possibility
3392             return make.at(tree.pos()).Exec(attr.makeNullCheck(tree.getExpression()));
3393         } else {
3394             /*
3395              * The general approach used is to translate a single
3396              * string switch statement into a series of two chained
3397              * switch statements: the first a synthesized statement


3425              * since at least JDK 1.2.  Since the algorithm has been
3426              * specified since that release as well, it is very
3427              * unlikely to be changed in the future.
3428              *
3429              * Different hashing algorithms, such as the length of the
3430              * strings or a perfect hashing algorithm over the
3431              * particular set of case labels, could potentially be
3432              * used instead of String.hashCode.
3433              */
3434 
3435             ListBuffer<JCStatement> stmtList = new ListBuffer<>();
3436 
3437             // Map from String case labels to their original position in
3438             // the list of case labels.
3439             Map<String, Integer> caseLabelToPosition = new LinkedHashMap<>(alternatives + 1, 1.0f);
3440 
3441             // Map of hash codes to the string case labels having that hashCode.
3442             Map<Integer, Set<String>> hashToString = new LinkedHashMap<>(alternatives + 1, 1.0f);
3443 
3444             int casePosition = 0;
3445             for(JCCase oneCase : caseList) {
3446                 JCExpression expression = oneCase.getExpression();
3447 
3448                 if (expression != null) { // expression for a "default" case is null


3449                     String labelExpr = (String) expression.type.constValue();
3450                     Integer mapping = caseLabelToPosition.put(labelExpr, casePosition);
3451                     Assert.checkNull(mapping);
3452                     int hashCode = labelExpr.hashCode();
3453 
3454                     Set<String> stringSet = hashToString.get(hashCode);
3455                     if (stringSet == null) {
3456                         stringSet = new LinkedHashSet<>(1, 1.0f);
3457                         stringSet.add(labelExpr);
3458                         hashToString.put(hashCode, stringSet);
3459                     } else {
3460                         boolean added = stringSet.add(labelExpr);
3461                         Assert.check(added);
3462                     }
3463                 }
3464                 casePosition++;
3465             }
3466 
3467             // Synthesize a switch statement that has the effect of
3468             // mapping from a string to the integer position of that


3512                 Set<String> stringsWithHashCode = entry.getValue();
3513                 Assert.check(stringsWithHashCode.size() >= 1);
3514 
3515                 JCStatement elsepart = null;
3516                 for(String caseLabel : stringsWithHashCode ) {
3517                     JCMethodInvocation stringEqualsCall = makeCall(make.Ident(dollar_s),
3518                                                                    names.equals,
3519                                                                    List.of(make.Literal(caseLabel)));
3520                     elsepart = make.If(stringEqualsCall,
3521                                        make.Exec(make.Assign(make.Ident(dollar_tmp),
3522                                                              make.Literal(caseLabelToPosition.get(caseLabel))).
3523                                                  setType(dollar_tmp.type)),
3524                                        elsepart);
3525                 }
3526 
3527                 ListBuffer<JCStatement> lb = new ListBuffer<>();
3528                 JCBreak breakStmt = make.Break(null);
3529                 breakStmt.target = switch1;
3530                 lb.append(elsepart).append(breakStmt);
3531 
3532                 caseBuffer.append(make.Case(make.Literal(hashCode), lb.toList()));
3533             }
3534 
3535             switch1.cases = caseBuffer.toList();
3536             stmtList.append(switch1);
3537 
3538             // Make isomorphic switch tree replacing string labels
3539             // with corresponding integer ones from the label to
3540             // position map.
3541 
3542             ListBuffer<JCCase> lb = new ListBuffer<>();
3543             JCSwitch switch2 = make.Switch(make.Ident(dollar_tmp), lb.toList());
3544             for(JCCase oneCase : caseList ) {
3545                 // Rewire up old unlabeled break statements to the
3546                 // replacement switch being created.
3547                 patchTargets(oneCase, tree, switch2);
3548 
3549                 boolean isDefault = (oneCase.getExpression() == null);
3550                 JCExpression caseExpr;
3551                 if (isDefault)
3552                     caseExpr = null;
3553                 else {
3554                     caseExpr = make.Literal(caseLabelToPosition.get((String)TreeInfo.skipParens(oneCase.
3555                                                                                                 getExpression()).
3556                                                                     type.constValue()));
3557                 }
3558 
3559                 lb.append(make.Case(caseExpr,
3560                                     oneCase.getStatements()));
3561             }
3562 
3563             switch2.cases = lb.toList();
3564             stmtList.append(switch2);
3565 
3566             return make.Block(0L, stmtList.toList());
3567         }
3568     }
3569 






































































3570     public void visitNewArray(JCNewArray tree) {
3571         tree.elemtype = translate(tree.elemtype);
3572         for (List<JCExpression> t = tree.dims; t.tail != null; t = t.tail)
3573             if (t.head != null) t.head = translate(t.head, syms.intType);
3574         tree.elems = translate(tree.elems, types.elemtype(tree.type));
3575         result = tree;
3576     }
3577 
3578     public void visitSelect(JCFieldAccess tree) {
3579         // need to special case-access of the form C.super.x
3580         // these will always need an access method, unless C
3581         // is a default interface subclassed by the current class.
3582         boolean qualifiedSuperAccess =
3583             tree.selected.hasTag(SELECT) &&
3584             TreeInfo.name(tree.selected) == names._super &&
3585             !types.isDirectSuperInterface(((JCFieldAccess)tree.selected).selected.type.tsym, currentClass);
3586         tree.selected = translate(tree.selected);
3587         if (tree.name == names._class) {
3588             result = classOf(tree.selected);
3589         }
3590         else if (tree.name == names._super &&
3591                 types.isDirectSuperInterface(tree.selected.type.tsym, currentClass)) {
3592             //default super call!! Not a classic qualified super call
3593             TypeSymbol supSym = tree.selected.type.tsym;
3594             Assert.checkNonNull(types.asSuper(currentClass.type, supSym));
3595             result = tree;
3596         }
3597         else if (tree.name == names._this || tree.name == names._super) {
3598             result = makeThis(tree.pos(), tree.selected.type.tsym);
3599         }
3600         else
3601             result = access(tree.sym, tree, enclOp, qualifiedSuperAccess);
3602     }
3603 
3604     public void visitLetExpr(LetExpr tree) {
3605         tree.defs = translateVarDefs(tree.defs);
3606         tree.expr = translate(tree.expr, tree.type);
3607         result = tree;
3608     }
3609 
3610     // There ought to be nothing to rewrite here;
3611     // we don't generate code.
3612     public void visitAnnotation(JCAnnotation tree) {
3613         result = tree;
3614     }
3615 
3616     @Override
3617     public void visitTry(JCTry tree) {
3618         if (tree.resources.nonEmpty()) {
3619             result = makeTwrTry(tree);
3620             return;
3621         }
3622 
3623         boolean hasBody = tree.body.getStatements().nonEmpty();
3624         boolean hasCatchers = tree.catchers.nonEmpty();
3625         boolean hasFinally = tree.finalizer != null &&




   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.tools.javac.comp;
  27 
  28 import java.util.*;
  29 import java.util.Map.Entry;
  30 import java.util.function.Function;
  31 import java.util.stream.Stream;
  32 
  33 import com.sun.source.tree.CaseTree.CaseKind;
  34 import com.sun.tools.javac.code.*;
  35 import com.sun.tools.javac.code.Kinds.KindSelector;
  36 import com.sun.tools.javac.code.Scope.WriteableScope;
  37 import com.sun.tools.javac.jvm.*;
  38 import com.sun.tools.javac.main.Option.PkgInfo;
  39 import com.sun.tools.javac.resources.CompilerProperties.Fragments;
  40 import com.sun.tools.javac.tree.*;
  41 import com.sun.tools.javac.util.*;
  42 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
  43 import com.sun.tools.javac.util.List;
  44 
  45 import com.sun.tools.javac.code.Symbol.*;
  46 import com.sun.tools.javac.code.Symbol.OperatorSymbol.AccessCode;
  47 import com.sun.tools.javac.resources.CompilerProperties.Errors;
  48 import com.sun.tools.javac.tree.JCTree.*;
  49 import com.sun.tools.javac.code.Type.*;
  50 
  51 import com.sun.tools.javac.jvm.Target;
  52 import com.sun.tools.javac.tree.EndPosTable;
  53 
  54 import static com.sun.tools.javac.code.Flags.*;
  55 import static com.sun.tools.javac.code.Flags.BLOCK;
  56 import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE;
  57 import static com.sun.tools.javac.code.TypeTag.*;
  58 import static com.sun.tools.javac.code.Kinds.Kind.*;
  59 import static com.sun.tools.javac.code.Symbol.OperatorSymbol.AccessCode.DEREF;
  60 import static com.sun.tools.javac.jvm.ByteCodes.*;
  61 import com.sun.tools.javac.tree.JCTree.JCBreak;
  62 import com.sun.tools.javac.tree.JCTree.JCCase;
  63 import com.sun.tools.javac.tree.JCTree.JCExpression;
  64 import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
  65 import static com.sun.tools.javac.tree.JCTree.JCOperatorExpression.OperandPos.LEFT;
  66 import static com.sun.tools.javac.tree.JCTree.Tag.*;
  67 
  68 /** This pass translates away some syntactic sugar: inner classes,
  69  *  class literals, assertions, foreach loops, etc.
  70  *
  71  *  <p><b>This is NOT part of any supported API.
  72  *  If you write code that depends on this, you do so at your own risk.
  73  *  This code and its internal interfaces are subject to change or
  74  *  deletion without notice.</b>
  75  */
  76 public class Lower extends TreeTranslator {
  77     protected static final Context.Key<Lower> lowerKey = new Context.Key<>();
  78 
  79     public static Lower instance(Context context) {
  80         Lower instance = context.get(lowerKey);
  81         if (instance == null)
  82             instance = new Lower(context);
  83         return instance;
  84     }


 254         abstract void visitSymbol(Symbol _sym);
 255 
 256         /** If tree refers to a class instance creation expression
 257          *  add all free variables of the freshly created class.
 258          */
 259         public void visitNewClass(JCNewClass tree) {
 260             ClassSymbol c = (ClassSymbol)tree.constructor.owner;
 261             addFreeVars(c);
 262             super.visitNewClass(tree);
 263         }
 264 
 265         /** If tree refers to a superclass constructor call,
 266          *  add all free variables of the superclass.
 267          */
 268         public void visitApply(JCMethodInvocation tree) {
 269             if (TreeInfo.name(tree.meth) == names._super) {
 270                 addFreeVars((ClassSymbol) TreeInfo.symbol(tree.meth).owner);
 271             }
 272             super.visitApply(tree);
 273         }
 274 
 275         @Override
 276         public void visitBreak(JCBreak tree) {
 277             if (tree.isValueBreak())
 278                 scan(tree.value);
 279         }
 280 
 281     }
 282 
 283     /**
 284      * Lower-specific subclass of {@code BasicFreeVarCollector}.
 285      */
 286     class FreeVarCollector extends BasicFreeVarCollector {
 287 
 288         /** The owner of the local class.
 289          */
 290         Symbol owner;
 291 
 292         /** The local class.
 293          */
 294         ClassSymbol clazz;
 295 
 296         /** The list of owner's variables accessed from within the local class,
 297          *  without any duplicates.
 298          */
 299         List<VarSymbol> fvs;
 300 


 362                 tree.selected.type.tsym != clazz &&
 363                 outerThisStack.head != null)
 364                 visitSymbol(outerThisStack.head);
 365             super.visitSelect(tree);
 366         }
 367 
 368         /** If tree refers to a superclass constructor call,
 369          *  add all free variables of the superclass.
 370          */
 371         public void visitApply(JCMethodInvocation tree) {
 372             if (TreeInfo.name(tree.meth) == names._super) {
 373                 Symbol constructor = TreeInfo.symbol(tree.meth);
 374                 ClassSymbol c = (ClassSymbol)constructor.owner;
 375                 if (c.hasOuterInstance() &&
 376                     !tree.meth.hasTag(SELECT) &&
 377                     outerThisStack.head != null)
 378                     visitSymbol(outerThisStack.head);
 379             }
 380             super.visitApply(tree);
 381         }
 382 
 383     }
 384 
 385     ClassSymbol ownerToCopyFreeVarsFrom(ClassSymbol c) {
 386         if (!c.isLocal()) {
 387             return null;
 388         }
 389         Symbol currentOwner = c.owner;
 390         while (currentOwner.owner.kind.matches(KindSelector.TYP) && currentOwner.isLocal()) {
 391             currentOwner = currentOwner.owner;
 392         }
 393         if (currentOwner.owner.kind.matches(KindSelector.VAL_MTH) && c.isSubClass(currentOwner, types)) {
 394             return (ClassSymbol)currentOwner;
 395         }
 396         return null;
 397     }
 398 
 399     /** Return the variables accessed from within a local class, which
 400      *  are declared in the local class' owner.
 401      *  (in reverse order of first access).
 402      */


3339     }
3340 
3341     public void visitForLoop(JCForLoop tree) {
3342         tree.init = translate(tree.init);
3343         if (tree.cond != null)
3344             tree.cond = translate(tree.cond, syms.booleanType);
3345         tree.step = translate(tree.step);
3346         tree.body = translate(tree.body);
3347         result = tree;
3348     }
3349 
3350     public void visitReturn(JCReturn tree) {
3351         if (tree.expr != null)
3352             tree.expr = translate(tree.expr,
3353                                   types.erasure(currentMethodDef
3354                                                 .restype.type));
3355         result = tree;
3356     }
3357 
3358     public void visitSwitch(JCSwitch tree) {
3359         //expand multiple label cases:
3360         ListBuffer<JCCase> cases = new ListBuffer<>();
3361 
3362         for (JCCase c : tree.cases) {
3363             switch (c.pats.size()) {
3364                 case 0: //default
3365                 case 1: //single label
3366                     cases.append(c);
3367                     break;
3368                 default: //multiple labels, expand:
3369                     //case C1, C2, C3: ...
3370                     //=>
3371                     //case C1:
3372                     //case C2:
3373                     //case C3: ...
3374                     List<JCExpression> patterns = c.pats;
3375                     while (patterns.tail.nonEmpty()) {
3376                         cases.append(make_at(c.pos()).Case(JCCase.STATEMENT,
3377                                                            List.of(patterns.head),
3378                                                            List.nil(),
3379                                                            null));
3380                         patterns = patterns.tail;
3381                     }
3382                     c.pats = patterns;
3383                     cases.append(c);
3384                     break;
3385             }
3386         }
3387 
3388         for (JCCase c : cases) {
3389             if (c.caseKind == JCCase.RULE && c.completesNormally) {
3390                 JCBreak b = make_at(c.pos()).Break(null);
3391                 b.target = tree;
3392                 c.stats = c.stats.append(b);
3393             }
3394         }
3395 
3396         tree.cases = cases.toList();
3397 
3398         Type selsuper = types.supertype(tree.selector.type);
3399         boolean enumSwitch = selsuper != null &&
3400             (tree.selector.type.tsym.flags() & ENUM) != 0;
3401         boolean stringSwitch = selsuper != null &&
3402             types.isSameType(tree.selector.type, syms.stringType);
3403         Type target = enumSwitch ? tree.selector.type :
3404             (stringSwitch? syms.stringType : syms.intType);
3405         tree.selector = translate(tree.selector, target);
3406         tree.cases = translateCases(tree.cases);
3407         if (enumSwitch) {
3408             result = visitEnumSwitch(tree);
3409         } else if (stringSwitch) {
3410             result = visitStringSwitch(tree);
3411         } else {
3412             result = tree;
3413         }
3414     }
3415 
3416     public JCTree visitEnumSwitch(JCSwitch tree) {
3417         TypeSymbol enumSym = tree.selector.type.tsym;
3418         EnumMapping map = mapForEnum(tree.pos(), enumSym);
3419         make_at(tree.pos());
3420         Symbol ordinalMethod = lookupMethod(tree.pos(),
3421                                             names.ordinal,
3422                                             tree.selector.type,
3423                                             List.nil());
3424         JCArrayAccess selector = make.Indexed(map.mapVar,
3425                                         make.App(make.Select(tree.selector,
3426                                                              ordinalMethod)));
3427         ListBuffer<JCCase> cases = new ListBuffer<>();
3428         for (JCCase c : tree.cases) {
3429             if (c.pats.nonEmpty()) {
3430                 VarSymbol label = (VarSymbol)TreeInfo.symbol(c.pats.head);
3431                 JCLiteral pat = map.forConstant(label);
3432                 cases.append(make.Case(JCCase.STATEMENT, List.of(pat), c.stats, null));
3433             } else {
3434                 cases.append(c);
3435             }
3436         }
3437         JCSwitch enumSwitch = make.Switch(selector, cases.toList());
3438         patchTargets(enumSwitch, tree, enumSwitch);
3439         return enumSwitch;
3440     }
3441 
3442     public JCTree visitStringSwitch(JCSwitch tree) {
3443         List<JCCase> caseList = tree.getCases();
3444         int alternatives = caseList.size();
3445 
3446         if (alternatives == 0) { // Strange but legal possibility
3447             return make.at(tree.pos()).Exec(attr.makeNullCheck(tree.getExpression()));
3448         } else {
3449             /*
3450              * The general approach used is to translate a single
3451              * string switch statement into a series of two chained
3452              * switch statements: the first a synthesized statement


3480              * since at least JDK 1.2.  Since the algorithm has been
3481              * specified since that release as well, it is very
3482              * unlikely to be changed in the future.
3483              *
3484              * Different hashing algorithms, such as the length of the
3485              * strings or a perfect hashing algorithm over the
3486              * particular set of case labels, could potentially be
3487              * used instead of String.hashCode.
3488              */
3489 
3490             ListBuffer<JCStatement> stmtList = new ListBuffer<>();
3491 
3492             // Map from String case labels to their original position in
3493             // the list of case labels.
3494             Map<String, Integer> caseLabelToPosition = new LinkedHashMap<>(alternatives + 1, 1.0f);
3495 
3496             // Map of hash codes to the string case labels having that hashCode.
3497             Map<Integer, Set<String>> hashToString = new LinkedHashMap<>(alternatives + 1, 1.0f);
3498 
3499             int casePosition = 0;


3500 
3501             for(JCCase oneCase : caseList) {
3502                 if (oneCase.pats.nonEmpty()) { // pats is empty for a "default" case
3503                     JCExpression expression = oneCase.pats.head;
3504                     String labelExpr = (String) expression.type.constValue();
3505                     Integer mapping = caseLabelToPosition.put(labelExpr, casePosition);
3506                     Assert.checkNull(mapping);
3507                     int hashCode = labelExpr.hashCode();
3508 
3509                     Set<String> stringSet = hashToString.get(hashCode);
3510                     if (stringSet == null) {
3511                         stringSet = new LinkedHashSet<>(1, 1.0f);
3512                         stringSet.add(labelExpr);
3513                         hashToString.put(hashCode, stringSet);
3514                     } else {
3515                         boolean added = stringSet.add(labelExpr);
3516                         Assert.check(added);
3517                     }
3518                 }
3519                 casePosition++;
3520             }
3521 
3522             // Synthesize a switch statement that has the effect of
3523             // mapping from a string to the integer position of that


3567                 Set<String> stringsWithHashCode = entry.getValue();
3568                 Assert.check(stringsWithHashCode.size() >= 1);
3569 
3570                 JCStatement elsepart = null;
3571                 for(String caseLabel : stringsWithHashCode ) {
3572                     JCMethodInvocation stringEqualsCall = makeCall(make.Ident(dollar_s),
3573                                                                    names.equals,
3574                                                                    List.of(make.Literal(caseLabel)));
3575                     elsepart = make.If(stringEqualsCall,
3576                                        make.Exec(make.Assign(make.Ident(dollar_tmp),
3577                                                              make.Literal(caseLabelToPosition.get(caseLabel))).
3578                                                  setType(dollar_tmp.type)),
3579                                        elsepart);
3580                 }
3581 
3582                 ListBuffer<JCStatement> lb = new ListBuffer<>();
3583                 JCBreak breakStmt = make.Break(null);
3584                 breakStmt.target = switch1;
3585                 lb.append(elsepart).append(breakStmt);
3586 
3587                 caseBuffer.append(make.Case(JCCase.STATEMENT, List.of(make.Literal(hashCode)), lb.toList(), null));
3588             }
3589 
3590             switch1.cases = caseBuffer.toList();
3591             stmtList.append(switch1);
3592 
3593             // Make isomorphic switch tree replacing string labels
3594             // with corresponding integer ones from the label to
3595             // position map.
3596 
3597             ListBuffer<JCCase> lb = new ListBuffer<>();
3598             JCSwitch switch2 = make.Switch(make.Ident(dollar_tmp), lb.toList());
3599             for(JCCase oneCase : caseList ) {
3600                 // Rewire up old unlabeled break statements to the
3601                 // replacement switch being created.
3602                 patchTargets(oneCase, tree, switch2);
3603 
3604                 boolean isDefault = (oneCase.pats.isEmpty());
3605                 JCExpression caseExpr;
3606                 if (isDefault)
3607                     caseExpr = null;
3608                 else {
3609                     caseExpr = make.Literal(caseLabelToPosition.get((String)TreeInfo.skipParens(oneCase.pats.head).

3610                                                                     type.constValue()));
3611                 }
3612 
3613                 lb.append(make.Case(JCCase.STATEMENT, caseExpr == null ? List.nil() : List.of(caseExpr),
3614                                     oneCase.getStatements(), null));
3615             }
3616 
3617             switch2.cases = lb.toList();
3618             stmtList.append(switch2);
3619 
3620             return make.Block(0L, stmtList.toList());
3621         }
3622     }
3623 
3624     @Override
3625     public void visitSwitchExpression(JCSwitchExpression tree) {
3626         //translates switch expression to statement switch:
3627         //switch (selector) {
3628         //    case C: break value;
3629         //    ...
3630         //}
3631         //=>
3632         //(letexpr T exprswitch$;
3633         //         switch (selector) {
3634         //             case C: { exprswitch$ = value; break; }
3635         //         }
3636         //         exprswitch$
3637         //)
3638         VarSymbol dollar_switchexpr = new VarSymbol(Flags.FINAL|Flags.SYNTHETIC,
3639                            names.fromString("exprswitch" + tree.pos + target.syntheticNameChar()),
3640                            tree.type,
3641                            currentMethodSym);
3642 
3643         ListBuffer<JCStatement> stmtList = new ListBuffer<>();
3644 
3645         stmtList.append(make.at(tree.pos()).VarDef(dollar_switchexpr, null).setType(dollar_switchexpr.type));
3646         JCSwitch switchStatement = make.Switch(tree.selector, null);
3647         switchStatement.cases =
3648                 tree.cases.stream()
3649                           .map(c -> convertCase(dollar_switchexpr, switchStatement, tree, c))
3650                           .collect(List.collector());
3651         if (tree.cases.stream().noneMatch(c -> c.pats.isEmpty())) {
3652             JCThrow thr = make.Throw(makeNewClass(syms.incompatibleClassChangeErrorType,
3653                                                   List.nil()));
3654             JCCase c = make.Case(JCCase.STATEMENT, List.nil(), List.of(thr), null);
3655             switchStatement.cases = switchStatement.cases.append(c);
3656         }
3657 
3658         stmtList.append(translate(switchStatement));
3659 
3660         result = make.LetExpr(stmtList.toList(), make.Ident(dollar_switchexpr))
3661                      .setType(dollar_switchexpr.type);
3662     }
3663         //where:
3664         private JCCase convertCase(VarSymbol dollar_switchexpr, JCSwitch switchStatement,
3665                                    JCSwitchExpression switchExpr, JCCase c) {
3666             make.at(c.pos());
3667             ListBuffer<JCStatement> statements = new ListBuffer<>();
3668             statements.addAll(new TreeTranslator() {
3669                 @Override
3670                 public void visitLambda(JCLambda tree) {}
3671                 @Override
3672                 public void visitClassDef(JCClassDecl tree) {}
3673                 @Override
3674                 public void visitMethodDef(JCMethodDecl tree) {}
3675                 @Override
3676                 public void visitBreak(JCBreak tree) {
3677                     if (tree.target == switchExpr) {
3678                         tree.target = switchStatement;
3679                         JCExpressionStatement assignment =
3680                                 make.Exec(make.Assign(make.Ident(dollar_switchexpr),
3681                                                       translate(tree.value))
3682                                               .setType(dollar_switchexpr.type));
3683                         result = make.Block(0, List.of(assignment,
3684                                                        tree));
3685                         tree.value = null;
3686                     } else {
3687                         result = tree;
3688                     }
3689                 }
3690             }.translate(c.stats));
3691             return make.Case(JCCase.STATEMENT, c.pats, statements.toList(), null);
3692         }
3693 
3694     public void visitNewArray(JCNewArray tree) {
3695         tree.elemtype = translate(tree.elemtype);
3696         for (List<JCExpression> t = tree.dims; t.tail != null; t = t.tail)
3697             if (t.head != null) t.head = translate(t.head, syms.intType);
3698         tree.elems = translate(tree.elems, types.elemtype(tree.type));
3699         result = tree;
3700     }
3701 
3702     public void visitSelect(JCFieldAccess tree) {
3703         // need to special case-access of the form C.super.x
3704         // these will always need an access method, unless C
3705         // is a default interface subclassed by the current class.
3706         boolean qualifiedSuperAccess =
3707             tree.selected.hasTag(SELECT) &&
3708             TreeInfo.name(tree.selected) == names._super &&
3709             !types.isDirectSuperInterface(((JCFieldAccess)tree.selected).selected.type.tsym, currentClass);
3710         tree.selected = translate(tree.selected);
3711         if (tree.name == names._class) {
3712             result = classOf(tree.selected);
3713         }
3714         else if (tree.name == names._super &&
3715                 types.isDirectSuperInterface(tree.selected.type.tsym, currentClass)) {
3716             //default super call!! Not a classic qualified super call
3717             TypeSymbol supSym = tree.selected.type.tsym;
3718             Assert.checkNonNull(types.asSuper(currentClass.type, supSym));
3719             result = tree;
3720         }
3721         else if (tree.name == names._this || tree.name == names._super) {
3722             result = makeThis(tree.pos(), tree.selected.type.tsym);
3723         }
3724         else
3725             result = access(tree.sym, tree, enclOp, qualifiedSuperAccess);
3726     }
3727 
3728     public void visitLetExpr(LetExpr tree) {
3729         tree.defs = translate(tree.defs);
3730         tree.expr = translate(tree.expr, tree.type);
3731         result = tree;
3732     }
3733 
3734     // There ought to be nothing to rewrite here;
3735     // we don't generate code.
3736     public void visitAnnotation(JCAnnotation tree) {
3737         result = tree;
3738     }
3739 
3740     @Override
3741     public void visitTry(JCTry tree) {
3742         if (tree.resources.nonEmpty()) {
3743             result = makeTwrTry(tree);
3744             return;
3745         }
3746 
3747         boolean hasBody = tree.body.getStatements().nonEmpty();
3748         boolean hasCatchers = tree.catchers.nonEmpty();
3749         boolean hasFinally = tree.finalizer != null &&


< prev index next >