use of com.github.anba.es6draft.ast.SwitchClause in project es6draft by anba.
the class SwitchStatementGenerator method CaseBlockEvaluation.
/**
* 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 code visitor
* @return the completion value
*/
private Completion CaseBlockEvaluation(SwitchStatement node, SwitchType type, Jump lblExit, Variable<?> switchValue, CodeVisitor 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());
}
use of com.github.anba.es6draft.ast.SwitchClause in project es6draft by anba.
the class SwitchStatementGenerator method DefaultCaseBlockEvaluation.
/**
* 13.12.9 Runtime Semantics: CaseBlockEvaluation
*
* @param node
* the switch statement
* @param mv
* the code visitor
* @return the completion value
*/
private Completion DefaultCaseBlockEvaluation(SwitchStatement node, CodeVisitor mv) {
// Skip leading clauses until default clause found.
List<SwitchClause> clauses = node.getClauses();
for (int i = 0; i < clauses.size(); ++i) {
if (clauses.get(i).isDefaultClause()) {
clauses = clauses.subList(i, clauses.size());
break;
}
}
assert !clauses.isEmpty() && clauses.get(0).isDefaultClause();
Completion lastResult = Completion.Normal;
if (clauses.size() > SWITCH_CASE_LIMIT) {
List<CaseBlock> blocks = computeCaseBlocks(node, list -> {
return new CaseBlock(list, null, null);
});
for (CaseBlock caseBlock : blocks) {
OutlinedCall call = mv.compile(caseBlock.blockKey(), () -> defaultCaseBlock(caseBlock, mv));
// Pessimistically assume we need to save the completion value.
lastResult = mv.invokeCompletion(call, mv.hasCompletion());
if (lastResult.isAbrupt()) {
break;
}
}
} else {
// Handle clauses following default clause until abrupt completion found.
for (SwitchClause switchClause : clauses) {
lastResult = switchClause.accept(this, mv);
if (lastResult.isAbrupt()) {
break;
}
}
}
return lastResult;
}
use of com.github.anba.es6draft.ast.SwitchClause in project es6draft by anba.
the class SwitchStatementGenerator method caseSelect.
/**
* Generates a case-select method.
*
* @param caseBlock
* the case-block
* @param switchVarType
* the switch-var type
* @param mv
* the code visitor
* @return the outlined-call object
*/
private OutlinedCall caseSelect(SwitchStatementGenerator.CaseBlock caseBlock, Type switchVarType, CodeVisitor mv) {
SwitchClause firstClause = caseBlock.clauses.get(0);
MethodTypeDescriptor methodDescriptor = SwitchSelectCodeVisitor.methodDescriptor(switchVarType, mv);
MethodCode method = codegen.method(mv, "select", methodDescriptor);
return outlined(new SwitchSelectCodeVisitor(firstClause, method, mv), body -> {
Variable<?> switchValue = body.getSwitchValueParameter();
MutableValue<Integer> switchTarget = body.iarrayElement(body.getSwitchTargetParameter(), 0);
List<SwitchClause> clauses = caseBlock.clauses;
int[] switchTargets = caseBlock.switchTargets;
int numTargets = caseBlock.numTargets();
Jump[] targetLabels = new Jump[numTargets];
for (int i = 0; i < targetLabels.length; ++i) {
targetLabels[i] = new Jump();
}
Jump lblExit = new Jump();
Jump[] labels = new Jump[clauses.size()];
for (int i = 0; i < clauses.size(); ++i) {
labels[i] = targetLabels[switchTargets[i] - 1];
}
caseSelector(SwitchType.of(clauses)).select(clauses, switchValue, labels, lblExit, body);
Jump setSwitchTarget = new Jump();
for (int i = 0; i < targetLabels.length; ++i) {
// targetLabels[i] is not reachable if only used by the default clause.
if (targetLabels[i].isTarget()) {
body.mark(targetLabels[i]);
body.iconst(i + 1);
body.goTo(setSwitchTarget);
}
}
if (setSwitchTarget.isTarget()) {
// stack: [newSwitchTarget] -> []
body.mark(setSwitchTarget);
body.store(switchTarget);
}
body.mark(lblExit);
return Completion.Normal;
});
}
use of com.github.anba.es6draft.ast.SwitchClause in project es6draft by anba.
the class SwitchStatementGenerator method CaseBlockEvaluation.
/**
* 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 code visitor
* @return the completion value
*/
private Completion CaseBlockEvaluation(SwitchStatement node, SwitchType type, boolean defaultClausePresent, Jump lblExit, Variable<?> switchValue, CodeVisitor mv) {
List<SwitchClause> clauses = node.getClauses();
Completion lastResult = Completion.Normal;
if (clauses.size() > SWITCH_CASE_LIMIT) {
List<CaseBlock> blocks = computeCaseBlocks(node, list -> {
// 0 is reserved
int targetCounter = 1;
int[] switchTargets = new int[list.size()];
for (int i = 0; i < list.size(); ++i) {
if (list.get(i).getStatements().isEmpty()) {
switchTargets[i] = targetCounter;
} else {
switchTargets[i] = targetCounter++;
}
}
return new CaseBlock(list, switchTargets, new Jump());
});
mv.enterVariableScope();
Variable<int[]> switchTargetRef = mv.newVariable("switchTarget", int[].class);
mv.newarray(1, Type.INT_TYPE);
mv.store(switchTargetRef);
MutableValue<Integer> switchTarget = mv.iarrayElement(switchTargetRef, 0);
for (CaseBlock caseBlock : blocks) {
OutlinedCall call = mv.compile(caseBlock.selectKey(), () -> caseSelect(caseBlock, switchValue.getType(), mv));
mv.invokeCompletion(call, false, switchValue, switchTargetRef);
mv.load(switchTarget);
mv.ifne(caseBlock.entry);
}
if (!defaultClausePresent) {
mv.goTo(lblExit);
} else {
for (CaseBlock caseBlock : blocks) {
int index = indexOf(caseBlock.clauses, SwitchClause::isDefaultClause);
if (index >= 0) {
mv.store(switchTarget, mv.vconst(caseBlock.switchTargets[index]));
mv.goTo(caseBlock.entry);
break;
}
}
}
for (CaseBlock caseBlock : blocks) {
OutlinedCall call = mv.compile(caseBlock.blockKey(), () -> caseBlock(caseBlock, mv));
// Pessimistically assume we need to save the completion value.
mv.mark(caseBlock.entry);
lastResult = mv.invokeCompletion(call, mv.hasCompletion(), switchTarget);
if (!lastResult.isAbrupt()) {
// Clear switchTarget on fall-thru.
mv.store(switchTarget, mv.vconst(0));
}
}
mv.exitVariableScope();
} else {
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];
}
}
assert defaultClausePresent == (lblDefault != null);
caseSelector(type).select(clauses, switchValue, labels, defaultClausePresent ? lblDefault : lblExit, mv);
int index = 0;
for (SwitchClause switchClause : clauses) {
Jump caseLabel = labels[index++];
if (caseLabel.isTarget()) {
mv.mark(caseLabel);
} else if (lastResult.isAbrupt()) {
// Ignore unreachable targets.
continue;
}
lastResult = switchClause.accept(this, mv);
}
}
return lastResult;
}
use of com.github.anba.es6draft.ast.SwitchClause in project es6draft by anba.
the class SwitchStatementGenerator method defaultCaseBlock.
/**
* Generates a case-block method.
*
* @param caseBlock
* the case-block
* @param mv
* the code visitor
* @return the outlined-call object
*/
private OutlinedCall defaultCaseBlock(SwitchStatementGenerator.CaseBlock caseBlock, CodeVisitor mv) {
SwitchClause firstClause = caseBlock.clauses.get(0);
MethodTypeDescriptor methodDescriptor = DefaultSwitchBlockCodeVisitor.methodDescriptor(mv);
MethodCode method = codegen.method(mv, "case", methodDescriptor);
return outlined(new DefaultSwitchBlockCodeVisitor(firstClause, method, mv), body -> {
Completion lastResult = Completion.Normal;
for (SwitchClause clause : caseBlock.clauses) {
lastResult = clause.accept(this, body);
if (lastResult.isAbrupt()) {
break;
}
}
return lastResult;
});
}
Aggregations