3360 .restype.type)); |
3361 .restype.type)); |
3361 result = tree; |
3362 result = tree; |
3362 } |
3363 } |
3363 |
3364 |
3364 public void visitSwitch(JCSwitch tree) { |
3365 public void visitSwitch(JCSwitch tree) { |
|
3366 handleSwitch(tree, tree.selector, tree.cases); |
|
3367 } |
|
3368 |
|
3369 @Override |
|
3370 public void visitSwitchExpression(JCSwitchExpression tree) { |
|
3371 if (tree.cases.stream().noneMatch(c -> c.pats.isEmpty())) { |
|
3372 JCThrow thr = make.Throw(makeNewClass(syms.incompatibleClassChangeErrorType, |
|
3373 List.nil())); |
|
3374 JCCase c = make.Case(JCCase.STATEMENT, List.nil(), List.of(thr), null); |
|
3375 tree.cases = tree.cases.append(c); |
|
3376 } |
|
3377 handleSwitch(tree, tree.selector, tree.cases); |
|
3378 } |
|
3379 |
|
3380 private void handleSwitch(JCTree tree, JCExpression selector, List<JCCase> cases) { |
3365 //expand multiple label cases: |
3381 //expand multiple label cases: |
3366 ListBuffer<JCCase> cases = new ListBuffer<>(); |
3382 ListBuffer<JCCase> convertedCases = new ListBuffer<>(); |
3367 |
3383 |
3368 for (JCCase c : tree.cases) { |
3384 for (JCCase c : cases) { |
3369 switch (c.pats.size()) { |
3385 switch (c.pats.size()) { |
3370 case 0: //default |
3386 case 0: //default |
3371 case 1: //single label |
3387 case 1: //single label |
3372 cases.append(c); |
3388 convertedCases.append(c); |
3373 break; |
3389 break; |
3374 default: //multiple labels, expand: |
3390 default: //multiple labels, expand: |
3375 //case C1, C2, C3: ... |
3391 //case C1, C2, C3: ... |
3376 //=> |
3392 //=> |
3377 //case C1: |
3393 //case C1: |
3378 //case C2: |
3394 //case C2: |
3379 //case C3: ... |
3395 //case C3: ... |
3380 List<JCExpression> patterns = c.pats; |
3396 List<JCExpression> patterns = c.pats; |
3381 while (patterns.tail.nonEmpty()) { |
3397 while (patterns.tail.nonEmpty()) { |
3382 cases.append(make_at(c.pos()).Case(JCCase.STATEMENT, |
3398 convertedCases.append(make_at(c.pos()).Case(JCCase.STATEMENT, |
3383 List.of(patterns.head), |
3399 List.of(patterns.head), |
3384 List.nil(), |
3400 List.nil(), |
3385 null)); |
3401 null)); |
3386 patterns = patterns.tail; |
3402 patterns = patterns.tail; |
3387 } |
3403 } |
3388 c.pats = patterns; |
3404 c.pats = patterns; |
3389 cases.append(c); |
3405 convertedCases.append(c); |
3390 break; |
3406 break; |
3391 } |
3407 } |
3392 } |
3408 } |
3393 |
3409 |
3394 for (JCCase c : cases) { |
3410 for (JCCase c : convertedCases) { |
3395 if (c.caseKind == JCCase.RULE && c.completesNormally) { |
3411 if (c.caseKind == JCCase.RULE && c.completesNormally) { |
3396 JCBreak b = make_at(c.pos()).Break(null); |
3412 JCBreak b = make_at(c.pos()).Break(null); |
3397 b.target = tree; |
3413 b.target = tree; |
3398 c.stats = c.stats.append(b); |
3414 c.stats = c.stats.append(b); |
3399 } |
3415 } |
3400 } |
3416 } |
3401 |
3417 |
3402 tree.cases = cases.toList(); |
3418 cases = convertedCases.toList(); |
3403 |
3419 |
3404 Type selsuper = types.supertype(tree.selector.type); |
3420 Type selsuper = types.supertype(selector.type); |
3405 boolean enumSwitch = selsuper != null && |
3421 boolean enumSwitch = selsuper != null && |
3406 (tree.selector.type.tsym.flags() & ENUM) != 0; |
3422 (selector.type.tsym.flags() & ENUM) != 0; |
3407 boolean stringSwitch = selsuper != null && |
3423 boolean stringSwitch = selsuper != null && |
3408 types.isSameType(tree.selector.type, syms.stringType); |
3424 types.isSameType(selector.type, syms.stringType); |
3409 Type target = enumSwitch ? tree.selector.type : |
3425 Type target = enumSwitch ? selector.type : |
3410 (stringSwitch? syms.stringType : syms.intType); |
3426 (stringSwitch? syms.stringType : syms.intType); |
3411 tree.selector = translate(tree.selector, target); |
3427 selector = translate(selector, target); |
3412 tree.cases = translateCases(tree.cases); |
3428 cases = translateCases(cases); |
|
3429 if (tree.hasTag(SWITCH)) { |
|
3430 ((JCSwitch) tree).selector = selector; |
|
3431 ((JCSwitch) tree).cases = cases; |
|
3432 } else if (tree.hasTag(SWITCH_EXPRESSION)) { |
|
3433 ((JCSwitchExpression) tree).selector = selector; |
|
3434 ((JCSwitchExpression) tree).cases = cases; |
|
3435 } else { |
|
3436 Assert.error(); |
|
3437 } |
3413 if (enumSwitch) { |
3438 if (enumSwitch) { |
3414 result = visitEnumSwitch(tree); |
3439 result = visitEnumSwitch(tree, selector, cases); |
3415 } else if (stringSwitch) { |
3440 } else if (stringSwitch) { |
3416 result = visitStringSwitch(tree); |
3441 result = visitStringSwitch(tree, selector, cases); |
3417 } else { |
3442 } else { |
3418 result = tree; |
3443 result = tree; |
3419 } |
3444 } |
3420 } |
3445 } |
3421 |
3446 |
3422 public JCTree visitEnumSwitch(JCSwitch tree) { |
3447 public JCTree visitEnumSwitch(JCTree tree, JCExpression selector, List<JCCase> cases) { |
3423 TypeSymbol enumSym = tree.selector.type.tsym; |
3448 TypeSymbol enumSym = selector.type.tsym; |
3424 EnumMapping map = mapForEnum(tree.pos(), enumSym); |
3449 EnumMapping map = mapForEnum(tree.pos(), enumSym); |
3425 make_at(tree.pos()); |
3450 make_at(tree.pos()); |
3426 Symbol ordinalMethod = lookupMethod(tree.pos(), |
3451 Symbol ordinalMethod = lookupMethod(tree.pos(), |
3427 names.ordinal, |
3452 names.ordinal, |
3428 tree.selector.type, |
3453 selector.type, |
3429 List.nil()); |
3454 List.nil()); |
3430 JCArrayAccess selector = make.Indexed(map.mapVar, |
3455 JCArrayAccess newSelector = make.Indexed(map.mapVar, |
3431 make.App(make.Select(tree.selector, |
3456 make.App(make.Select(selector, |
3432 ordinalMethod))); |
3457 ordinalMethod))); |
3433 ListBuffer<JCCase> cases = new ListBuffer<>(); |
3458 ListBuffer<JCCase> newCases = new ListBuffer<>(); |
3434 for (JCCase c : tree.cases) { |
3459 for (JCCase c : cases) { |
3435 if (c.pats.nonEmpty()) { |
3460 if (c.pats.nonEmpty()) { |
3436 VarSymbol label = (VarSymbol)TreeInfo.symbol(c.pats.head); |
3461 VarSymbol label = (VarSymbol)TreeInfo.symbol(c.pats.head); |
3437 JCLiteral pat = map.forConstant(label); |
3462 JCLiteral pat = map.forConstant(label); |
3438 cases.append(make.Case(JCCase.STATEMENT, List.of(pat), c.stats, null)); |
3463 newCases.append(make.Case(JCCase.STATEMENT, List.of(pat), c.stats, null)); |
3439 } else { |
3464 } else { |
3440 cases.append(c); |
3465 newCases.append(c); |
3441 } |
3466 } |
3442 } |
3467 } |
3443 JCSwitch enumSwitch = make.Switch(selector, cases.toList()); |
3468 JCTree enumSwitch; |
|
3469 if (tree.hasTag(SWITCH)) { |
|
3470 enumSwitch = make.Switch(newSelector, newCases.toList()); |
|
3471 } else if (tree.hasTag(SWITCH_EXPRESSION)) { |
|
3472 enumSwitch = make.SwitchExpression(newSelector, newCases.toList()); |
|
3473 enumSwitch.setType(tree.type); |
|
3474 } else { |
|
3475 Assert.error(); |
|
3476 throw new AssertionError(); |
|
3477 } |
3444 patchTargets(enumSwitch, tree, enumSwitch); |
3478 patchTargets(enumSwitch, tree, enumSwitch); |
3445 return enumSwitch; |
3479 return enumSwitch; |
3446 } |
3480 } |
3447 |
3481 |
3448 public JCTree visitStringSwitch(JCSwitch tree) { |
3482 public JCTree visitStringSwitch(JCTree tree, JCExpression selector, List<JCCase> caseList) { |
3449 List<JCCase> caseList = tree.getCases(); |
|
3450 int alternatives = caseList.size(); |
3483 int alternatives = caseList.size(); |
3451 |
3484 |
3452 if (alternatives == 0) { // Strange but legal possibility |
3485 if (alternatives == 0) { // Strange but legal possibility (only legal for switch statement) |
3453 return make.at(tree.pos()).Exec(attr.makeNullCheck(tree.getExpression())); |
3486 return make.at(tree.pos()).Exec(attr.makeNullCheck(selector)); |
3454 } else { |
3487 } else { |
3455 /* |
3488 /* |
3456 * The general approach used is to translate a single |
3489 * The general approach used is to translate a single |
3457 * string switch statement into a series of two chained |
3490 * string switch statement into a series of two chained |
3458 * switch statements: the first a synthesized statement |
3491 * switch statements: the first a synthesized statement |
3599 // Make isomorphic switch tree replacing string labels |
3632 // Make isomorphic switch tree replacing string labels |
3600 // with corresponding integer ones from the label to |
3633 // with corresponding integer ones from the label to |
3601 // position map. |
3634 // position map. |
3602 |
3635 |
3603 ListBuffer<JCCase> lb = new ListBuffer<>(); |
3636 ListBuffer<JCCase> lb = new ListBuffer<>(); |
3604 JCSwitch switch2 = make.Switch(make.Ident(dollar_tmp), lb.toList()); |
|
3605 for(JCCase oneCase : caseList ) { |
3637 for(JCCase oneCase : caseList ) { |
3606 // Rewire up old unlabeled break statements to the |
|
3607 // replacement switch being created. |
|
3608 patchTargets(oneCase, tree, switch2); |
|
3609 |
|
3610 boolean isDefault = (oneCase.pats.isEmpty()); |
3638 boolean isDefault = (oneCase.pats.isEmpty()); |
3611 JCExpression caseExpr; |
3639 JCExpression caseExpr; |
3612 if (isDefault) |
3640 if (isDefault) |
3613 caseExpr = null; |
3641 caseExpr = null; |
3614 else { |
3642 else { |
3615 caseExpr = make.Literal(caseLabelToPosition.get((String)TreeInfo.skipParens(oneCase.pats.head). |
3643 caseExpr = make.Literal(caseLabelToPosition.get((String)TreeInfo.skipParens(oneCase.pats.head). |
3616 type.constValue())); |
3644 type.constValue())); |
3617 } |
3645 } |
3618 |
3646 |
3619 lb.append(make.Case(JCCase.STATEMENT, caseExpr == null ? List.nil() : List.of(caseExpr), |
3647 lb.append(make.Case(JCCase.STATEMENT, caseExpr == null ? List.nil() : List.of(caseExpr), |
3620 oneCase.getStatements(), null)); |
3648 oneCase.stats, null)); |
3621 } |
3649 } |
3622 |
3650 |
3623 switch2.cases = lb.toList(); |
3651 if (tree.hasTag(SWITCH)) { |
3624 stmtList.append(switch2); |
3652 JCSwitch switch2 = make.Switch(make.Ident(dollar_tmp), lb.toList()); |
3625 |
3653 // Rewire up old unlabeled break statements to the |
3626 return make.Block(0L, stmtList.toList()); |
3654 // replacement switch being created. |
|
3655 patchTargets(switch2, tree, switch2); |
|
3656 |
|
3657 stmtList.append(switch2); |
|
3658 |
|
3659 return make.Block(0L, stmtList.toList()); |
|
3660 } else { |
|
3661 JCSwitchExpression switch2 = make.SwitchExpression(make.Ident(dollar_tmp), lb.toList()); |
|
3662 |
|
3663 // Rewire up old unlabeled break statements to the |
|
3664 // replacement switch being created. |
|
3665 patchTargets(switch2, tree, switch2); |
|
3666 |
|
3667 switch2.setType(tree.type); |
|
3668 |
|
3669 LetExpr res = make.LetExpr(stmtList.toList(), switch2); |
|
3670 |
|
3671 res.needsCond = true; |
|
3672 res.setType(tree.type); |
|
3673 |
|
3674 return res; |
|
3675 } |
3627 } |
3676 } |
3628 } |
3677 } |
3629 |
3678 |
3630 @Override |
3679 @Override |
3631 public void visitSwitchExpression(JCSwitchExpression tree) { |
3680 public void visitBreak(JCBreak tree) { |
3632 //translates switch expression to statement switch: |
3681 if (tree.isValueBreak()) { |
3633 //switch (selector) { |
3682 tree.value = translate(tree.value, tree.target.type); |
3634 // case C: break value; |
3683 } |
3635 // ... |
3684 result = tree; |
3636 //} |
3685 } |
3637 //=> |
|
3638 //(letexpr T exprswitch$; |
|
3639 // switch (selector) { |
|
3640 // case C: { exprswitch$ = value; break; } |
|
3641 // } |
|
3642 // exprswitch$ |
|
3643 //) |
|
3644 VarSymbol dollar_switchexpr = new VarSymbol(Flags.FINAL|Flags.SYNTHETIC, |
|
3645 names.fromString("exprswitch" + tree.pos + target.syntheticNameChar()), |
|
3646 tree.type, |
|
3647 currentMethodSym); |
|
3648 |
|
3649 ListBuffer<JCStatement> stmtList = new ListBuffer<>(); |
|
3650 |
|
3651 stmtList.append(make.at(tree.pos()).VarDef(dollar_switchexpr, null).setType(dollar_switchexpr.type)); |
|
3652 JCSwitch switchStatement = make.Switch(tree.selector, null); |
|
3653 switchStatement.cases = |
|
3654 tree.cases.stream() |
|
3655 .map(c -> convertCase(dollar_switchexpr, switchStatement, tree, c)) |
|
3656 .collect(List.collector()); |
|
3657 if (tree.cases.stream().noneMatch(c -> c.pats.isEmpty())) { |
|
3658 JCThrow thr = make.Throw(makeNewClass(syms.incompatibleClassChangeErrorType, |
|
3659 List.nil())); |
|
3660 JCCase c = make.Case(JCCase.STATEMENT, List.nil(), List.of(thr), null); |
|
3661 switchStatement.cases = switchStatement.cases.append(c); |
|
3662 } |
|
3663 |
|
3664 stmtList.append(translate(switchStatement)); |
|
3665 |
|
3666 result = make.LetExpr(stmtList.toList(), make.Ident(dollar_switchexpr)) |
|
3667 .setType(dollar_switchexpr.type); |
|
3668 } |
|
3669 //where: |
|
3670 private JCCase convertCase(VarSymbol dollar_switchexpr, JCSwitch switchStatement, |
|
3671 JCSwitchExpression switchExpr, JCCase c) { |
|
3672 make.at(c.pos()); |
|
3673 ListBuffer<JCStatement> statements = new ListBuffer<>(); |
|
3674 statements.addAll(new TreeTranslator() { |
|
3675 @Override |
|
3676 public void visitLambda(JCLambda tree) {} |
|
3677 @Override |
|
3678 public void visitClassDef(JCClassDecl tree) {} |
|
3679 @Override |
|
3680 public void visitMethodDef(JCMethodDecl tree) {} |
|
3681 @Override |
|
3682 public void visitBreak(JCBreak tree) { |
|
3683 if (tree.target == switchExpr) { |
|
3684 tree.target = switchStatement; |
|
3685 JCExpressionStatement assignment = |
|
3686 make.Exec(make.Assign(make.Ident(dollar_switchexpr), |
|
3687 translate(tree.value)) |
|
3688 .setType(dollar_switchexpr.type)); |
|
3689 result = make.Block(0, List.of(assignment, |
|
3690 tree)); |
|
3691 tree.value = null; |
|
3692 } else { |
|
3693 result = tree; |
|
3694 } |
|
3695 } |
|
3696 }.translate(c.stats)); |
|
3697 return make.Case(JCCase.STATEMENT, c.pats, statements.toList(), null); |
|
3698 } |
|
3699 |
3686 |
3700 public void visitNewArray(JCNewArray tree) { |
3687 public void visitNewArray(JCNewArray tree) { |
3701 tree.elemtype = translate(tree.elemtype); |
3688 tree.elemtype = translate(tree.elemtype); |
3702 for (List<JCExpression> t = tree.dims; t.tail != null; t = t.tail) |
3689 for (List<JCExpression> t = tree.dims; t.tail != null; t = t.tail) |
3703 if (t.head != null) t.head = translate(t.head, syms.intType); |
3690 if (t.head != null) t.head = translate(t.head, syms.intType); |