private static boolean hasDefaultClause(SwitchStatement node) { for (SwitchClause switchClause : node.getClauses()) { if (switchClause.isDefaultClause()) { return true; } } return false; }
private static boolean isStringSwitch(SwitchStatement node) { for (SwitchClause switchClause : node.getClauses()) { Expression expr = switchClause.getExpression(); if (expr != null && !(expr instanceof StringLiteral)) { return false; } } return true; }
private static boolean isCharSwitch(SwitchStatement node) { for (SwitchClause switchClause : node.getClauses()) { Expression expr = switchClause.getExpression(); if (expr != null) { if (expr instanceof StringLiteral && ((StringLiteral) expr).getValue().length() == 1) { continue; } return false; } } return true; }
static SwitchType of(SwitchStatement node) { List<SwitchClause> clauses = node.getClauses(); if (clauses.size() == 0 || clauses.size() == 1 && clauses.get(0).isDefaultClause()) { // empty or only default clause return Default; } if (isIntSwitch(node)) { return Int; } if (isCharSwitch(node)) { return Char; } if (isStringSwitch(node)) { return String; } return Generic; }
private static boolean isIntSwitch(SwitchStatement node) { for (SwitchClause switchClause : node.getClauses()) { Expression expr = switchClause.getExpression(); if (expr != null) { if (expr instanceof NumericLiteral && ((NumericLiteral) expr).isInt()) { continue; } if (expr instanceof UnaryExpression && ((UnaryExpression) expr).getOperator() == UnaryExpression.Operator.NEG && ((UnaryExpression) expr).getOperand() instanceof NumericLiteral && ((NumericLiteral) ((UnaryExpression) expr).getOperand()).isInt() && ((NumericLiteral) ((UnaryExpression) expr).getOperand()).intValue() != 0) { continue; } return false; } } return true; }
/** * 13.12.9 Runtime Semantics: CaseBlockEvaluation * * @param node the switch statement * @param type the switch statement type * @param lblExit the exit label * @param switchValue the variable which holds the switch value * @param mv the statement visitor * @return the completion value */ private Completion CaseBlockEvaluation( SwitchStatement node, SwitchType type, Jump lblExit, Variable<?> switchValue, StatementVisitor mv) { List<SwitchClause> clauses = node.getClauses(); Jump lblDefault = null; Jump[] labels = new Jump[clauses.size()]; for (int i = 0, size = clauses.size(); i < size; ++i) { labels[i] = new Jump(); if (clauses.get(i).isDefaultClause()) { assert lblDefault == null; lblDefault = labels[i]; } } if (type == SwitchType.Int) { emitIntSwitch(clauses, labels, lblDefault, lblExit, switchValue, mv); } else if (type == SwitchType.Char) { emitCharSwitch(clauses, labels, lblDefault, lblExit, switchValue, mv); } else if (type == SwitchType.String) { emitStringSwitch(clauses, labels, lblDefault, lblExit, switchValue, mv); } else if (type == SwitchType.Generic) { emitGenericSwitch(clauses, labels, lblDefault, lblExit, switchValue, mv); } else { assert type == SwitchType.Default; assert switchValue == null; // Directly jump to default clause; since switch clauses before default clause are not // emitted, jump instruction can be elided as well, so we directly fall into the default // clause. } Completion result = Completion.Normal, lastResult = Completion.Normal; if (type == SwitchType.Default) { Iterator<SwitchClause> iter = clauses.iterator(); // skip leading clauses until default clause found while (iter.hasNext()) { SwitchClause switchClause = iter.next(); if (switchClause.isDefaultClause()) { lastResult = switchClause.accept(this, mv); break; } } // handle clauses following default clause until abrupt completion while (iter.hasNext() && !lastResult.isAbrupt()) { lastResult = iter.next().accept(this, mv); } result = lastResult; } else { int index = 0; for (SwitchClause switchClause : clauses) { Jump caseLabel = labels[index++]; if (caseLabel != null) { mv.mark(caseLabel); } else if (lastResult.isAbrupt()) { // Ignore unreachable targets continue; } Completion innerResult = switchClause.accept(this, mv); if (innerResult.isAbrupt()) { // not fall-thru result = result.isAbrupt() ? result.select(innerResult) : innerResult; } lastResult = innerResult; } } return result.normal(lblDefault == null || !lastResult.isAbrupt()); }
/** 13.12.11 Runtime Semantics: Evaluation */ @Override public Completion visit(SwitchStatement node, StatementVisitor mv) { // stack -> switchValue ValType switchValueType = expression(node.getExpression(), mv); SwitchType type = SwitchType.of(node); boolean defaultOrReturn = false; if (type == SwitchType.Int) { if (!switchValueType.isNumeric() && switchValueType != ValType.Any) { defaultOrReturn = true; } } else if (type == SwitchType.Char) { if (switchValueType != ValType.String && switchValueType != ValType.Any) { defaultOrReturn = true; } } else if (type == SwitchType.String) { if (switchValueType != ValType.String && switchValueType != ValType.Any) { defaultOrReturn = true; } } else if (type == SwitchType.Generic) { mv.toBoxed(switchValueType); switchValueType = ValType.Any; } else { assert type == SwitchType.Default; defaultOrReturn = true; } final boolean defaultClausePresent = hasDefaultClause(node); if (defaultOrReturn) { // never true -> emit default switch or return mv.pop(switchValueType); if (defaultClausePresent) { type = SwitchType.Default; } else { return Completion.Normal; } } mv.enterVariableScope(); Variable<LexicalEnvironment<?>> savedEnv = saveEnvironment(node, mv); Variable<?> switchValue = null; if (type != SwitchType.Default) { switchValue = mv.newVariable("switchValue", switchValueType.toClass()); mv.store(switchValue); } BlockScope scope = node.getScope(); if (scope.isPresent()) { newDeclarativeEnvironment(scope, mv); codegen.blockInit(node, mv); pushLexicalEnvironment(mv); } Jump lblExit = new Jump(); BreakLabel lblBreak = new BreakLabel(); mv.enterScope(node); mv.enterBreakable(node, lblBreak); Completion result = CaseBlockEvaluation(node, type, lblExit, switchValue, mv); mv.exitBreakable(node); mv.exitScope(); if (!defaultClausePresent) { mv.mark(lblExit); } if (scope.isPresent() && !result.isAbrupt()) { popLexicalEnvironment(mv); } if (lblBreak.isTarget()) { mv.mark(lblBreak); restoreEnvironment(savedEnv, mv); } mv.exitVariableScope(); return result.normal(lblBreak.isTarget()); }