Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(85)

Side by Side Diff: pkg/compiler/lib/src/js_model/closure.dart

Issue 2992763002: Add boxed fields in closure classes. (Closed)
Patch Set: . Created 3 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 import 'package:kernel/ast.dart' as ir; 5 import 'package:kernel/ast.dart' as ir;
6 6
7 import '../closure.dart'; 7 import '../closure.dart';
8 import '../common.dart'; 8 import '../common.dart';
9 import '../common/tasks.dart'; 9 import '../common/tasks.dart';
10 import '../elements/entities.dart'; 10 import '../elements/entities.dart';
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
147 147
148 // TODO(efortuna): Eventually capturedScopesMap[node] should always 148 // TODO(efortuna): Eventually capturedScopesMap[node] should always
149 // be non-null, and we should just test that with an assert. 149 // be non-null, and we should just test that with an assert.
150 @override 150 @override
151 CapturedScope getCapturedScope(MemberEntity entity) { 151 CapturedScope getCapturedScope(MemberEntity entity) {
152 MemberDefinition definition = _elementMap.getMemberDefinition(entity); 152 MemberDefinition definition = _elementMap.getMemberDefinition(entity);
153 switch (definition.kind) { 153 switch (definition.kind) {
154 case MemberKind.regular: 154 case MemberKind.regular:
155 case MemberKind.constructor: 155 case MemberKind.constructor:
156 case MemberKind.constructorBody: 156 case MemberKind.constructorBody:
157 case MemberKind.closureCall:
157 return _capturedScopesMap[definition.node] ?? const CapturedScope(); 158 return _capturedScopesMap[definition.node] ?? const CapturedScope();
158 default: 159 default:
159 throw failedAt(entity, "Unexpected member definition $definition"); 160 throw failedAt(entity, "Unexpected member definition $definition");
160 } 161 }
161 } 162 }
162 163
163 @override 164 @override
164 // TODO(efortuna): Eventually capturedScopesMap[node] should always 165 // TODO(efortuna): Eventually capturedScopesMap[node] should always
165 // be non-null, and we should just test that with an assert. 166 // be non-null, and we should just test that with an assert.
166 CapturedLoopScope getCapturedLoopScope(ir.Node loopNode) => 167 CapturedLoopScope getCapturedLoopScope(ir.Node loopNode) =>
167 _capturedScopesMap[loopNode] ?? const CapturedLoopScope(); 168 _capturedScopesMap[loopNode] ?? const CapturedLoopScope();
168 169
169 @override 170 @override
170 // TODO(efortuna): Eventually closureRepresentationMap[node] should always be 171 // TODO(efortuna): Eventually closureRepresentationMap[node] should always be
171 // non-null, and we should just test that with an assert. 172 // non-null, and we should just test that with an assert.
172 ClosureRepresentationInfo getClosureRepresentationInfo(Entity entity) { 173 ClosureRepresentationInfo getClosureRepresentationInfo(Entity entity) {
173 return _closureRepresentationMap[entity] ?? 174 return _closureRepresentationMap[entity] ??
174 const ClosureRepresentationInfo(); 175 const ClosureRepresentationInfo();
175 } 176 }
176 } 177 }
177 178
178 class KernelScopeInfo { 179 class KernelScopeInfo {
179 final Set<ir.VariableDeclaration> localsUsedInTryOrSync; 180 final Set<ir.VariableDeclaration> localsUsedInTryOrSync;
180 final bool hasThisLocal; 181 final bool hasThisLocal;
181 final Set<ir.VariableDeclaration> boxedVariables; 182 final Set<ir.VariableDeclaration> boxedVariables;
183 // If boxedVariables is empty, this will be null, because no variables will
184 // need to be boxed.
185 final NodeBox capturedVariablesAccessor;
182 186
183 /// The set of variables that were defined in another scope, but are used in 187 /// The set of variables that were defined in another scope, but are used in
184 /// this scope. 188 /// this scope.
185 Set<ir.VariableDeclaration> freeVariables = new Set<ir.VariableDeclaration>(); 189 Set<ir.VariableDeclaration> freeVariables = new Set<ir.VariableDeclaration>();
186 190
187 KernelScopeInfo(this.hasThisLocal) 191 KernelScopeInfo(this.hasThisLocal)
188 : localsUsedInTryOrSync = new Set<ir.VariableDeclaration>(), 192 : localsUsedInTryOrSync = new Set<ir.VariableDeclaration>(),
189 boxedVariables = new Set<ir.VariableDeclaration>(); 193 boxedVariables = new Set<ir.VariableDeclaration>(),
194 capturedVariablesAccessor = null;
190 195
191 KernelScopeInfo.from(this.hasThisLocal, KernelScopeInfo info) 196 KernelScopeInfo.from(this.hasThisLocal, KernelScopeInfo info)
192 : localsUsedInTryOrSync = info.localsUsedInTryOrSync, 197 : localsUsedInTryOrSync = info.localsUsedInTryOrSync,
193 boxedVariables = info.boxedVariables; 198 boxedVariables = info.boxedVariables,
199 capturedVariablesAccessor = null;
194 200
195 KernelScopeInfo.withBoxedVariables(this.boxedVariables, 201 KernelScopeInfo.withBoxedVariables(
196 this.localsUsedInTryOrSync, this.freeVariables, this.hasThisLocal); 202 this.boxedVariables,
203 this.capturedVariablesAccessor,
204 this.localsUsedInTryOrSync,
205 this.freeVariables,
206 this.hasThisLocal);
197 207
198 String toString() { 208 String toString() {
199 StringBuffer sb = new StringBuffer(); 209 StringBuffer sb = new StringBuffer();
200 sb.write('this=$hasThisLocal,'); 210 sb.write('this=$hasThisLocal,');
201 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}'); 211 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}');
202 return sb.toString(); 212 return sb.toString();
203 } 213 }
204 } 214 }
205 215
206 class JsScopeInfo extends ScopeInfo { 216 class JsScopeInfo extends ScopeInfo {
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
243 } 253 }
244 254
245 bool isBoxed(Local variable) => boxedVariables.contains(variable); 255 bool isBoxed(Local variable) => boxedVariables.contains(variable);
246 } 256 }
247 257
248 class KernelCapturedScope extends KernelScopeInfo { 258 class KernelCapturedScope extends KernelScopeInfo {
249 final ir.TreeNode context; 259 final ir.TreeNode context;
250 260
251 KernelCapturedScope( 261 KernelCapturedScope(
252 Set<ir.VariableDeclaration> boxedVariables, 262 Set<ir.VariableDeclaration> boxedVariables,
263 NodeBox capturedVariablesAccessor,
253 this.context, 264 this.context,
254 Set<ir.VariableDeclaration> localsUsedInTryOrSync, 265 Set<ir.VariableDeclaration> localsUsedInTryOrSync,
255 Set<ir.VariableDeclaration> freeVariables, 266 Set<ir.VariableDeclaration> freeVariables,
256 bool hasThisLocal) 267 bool hasThisLocal)
257 : super.withBoxedVariables( 268 : super.withBoxedVariables(boxedVariables, capturedVariablesAccessor,
258 boxedVariables, localsUsedInTryOrSync, freeVariables, hasThisLocal); 269 localsUsedInTryOrSync, freeVariables, hasThisLocal);
259 270
260 bool get requiresContextBox => boxedVariables.isNotEmpty; 271 bool get requiresContextBox => boxedVariables.isNotEmpty;
261 } 272 }
262 273
263 class JsCapturedScope extends JsScopeInfo implements CapturedScope { 274 class JsCapturedScope extends JsScopeInfo implements CapturedScope {
264 final Local context; 275 final Local context;
265 276
266 JsCapturedScope.from( 277 JsCapturedScope.from(
267 KernelCapturedScope capturedScope, KernelToLocalsMap localsMap) 278 KernelCapturedScope capturedScope, KernelToLocalsMap localsMap)
268 : this.context = localsMap.getLocalVariable(capturedScope.context), 279 : this.context = localsMap.getLocalVariable(capturedScope.context),
269 super.from(capturedScope, localsMap); 280 super.from(capturedScope, localsMap);
270 281
271 bool get requiresContextBox => boxedVariables.isNotEmpty; 282 bool get requiresContextBox => boxedVariables.isNotEmpty;
272 } 283 }
273 284
274 class KernelCapturedLoopScope extends KernelCapturedScope { 285 class KernelCapturedLoopScope extends KernelCapturedScope {
275 final List<ir.VariableDeclaration> boxedLoopVariables; 286 final List<ir.VariableDeclaration> boxedLoopVariables;
276 287
277 KernelCapturedLoopScope( 288 KernelCapturedLoopScope(
278 Set<ir.VariableDeclaration> boxedVariables, 289 Set<ir.VariableDeclaration> boxedVariables,
290 NodeBox capturedVariablesAccessor,
279 this.boxedLoopVariables, 291 this.boxedLoopVariables,
280 ir.TreeNode context, 292 ir.TreeNode context,
281 Set<ir.VariableDeclaration> localsUsedInTryOrSync, 293 Set<ir.VariableDeclaration> localsUsedInTryOrSync,
282 Set<ir.VariableDeclaration> freeVariables, 294 Set<ir.VariableDeclaration> freeVariables,
283 bool hasThisLocal) 295 bool hasThisLocal)
284 : super(boxedVariables, context, localsUsedInTryOrSync, freeVariables, 296 : super(boxedVariables, capturedVariablesAccessor, context,
285 hasThisLocal); 297 localsUsedInTryOrSync, freeVariables, hasThisLocal);
286 298
287 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; 299 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty;
288 } 300 }
289 301
290 class JsCapturedLoopScope extends JsCapturedScope implements CapturedLoopScope { 302 class JsCapturedLoopScope extends JsCapturedScope implements CapturedLoopScope {
291 final List<Local> boxedLoopVariables; 303 final List<Local> boxedLoopVariables;
292 304
293 JsCapturedLoopScope.from( 305 JsCapturedLoopScope.from(
294 KernelCapturedLoopScope capturedScope, KernelToLocalsMap localsMap) 306 KernelCapturedLoopScope capturedScope, KernelToLocalsMap localsMap)
295 : this.boxedLoopVariables = capturedScope.boxedLoopVariables 307 : this.boxedLoopVariables = capturedScope.boxedLoopVariables
296 .map(localsMap.getLocalVariable) 308 .map(localsMap.getLocalVariable)
297 .toList(), 309 .toList(),
298 super.from(capturedScope, localsMap); 310 super.from(capturedScope, localsMap);
299 311
300 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; 312 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty;
301 } 313 }
302 314
303 // TODO(johnniwinther): Add unittest for the computed [ClosureClass]. 315 // TODO(johnniwinther): Add unittest for the computed [ClosureClass].
304 class KernelClosureClass extends JsScopeInfo 316 class KernelClosureClass extends JsScopeInfo
305 implements ClosureRepresentationInfo, JClass { 317 implements ClosureRepresentationInfo, JClass {
306 final ir.Location location; 318 final ir.Location location;
307 319
308 final String name; 320 final String name;
309 final JLibrary library; 321 final JLibrary library;
310 JFunction callMethod; 322 JFunction callMethod;
323 final Local closureEntity;
311 324
312 /// Index into the classData, classList and classEnvironment lists where this 325 /// Index into the classData, classList and classEnvironment lists where this
313 /// entity is stored in [JsToFrontendMapImpl]. 326 /// entity is stored in [JsToFrontendMapImpl].
314 final int classIndex; 327 final int classIndex;
315 328
316 final Map<Local, JField> localToFieldMap = new Map<Local, JField>(); 329 final Map<Local, JField> localToFieldMap = new Map<Local, JField>();
317 330
318 KernelClosureClass.fromScopeInfo(this.name, this.classIndex, this.library, 331 KernelClosureClass.fromScopeInfo(
319 KernelScopeInfo info, this.location, KernelToLocalsMap localsMap) 332 ir.FunctionNode closureSourceNode,
320 : super.from(info, localsMap); 333 this.name,
321 334 this.classIndex,
322 // TODO(efortuna): Implement. 335 this.library,
323 Local get closureEntity => null; 336 KernelScopeInfo info,
337 this.location,
338 KernelToLocalsMap localsMap)
339 : super.from(info, localsMap),
340 closureEntity = localsMap.getLocalFunction(closureSourceNode.parent);
324 341
325 ClassEntity get closureClassEntity => this; 342 ClassEntity get closureClassEntity => this;
326 343
327 List<Local> get createdFieldEntities => localToFieldMap.keys.toList(); 344 List<Local> get createdFieldEntities => localToFieldMap.keys.toList();
328 345
329 // TODO(efortuna): Implement. 346 // TODO(efortuna): Implement.
330 FieldEntity get thisFieldEntity => null; 347 FieldEntity get thisFieldEntity => null;
331 348
332 void forEachCapturedVariable(f(Local from, JField to)) { 349 void forEachCapturedVariable(f(Local from, JField to)) {
333 localToFieldMap.forEach(f); 350 localToFieldMap.forEach(f);
334 } 351 }
335 352
336 // TODO(efortuna): Implement.
337 @override 353 @override
338 void forEachBoxedVariable(f(Local local, JField field)) {} 354 void forEachBoxedVariable(f(Local local, JField field)) {
355 for (Local l in localToFieldMap.keys) {
356 if (localToFieldMap[l] is JBoxedField) f(l, localToFieldMap[l]);
357 }
358 }
339 359
340 // TODO(efortuna): Implement. 360 void forEachFreeVariable(f(Local variable, JField field)) {
341 void forEachFreeVariable(f(Local variable, JField field)) {} 361 for (Local l in localToFieldMap.keys) {
362 var jField = localToFieldMap[l];
363 if (jField is! JBoxedField && jField is! BoxLocal) f(l, jField);
364 }
365 }
342 366
343 // TODO(efortuna): Implement. 367 bool isVariableBoxed(Local variable) =>
344 bool isVariableBoxed(Local variable) => false; 368 localToFieldMap.keys.contains(variable);
345 369
346 bool get isClosure => true; 370 bool get isClosure => true;
347 371
348 bool get isAbstract => false; 372 bool get isAbstract => false;
349 373
350 String toString() => '${jsElementPrefix}class($name)'; 374 String toString() => '${jsElementPrefix}class($name)';
351 } 375 }
352 376
377 /// A local variable to disambiguate between a variable that has been captured
378 /// from one scope to another. This is the ir.Node version that corresponds to
379 /// [BoxLocal].
380 class NodeBox {
381 final String name;
382 final ir.TreeNode executableContext;
383 final ir.Member memberContext;
384 NodeBox(this.name, this.executableContext, this.memberContext);
385 }
386
353 class JClosureField extends JField { 387 class JClosureField extends JField {
354 JClosureField(String name, int memberIndex, 388 JClosureField(String name, int memberIndex,
355 KernelClosureClass containingClass, bool isConst, bool isAssignable) 389 KernelClosureClass containingClass, bool isConst, bool isAssignable)
356 : super(memberIndex, containingClass.library, containingClass, 390 : super(memberIndex, containingClass.library, containingClass,
357 new Name(name, containingClass.library), 391 new Name(name, containingClass.library),
358 isAssignable: isAssignable, isConst: isConst); 392 isAssignable: isAssignable, isConst: isConst);
359 } 393 }
360 394
395 /// A ClosureField that has been "boxed" to prevent name shadowing with the
396 /// original variable and ensure that this variable is updated/read with the
397 /// most recent value.
398 /// This corresponds to BoxFieldElement; we reuse BoxLocal from the original
399 /// algorithm to correspond to the actual name of the variable.
400 class JBoxedField extends JField {
401 final BoxLocal box;
402 JBoxedField(String name, int memberIndex, this.box,
403 KernelClosureClass containingClass, bool isConst, bool isAssignable)
404 : super(memberIndex, containingClass.library, containingClass,
405 new Name(name, containingClass.library),
406 isAssignable: isAssignable, isConst: isConst);
407 }
408
361 class ClosureClassDefinition implements ClassDefinition { 409 class ClosureClassDefinition implements ClassDefinition {
362 final ClassEntity cls; 410 final ClassEntity cls;
363 final ir.Location location; 411 final ir.Location location;
364 412
365 ClosureClassDefinition(this.cls, this.location); 413 ClosureClassDefinition(this.cls, this.location);
366 414
367 ClassKind get kind => ClassKind.closure; 415 ClassKind get kind => ClassKind.closure;
368 416
369 ir.Node get node => 417 ir.Node get node =>
370 throw new UnsupportedError('ClosureClassDefinition.node for $cls'); 418 throw new UnsupportedError('ClosureClassDefinition.node for $cls');
(...skipping 23 matching lines...) Expand all
394 KernelScopeInfo scopeInfo; 442 KernelScopeInfo scopeInfo;
395 443
396 /// Collected [CapturedScope] data for nodes. 444 /// Collected [CapturedScope] data for nodes.
397 Map<ir.Node, KernelCapturedScope> capturedScopesMap = 445 Map<ir.Node, KernelCapturedScope> capturedScopesMap =
398 <ir.Node, KernelCapturedScope>{}; 446 <ir.Node, KernelCapturedScope>{};
399 447
400 /// Collected [ScopeInfo] data for nodes. 448 /// Collected [ScopeInfo] data for nodes.
401 Map<ir.FunctionNode, KernelScopeInfo> closuresToGenerate = 449 Map<ir.FunctionNode, KernelScopeInfo> closuresToGenerate =
402 <ir.FunctionNode, KernelScopeInfo>{}; 450 <ir.FunctionNode, KernelScopeInfo>{};
403 } 451 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698