Skip to content

Commit d746c5f

Browse files
osa1Commit Queue
authored andcommitted
[dart2wasm] Some refactoring around context and capture generation
This mainly refactors code that use the `Closures` type to make it a bit more clear what it does and how it does it, and make it more difficult to misuse. Changes: - Make `CaptureFinder`, `ContextCollector`, `Closures.findCapures`, `Closures.collectContexts`, `Closures.buildContexts` private. It doesn't make sense to use these types outside, and the `Closures` members need to be called together and in the right order. Make them private and call them in the constructor. Reduces API surface of `closures.dart` and makes it easier to use. - Document all public members of `Closures`. - Remove unused `ClosureRepresentation.exportSuffix`. - In `TearOffCodeGenerator`, inline the single-use function `generateTearOffGetter`. Makes it clear that the code is not reused elsewhere. - Make the type of `Types.nonNullableTypeType` more precise. Use it in `closures.dart` instead of having a separate copy of the same thing. - In a few places where we had function body entirely guarded with an `if`, add an early return. For example: ``` void f() { if (x) { ... lots of code ... } } ``` Becomes: ``` void f() { if (!x) return; ... lots of code ... } ``` Similarly do it in loop bodies. Change-Id: Ia2a74b89ae311b8f32b9f1a4e72c51d4ea3861e8 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/392942 Reviewed-by: Martin Kustermann <[email protected]> Commit-Queue: Ömer Ağacan <[email protected]>
1 parent 2bcb736 commit d746c5f

File tree

5 files changed

+159
-126
lines changed

5 files changed

+159
-126
lines changed

pkg/dart2wasm/lib/closures.dart

Lines changed: 117 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,6 @@ class ClosureRepresentation {
6161
/// The struct type for the context of an instantiated closure.
6262
final w.StructType? instantiationContextStruct;
6363

64-
final String exportSuffix;
65-
6664
/// Entry point functions for instantiations of this generic closure.
6765
final Map<w.ModuleBuilder, List<w.BaseFunction>> _instantiationTrampolines =
6866
{};
@@ -121,8 +119,7 @@ class ClosureRepresentation {
121119
this.vtableStruct,
122120
this.closureStruct,
123121
this._indexOfCombination,
124-
this.instantiationContextStruct,
125-
this.exportSuffix);
122+
this.instantiationContextStruct);
126123

127124
bool get isGeneric => typeCount > 0;
128125

@@ -265,8 +262,8 @@ class ClosureLayouter extends RecursiveVisitor {
265262
late final w.StructType closureBaseStruct = _makeClosureStruct(
266263
"#ClosureBase", _vtableBaseStructBare, translator.closureInfo.struct);
267264

268-
late final w.RefType typeType =
269-
translator.classInfo[translator.typeClass]!.nonNullableType;
265+
w.RefType get typeType => translator.types.nonNullableTypeType;
266+
270267
late final w.RefType functionTypeType =
271268
translator.classInfo[translator.functionTypeClass]!.nonNullableType;
272269

@@ -469,8 +466,7 @@ class ClosureLayouter extends RecursiveVisitor {
469466
vtableStruct,
470467
closureStruct,
471468
indexOfCombination,
472-
instantiationContextStruct,
473-
nameTags.join('-'));
469+
instantiationContextStruct);
474470

475471
if (typeCount > 0) {
476472
// The instantiation trampolines and the instantiation function can't be
@@ -1073,10 +1069,15 @@ class Context {
10731069
Context(this.owner, this.parent, this.containsThis);
10741070
}
10751071

1076-
/// A captured variable.
1072+
/// A captured variable or type parameter.
10771073
class Capture {
1074+
/// The captured [VariableDeclaration] or [TypeParameter].
10781075
final TreeNode variable;
1076+
10791077
late final Context context;
1078+
1079+
/// The index of the captured variable or type parameter in its context
1080+
/// struct.
10801081
late final int fieldIndex;
10811082

10821083
/// Whether the captured variable is updated after initialization.
@@ -1086,39 +1087,76 @@ class Capture {
10861087
/// context.
10871088
bool written = false;
10881089

1089-
Capture(this.variable);
1090+
Capture(this.variable) {
1091+
assert(variable is VariableDeclaration || variable is TypeParameter);
1092+
}
10901093

10911094
w.ValueType get type => context.struct.fields[fieldIndex].type.unpacked;
10921095
}
10931096

1094-
/// Compiler passes to find all captured variables and construct the context
1095-
/// tree for a member.
1097+
/// Information about contexts and closures of a member.
10961098
class Closures {
10971099
final Translator translator;
1098-
final Class? enclosingClass;
1099-
final Map<TreeNode, Capture> captures = {};
1100-
bool isThisCaptured = false;
1100+
1101+
/// Maps [FunctionDeclaration]s and [FunctionExpression]s in the member to
1102+
/// [Lambda]s.
11011103
final Map<FunctionNode, Lambda> lambdas = {};
1102-
late final w.RefType? nullableThisType;
11031104

1104-
// This [TreeNode] is the context owner, and can be a [FunctionNode],
1105-
// [Constructor], [ForStatement], [DoStatement] or a [WhileStatement].
1105+
/// Maps [VariableDeclaration]s and [TypeParameter]s in the member to
1106+
/// [Capture]s.
1107+
final Map<TreeNode, Capture> captures = {};
1108+
1109+
/// Maps AST nodes with contexts to their contexts.
1110+
///
1111+
/// AST nodes that can have a context are:
1112+
///
1113+
/// - [FunctionNode]
1114+
/// - [Constructor]
1115+
/// - [ForStatement]
1116+
/// - [DoStatement]
1117+
/// - [WhileStatement]
11061118
final Map<TreeNode, Context> contexts = {};
1119+
1120+
/// Set of function declarations in the member that need to be compiled as
1121+
/// closures. These functions are used as variables. Example:
1122+
/// ```
1123+
/// void f() {
1124+
/// void g () {}
1125+
/// print(g);
1126+
/// }
1127+
/// ```
1128+
/// In the `Closures` for `f`, `g` will be in this set.
11071129
final Set<FunctionDeclaration> closurizedFunctions = {};
11081130

1109-
Closures(this.translator, Member member)
1110-
: enclosingClass = member.enclosingClass {
1111-
final hasThis = member is Constructor || member.isInstanceMember;
1112-
nullableThisType = hasThis
1113-
? translator.preciseThisFor(member, nullable: true) as w.RefType
1114-
: null;
1131+
final Member _member;
1132+
1133+
/// Whether the member captures `this`. Set by [_CaptureFinder].
1134+
bool _isThisCaptured = false;
1135+
1136+
/// When the member is a constructor or an instance member, nullable type of
1137+
/// `this`.
1138+
final w.RefType? _nullableThisType;
1139+
1140+
/// When `findCaptures` is `false`, this does not analyze the member body and
1141+
/// does not populate [lambdas], [contexts], [captures], and
1142+
/// [closurizedFunctions]. This mode is useful in the code generators that
1143+
/// always have direct access to variables (instead of via a context).
1144+
Closures(this.translator, this._member, {bool findCaptures = true})
1145+
: _nullableThisType = _member is Constructor || _member.isInstanceMember
1146+
? translator.preciseThisFor(_member, nullable: true) as w.RefType
1147+
: null {
1148+
if (findCaptures) {
1149+
_findCaptures();
1150+
_collectContexts();
1151+
_buildContexts();
1152+
}
11151153
}
11161154

1117-
late final w.ValueType typeType =
1118-
translator.classInfo[translator.typeClass]!.nonNullableType;
1155+
w.RefType get typeType => translator.types.nonNullableTypeType;
11191156

1120-
void findCaptures(Member member) {
1121-
var find = CaptureFinder(this, member);
1157+
void _findCaptures() {
1158+
final member = _member;
1159+
final find = _CaptureFinder(this, member);
11221160
if (member is Constructor) {
11231161
Class cls = member.enclosingClass;
11241162
for (Field field in cls.fields) {
@@ -1130,62 +1168,63 @@ class Closures {
11301168
member.accept(find);
11311169
}
11321170

1133-
void collectContexts(TreeNode node) {
1134-
if (captures.isNotEmpty || isThisCaptured) {
1135-
node.accept(ContextCollector(this, translator.options.enableAsserts));
1171+
void _collectContexts() {
1172+
if (captures.isNotEmpty || _isThisCaptured) {
1173+
_member.accept(_ContextCollector(this, translator.options.enableAsserts));
11361174
}
11371175
}
11381176

1139-
void buildContexts() {
1177+
void _buildContexts() {
11401178
// Make struct definitions
11411179
for (Context context in contexts.values) {
1142-
if (!context.isEmpty) {
1143-
if (context.owner is Constructor) {
1144-
Constructor constructor = context.owner as Constructor;
1145-
context.struct = translator.typesBuilder
1146-
.defineStruct("<$constructor-constructor-context>");
1147-
} else if (context.owner.parent is Constructor) {
1148-
Constructor constructor = context.owner.parent as Constructor;
1149-
context.struct = translator.typesBuilder
1150-
.defineStruct("<$constructor-constructor-body-context>");
1151-
} else {
1152-
context.struct = translator.typesBuilder
1153-
.defineStruct("<context ${context.owner.location}>");
1154-
}
1180+
if (context.isEmpty) continue;
1181+
1182+
final owner = context.owner;
1183+
if (owner is Constructor) {
1184+
context.struct = translator.typesBuilder
1185+
.defineStruct("<$owner-constructor-context>");
1186+
} else if (owner.parent is Constructor) {
1187+
Constructor constructor = owner.parent as Constructor;
1188+
context.struct = translator.typesBuilder
1189+
.defineStruct("<$constructor-constructor-body-context>");
1190+
} else {
1191+
context.struct =
1192+
translator.typesBuilder.defineStruct("<context ${owner.location}>");
11551193
}
11561194
}
11571195

11581196
// Build object layouts
11591197
for (Context context in contexts.values) {
1160-
if (!context.isEmpty) {
1161-
w.StructType struct = context.struct;
1162-
if (context.parent != null) {
1163-
assert(!context.parent!.isEmpty);
1164-
struct.fields.add(w.FieldType(
1165-
w.RefType.def(context.parent!.struct, nullable: true)));
1166-
}
1167-
if (context.containsThis) {
1168-
assert(enclosingClass != null);
1169-
struct.fields.add(w.FieldType(nullableThisType!));
1170-
}
1171-
for (VariableDeclaration variable in context.variables) {
1172-
int index = struct.fields.length;
1173-
struct.fields.add(w.FieldType(translator
1174-
.translateTypeOfLocalVariable(variable)
1175-
.withNullability(true)));
1176-
captures[variable]!.fieldIndex = index;
1177-
}
1178-
for (TypeParameter parameter in context.typeParameters) {
1179-
int index = struct.fields.length;
1180-
struct.fields.add(w.FieldType(typeType.withNullability(true)));
1181-
captures[parameter]!.fieldIndex = index;
1182-
}
1198+
if (context.isEmpty) continue;
1199+
1200+
w.StructType struct = context.struct;
1201+
final parent = context.parent;
1202+
if (parent != null) {
1203+
assert(!parent.isEmpty);
1204+
struct.fields
1205+
.add(w.FieldType(w.RefType.def(parent.struct, nullable: true)));
1206+
}
1207+
if (context.containsThis) {
1208+
assert(_member.enclosingClass != null);
1209+
struct.fields.add(w.FieldType(_nullableThisType!));
1210+
}
1211+
for (VariableDeclaration variable in context.variables) {
1212+
int index = struct.fields.length;
1213+
struct.fields.add(w.FieldType(translator
1214+
.translateTypeOfLocalVariable(variable)
1215+
.withNullability(true)));
1216+
captures[variable]!.fieldIndex = index;
1217+
}
1218+
for (TypeParameter parameter in context.typeParameters) {
1219+
int index = struct.fields.length;
1220+
struct.fields.add(w.FieldType(typeType.withNullability(true)));
1221+
captures[parameter]!.fieldIndex = index;
11831222
}
11841223
}
11851224
}
11861225
}
11871226

1188-
class CaptureFinder extends RecursiveVisitor {
1227+
class _CaptureFinder extends RecursiveVisitor {
11891228
final Closures closures;
11901229
final Member member;
11911230

@@ -1196,7 +1235,7 @@ class CaptureFinder extends RecursiveVisitor {
11961235

11971236
int get depth => functionIsSyncStarOrAsync.length - 1;
11981237

1199-
CaptureFinder(this.closures, this.member)
1238+
_CaptureFinder(this.closures, this.member)
12001239
: _currentSource =
12011240
member.enclosingComponent!.uriToSource[member.fileUri]!;
12021241

@@ -1259,6 +1298,8 @@ class CaptureFinder extends RecursiveVisitor {
12591298
if (functionIsSyncStarOrAsync[declDepth]) capture.written = true;
12601299
} else if (variable is VariableDeclaration &&
12611300
variable.parent is FunctionDeclaration) {
1301+
// Variable is for a function declaration, the function needs to be
1302+
// compiled as a closure.
12621303
closures.closurizedFunctions.add(variable.parent as FunctionDeclaration);
12631304
}
12641305
}
@@ -1277,7 +1318,7 @@ class CaptureFinder extends RecursiveVisitor {
12771318

12781319
void _visitThis() {
12791320
if (depth > 0 || functionIsSyncStarOrAsync[0]) {
1280-
closures.isThisCaptured = true;
1321+
closures._isThisCaptured = true;
12811322
}
12821323
}
12831324

@@ -1365,12 +1406,12 @@ class CaptureFinder extends RecursiveVisitor {
13651406
}
13661407
}
13671408

1368-
class ContextCollector extends RecursiveVisitor {
1409+
class _ContextCollector extends RecursiveVisitor {
13691410
final Closures closures;
13701411
Context? currentContext;
13711412
final bool enableAsserts;
13721413

1373-
ContextCollector(this.closures, this.enableAsserts);
1414+
_ContextCollector(this.closures, this.enableAsserts);
13741415

13751416
@override
13761417
void visitAssertStatement(AssertStatement node) {
@@ -1393,7 +1434,7 @@ class ContextCollector extends RecursiveVisitor {
13931434
while (parent != null && parent.isEmpty) {
13941435
parent = parent.parent;
13951436
}
1396-
bool containsThis = closures.isThisCaptured && outerMost;
1437+
bool containsThis = closures._isThisCaptured && outerMost;
13971438
currentContext = Context(node, parent, containsThis);
13981439
closures.contexts[node] = currentContext!;
13991440
node.visitChildren(this);
@@ -1438,7 +1479,7 @@ class ContextCollector extends RecursiveVisitor {
14381479
// Some type arguments or variables have been captured by the
14391480
// initializer list.
14401481

1441-
if (closures.isThisCaptured) {
1482+
if (closures._isThisCaptured) {
14421483
// In this case, we need two contexts: a constructor context to store
14431484
// the captured arguments/type parameters (shared by the initializer
14441485
// and constructor body, and a separate context just for the
@@ -1467,7 +1508,7 @@ class ContextCollector extends RecursiveVisitor {
14671508
// (node.function) for debugging purposes, and drop the
14681509
// constructor allocator context as it is not used.
14691510
final Context constructorBodyContext =
1470-
Context(node.function, null, closures.isThisCaptured);
1511+
Context(node.function, null, closures._isThisCaptured);
14711512
currentContext = constructorBodyContext;
14721513

14731514
node.function.body?.accept(this);

0 commit comments

Comments
 (0)