Skip to content

Commit 057426e

Browse files
natebiggsCommit Queue
authored andcommitted
[dart2js] Include block statements as potentially captured scopes.
Some scopes that are capturable by closures are not getting marked as such. Instead the parent scope (e.g. the enclosing member) is being treated as the captured scope that owns all the variable declarations down to the closure. The scopes are then being shared across different closures and variables with the same name are able to be overwritten. This bug occurs for any block that isn't a loop or function (i.e. raw blocks, if blocks, try blocks). Loops are correctly handled as defining a scope and therefore those already work correctly. Note: This may cause some small code size increase. There may now be extra assignements reseting capture boxes in more scopes. Bug: #59843 Change-Id: Ic5009188795daabc1544f703fbeca01c0f1951d9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/403263 Commit-Queue: Nate Biggs <[email protected]> Reviewed-by: Stephen Adams <[email protected]>
1 parent 2258ea7 commit 057426e

File tree

7 files changed

+97
-5
lines changed

7 files changed

+97
-5
lines changed

pkg/compiler/lib/src/closure.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ abstract class ClosureData {
3838
/// the SSA builder.
3939
CapturedScope getCapturedScope(MemberEntity entity);
4040

41+
/// Look up scope information about a block. Used by the SSA builder to box
42+
/// variables if needed.
43+
CapturedScope getCapturedBlockScope(ir.Block node);
44+
4145
/// If [entity] is a closure call method or closure signature method, the
4246
/// original enclosing member is returned. Otherwise [entity] is returned.
4347
///

pkg/compiler/lib/src/io/kernel_source_information.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,11 @@ class KernelSourceInformationBuilder implements SourceInformationBuilder {
487487
return _buildTreeNode(node);
488488
}
489489

490+
@override
491+
SourceInformation buildBlock(ir.TreeNode node) {
492+
return _buildTreeNode(node);
493+
}
494+
490495
@override
491496
SourceInformation buildCall(
492497
covariant ir.TreeNode receiver,

pkg/compiler/lib/src/io/source_information.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,9 @@ class SourceInformationBuilder {
181181
/// Generate [SourceInformation] for the if statement in [node].
182182
SourceInformation? buildIf(ir.TreeNode node) => null;
183183

184+
/// Generate [SourceInformation] for the block statement in [node].
185+
SourceInformation? buildBlock(ir.TreeNode node) => null;
186+
184187
/// Generate [SourceInformation] for the constructor invocation in [node].
185188
SourceInformation? buildNew(ir.TreeNode node) => null;
186189

pkg/compiler/lib/src/ir/scope_visitor.dart

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -205,10 +205,7 @@ class ScopeModelBuilder extends ir.VisitorDefault<EvaluationComplexity>
205205

206206
KernelCapturedScope capturedScope;
207207
var nodeBox = NodeBox(getBoxName(), _executableContext!);
208-
if (node is ir.ForStatement ||
209-
node is ir.ForInStatement ||
210-
node is ir.WhileStatement ||
211-
node is ir.DoStatement) {
208+
if (node is ir.LoopStatement) {
212209
capturedScope = KernelCapturedLoopScope(
213210
capturedVariablesForScope,
214211
nodeBox,
@@ -1373,7 +1370,17 @@ class ScopeModelBuilder extends ir.VisitorDefault<EvaluationComplexity>
13731370

13741371
@override
13751372
EvaluationComplexity visitBlock(ir.Block node) {
1376-
visitNodes(node.statements);
1373+
final parent = node.parent;
1374+
if (parent is ir.FunctionNode || parent is ir.LoopStatement) {
1375+
// Scoping for these blocks are handled by their parent nodes.
1376+
visitNodes(node.statements);
1377+
} else {
1378+
enterNewScope(node, () {
1379+
visitInVariableScope(node, () {
1380+
visitNodes(node.statements);
1381+
});
1382+
});
1383+
}
13771384
return const EvaluationComplexity.lazy();
13781385
}
13791386

pkg/compiler/lib/src/js_model/closure.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,10 @@ class ClosureDataImpl implements ClosureData {
200200
}
201201
}
202202

203+
@override
204+
CapturedScope getCapturedBlockScope(ir.Block blockNode) =>
205+
_capturedScopesMap.loaded()[blockNode] ?? const CapturedScope();
206+
203207
@override
204208
// TODO(efortuna): Eventually capturedScopesMap[node] should always
205209
// be non-null, and we should just test that with an assert.

pkg/compiler/lib/src/ssa/builder.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2481,6 +2481,11 @@ class KernelSsaGraphBuilder extends ir.VisitorDefault<void>
24812481
// a throwing prior subexpression, e.g. `[throw e, {...[]}]`.
24822482
if (!_isReachable) return;
24832483

2484+
localsHandler.enterScope(
2485+
_closureDataLookup.getCapturedBlockScope(node),
2486+
_sourceInformationBuilder.buildBlock(node),
2487+
);
2488+
24842489
for (ir.Statement statement in node.statements) {
24852490
statement.accept(this);
24862491
if (!_isReachable) {
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import 'package:expect/expect.dart';
2+
3+
void testBlock() {
4+
List<void Function()> functions = [];
5+
{
6+
var sub;
7+
functions.add(() {
8+
Expect.isNull(sub);
9+
sub = 1;
10+
});
11+
}
12+
{
13+
var sub;
14+
functions.add(() {
15+
Expect.equals(sub, 2);
16+
});
17+
sub = 2;
18+
}
19+
for (var function in functions) function();
20+
}
21+
22+
void testIf() {
23+
List<void Function()> functions = [];
24+
if ('1234'.length > 2) {
25+
var sub;
26+
functions.add(() {
27+
Expect.isNull(sub);
28+
sub = 1;
29+
});
30+
}
31+
if ('1234'.length > 2) {
32+
var sub;
33+
functions.add(() {
34+
Expect.equals(sub, 2);
35+
});
36+
sub = 2;
37+
}
38+
for (var function in functions) function();
39+
}
40+
41+
void testTry() {
42+
List<void Function()> functions = [];
43+
try {
44+
var sub;
45+
functions.add(() {
46+
Expect.isNull(sub);
47+
sub = 1;
48+
});
49+
} catch (e) {}
50+
try {
51+
var sub;
52+
functions.add(() {
53+
Expect.equals(sub, 2);
54+
});
55+
sub = 2;
56+
} catch (e) {}
57+
for (var function in functions) function();
58+
}
59+
60+
void main() {
61+
testBlock();
62+
testIf();
63+
testTry();
64+
}

0 commit comments

Comments
 (0)