Support for loops in analyzer2dart.
BUG= R=sigurdm@google.com Review URL: https://codereview.chromium.org//683803003 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@41355 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
@@ -302,4 +302,19 @@ class CpsGeneratingVisitor extends SemanticVisitor<ir.Node>
|
||||
visitBlock(Block node) {
|
||||
irBuilder.buildBlock(node.statements, build);
|
||||
}
|
||||
|
||||
@override
|
||||
visitForStatement(ForStatement node) {
|
||||
// TODO(johnniwinther): Support `for` as a jump target.
|
||||
SubbuildFunction buildInitializer;
|
||||
if (node.variables != null) {
|
||||
buildInitializer = subbuild(node.variables);
|
||||
} else {
|
||||
buildInitializer = subbuild(node.initialization);
|
||||
}
|
||||
irBuilder.buildFor(buildInitializer: buildInitializer,
|
||||
buildCondition: subbuild(node.condition),
|
||||
buildBody: subbuild(node.body),
|
||||
buildUpdate: subbuildSequence(node.updaters));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,11 @@ void compileToDart(Driver driver, ConvertedWorld convertedWorld) {
|
||||
mainFunction: convertedWorld.mainFunction,
|
||||
computeElementAst: (Element element) {
|
||||
return DartBackend.createElementAst(
|
||||
null, null, null, null, convertedWorld.getIr(element));
|
||||
null, // No compiler.
|
||||
null, // No tracer.
|
||||
DART_CONSTANT_SYSTEM,
|
||||
element,
|
||||
convertedWorld.getIr(element));
|
||||
},
|
||||
shouldOutput: (_) => true,
|
||||
isSafeToRemoveTypeDeclarations: (_) => false);
|
||||
|
||||
@@ -512,6 +512,50 @@ const TestSpec('''
|
||||
main(a) {
|
||||
new Deprecated("");
|
||||
}
|
||||
'''),
|
||||
]),
|
||||
|
||||
const Group('For loop', const <TestSpec>[
|
||||
const TestSpec('''
|
||||
main() {
|
||||
for (;;) {}
|
||||
}
|
||||
''', '''
|
||||
main() {
|
||||
while (true) {}
|
||||
}
|
||||
'''),
|
||||
|
||||
const TestSpec('''
|
||||
main() {
|
||||
for (int i = 0; i < 10; i = i + 1) {
|
||||
print(i);
|
||||
}
|
||||
}
|
||||
''', '''
|
||||
main() {
|
||||
var i = 0;
|
||||
while (i < 10) {
|
||||
print(i);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
'''),
|
||||
|
||||
const TestSpec('''
|
||||
main(i) {
|
||||
for (i = 0; i < 10; i = i + 1) {
|
||||
print(i);
|
||||
}
|
||||
}
|
||||
''', '''
|
||||
main(i) {
|
||||
i = 0;
|
||||
while (i < 10) {
|
||||
print(i);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
'''),
|
||||
]),
|
||||
];
|
||||
|
||||
@@ -813,6 +813,79 @@ main(a) {
|
||||
(LetPrim v2 (Constant NullConstant))
|
||||
(InvokeContinuation return v2))
|
||||
(InvokeConstructor Deprecated v0 k0))
|
||||
'''),
|
||||
]),
|
||||
|
||||
const Group('For loop', const <TestSpec>[
|
||||
const TestSpec('''
|
||||
main() {
|
||||
for (;;) {}
|
||||
}
|
||||
''', '''
|
||||
(FunctionDefinition main ( return)
|
||||
(LetCont* (k0)
|
||||
(LetPrim v0 (Constant BoolConstant(true)))
|
||||
(LetCont (k1)
|
||||
(LetPrim v1 (Constant NullConstant))
|
||||
(InvokeContinuation return v1))
|
||||
(LetCont (k2)
|
||||
(InvokeContinuation* k0 ))
|
||||
(Branch (IsTrue v0) k2 k1))
|
||||
(InvokeContinuation k0 ))
|
||||
'''),
|
||||
|
||||
const TestSpec('''
|
||||
main() {
|
||||
for (int i = 0; i < 10; i = i + 1) {
|
||||
print(i);
|
||||
}
|
||||
}
|
||||
''', '''
|
||||
(FunctionDefinition main ( return)
|
||||
(LetPrim v0 (Constant IntConstant(0)))
|
||||
(LetCont* (k0 v1)
|
||||
(LetPrim v2 (Constant IntConstant(10)))
|
||||
(LetCont (k1 v3)
|
||||
(LetCont (k2)
|
||||
(LetPrim v4 (Constant NullConstant))
|
||||
(InvokeContinuation return v4))
|
||||
(LetCont (k3)
|
||||
(LetCont (k4 v5)
|
||||
(LetPrim v6 (Constant IntConstant(1)))
|
||||
(LetCont (k5 v7)
|
||||
(InvokeContinuation* k0 v7))
|
||||
(InvokeMethod v1 + v6 k5))
|
||||
(InvokeStatic print v1 k4))
|
||||
(Branch (IsTrue v3) k3 k2))
|
||||
(InvokeMethod v1 < v2 k1))
|
||||
(InvokeContinuation k0 v0))
|
||||
'''),
|
||||
|
||||
const TestSpec('''
|
||||
main(i) {
|
||||
for (i = 0; i < 10; i = i + 1) {
|
||||
print(i);
|
||||
}
|
||||
}
|
||||
''', '''
|
||||
(FunctionDefinition main (i return)
|
||||
(LetPrim v0 (Constant IntConstant(0)))
|
||||
(LetCont* (k0 v1)
|
||||
(LetPrim v2 (Constant IntConstant(10)))
|
||||
(LetCont (k1 v3)
|
||||
(LetCont (k2)
|
||||
(LetPrim v4 (Constant NullConstant))
|
||||
(InvokeContinuation return v4))
|
||||
(LetCont (k3)
|
||||
(LetCont (k4 v5)
|
||||
(LetPrim v6 (Constant IntConstant(1)))
|
||||
(LetCont (k5 v7)
|
||||
(InvokeContinuation* k0 v7))
|
||||
(InvokeMethod v1 + v6 k5))
|
||||
(InvokeStatic print v1 k4))
|
||||
(Branch (IsTrue v3) k3 k2))
|
||||
(InvokeMethod v1 < v2 k1))
|
||||
(InvokeContinuation k0 v0))
|
||||
'''),
|
||||
]),
|
||||
];
|
||||
|
||||
@@ -162,6 +162,16 @@ abstract class IrBuilderMixin<N> {
|
||||
SubbuildFunction subbuild(N node) {
|
||||
return (IrBuilder builder) => withBuilder(builder, () => build(node));
|
||||
}
|
||||
|
||||
/// Returns a closure that takes an [IrBuilder] and builds the sequence of
|
||||
/// [nodes] in its context using [build].
|
||||
// TODO(johnniwinther): Type [nodes] as `Iterable<N>` when `NodeList` uses
|
||||
// `List` instead of `Link`.
|
||||
SubbuildFunction subbuildSequence(/*Iterable<N>*/ nodes) {
|
||||
return (IrBuilder builder) {
|
||||
return withBuilder(builder, () => builder.buildSequence(nodes, build));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Shared state between nested builders.
|
||||
@@ -619,6 +629,154 @@ class IrBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
/// Invoke a join-point continuation that contains arguments for all local
|
||||
/// variables.
|
||||
///
|
||||
/// Given the continuation and a list of uninitialized invocations, fill
|
||||
/// in each invocation with the continuation and appropriate arguments.
|
||||
void invokeFullJoin(ir.Continuation join,
|
||||
JumpCollector jumps,
|
||||
{recursive: false}) {
|
||||
join.isRecursive = recursive;
|
||||
for (int i = 0; i < jumps.length; ++i) {
|
||||
Environment currentEnvironment = jumps.environments[i];
|
||||
ir.InvokeContinuation invoke = jumps.invocations[i];
|
||||
invoke.continuation = new ir.Reference(join);
|
||||
invoke.arguments = new List<ir.Reference>.generate(
|
||||
join.parameters.length,
|
||||
(i) => new ir.Reference(currentEnvironment[i]));
|
||||
invoke.isRecursive = recursive;
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a for loop in which the initializer, condition, body, update are
|
||||
/// created by [buildInitializer], [buildCondition], [buildBody] and
|
||||
/// [buildUpdate], respectively.
|
||||
///
|
||||
/// The jump [target] is used to identify which `break` and `continue`
|
||||
/// statements that have this `for` statement as their target.
|
||||
void buildFor({SubbuildFunction buildInitializer,
|
||||
SubbuildFunction buildCondition,
|
||||
SubbuildFunction buildBody,
|
||||
SubbuildFunction buildUpdate,
|
||||
JumpTarget target}) {
|
||||
assert(isOpen);
|
||||
|
||||
// For loops use four named continuations: the entry to the condition,
|
||||
// the entry to the body, the loop exit, and the loop successor (break).
|
||||
// The CPS translation of
|
||||
// [[for (initializer; condition; update) body; successor]] is:
|
||||
//
|
||||
// [[initializer]];
|
||||
// let cont loop(x, ...) =
|
||||
// let prim cond = [[condition]] in
|
||||
// let cont break() = [[successor]] in
|
||||
// let cont exit() = break(v, ...) in
|
||||
// let cont body() =
|
||||
// let cont continue(x, ...) = [[update]]; loop(v, ...) in
|
||||
// [[body]]; continue(v, ...) in
|
||||
// branch cond (body, exit) in
|
||||
// loop(v, ...)
|
||||
//
|
||||
// If there are no breaks in the body, the break continuation is inlined
|
||||
// in the exit continuation (i.e., the translation of the successor
|
||||
// statement occurs in the exit continuation). If there is only one
|
||||
// invocation of the continue continuation (i.e., no continues in the
|
||||
// body), the continue continuation is inlined in the body.
|
||||
|
||||
buildInitializer(this);
|
||||
|
||||
IrBuilder condBuilder = new IrBuilder.recursive(this);
|
||||
ir.Primitive condition = buildCondition(condBuilder);
|
||||
if (condition == null) {
|
||||
// If the condition is empty then the body is entered unconditionally.
|
||||
condition = condBuilder.buildBooleanLiteral(true);
|
||||
}
|
||||
|
||||
JumpCollector breakCollector = new JumpCollector(target);
|
||||
JumpCollector continueCollector = new JumpCollector(target);
|
||||
state.breakCollectors.add(breakCollector);
|
||||
state.continueCollectors.add(continueCollector);
|
||||
|
||||
IrBuilder bodyBuilder = new IrBuilder.delimited(condBuilder);
|
||||
buildBody(bodyBuilder);
|
||||
assert(state.breakCollectors.last == breakCollector);
|
||||
assert(state.continueCollectors.last == continueCollector);
|
||||
state.breakCollectors.removeLast();
|
||||
state.continueCollectors.removeLast();
|
||||
|
||||
// The binding of the continue continuation should occur as late as
|
||||
// possible, that is, at the nearest common ancestor of all the continue
|
||||
// sites in the body. However, that is difficult to compute here, so it
|
||||
// is instead placed just outside the body of the body continuation.
|
||||
bool hasContinues = !continueCollector.isEmpty;
|
||||
IrBuilder updateBuilder = hasContinues
|
||||
? new IrBuilder.recursive(condBuilder)
|
||||
: bodyBuilder;
|
||||
buildUpdate(updateBuilder);
|
||||
|
||||
// Create body entry and loop exit continuations and a branch to them.
|
||||
ir.Continuation bodyContinuation = new ir.Continuation([]);
|
||||
ir.Continuation exitContinuation = new ir.Continuation([]);
|
||||
ir.LetCont branch =
|
||||
new ir.LetCont(exitContinuation,
|
||||
new ir.LetCont(bodyContinuation,
|
||||
new ir.Branch(new ir.IsTrue(condition),
|
||||
bodyContinuation,
|
||||
exitContinuation)));
|
||||
// If there are breaks in the body, then there must be a join-point
|
||||
// continuation for the normal exit and the breaks.
|
||||
bool hasBreaks = !breakCollector.isEmpty;
|
||||
ir.LetCont letJoin;
|
||||
if (hasBreaks) {
|
||||
letJoin = new ir.LetCont(null, branch);
|
||||
condBuilder.add(letJoin);
|
||||
condBuilder._current = branch;
|
||||
} else {
|
||||
condBuilder.add(branch);
|
||||
}
|
||||
ir.Continuation continueContinuation;
|
||||
if (hasContinues) {
|
||||
// If there are continues in the body, we need a named continue
|
||||
// continuation as a join point.
|
||||
continueContinuation = new ir.Continuation(updateBuilder._parameters);
|
||||
if (bodyBuilder.isOpen) continueCollector.addJump(bodyBuilder);
|
||||
invokeFullJoin(continueContinuation, continueCollector);
|
||||
}
|
||||
ir.Continuation loopContinuation =
|
||||
new ir.Continuation(condBuilder._parameters);
|
||||
if (updateBuilder.isOpen) {
|
||||
JumpCollector backEdges = new JumpCollector(null);
|
||||
backEdges.addJump(updateBuilder);
|
||||
invokeFullJoin(loopContinuation, backEdges, recursive: true);
|
||||
}
|
||||
|
||||
// Fill in the body and possible continue continuation bodies. Do this
|
||||
// only after it is guaranteed that they are not empty.
|
||||
if (hasContinues) {
|
||||
continueContinuation.body = updateBuilder._root;
|
||||
bodyContinuation.body =
|
||||
new ir.LetCont(continueContinuation, bodyBuilder._root);
|
||||
} else {
|
||||
bodyContinuation.body = bodyBuilder._root;
|
||||
}
|
||||
|
||||
loopContinuation.body = condBuilder._root;
|
||||
add(new ir.LetCont(loopContinuation,
|
||||
new ir.InvokeContinuation(loopContinuation,
|
||||
environment.index2value)));
|
||||
if (hasBreaks) {
|
||||
_current = branch;
|
||||
environment = condBuilder.environment;
|
||||
breakCollector.addJump(this);
|
||||
letJoin.continuation = createJoin(environment.length, breakCollector);
|
||||
_current = letJoin;
|
||||
} else {
|
||||
_current = condBuilder._current;
|
||||
environment = condBuilder.environment;
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a return statement `return value;` or `return;` if [value] is
|
||||
/// null.
|
||||
void buildReturn([ir.Primitive value]) {
|
||||
@@ -635,16 +793,25 @@ class IrBuilder {
|
||||
}
|
||||
|
||||
/// Create a blocks of [statements] by applying [build] to all reachable
|
||||
/// statements.
|
||||
/// statements. The first statement is assumed to be reachable.
|
||||
// TODO(johnniwinther): Type [statements] as `Iterable` when `NodeList` uses
|
||||
// `List` instead of `Link`.
|
||||
void buildBlock(var statements, build(statement)) {
|
||||
// Build(Block(stamements), C) = C'
|
||||
// where C' = statements.fold(Build, C)
|
||||
assert(isOpen);
|
||||
for (var statement in statements) {
|
||||
build(statement);
|
||||
return buildSequence(statements, build);
|
||||
}
|
||||
|
||||
/// Creates a sequence of [nodes] by applying [build] to all reachable nodes.
|
||||
///
|
||||
/// The first node in the sequence does not need to be reachable.
|
||||
// TODO(johnniwinther): Type [nodes] as `Iterable` when `NodeList` uses
|
||||
// `List` instead of `Link`.
|
||||
void buildSequence(var nodes, build(node)) {
|
||||
for (var node in nodes) {
|
||||
if (!isOpen) return;
|
||||
build(node);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -210,28 +210,7 @@ class IrBuilderVisitor extends ResolvedVisitor<ir.Primitive>
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Invoke a join-point continuation that contains arguments for all local
|
||||
/// variables.
|
||||
///
|
||||
/// Given the continuation and a list of uninitialized invocations, fill
|
||||
/// in each invocation with the continuation and appropriate arguments.
|
||||
void invokeFullJoin(ir.Continuation join,
|
||||
JumpCollector jumps,
|
||||
{recursive: false}) {
|
||||
join.isRecursive = recursive;
|
||||
for (int i = 0; i < jumps.length; ++i) {
|
||||
Environment currentEnvironment = jumps.environments[i];
|
||||
ir.InvokeContinuation invoke = jumps.invocations[i];
|
||||
invoke.continuation = new ir.Reference(join);
|
||||
invoke.arguments = new List<ir.Reference>.generate(
|
||||
join.parameters.length,
|
||||
(i) => new ir.Reference(currentEnvironment[i]));
|
||||
invoke.isRecursive = recursive;
|
||||
}
|
||||
}
|
||||
|
||||
ir.Primitive visitFor(ast.For node) {
|
||||
assert(irBuilder.isOpen);
|
||||
visitFor(ast.For node) {
|
||||
// TODO(kmillikin,sigurdm): Handle closure variables declared in a for-loop.
|
||||
if (node.initializer is ast.VariableDefinitions) {
|
||||
ast.VariableDefinitions definitions = node.initializer;
|
||||
@@ -243,130 +222,16 @@ class IrBuilderVisitor extends ResolvedVisitor<ir.Primitive>
|
||||
}
|
||||
}
|
||||
|
||||
// For loops use four named continuations: the entry to the condition,
|
||||
// the entry to the body, the loop exit, and the loop successor (break).
|
||||
// The CPS translation of
|
||||
// [[for (initializer; condition; update) body; successor]] is:
|
||||
//
|
||||
// [[initializer]];
|
||||
// let cont loop(x, ...) =
|
||||
// let prim cond = [[condition]] in
|
||||
// let cont break() = [[successor]] in
|
||||
// let cont exit() = break(v, ...) in
|
||||
// let cont body() =
|
||||
// let cont continue(x, ...) = [[update]]; loop(v, ...) in
|
||||
// [[body]]; continue(v, ...) in
|
||||
// branch cond (body, exit) in
|
||||
// loop(v, ...)
|
||||
//
|
||||
// If there are no breaks in the body, the break continuation is inlined
|
||||
// in the exit continuation (i.e., the translation of the successor
|
||||
// statement occurs in the exit continuation). If there is only one
|
||||
// invocation of the continue continuation (i.e., no continues in the
|
||||
// body), the continue continuation is inlined in the body.
|
||||
|
||||
if (node.initializer != null) visit(node.initializer);
|
||||
|
||||
IrBuilder condBuilder = new IrBuilder.recursive(irBuilder);
|
||||
ir.Primitive condition;
|
||||
if (node.condition == null) {
|
||||
// If the condition is empty then the body is entered unconditionally.
|
||||
condition = condBuilder.buildBooleanLiteral(true);
|
||||
} else {
|
||||
condition = withBuilder(condBuilder, () => visit(node.condition));
|
||||
}
|
||||
|
||||
JumpTarget target = elements.getTargetDefinition(node);
|
||||
JumpCollector breakCollector = new JumpCollector(target);
|
||||
JumpCollector continueCollector = new JumpCollector(target);
|
||||
irBuilder.state.breakCollectors.add(breakCollector);
|
||||
irBuilder.state.continueCollectors.add(continueCollector);
|
||||
|
||||
IrBuilder bodyBuilder = new IrBuilder.delimited(condBuilder);
|
||||
withBuilder(bodyBuilder, () => visit(node.body));
|
||||
assert(irBuilder.state.breakCollectors.last == breakCollector);
|
||||
assert(irBuilder.state.continueCollectors.last == continueCollector);
|
||||
irBuilder.state.breakCollectors.removeLast();
|
||||
irBuilder.state.continueCollectors.removeLast();
|
||||
|
||||
// The binding of the continue continuation should occur as late as
|
||||
// possible, that is, at the nearest common ancestor of all the continue
|
||||
// sites in the body. However, that is difficult to compute here, so it
|
||||
// is instead placed just outside the body of the body continuation.
|
||||
bool hasContinues = !continueCollector.isEmpty;
|
||||
IrBuilder updateBuilder = hasContinues
|
||||
? new IrBuilder.recursive(condBuilder)
|
||||
: bodyBuilder;
|
||||
for (ast.Node n in node.update) {
|
||||
if (!updateBuilder.isOpen) break;
|
||||
withBuilder(updateBuilder, () => visit(n));
|
||||
}
|
||||
|
||||
// Create body entry and loop exit continuations and a branch to them.
|
||||
ir.Continuation bodyContinuation = new ir.Continuation([]);
|
||||
ir.Continuation exitContinuation = new ir.Continuation([]);
|
||||
ir.LetCont branch =
|
||||
new ir.LetCont(exitContinuation,
|
||||
new ir.LetCont(bodyContinuation,
|
||||
new ir.Branch(new ir.IsTrue(condition),
|
||||
bodyContinuation,
|
||||
exitContinuation)));
|
||||
// If there are breaks in the body, then there must be a join-point
|
||||
// continuation for the normal exit and the breaks.
|
||||
bool hasBreaks = !breakCollector.isEmpty;
|
||||
ir.LetCont letJoin;
|
||||
if (hasBreaks) {
|
||||
letJoin = new ir.LetCont(null, branch);
|
||||
condBuilder.add(letJoin);
|
||||
condBuilder._current = branch;
|
||||
} else {
|
||||
condBuilder.add(branch);
|
||||
}
|
||||
ir.Continuation continueContinuation;
|
||||
if (hasContinues) {
|
||||
// If there are continues in the body, we need a named continue
|
||||
// continuation as a join point.
|
||||
continueContinuation = new ir.Continuation(updateBuilder._parameters);
|
||||
if (bodyBuilder.isOpen) continueCollector.addJump(bodyBuilder);
|
||||
invokeFullJoin(continueContinuation, continueCollector);
|
||||
}
|
||||
ir.Continuation loopContinuation =
|
||||
new ir.Continuation(condBuilder._parameters);
|
||||
if (updateBuilder.isOpen) {
|
||||
JumpCollector backEdges = new JumpCollector(null);
|
||||
backEdges.addJump(updateBuilder);
|
||||
invokeFullJoin(loopContinuation, backEdges, recursive: true);
|
||||
}
|
||||
|
||||
// Fill in the body and possible continue continuation bodies. Do this
|
||||
// only after it is guaranteed that they are not empty.
|
||||
if (hasContinues) {
|
||||
continueContinuation.body = updateBuilder._root;
|
||||
bodyContinuation.body =
|
||||
new ir.LetCont(continueContinuation, bodyBuilder._root);
|
||||
} else {
|
||||
bodyContinuation.body = bodyBuilder._root;
|
||||
}
|
||||
|
||||
loopContinuation.body = condBuilder._root;
|
||||
irBuilder.add(new ir.LetCont(loopContinuation,
|
||||
new ir.InvokeContinuation(loopContinuation,
|
||||
irBuilder.environment.index2value)));
|
||||
if (hasBreaks) {
|
||||
irBuilder._current = branch;
|
||||
irBuilder.environment = condBuilder.environment;
|
||||
breakCollector.addJump(irBuilder);
|
||||
letJoin.continuation =
|
||||
irBuilder.createJoin(irBuilder.environment.length, breakCollector);
|
||||
irBuilder._current = letJoin;
|
||||
} else {
|
||||
irBuilder._current = condBuilder._current;
|
||||
irBuilder.environment = condBuilder.environment;
|
||||
}
|
||||
return null;
|
||||
irBuilder.buildFor(
|
||||
buildInitializer: subbuild(node.initializer),
|
||||
buildCondition: subbuild(node.condition),
|
||||
buildBody: subbuild(node.body),
|
||||
buildUpdate: subbuildSequence(node.update),
|
||||
target: target);
|
||||
}
|
||||
|
||||
visitIf(ast.If node) {
|
||||
visitIf(ast.If node) {
|
||||
irBuilder.buildIf(
|
||||
build(node.condition),
|
||||
subbuild(node.thenPart),
|
||||
@@ -439,7 +304,8 @@ class IrBuilderVisitor extends ResolvedVisitor<ir.Primitive>
|
||||
ir.Continuation loopContinuation =
|
||||
new ir.Continuation(condBuilder._parameters);
|
||||
if (bodyBuilder.isOpen) continueCollector.addJump(bodyBuilder);
|
||||
invokeFullJoin(loopContinuation, continueCollector, recursive: true);
|
||||
irBuilder.invokeFullJoin(
|
||||
loopContinuation, continueCollector, recursive: true);
|
||||
bodyContinuation.body = bodyBuilder._root;
|
||||
|
||||
loopContinuation.body = condBuilder._root;
|
||||
@@ -559,7 +425,8 @@ class IrBuilderVisitor extends ResolvedVisitor<ir.Primitive>
|
||||
ir.Continuation loopContinuation =
|
||||
new ir.Continuation(condBuilder._parameters);
|
||||
if (bodyBuilder.isOpen) continueCollector.addJump(bodyBuilder);
|
||||
invokeFullJoin(loopContinuation, continueCollector, recursive: true);
|
||||
irBuilder.invokeFullJoin(
|
||||
loopContinuation, continueCollector, recursive: true);
|
||||
bodyContinuation.body = bodyBuilder._root;
|
||||
|
||||
loopContinuation.body = condBuilder._root;
|
||||
|
||||
@@ -667,8 +667,10 @@ class For extends Loop {
|
||||
For asFor() => this;
|
||||
|
||||
Expression get condition {
|
||||
if (conditionStatement is ExpressionStatement) {
|
||||
return conditionStatement.asExpressionStatement().expression;
|
||||
ExpressionStatement expressionStatement =
|
||||
conditionStatement.asExpressionStatement();
|
||||
if (expressionStatement != null) {
|
||||
return expressionStatement.expression;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user