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
|