Index: pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart |
diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart |
index fd3695ee1cc3882ffec3af0a59fb7214493eced3..5e224e25438c518d8e2f62451d016941f83e659f 100644 |
--- a/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart |
+++ b/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart |
@@ -2277,11 +2277,16 @@ abstract class IrBuilderVisitor extends ast.Visitor<ir.Primitive> |
final String ABORT_IRNODE_BUILDER = "IrNode builder aborted"; |
-/// Determines which local variables should be boxed in a mutable variable |
-/// inside a given try block. |
-class TryBoxedVariables extends ast.Visitor { |
+/// Classifies local variables and local functions as captured, if they |
+/// are accessed from within a nested function. |
+/// |
+/// This class is specific to the [DartIrBuilder], in that it gives up if it |
+/// sees a feature that is currently unsupport by that builder. In particular, |
+/// loop variables captured in a for-loop initializer, condition, or update |
+/// expression are unsupported. |
+class DartCapturedVariables extends ast.Visitor { |
final TreeElements elements; |
- TryBoxedVariables(this.elements); |
+ DartCapturedVariables(this.elements); |
FunctionElement currentFunction; |
bool insideInitializer = false; |
@@ -2327,6 +2332,25 @@ class TryBoxedVariables extends ast.Visitor { |
node.visitChildren(this); |
} |
+ visitFor(ast.For node) { |
+ if (node.initializer != null) visit(node.initializer); |
+ if (node.condition != null) visit(node.condition); |
+ if (node.update != null) visit(node.update); |
+ |
+ // Give up if a variable was captured outside of the loop body. |
+ if (node.initializer is ast.VariableDefinitions) { |
+ ast.VariableDefinitions definitions = node.initializer; |
+ for (ast.Node node in definitions.definitions.nodes) { |
+ LocalElement loopVariable = elements[node]; |
+ if (capturedVariables.contains(loopVariable)) { |
+ return giveup(node, 'For-loop variable captured in loop header'); |
+ } |
+ } |
+ } |
+ |
+ if (node.body != null) visit(node.body); |
+ } |
+ |
void handleSend(ast.Send node) { |
Element element = elements[node]; |
if (Elements.isLocal(element) && |
@@ -2384,14 +2408,18 @@ class TryBoxedVariables extends ast.Visitor { |
if (currentFunction.asyncMarker != AsyncMarker.SYNC && |
currentFunction.asyncMarker != AsyncMarker.SYNC_STAR && |
currentFunction.asyncMarker != AsyncMarker.ASYNC) { |
- giveup(node, "cannot handle async* functions"); |
+ giveup(node, "cannot handle sync*/async* functions"); |
} |
+ bool savedInsideInitializer = insideInitializer; |
if (node.initializers != null) { |
+ insideInitializer = true; |
visit(node.initializers); |
} |
+ insideInitializer = false; |
visit(node.body); |
currentFunction = savedFunction; |
+ insideInitializer = savedInsideInitializer; |
} |
visitTryStatement(ast.TryStatement node) { |
@@ -3099,8 +3127,8 @@ class JsIrBuilderVisitor extends IrBuilderVisitor { |
return parameters; |
} |
- TryBoxedVariables _analyzeTryBoxedVariables(ast.Node node) { |
- TryBoxedVariables variables = new TryBoxedVariables(elements); |
+ DartCapturedVariables _analyzeCapturedVariables(ast.Node node) { |
+ DartCapturedVariables variables = new DartCapturedVariables(elements); |
try { |
variables.analyze(node); |
} catch (e) { |
@@ -3130,7 +3158,7 @@ class JsIrBuilderVisitor extends IrBuilderVisitor { |
// error-prone. |
// TODO(kmillikin): We should combine closure conversion and try/catch |
// variable analysis in some way. |
- TryBoxedVariables variables = _analyzeTryBoxedVariables(node); |
+ DartCapturedVariables variables = _analyzeCapturedVariables(node); |
tryStatements = variables.tryStatements; |
IrBuilder builder = getBuilderFor(body); |
@@ -3155,7 +3183,7 @@ class JsIrBuilderVisitor extends IrBuilderVisitor { |
element, |
node, |
elements); |
- TryBoxedVariables variables = _analyzeTryBoxedVariables(node); |
+ DartCapturedVariables variables = _analyzeCapturedVariables(node); |
tryStatements = variables.tryStatements; |
IrBuilder builder = getBuilderFor(element); |
return withBuilder(builder, () => _makeFunctionBody(element, node)); |