/** * * * <h3>Generic-switch</h3> * * <pre> * switch (v) { * case key1: ... * case key2: ... * } * * var $v = v; * if (strictEquals($v, key1)) goto L1 * if (strictEquals($v, key2)) goto L2 * goTo (default | break) * L1: ... * L2: ... * </pre> * * @param clauses the switch clauses * @param labels the labels for each switch clause * @param defaultClause the label for the default clause * @param lblExit the exit label * @param switchValue the variable which holds the switch value * @param mv the statement visitor */ private void emitGenericSwitch( List<SwitchClause> clauses, Jump[] labels, Jump defaultClause, Jump lblExit, Variable<?> switchValue, StatementVisitor mv) { assert switchValue.getType().equals(Types.Object); Jump switchDefault = defaultClause != null ? defaultClause : lblExit; int index = 0; for (SwitchClause switchClause : clauses) { Jump caseLabel = labels[index++]; Expression expr = switchClause.getExpression(); if (expr != null) { mv.load(switchValue); // 13.11.10 Runtime Semantics: CaseSelectorEvaluation expressionBoxed(expr, mv); invokeDynamicOperator(BinaryExpression.Operator.SHEQ, mv); mv.ifne(caseLabel); } } mv.goTo(switchDefault); }
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; }
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(SwitchClause node, StatementVisitor mv) { return codegen.statements(node.getStatements(), mv); }