dart2js: CPS translation of switches with continue to their labels.

Implement switches with continue to their labels by a first 'non-recursive'
switch followed by a second (only) 'recursive' switch inside a loop.
Continue is implemented by assignment to a state variable that the second
switch switches over.

R=asgerf@google.com

Review URL: https://codereview.chromium.org/1585503002 .
This commit is contained in:
Kevin Millikin
2016-01-26 09:43:13 +01:00
parent e3810ceff2
commit 65ae116a01
7 changed files with 349 additions and 136 deletions
+87 -91
View File
@@ -166,6 +166,11 @@ abstract class JumpCollector {
JumpCollector.retrn(this._continuation)
: _continuationEnvironment = null, target = null;
/// Construct a collector for collecting goto jumps.
///
/// There is no continuation or environment at the destination.
JumpCollector.goto(this.target) : _continuationEnvironment = null;
/// True if the collector has not recorded any jumps to its continuation.
bool get isEmpty;
@@ -421,6 +426,31 @@ class ReturnJumpCollector extends JumpCollector {
}
}
/// Collect 'goto' jumps, continue to a labeled case from within a switch.
///
/// These jumps are unrestricted within the switch. They can be forward or
/// backward. They are implemented by assigning to a state variable.
class GotoJumpCollector extends JumpCollector {
bool isEmpty = true;
final ir.Continuation continuation = null;
final Environment environment = null;
int _stateVariableIndex;
int _stateValue;
JumpCollector _breakJoin;
GotoJumpCollector(JumpTarget target, this._stateVariableIndex,
this._stateValue, this._breakJoin) : super.goto(target);
void addJump(IrBuilder builder,
[ir.Primitive value, SourceInformation sourceInformation]) {
isEmpty = false;
ir.Primitive constant = builder.buildIntegerConstant(_stateValue);
builder.environment.index2value[_stateVariableIndex] = constant;
builder.jumpTo(_breakJoin);
}
}
/// Function for building a node in the context of the current builder.
typedef ir.Node BuildFunction(node);
@@ -532,7 +562,7 @@ class ThisParameterLocal implements Local {
///
/// The IR fragment is an expression with a hole in it. The hole represents
/// the focus where new expressions can be added. The fragment is implemented
/// by [_root] which is the root of the expression and [_current] which is the
/// by [root] which is the root of the expression and [_current] which is the
/// expression that immediately contains the hole. Not all expressions have a
/// hole (e.g., invocations, which always occur in tail position, do not have a
/// hole). Expressions with a hole have a plug method.
@@ -559,7 +589,7 @@ class IrBuilder {
/// side effects.
Map<Local, ir.MutableVariable> mutableVariables;
ir.Expression _root = null;
ir.Expression root = null;
ir.Expression _current = null;
GlobalProgramInformation get program => state.program;
@@ -613,7 +643,7 @@ class IrBuilder {
return mutableVariables[local];
}
bool get isOpen => _root == null || _current != null;
bool get isOpen => root == null || _current != null;
List<ir.Primitive> buildFunctionHeader(Iterable<Local> parameters,
{ClosureScope closureScope,
@@ -640,8 +670,8 @@ class IrBuilder {
/// new value of current.
void add(ir.Expression expr) {
assert(isOpen);
if (_root == null) {
_root = _current = expr;
if (root == null) {
root = _current = expr;
} else {
_current = _current.plug(expr);
}
@@ -789,8 +819,8 @@ class IrBuilder {
// if condition (then, else)
ir.Continuation thenContinuation = new ir.Continuation([]);
ir.Continuation elseContinuation = new ir.Continuation([]);
thenContinuation.body = thenBuilder._root;
elseContinuation.body = elseBuilder._root;
thenContinuation.body = thenBuilder.root;
elseContinuation.body = elseBuilder.root;
add(new ir.LetCont(join.continuation,
new ir.LetCont.two(thenContinuation, elseContinuation,
new ir.Branch.strict(condition,
@@ -812,7 +842,7 @@ class IrBuilder {
_current = null;
}
/// Create a [ir.FunctionDefinition] using [_root] as the body.
/// Create a [ir.FunctionDefinition] using [root] as the body.
///
/// The protocol for building a function is:
/// 1. Call [buildFunctionHeader].
@@ -825,7 +855,7 @@ class IrBuilder {
state.thisParameter,
state.functionParameters,
state.returnContinuation,
_root);
root);
}
/// Create a invocation of the [method] on the super class where the call
@@ -1107,17 +1137,17 @@ class IrBuilder {
// case that one of them is null, it must be the only one that is open
// and thus contains the new hole in the context. This case is handled
// after the branch is plugged into the current hole.
thenContinuation.body = thenBuilder._root;
elseContinuation.body = elseBuilder._root;
thenContinuation.body = thenBuilder.root;
elseContinuation.body = elseBuilder.root;
add(result);
if (join == null) {
// At least one subexpression is closed.
if (thenBuilder.isOpen) {
if (thenBuilder._root != null) _current = thenBuilder._current;
if (thenBuilder.root != null) _current = thenBuilder._current;
environment = thenBuilder.environment;
} else if (elseBuilder.isOpen) {
if (elseBuilder._root != null) _current = elseBuilder._current;
if (elseBuilder.root != null) _current = elseBuilder._current;
environment = elseBuilder.environment;
} else {
_current = null;
@@ -1243,16 +1273,16 @@ class IrBuilder {
// it is guaranteed that the updateBuilder has a non-empty term.
if (hasContinues) {
outerBodyBuilder.add(new ir.LetCont(continueCollector.continuation,
innerBodyBuilder._root));
continueCollector.continuation.body = updateBuilder._root;
innerBodyBuilder.root));
continueCollector.continuation.body = updateBuilder.root;
} else {
outerBodyBuilder.add(innerBodyBuilder._root);
outerBodyBuilder.add(innerBodyBuilder.root);
}
// Create loop exit and body entry continuations and a branch to them.
ir.Continuation exitContinuation = new ir.Continuation([]);
ir.Continuation bodyContinuation = new ir.Continuation([]);
bodyContinuation.body = outerBodyBuilder._root;
bodyContinuation.body = outerBodyBuilder.root;
// Note the order of continuations: the first one is the one that will
// be filled by LetCont.plug.
ir.LetCont branch =
@@ -1268,7 +1298,7 @@ class IrBuilder {
if (hasBreaks) {
IrBuilder exitBuilder = makeDelimitedBuilder();
exitBuilder.jumpTo(breakCollector);
exitContinuation.body = exitBuilder._root;
exitContinuation.body = exitBuilder.root;
letBreak = new ir.LetCont(breakCollector.continuation, branch);
add(letBreak);
environment = breakCollector.environment;
@@ -1411,7 +1441,7 @@ class IrBuilder {
// in branch condition (body, exit)
ir.Continuation exitContinuation = new ir.Continuation([]);
ir.Continuation bodyContinuation = new ir.Continuation([]);
bodyContinuation.body = bodyBuilder._root;
bodyContinuation.body = bodyBuilder.root;
// Note the order of continuations: the first one is the one that will
// be filled by LetCont.plug.
ir.LetCont branch =
@@ -1427,7 +1457,7 @@ class IrBuilder {
if (hasBreaks) {
IrBuilder exitBuilder = makeDelimitedBuilder();
exitBuilder.jumpTo(breakCollector);
exitContinuation.body = exitBuilder._root;
exitContinuation.body = exitBuilder.root;
letBreak = new ir.LetCont(breakCollector.continuation, branch);
add(letBreak);
environment = breakCollector.environment;
@@ -1486,7 +1516,7 @@ class IrBuilder {
// Create body entry and loop exit continuations and a branch to them.
ir.Continuation exitContinuation = new ir.Continuation([]);
ir.Continuation bodyContinuation = new ir.Continuation([]);
bodyContinuation.body = bodyBuilder._root;
bodyContinuation.body = bodyBuilder.root;
// Note the order of continuations: the first one is the one that will
// be filled by LetCont.plug.
ir.LetCont branch =
@@ -1502,7 +1532,7 @@ class IrBuilder {
if (hasBreaks) {
IrBuilder exitBuilder = makeDelimitedBuilder();
exitBuilder.jumpTo(breakCollector);
exitContinuation.body = exitBuilder._root;
exitContinuation.body = exitBuilder.root;
letBreak = new ir.LetCont(breakCollector.continuation, branch);
add(letBreak);
environment = breakCollector.environment;
@@ -1569,18 +1599,18 @@ class IrBuilder {
ir.Continuation exitContinuation = new ir.Continuation([]);
IrBuilder exitBuilder = continueBuilder.makeDelimitedBuilder();
exitBuilder.jumpTo(breakCollector);
exitContinuation.body = exitBuilder._root;
exitContinuation.body = exitBuilder.root;
ir.Continuation repeatContinuation = new ir.Continuation([]);
IrBuilder repeatBuilder = continueBuilder.makeDelimitedBuilder();
repeatBuilder.jumpTo(loop);
repeatContinuation.body = repeatBuilder._root;
repeatContinuation.body = repeatBuilder.root;
continueBuilder.add(
new ir.LetCont.two(exitContinuation, repeatContinuation,
new ir.Branch.strict(condition,
repeatContinuation,
exitContinuation)));
continueCollector.continuation.body = continueBuilder._root;
continueCollector.continuation.body = continueBuilder.root;
// Construct the loop continuation (i.e., the body and condition).
// <Loop> =
@@ -1589,56 +1619,25 @@ class IrBuilder {
// in [[body]]; continue(v, ...)
loopBuilder.add(
new ir.LetCont(continueCollector.continuation,
bodyBuilder._root));
bodyBuilder.root));
// And tie it all together.
add(new ir.LetCont(breakCollector.continuation, loopBuilder._root));
add(new ir.LetCont(breakCollector.continuation, loopBuilder.root));
environment = breakCollector.environment;
}
void buildSimpleSwitch(JumpTarget target,
ir.Primitive value,
void buildSimpleSwitch(JumpCollector join,
List<SwitchCaseInfo> cases,
SwitchCaseInfo defaultCase,
Element error,
SourceInformation sourceInformation) {
assert(isOpen);
JumpCollector join = new ForwardJumpCollector(environment, target: target);
SubbuildFunction buildDefaultBody) {
IrBuilder casesBuilder = makeDelimitedBuilder();
casesBuilder.state.breakCollectors.add(join);
for (SwitchCaseInfo caseInfo in cases) {
buildConditionsFrom(int index) => (IrBuilder builder) {
ir.Primitive comparison = builder.buildIdentical(
value, caseInfo.constants[index]);
return (index == caseInfo.constants.length - 1)
? comparison
: builder.buildLogicalOperator(
comparison, buildConditionsFrom(index + 1), isLazyOr: true);
};
ir.Primitive condition = buildConditionsFrom(0)(casesBuilder);
ir.Primitive condition = caseInfo.buildCondition(casesBuilder);
IrBuilder thenBuilder = makeDelimitedBuilder();
caseInfo.buildBody(thenBuilder);
if (thenBuilder.isOpen) {
// It is a runtime error to reach the end of a switch case, unless
// it is the last case.
if (caseInfo == cases.last && defaultCase == null) {
thenBuilder.jumpTo(join);
} else {
ir.Primitive exception = thenBuilder.buildInvokeStatic(
error,
new Selector.fromElement(error),
<ir.Primitive>[],
sourceInformation);
thenBuilder.buildThrow(exception);
}
}
ir.Continuation thenContinuation = new ir.Continuation([]);
thenContinuation.body = thenBuilder._root;
thenContinuation.body = thenBuilder.root;
ir.Continuation elseContinuation = new ir.Continuation([]);
// A LetCont.many term has a hole as the body of the first listed
// A LetCont.two term has a hole as the body of the first listed
// continuation, to be plugged by the translation. Therefore put the
// else continuation first.
casesBuilder.add(
@@ -1648,18 +1647,17 @@ class IrBuilder {
elseContinuation)));
}
if (defaultCase != null) {
defaultCase.buildBody(casesBuilder);
if (buildDefaultBody == null) {
casesBuilder.jumpTo(join);
} else {
buildDefaultBody(casesBuilder);
}
if (casesBuilder.isOpen) casesBuilder.jumpTo(join);
casesBuilder.state.breakCollectors.removeLast();
if (!join.isEmpty) {
add(new ir.LetCont(join.continuation, casesBuilder._root));
add(new ir.LetCont(join.continuation, casesBuilder.root));
environment = join.environment;
} else if (casesBuilder._root != null) {
add(casesBuilder._root);
} else if (casesBuilder.root != null) {
add(casesBuilder.root);
_current = casesBuilder._current;
environment = casesBuilder.environment;
} else {
@@ -1721,11 +1719,11 @@ class IrBuilder {
List<ir.Parameter> catchParameters = buildCatch(catchBuilder, join);
ir.Continuation catchContinuation = new ir.Continuation(catchParameters);
catchContinuation.body = catchBuilder._root;
catchContinuation.body = catchBuilder.root;
tryCatchBuilder.add(
new ir.LetHandler(catchContinuation, tryBuilder._root));
new ir.LetHandler(catchContinuation, tryBuilder.root));
leaveTryCatch(this, join, tryCatchBuilder._root);
leaveTryCatch(this, join, tryCatchBuilder.root);
}
/// Translates a try/catch.
@@ -1829,7 +1827,7 @@ class IrBuilder {
}
clause.buildCatchBlock(clauseBuilder);
if (clauseBuilder.isOpen) clauseBuilder.jumpTo(join);
return clauseBuilder._root;
return clauseBuilder.root;
}
// Expand multiple catch clauses into an explicit if/then/else. Iterate
@@ -1855,7 +1853,7 @@ class IrBuilder {
new ir.Branch.strict(typeMatches,
thenContinuation,
elseContinuation)));
catchBody = checkBuilder._root;
catchBody = checkBuilder.root;
}
builder.add(catchBody);
@@ -1960,7 +1958,7 @@ class IrBuilder {
IrBuilder builder = makeDelimitedBuilder(newCollector.environment);
buildFinallyBlock(builder);
if (builder.isOpen) builder.jumpTo(originalCollector);
newCollector.continuation.body = builder._root;
newCollector.continuation.body = builder.root;
exits.add(newCollector.continuation);
}
for (int i = 0; i < newBreaks.length; ++i) {
@@ -1974,7 +1972,7 @@ class IrBuilder {
ir.Primitive value = builder.environment.discard(1);
buildFinallyBlock(builder);
if (builder.isOpen) builder.buildReturn(value: value);
newReturn.continuation.body = builder._root;
newReturn.continuation.body = builder.root;
exits.add(newReturn.continuation);
}
builder.add(new ir.LetCont.many(exits, body));
@@ -2101,10 +2099,10 @@ class IrBuilder {
bool hasBreaks = !join.isEmpty;
if (hasBreaks) {
if (innerBuilder.isOpen) innerBuilder.jumpTo(join);
add(new ir.LetCont(join.continuation, innerBuilder._root));
add(new ir.LetCont(join.continuation, innerBuilder.root));
environment = join.environment;
} else if (innerBuilder._root != null) {
add(innerBuilder._root);
} else if (innerBuilder.root != null) {
add(innerBuilder.root);
_current = innerBuilder._current;
environment = innerBuilder.environment;
} else {
@@ -2240,8 +2238,8 @@ class IrBuilder {
ir.Continuation leftFalseContinuation = new ir.Continuation([]);
ir.Continuation rightTrueContinuation = new ir.Continuation([]);
ir.Continuation rightFalseContinuation = new ir.Continuation([]);
rightTrueContinuation.body = rightTrueBuilder._root;
rightFalseContinuation.body = rightFalseBuilder._root;
rightTrueContinuation.body = rightTrueBuilder.root;
rightFalseContinuation.body = rightFalseBuilder.root;
// The right subexpression has two continuations.
rightBuilder.add(
new ir.LetCont.two(rightTrueContinuation, rightFalseContinuation,
@@ -2252,11 +2250,11 @@ class IrBuilder {
// either the right subexpression or an invocation of the join-point
// continuation.
if (isLazyOr) {
leftTrueContinuation.body = emptyBuilder._root;
leftFalseContinuation.body = rightBuilder._root;
leftTrueContinuation.body = emptyBuilder.root;
leftFalseContinuation.body = rightBuilder.root;
} else {
leftTrueContinuation.body = rightBuilder._root;
leftFalseContinuation.body = emptyBuilder._root;
leftTrueContinuation.body = rightBuilder.root;
leftFalseContinuation.body = emptyBuilder.root;
}
add(new ir.LetCont(join.continuation,
@@ -2838,10 +2836,8 @@ class CatchClauseInfo {
}
class SwitchCaseInfo {
final List<ir.Primitive> constants = <ir.Primitive>[];
final SubbuildFunction buildCondition;
final SubbuildFunction buildBody;
SwitchCaseInfo(this.buildBody);
void addConstant(ir.Primitive constant) => constants.add(constant);
SwitchCaseInfo(this.buildCondition, this.buildBody);
}
@@ -1172,44 +1172,270 @@ class IrBuilderVisitor extends ast.Visitor<ir.Primitive>
}
visitSwitchStatement(ast.SwitchStatement node) {
// Dart switch cases can be labeled and be the target of continue from
// within the switch. Such cases are 'recursive'. If there are any
// recursive cases, we implement the switch using a pair of switches with
// the second one switching over a state variable in a loop. The first
// switch contains the non-recursive cases, and the second switch contains
// the recursive ones.
//
// For example, for the Dart switch:
//
// switch (E) {
// case 0:
// BODY0;
// break;
// LABEL0: case 1:
// BODY1;
// break;
// case 2:
// BODY2;
// continue LABEL1;
// LABEL1: case 3:
// BODY3;
// continue LABEL0;
// default:
// BODY4;
// }
//
// We translate it as if it were the JavaScript:
//
// var state = -1;
// switch (E) {
// case 0:
// BODY0;
// break;
// case 1:
// state = 0; // Recursive, label ID = 0.
// break;
// case 2:
// BODY2;
// state = 1; // Continue to label ID = 1.
// break;
// case 3:
// state = 1; // Recursive, label ID = 1.
// break;
// default:
// BODY4;
// }
// L: while (state != -1) {
// case 0:
// BODY1;
// break L; // Break from switch becomes break from loop.
// case 1:
// BODY2;
// state = 0; // Continue to label ID = 0.
// break;
// }
assert(irBuilder.isOpen);
// We do not handle switch statements with continue to labeled cases.
for (ast.SwitchCase switchCase in node.cases) {
// Preprocess: compute a list of cases that are the target of continue.
// These are the so-called 'recursive' cases.
List<JumpTarget> continueTargets = <JumpTarget>[];
List<ast.SwitchCase> switchCases = node.cases.nodes.toList();
for (ast.SwitchCase switchCase in switchCases) {
for (ast.Node labelOrCase in switchCase.labelsAndCases) {
if (labelOrCase is ast.Label) {
LabelDefinition definition = elements.getLabelDefinition(labelOrCase);
if (definition != null && definition.isContinueTarget) {
return giveup(node, "continue to a labeled switch case");
continueTargets.add(definition.target);
}
}
}
}
// Each switch case contains a list of interleaved labels and expressions
// and a non-empty body. We can ignore the labels because they are not
// jump targets.
List<SwitchCaseInfo> cases = <SwitchCaseInfo>[];
SwitchCaseInfo defaultCase;
for (ast.SwitchCase switchCase in node.cases) {
SwitchCaseInfo caseInfo =
new SwitchCaseInfo(subbuildSequence(switchCase.statements));
if (switchCase.isDefaultCase) {
defaultCase = caseInfo;
} else {
cases.add(caseInfo);
for (ast.Node labelOrCase in switchCase.labelsAndCases) {
if (labelOrCase is ast.CaseMatch) {
ir.Primitive constant = translateConstant(labelOrCase.expression);
caseInfo.addConstant(constant);
}
}
}
// If any cases are continue targets, use an anonymous local value to
// implement a state machine. The initial value is -1.
ir.Primitive initial;
int stateIndex;
if (continueTargets.isNotEmpty) {
initial = irBuilder.buildIntegerConstant(-1);
stateIndex = irBuilder.environment.length;
irBuilder.environment.extend(null, initial);
}
// Use a simple switch for the non-recursive cases. A break will go to the
// join-point after the switch. A continue to a labeled case will assign
// to the state variable and go to the join-point.
ir.Primitive value = visit(node.expression);
JumpTarget target = elements.getTargetDefinition(node);
Element error = helpers.fallThroughError;
irBuilder.buildSimpleSwitch(target, value, cases, defaultCase, error,
sourceInformationBuilder.buildGeneric(node));
JumpCollector join = new ForwardJumpCollector(irBuilder.environment,
target: elements.getTargetDefinition(node));
irBuilder.state.breakCollectors.add(join);
for (int i = 0; i < continueTargets.length; ++i) {
// The state value is i, the case's position in the list of recursive
// cases.
irBuilder.state.continueCollectors.add(new GotoJumpCollector(
continueTargets[i], stateIndex, i, join));
}
// For each non-default case use a pair of functions, one to translate the
// condition and one to translate the body. For the default case use a
// function to translate the body. Use continueTargetIterator as a pointer
// to the next recursive case.
Iterator<JumpTarget> continueTargetIterator = continueTargets.iterator;
continueTargetIterator.moveNext();
List<SwitchCaseInfo> cases = <SwitchCaseInfo>[];
SubbuildFunction buildDefaultBody;
for (ast.SwitchCase switchCase in switchCases) {
JumpTarget nextContinueTarget = continueTargetIterator.current;
if (switchCase.isDefaultCase) {
if (nextContinueTarget != null &&
switchCase == nextContinueTarget.statement) {
// In this simple switch, recursive cases are as if they immediately
// continued to themselves.
buildDefaultBody = nested(() {
irBuilder.buildContinue(nextContinueTarget);
});
continueTargetIterator.moveNext();
} else {
// Non-recursive cases consist of the translation of the body.
// For the default case, there is implicitly a break if control
// flow reaches the end.
buildDefaultBody = nested(() {
irBuilder.buildSequence(switchCase.statements, visit);
if (irBuilder.isOpen) irBuilder.jumpTo(join);
});
}
continue;
}
ir.Primitive buildCondition(IrBuilder builder) {
// There can be multiple cases sharing the same body, because empty
// cases are allowed to fall through to the next one. Each case is
// a comparison, build a short-circuited disjunction of all of them.
return withBuilder(builder, () {
ir.Primitive condition;
for (ast.Node labelOrCase in switchCase.labelsAndCases) {
if (labelOrCase is ast.CaseMatch) {
ir.Primitive buildComparison() {
ir.Primitive constant =
translateConstant(labelOrCase.expression);
return irBuilder.buildIdentical(value, constant);
}
if (condition == null) {
condition = buildComparison();
} else {
condition = irBuilder.buildLogicalOperator(condition,
nested(buildComparison), isLazyOr: true);
}
}
}
return condition;
});
}
SubbuildFunction buildBody;
if (nextContinueTarget != null &&
switchCase == nextContinueTarget.statement) {
// Recursive cases are as if they immediately continued to themselves.
buildBody = nested(() {
irBuilder.buildContinue(nextContinueTarget);
});
continueTargetIterator.moveNext();
} else {
// Non-recursive cases consist of the translation of the body. It is a
// runtime error if control-flow reaches the end of the body of any but
// the last case.
buildBody = (IrBuilder builder) {
withBuilder(builder, () {
irBuilder.buildSequence(switchCase.statements, visit);
if (irBuilder.isOpen) {
if (switchCase == switchCases.last) {
irBuilder.jumpTo(join);
} else {
Element error = helpers.fallThroughError;
ir.Primitive exception = irBuilder.buildInvokeStatic(
error,
new Selector.fromElement(error),
<ir.Primitive>[],
sourceInformationBuilder.buildGeneric(node));
irBuilder.buildThrow(exception);
}
}
});
return null;
};
}
cases.add(new SwitchCaseInfo(buildCondition, buildBody));
}
irBuilder.buildSimpleSwitch(join, cases, buildDefaultBody);
irBuilder.state.breakCollectors.removeLast();
irBuilder.state.continueCollectors.length -= continueTargets.length;
if (continueTargets.isEmpty) return;
// If there were recursive cases build a while loop whose body is a
// switch containing (only) the recursive cases. The condition is
// 'state != initialValue' so the loop is not taken when the state variable
// has not been assigned.
//
// 'loop' is the join-point of the exits from the inner switch which will
// perform another iteration of the loop. 'exit' is the join-point of the
// breaks from the switch, outside the loop.
JumpCollector loop = new ForwardJumpCollector(irBuilder.environment);
JumpCollector exit = new ForwardJumpCollector(irBuilder.environment,
target: elements.getTargetDefinition(node));
irBuilder.state.breakCollectors.add(exit);
for (int i = 0; i < continueTargets.length; ++i) {
irBuilder.state.continueCollectors.add(new GotoJumpCollector(
continueTargets[i], stateIndex, i, loop));
}
cases.clear();
for (int i = 0; i < continueTargets.length; ++i) {
// The conditions compare to the recursive case index.
ir.Primitive buildCondition(IrBuilder builder) {
ir.Primitive constant = builder.buildIntegerConstant(i);
return builder.buildIdentical(
builder.environment.index2value[stateIndex], constant);
}
ir.Primitive buildBody(IrBuilder builder) {
withBuilder(builder, () {
ast.SwitchCase switchCase = continueTargets[i].statement;
irBuilder.buildSequence(switchCase.statements, visit);
if (irBuilder.isOpen) {
if (switchCase == switchCases.last) {
irBuilder.jumpTo(exit);
} else {
Element error = helpers.fallThroughError;
ir.Primitive exception = irBuilder.buildInvokeStatic(
error,
new Selector.fromElement(error),
<ir.Primitive>[],
sourceInformationBuilder.buildGeneric(node));
irBuilder.buildThrow(exception);
}
}
});
return null;
}
cases.add(new SwitchCaseInfo(buildCondition, buildBody));
}
// A loop with a simple switch in the body.
IrBuilder whileBuilder = irBuilder.makeDelimitedBuilder();
whileBuilder.buildWhile(
buildCondition: (IrBuilder builder) {
ir.Primitive condition = builder.buildIdentical(
builder.environment.index2value[stateIndex], initial);
return builder.buildNegation(condition);
},
buildBody: (IrBuilder builder) {
builder.buildSimpleSwitch(loop, cases, null);
});
// Jump to the exit continuation. This jump is the body of the loop exit
// continuation, so the loop exit continuation can be eta-reduced. The
// jump is here for simplicity because `buildWhile` does not expose the
// loop's exit continuation directly and has already emitted all jumps
// to it anyway.
whileBuilder.jumpTo(exit);
irBuilder.add(new ir.LetCont(exit.continuation, whileBuilder.root));
irBuilder.environment = exit.environment;
irBuilder.environment.discard(1); // Discard the state variable.
irBuilder.state.breakCollectors.removeLast();
irBuilder.state.continueCollectors.length -= continueTargets.length;
}
visitTryStatement(ast.TryStatement node) {
@@ -30,6 +30,7 @@ class SExpressionStringifier extends Indentation implements Visitor<String> {
}
String access(Reference<Definition> r) {
if (r == null) return '**** NULL ****';
return decorator(r, namer.getName(r.definition));
}
@@ -192,7 +193,9 @@ class SExpressionStringifier extends Indentation implements Visitor<String> {
String visitInvokeContinuation(InvokeContinuation node) {
String name = access(node.continuation);
if (node.isRecursive) name = 'rec $name';
String args = node.arguments.map(access).join(' ');
String args = node.arguments == null
? '**** NULL ****'
: node.arguments.map(access).join(' ');
String escaping = node.isEscapingTry ? ' escape' : '';
return '$indentation(InvokeContinuation $name ($args)$escaping)';
}
-3
View File
@@ -9616,9 +9616,6 @@ WebPlatformTest/webstorage/storage_builtins_t01: RuntimeError # Please triage th
WebPlatformTest/webstorage/storage_local_setitem_quotaexceedederr_t01: Skip # Times out. Please triage this failure
[ $compiler == dart2js && $cps_ir ]
Language/Statements/Labels/scope_t04: Crash # (switch (i){L:case 0:flag=true;break;case 2:continue L;}): continue to a labeled switch case
Language/Statements/Continue/label_t12: Crash # (switch (2){L:case 1:flag=true;break;case 2:continue L;}): continue to a labeled switch case
Language/Statements/Continue/label_t13: Crash # (switch (2){case 2:continue L;L:case 1:flag=true;}): continue to a labeled switch case
Language/Types/Interface_Types/subtype_t09: Crash # Pending static: JSArray
Language/Types/Interface_Types/subtype_t39: RuntimeError # Please triage this failure.
LibTest/collection/ListBase/ListBase_class_A01_t02: Pass, Timeout
@@ -68,4 +68,3 @@ big_allocation_expression_test: Crash # Issue 24635
[ $compiler == dart2js && $cps_ir ]
async_stacktrace_test/asyncStar: Crash # (foo()async*{try {tr... cannot handle sync*/async* functions
switch_test/none: Crash # (switch (val){foo:ba... continue to a labeled switch case
+3 -8
View File
@@ -251,9 +251,9 @@ async_await_syntax_test/d03a: Crash # (()async*{}): cannot handle sync*/async* f
async_await_syntax_test/d06a: Crash # (await for(var o in st){}): await for
async_await_syntax_test/d09a: Crash # (()async*{yield 0;}): cannot handle sync*/async* functions
async_await_syntax_test/d10a: Crash # (()async*{yield* [] ;}): cannot handle sync*/async* functions
async_await_test/02: Crash # (switch (v){label:ca... continue to a labeled switch case
async_await_test/03: Crash # (switch (v){label:ca... continue to a labeled switch case
async_await_test/none: Crash # (switch (v){label:ca... continue to a labeled switch case
async_await_test/02: Crash # bailout: (await for
async_await_test/03: Crash # bailout: (await for
async_await_test/none: Crash # bailout: (await for
async_or_generator_return_type_stacktrace_test/02: Crash # (void badReturnTypeAsyncStar()async*{}): cannot handle sync*/async* functions
async_return_types_test/nestedFuture: Crash # cannot handle sync*/async* functions
async_return_types_test/none: Crash # cannot handle sync*/async* functions
@@ -287,14 +287,12 @@ generic2_test: RuntimeError # Please triage this failure.
generic_instanceof_test: RuntimeError # Please triage this failure.
generic_native_test: RuntimeError # Please triage this failure.
gc_test: Crash # Internal Error: Pending statics (see above).
infinite_switch_label_test: Crash # (switch (target){l0:... continue to a labeled switch case
instanceof2_test: RuntimeError # Please triage this failure.
instanceof4_test/01: RuntimeError # Please triage this failure.
invocation_mirror_test: Crash # (super[37]=42): visitUnresolvedSuperIndexSet
list_is_test: RuntimeError # Please triage this failure.
list_test: RuntimeError # Please triage this failure.
many_generic_instanceof_test: RuntimeError # Please triage this failure.
nested_switch_label_test: Crash # (switch (target){out... continue to a labeled switch case
regress_23500_test/01: Crash # (await for(var c in new Stream.fromIterable([] )){}): await for
savannah_test: RuntimeError # Success depends on the variable hints.
super_call4_test: RuntimeError # Please triage this failure.
@@ -304,9 +302,6 @@ super_operator_index7_test: Crash # (super[0]=42): visitUnresolvedSuperIndexSet
super_operator_index8_test: Crash # (super[f()]=g()): visitUnresolvedSuperIndexSet
super_operator_index_test/03: Crash # (super[4]=42): visitUnresolvedSuperIndexSet
super_operator_index_test/05: Crash # (super[4]=42): visitUnresolvedSuperIndexSet
switch_label2_test: Crash # (switch (target){cas... continue to a labeled switch case
switch_label_test: Crash # (switch (animal){cas... continue to a labeled switch case
switch_try_catch_test: Crash # (switch (0){_0:case ... continue to a labeled switch case
switch8_test: Crash # Pending statics: JSArray
type_variable_closure2_test: RuntimeError # Issue 25309: T lost in List<T>
type_variable_field_initializer_closure_test: RuntimeError # Issue 25309: T lost in List<T>
+3 -6
View File
@@ -5,8 +5,10 @@
[ $compiler == dart2js && $host_checked ]
dummy_compiler_test: Crash # Issue 22809
[ $compiler == dart2js ]
[ $compiler == dart2js && $host_checked == false ]
dummy_compiler_test: Slow, Pass
[ $compiler == dart2js ]
recursive_import_test: Slow, Pass
source_mirrors_test: Slow, Pass
@@ -25,10 +27,5 @@ dummy_compiler_test: Pass, RuntimeError # Issue 17662
recursive_import_test: Pass, RuntimeError # Issue 17662
source_mirrors_test: Pass, RuntimeError # Issue 17662
[ $compiler == dart2js && $cps_ir ]
dummy_compiler_test: Crash # (switch (function.na... continue to a labeled switch case
recursive_import_test: Crash # (switch (function.na... continue to a labeled switch case
source_mirrors_test: Crash, Slow # (switch (function.na... continue to a labeled switch case
[ ($noopt || $compiler == precompiler) ]
source_mirrors_test: SkipByDesign # Imports dart:mirrors