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

Print this page




 340      *  switches.
 341      *
 342      *  <p>For each enum that appears as the type of a switch
 343      *  expression, we maintain an EnumMapping to assist in the
 344      *  translation, as exemplified by the following example:
 345      *
 346      *  <p>we translate
 347      *  <pre>
 348      *          switch(colorExpression) {
 349      *          case red: stmt1;
 350      *          case green: stmt2;
 351      *          }
 352      *  </pre>
 353      *  into
 354      *  <pre>
 355      *          switch(Outer$0.$EnumMap$Color[colorExpression.ordinal()]) {
 356      *          case 1: stmt1;
 357      *          case 2: stmt2
 358      *          }
 359      *  </pre>
 360      *  with the auxilliary table intialized as follows:
 361      *  <pre>
 362      *          class Outer$0 {
 363      *              synthetic final int[] $EnumMap$Color = new int[Color.values().length];
 364      *              static {
 365      *                  try { $EnumMap$Color[red.ordinal()] = 1; } catch (NoSuchFieldError ex) {}
 366      *                  try { $EnumMap$Color[green.ordinal()] = 2; } catch (NoSuchFieldError ex) {}
 367      *              }
 368      *          }
 369      *  </pre>
 370      *  class EnumMapping provides mapping data and support methods for this translation.
 371      */
 372     class EnumMapping {
 373         EnumMapping(DiagnosticPosition pos, TypeSymbol forEnum) {
 374             this.forEnum = forEnum;
 375             this.values = new LinkedHashMap<VarSymbol,Integer>();
 376             this.pos = pos;
 377             Name varName = names
 378                 .fromString(target.syntheticNameChar() +
 379                             "SwitchMap" +
 380                             target.syntheticNameChar() +


3092         tree.init = translate(tree.init);
3093         if (tree.cond != null)
3094             tree.cond = translate(tree.cond, syms.booleanType);
3095         tree.step = translate(tree.step);
3096         tree.body = translate(tree.body);
3097         result = tree;
3098     }
3099 
3100     public void visitReturn(JCReturn tree) {
3101         if (tree.expr != null)
3102             tree.expr = translate(tree.expr,
3103                                   types.erasure(currentMethodDef
3104                                                 .restype.type));
3105         result = tree;
3106     }
3107 
3108     public void visitSwitch(JCSwitch tree) {
3109         Type selsuper = types.supertype(tree.selector.type);
3110         boolean enumSwitch = selsuper != null &&
3111             (tree.selector.type.tsym.flags() & ENUM) != 0;
3112         Type target = enumSwitch ? tree.selector.type : syms.intType;



3113         tree.selector = translate(tree.selector, target);
3114         tree.cases = translateCases(tree.cases);
3115         if (enumSwitch) {
3116             result = visitEnumSwitch(tree);
3117             patchTargets(result, tree, result);



3118         } else {
3119             result = tree;
3120         }
3121     }
3122 
3123     public JCTree visitEnumSwitch(JCSwitch tree) {
3124         TypeSymbol enumSym = tree.selector.type.tsym;
3125         EnumMapping map = mapForEnum(tree.pos(), enumSym);
3126         make_at(tree.pos());
3127         Symbol ordinalMethod = lookupMethod(tree.pos(),
3128                                             names.ordinal,
3129                                             tree.selector.type,
3130                                             List.<Type>nil());
3131         JCArrayAccess selector = make.Indexed(map.mapVar,
3132                                         make.App(make.Select(tree.selector,
3133                                                              ordinalMethod)));
3134         ListBuffer<JCCase> cases = new ListBuffer<JCCase>();
3135         for (JCCase c : tree.cases) {
3136             if (c.pat != null) {
3137                 VarSymbol label = (VarSymbol)TreeInfo.symbol(c.pat);
3138                 JCLiteral pat = map.forConstant(label);
3139                 cases.append(make.Case(pat, c.stats));
3140             } else {
3141                 cases.append(c);
3142             }
3143         }
3144         return make.Switch(selector, cases.toList());
3145     }
3146 


















































































































































































3147     public void visitNewArray(JCNewArray tree) {
3148         tree.elemtype = translate(tree.elemtype);
3149         for (List<JCExpression> t = tree.dims; t.tail != null; t = t.tail)
3150             if (t.head != null) t.head = translate(t.head, syms.intType);
3151         tree.elems = translate(tree.elems, types.elemtype(tree.type));
3152         result = tree;
3153     }
3154 
3155     public void visitSelect(JCFieldAccess tree) {
3156         // need to special case-access of the form C.super.x
3157         // these will always need an access method.
3158         boolean qualifiedSuperAccess =
3159             tree.selected.getTag() == JCTree.SELECT &&
3160             TreeInfo.name(tree.selected) == names._super;
3161         tree.selected = translate(tree.selected);
3162         if (tree.name == names._class)
3163             result = classOf(tree.selected);
3164         else if (tree.name == names._this || tree.name == names._super)
3165             result = makeThis(tree.pos(), tree.selected.type.tsym);
3166         else




 340      *  switches.
 341      *
 342      *  <p>For each enum that appears as the type of a switch
 343      *  expression, we maintain an EnumMapping to assist in the
 344      *  translation, as exemplified by the following example:
 345      *
 346      *  <p>we translate
 347      *  <pre>
 348      *          switch(colorExpression) {
 349      *          case red: stmt1;
 350      *          case green: stmt2;
 351      *          }
 352      *  </pre>
 353      *  into
 354      *  <pre>
 355      *          switch(Outer$0.$EnumMap$Color[colorExpression.ordinal()]) {
 356      *          case 1: stmt1;
 357      *          case 2: stmt2
 358      *          }
 359      *  </pre>
 360      *  with the auxiliary table intialized as follows:
 361      *  <pre>
 362      *          class Outer$0 {
 363      *              synthetic final int[] $EnumMap$Color = new int[Color.values().length];
 364      *              static {
 365      *                  try { $EnumMap$Color[red.ordinal()] = 1; } catch (NoSuchFieldError ex) {}
 366      *                  try { $EnumMap$Color[green.ordinal()] = 2; } catch (NoSuchFieldError ex) {}
 367      *              }
 368      *          }
 369      *  </pre>
 370      *  class EnumMapping provides mapping data and support methods for this translation.
 371      */
 372     class EnumMapping {
 373         EnumMapping(DiagnosticPosition pos, TypeSymbol forEnum) {
 374             this.forEnum = forEnum;
 375             this.values = new LinkedHashMap<VarSymbol,Integer>();
 376             this.pos = pos;
 377             Name varName = names
 378                 .fromString(target.syntheticNameChar() +
 379                             "SwitchMap" +
 380                             target.syntheticNameChar() +


3092         tree.init = translate(tree.init);
3093         if (tree.cond != null)
3094             tree.cond = translate(tree.cond, syms.booleanType);
3095         tree.step = translate(tree.step);
3096         tree.body = translate(tree.body);
3097         result = tree;
3098     }
3099 
3100     public void visitReturn(JCReturn tree) {
3101         if (tree.expr != null)
3102             tree.expr = translate(tree.expr,
3103                                   types.erasure(currentMethodDef
3104                                                 .restype.type));
3105         result = tree;
3106     }
3107 
3108     public void visitSwitch(JCSwitch tree) {
3109         Type selsuper = types.supertype(tree.selector.type);
3110         boolean enumSwitch = selsuper != null &&
3111             (tree.selector.type.tsym.flags() & ENUM) != 0;
3112         boolean stringSwitch = selsuper != null &&
3113             types.isSameType(tree.selector.type, syms.stringType);
3114         Type target = enumSwitch ? tree.selector.type : 
3115             (stringSwitch? syms.stringType : syms.intType);
3116         tree.selector = translate(tree.selector, target);
3117         tree.cases = translateCases(tree.cases);
3118         if (enumSwitch) {
3119             result = visitEnumSwitch(tree);
3120             patchTargets(result, tree, result);
3121         } else if (stringSwitch) {
3122             result = visitStringSwitch(tree);
3123             patchTargets(result, tree, result);
3124         } else {
3125             result = tree;
3126         }
3127     }
3128 
3129     public JCTree visitEnumSwitch(JCSwitch tree) {
3130         TypeSymbol enumSym = tree.selector.type.tsym;
3131         EnumMapping map = mapForEnum(tree.pos(), enumSym);
3132         make_at(tree.pos());
3133         Symbol ordinalMethod = lookupMethod(tree.pos(),
3134                                             names.ordinal,
3135                                             tree.selector.type,
3136                                             List.<Type>nil());
3137         JCArrayAccess selector = make.Indexed(map.mapVar,
3138                                         make.App(make.Select(tree.selector,
3139                                                              ordinalMethod)));
3140         ListBuffer<JCCase> cases = new ListBuffer<JCCase>();
3141         for (JCCase c : tree.cases) {
3142             if (c.pat != null) {
3143                 VarSymbol label = (VarSymbol)TreeInfo.symbol(c.pat);
3144                 JCLiteral pat = map.forConstant(label);
3145                 cases.append(make.Case(pat, c.stats));
3146             } else {
3147                 cases.append(c);
3148             }
3149         }
3150         return make.Switch(selector, cases.toList());
3151     }
3152 
3153     public JCTree visitStringSwitch(JCSwitch tree) {
3154         List<JCCase> caseList = tree.getCases();
3155         int alternatives = caseList.size();
3156 
3157         if (alternatives == 0) { // Strange but legal possibility
3158             return make.at(tree.pos()).Exec(attr.makeNullCheck(tree.getExpression()));
3159         } else {
3160             /*
3161              * The general approach used is to translate a single
3162              * string switch statement into a series of two chained
3163              * switch statements: the first a synthesized statement
3164              * switching on the argument string's hash value and
3165              * computing a string's position in the list of original
3166              * case labels, if any, followed by a second switch on the
3167              * computed integer value.  The second switch has the same
3168              * code structure as the original string switch statement
3169              * except that the string case labels are replaced with
3170              * positional integer constants starting at 0.
3171              *
3172              * The first switch statement can be thought of as an
3173              * inlined map from strings to their position in the case
3174              * label list.  An alternate implementation would use an
3175              * actual Map for this purpose, as done for enum switches.
3176              *
3177              * With some additional effort, it would be possible to
3178              * use a single switch statement on the hash code of the
3179              * argument, but care would need to be taken to preserve
3180              * the proper control flow in the presence of hash
3181              * collisions and other complications, such as
3182              * fallthroughs.  Switch statements with one or two
3183              * alternatives could also be specially translated into
3184              * if-then statements to omit the computation of the hash
3185              * code.
3186              *
3187              * The generated code assumes that the hashing algorithm
3188              * of String is the same in the compilation environment as
3189              * in the environment the code will run in.  The string
3190              * hashing algorithm in the SE JDK has been unchanged
3191              * since at least JDK 1.2.
3192              */
3193 
3194             ListBuffer<JCStatement> stmtList = new ListBuffer<JCStatement>();
3195 
3196             // Map from String case labels to their original position in
3197             // the list of case labels.
3198             Map<String, Integer> caseLabelToPosition = 
3199                 new LinkedHashMap<String, Integer>(alternatives + 1, 1.0f);
3200         
3201             // Map of hash codes to the string case labels having that hashCode.
3202             Map<Integer, Set<String>> hashToString = 
3203                 new LinkedHashMap<Integer, Set<String>>(alternatives + 1, 1.0f);
3204 
3205             int casePosition = 0;
3206             for(JCCase oneCase : caseList) {
3207                 JCExpression expression = oneCase.getExpression();
3208             
3209                 if (expression != null) { // expression for a "default" case is null
3210                     String labelExpr = (String) expression.type.constValue();
3211                     Integer mapping = caseLabelToPosition.put(labelExpr, casePosition);
3212                     assert mapping == null;
3213                     int hashCode = labelExpr.hashCode();
3214 
3215                     Set<String> stringSet = hashToString.get(hashCode);
3216                     if (stringSet == null) {
3217                         stringSet = new LinkedHashSet<String>(1, 1.0f);
3218                         stringSet.add(labelExpr);
3219                         hashToString.put(hashCode, stringSet);
3220                     } else {
3221                         boolean added = stringSet.add(labelExpr);
3222                         assert added;
3223                     }
3224                 }
3225                 casePosition++;
3226             }
3227 
3228             // Synthesize a switch statement that has the effect of
3229             // mapping from a string to the integer position of that
3230             // string in the list of case labels.  This is done by
3231             // switching on the hashCode of the string followed by an
3232             // if-then-else chain comparing the input for equality
3233             // with all the case labels having that hash value.
3234 
3235             /*
3236              * s$ = top of stack;
3237              * tmp$ = -1;
3238              * switch($s.hashCode()) {
3239              *     case caseLabel.hashCode:
3240              *         if (s$.equals("caseLabel_1")
3241              *           tmp$ = caseLabelToPosition("caseLabel_1");
3242              *         else if (s$.equals("caseLabel_2"))
3243              *           tmp$ = caseLabelToPosition("caseLabel_2");
3244              *         ...
3245              *         break;
3246              * ...
3247              * }
3248              */
3249 
3250             VarSymbol dollar_s = new VarSymbol(FINAL|SYNTHETIC,
3251                                                names.fromString("s" + tree.pos + target.syntheticNameChar()),
3252                                                syms.stringType,
3253                                                currentMethodSym);
3254             stmtList.append(make.at(tree.pos()).VarDef(dollar_s, tree.getExpression()).setType(dollar_s.type));
3255 
3256             VarSymbol dollar_tmp = new VarSymbol(SYNTHETIC,
3257                                                  names.fromString("tmp" + tree.pos + target.syntheticNameChar()),
3258                                                  syms.intType,
3259                                                  currentMethodSym);
3260             JCVariableDecl dollar_tmp_def = 
3261                 (JCVariableDecl)make.VarDef(dollar_tmp, make.Literal(INT, -1)).setType(dollar_tmp.type);
3262             dollar_tmp_def.init.type = dollar_tmp.type = syms.intType;
3263             stmtList.append(dollar_tmp_def);
3264             ListBuffer<JCCase> caseBuffer = ListBuffer.lb();
3265             // hashCode will trigger nullcheck on original switch expression
3266             JCMethodInvocation hashCodeCall = makeCall(make.Ident(dollar_s),
3267                                                        names.hashCode,
3268                                                        List.<JCExpression>nil()).setType(syms.intType);
3269             JCSwitch switch1 = make.Switch(hashCodeCall,
3270                                         caseBuffer.toList());
3271             for(Map.Entry<Integer, Set<String>> entry : hashToString.entrySet()) {
3272                 int hashCode = entry.getKey();
3273                 Set<String> stringsWithHashCode = entry.getValue();
3274                 assert stringsWithHashCode.size() >= 1;
3275 
3276                 JCStatement elsepart = null;
3277                 for(String caseLabel : stringsWithHashCode ) {
3278                     JCMethodInvocation stringEqualsCall = makeCall(make.Ident(dollar_s),
3279                                                                    names.equals,
3280                                                                    List.<JCExpression>of(make.Literal(caseLabel)));
3281                     elsepart = make.If(stringEqualsCall,
3282                                        make.Exec(make.Assign(make.Ident(dollar_tmp),
3283                                                              make.Literal(caseLabelToPosition.get(caseLabel))).
3284                                                  setType(dollar_tmp.type)),
3285                                        elsepart);
3286                 }
3287 
3288                 ListBuffer<JCStatement> lb = ListBuffer.lb();
3289                 JCBreak breakStmt = make.Break(null);
3290                 breakStmt.target = switch1;
3291                 lb.append(elsepart).append(breakStmt);
3292             
3293                 caseBuffer.append(make.Case(make.Literal(hashCode), lb.toList()));
3294             }
3295             
3296             switch1.cases = caseBuffer.toList();
3297             stmtList.append(switch1);
3298 
3299             // Make isomorphic switch tree replacing string labels
3300             // with corresponding integer ones from the label to
3301             // position map.
3302 
3303             ListBuffer<JCCase> lb = ListBuffer.lb();
3304             JCSwitch switch2 = make.Switch(make.Ident(dollar_tmp), lb.toList());
3305             for(JCCase oneCase : caseList ) {
3306                 // Rewire up old unlabeled break statements to the
3307                 // replacement switch being created.
3308                 patchTargets(oneCase, tree, switch2);
3309 
3310                 boolean isDefault = (oneCase.getExpression() == null);
3311                 JCExpression caseExpr;
3312                 if (isDefault)
3313                     caseExpr = null;
3314                 else {
3315                     caseExpr = make.Literal(caseLabelToPosition.get((String)oneCase.
3316                                                                     getExpression().
3317                                                                     type.constValue()));
3318                 }
3319                 
3320                 lb.append(make.Case(caseExpr,
3321                                     oneCase.getStatements()));
3322             }
3323         
3324             switch2.cases = lb.toList();
3325             stmtList.append(switch2);
3326 
3327             return make.Block(0L, stmtList.toList());
3328         }
3329     }
3330 
3331     public void visitNewArray(JCNewArray tree) {
3332         tree.elemtype = translate(tree.elemtype);
3333         for (List<JCExpression> t = tree.dims; t.tail != null; t = t.tail)
3334             if (t.head != null) t.head = translate(t.head, syms.intType);
3335         tree.elems = translate(tree.elems, types.elemtype(tree.type));
3336         result = tree;
3337     }
3338 
3339     public void visitSelect(JCFieldAccess tree) {
3340         // need to special case-access of the form C.super.x
3341         // these will always need an access method.
3342         boolean qualifiedSuperAccess =
3343             tree.selected.getTag() == JCTree.SELECT &&
3344             TreeInfo.name(tree.selected) == names._super;
3345         tree.selected = translate(tree.selected);
3346         if (tree.name == names._class)
3347             result = classOf(tree.selected);
3348         else if (tree.name == names._this || tree.name == names._super)
3349             result = makeThis(tree.pos(), tree.selected.type.tsym);
3350         else