OLD | NEW |
---|---|
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 '../constants/expressions.dart'; | 10 import '../constants/expressions.dart'; |
(...skipping 248 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
259 var closure = _localClosureRepresentationMap[node]; | 259 var closure = _localClosureRepresentationMap[node]; |
260 assert( | 260 assert( |
261 closure != null, | 261 closure != null, |
262 "Corresponding closure class not found for $node. " | 262 "Corresponding closure class not found for $node. " |
263 "Closures found for ${_localClosureRepresentationMap.keys}"); | 263 "Closures found for ${_localClosureRepresentationMap.keys}"); |
264 return closure; | 264 return closure; |
265 } | 265 } |
266 } | 266 } |
267 | 267 |
268 class KernelScopeInfo { | 268 class KernelScopeInfo { |
269 // TODO(johnniwinther): Remove this. It seems to be unneeded. | |
269 final ir.TreeNode node; | 270 final ir.TreeNode node; |
Emily Fortuna
2017/08/22 23:47:36
why not just delete it then?
Johnni Winther
2017/08/23 07:47:18
It's good for debugging and it seemed that I might
| |
270 final Set<ir.VariableDeclaration> localsUsedInTryOrSync; | 271 final Set<ir.VariableDeclaration> localsUsedInTryOrSync; |
271 final bool hasThisLocal; | 272 final bool hasThisLocal; |
272 | 273 |
273 /// The set of variables that were defined in another scope, but are used in | 274 /// The set of variables that were defined in another scope, but are used in |
274 /// this scope. | 275 /// this scope. |
275 Set<ir.VariableDeclaration> freeVariables = new Set<ir.VariableDeclaration>(); | 276 Set<ir.VariableDeclaration> freeVariables = new Set<ir.VariableDeclaration>(); |
276 | 277 |
277 KernelScopeInfo(this.node, this.hasThisLocal) | 278 KernelScopeInfo(this.node, this.hasThisLocal) |
278 : localsUsedInTryOrSync = new Set<ir.VariableDeclaration>(); | 279 : localsUsedInTryOrSync = new Set<ir.VariableDeclaration>(); |
279 | 280 |
280 KernelScopeInfo.from(this.node, this.hasThisLocal, KernelScopeInfo info) | 281 KernelScopeInfo.from(this.node, this.hasThisLocal, KernelScopeInfo info) |
281 : localsUsedInTryOrSync = info.localsUsedInTryOrSync; | 282 : localsUsedInTryOrSync = info.localsUsedInTryOrSync; |
282 | 283 |
283 KernelScopeInfo.withBoxedVariables(this.node, this.localsUsedInTryOrSync, | 284 KernelScopeInfo.withBoxedVariables(this.node, this.localsUsedInTryOrSync, |
284 this.freeVariables, this.hasThisLocal); | 285 this.freeVariables, this.hasThisLocal); |
285 | 286 |
287 bool _printOn(StringBuffer sb, bool needsComma) { | |
288 if (node != null) { | |
289 if (needsComma) { | |
290 sb.write(','); | |
291 } | |
292 sb.write('node=$node'); | |
293 needsComma = true; | |
294 } | |
295 if (hasThisLocal) { | |
296 if (needsComma) { | |
297 sb.write(','); | |
298 } | |
299 sb.write('hasThisLocal'); | |
300 needsComma = true; | |
301 } | |
302 if (localsUsedInTryOrSync.isNotEmpty) { | |
303 if (needsComma) { | |
304 sb.write(','); | |
305 } | |
306 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}'); | |
307 needsComma = true; | |
308 } | |
309 /*if (freeVariables.isNotEmpty) { | |
Emily Fortuna
2017/08/22 23:47:37
delete or uncomment?
| |
310 if (needsComma) { | |
311 sb.write(','); | |
312 } | |
313 sb.write('freeVariables=${freeVariables}'); | |
314 needsComma = true; | |
315 }*/ | |
316 return needsComma; | |
317 } | |
318 | |
286 String toString() { | 319 String toString() { |
287 StringBuffer sb = new StringBuffer(); | 320 StringBuffer sb = new StringBuffer(); |
288 sb.write('this=$hasThisLocal,'); | 321 sb.write('KernelScopeInfo('); |
289 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}'); | 322 _printOn(sb, false); |
323 sb.write(')'); | |
290 return sb.toString(); | 324 return sb.toString(); |
291 } | 325 } |
292 } | 326 } |
293 | 327 |
294 class JsScopeInfo extends ScopeInfo { | 328 class JsScopeInfo extends ScopeInfo { |
295 final Set<Local> localsUsedInTryOrSync; | 329 final Set<Local> localsUsedInTryOrSync; |
296 final Local thisLocal; | 330 final Local thisLocal; |
297 | 331 |
298 /// The set of variables that were defined in another scope, but are used in | |
299 /// this scope. | |
300 final Set<Local> freeVariables; | |
301 | |
302 JsScopeInfo.from(KernelScopeInfo info, KernelToLocalsMap localsMap) | 332 JsScopeInfo.from(KernelScopeInfo info, KernelToLocalsMap localsMap) |
303 : this.thisLocal = | 333 : this.thisLocal = |
304 info.hasThisLocal ? new ThisLocal(localsMap.currentMember) : null, | 334 info.hasThisLocal ? new ThisLocal(localsMap.currentMember) : null, |
305 this.localsUsedInTryOrSync = | 335 this.localsUsedInTryOrSync = |
306 info.localsUsedInTryOrSync.map(localsMap.getLocalVariable).toSet(), | 336 info.localsUsedInTryOrSync.map(localsMap.getLocalVariable).toSet(); |
307 this.freeVariables = | |
308 info.freeVariables.map(localsMap.getLocalVariable).toSet(); | |
309 | 337 |
310 bool localIsUsedInTryOrSync(Local variable) => | 338 bool localIsUsedInTryOrSync(Local variable) => |
311 localsUsedInTryOrSync.contains(variable); | 339 localsUsedInTryOrSync.contains(variable); |
312 | 340 |
341 bool _printOn(StringBuffer sb, bool needsComma) { | |
342 if (thisLocal != null) { | |
343 if (needsComma) { | |
344 sb.write(','); | |
345 } | |
346 sb.write('this=$thisLocal'); | |
347 needsComma = true; | |
348 } | |
349 if (localsUsedInTryOrSync.isNotEmpty) { | |
350 if (needsComma) { | |
351 sb.write(','); | |
352 } | |
353 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}'); | |
354 needsComma = true; | |
355 } | |
356 return needsComma; | |
357 } | |
358 | |
313 String toString() { | 359 String toString() { |
314 StringBuffer sb = new StringBuffer(); | 360 StringBuffer sb = new StringBuffer(); |
315 sb.write('this=$thisLocal,'); | 361 sb.write('JsScopeInfo('); |
316 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}'); | 362 _printOn(sb, false); |
363 sb.write(')'); | |
317 return sb.toString(); | 364 return sb.toString(); |
318 } | 365 } |
319 } | 366 } |
320 | 367 |
321 class KernelCapturedScope extends KernelScopeInfo { | 368 class KernelCapturedScope extends KernelScopeInfo { |
322 final NodeBox box; | 369 final NodeBox box; |
323 final Set<ir.VariableDeclaration> boxedVariables; | 370 final Set<ir.VariableDeclaration> boxedVariables; |
324 | 371 |
325 KernelCapturedScope( | 372 KernelCapturedScope( |
326 ir.TreeNode node, | 373 ir.TreeNode node, |
327 this.box, | 374 this.box, |
328 this.boxedVariables, | 375 this.boxedVariables, |
329 Set<ir.VariableDeclaration> localsUsedInTryOrSync, | 376 Set<ir.VariableDeclaration> localsUsedInTryOrSync, |
330 Set<ir.VariableDeclaration> freeVariables, | 377 Set<ir.VariableDeclaration> freeVariables, |
331 bool hasThisLocal) | 378 bool hasThisLocal) |
332 : super.withBoxedVariables( | 379 : super.withBoxedVariables( |
333 node, localsUsedInTryOrSync, freeVariables, hasThisLocal); | 380 node, localsUsedInTryOrSync, freeVariables, hasThisLocal); |
334 | 381 |
335 bool get requiresContextBox => boxedVariables.isNotEmpty; | 382 bool get requiresContextBox => boxedVariables.isNotEmpty; |
383 | |
384 bool _printOn(StringBuffer sb, bool needsComma) { | |
385 needsComma = super._printOn(sb, needsComma); | |
386 if (box != null) { | |
387 if (needsComma) { | |
388 sb.write(','); | |
389 } | |
390 sb.write('box=${box}'); | |
391 needsComma = true; | |
392 } | |
393 if (boxedVariables.isNotEmpty) { | |
394 if (needsComma) { | |
395 sb.write(','); | |
396 } | |
397 sb.write('boxedVariables={${boxedVariables.join(',')}}'); | |
398 needsComma = true; | |
399 } | |
400 return needsComma; | |
401 } | |
402 | |
403 String toString() { | |
404 StringBuffer sb = new StringBuffer(); | |
405 sb.write('KernelCapturedScope('); | |
406 _printOn(sb, false); | |
407 sb.write(')'); | |
408 return sb.toString(); | |
409 } | |
336 } | 410 } |
337 | 411 |
338 class JsCapturedScope extends JsScopeInfo implements CapturedScope { | 412 class JsCapturedScope extends JsScopeInfo implements CapturedScope { |
339 final Local context; | 413 final Local box; |
340 final Map<Local, FieldEntity> boxedVariables; | 414 final Map<Local, FieldEntity> boxedVariables; |
341 | 415 |
342 JsCapturedScope.from(KernelCapturedScope capturedScope, | 416 JsCapturedScope.from(KernelCapturedScope capturedScope, |
343 KernelToLocalsMap localsMap, this.context, this.boxedVariables) | 417 KernelToLocalsMap localsMap, this.box, this.boxedVariables) |
344 : super.from(capturedScope, localsMap); | 418 : super.from(capturedScope, localsMap); |
345 | 419 |
346 bool get requiresContextBox => boxedVariables.isNotEmpty; | 420 bool get hasBox => box != null; |
347 | 421 |
348 void forEachBoxedVariable(f(Local local, FieldEntity field)) { | 422 void forEachBoxedVariable(f(Local local, FieldEntity field)) { |
349 boxedVariables.forEach(f); | 423 boxedVariables.forEach(f); |
350 } | 424 } |
351 | 425 |
352 bool isBoxed(Local variable) => boxedVariables.containsKey(variable); | 426 bool isBoxed(Local variable) => boxedVariables.containsKey(variable); |
427 | |
428 bool _printOn(StringBuffer sb, bool needsComma) { | |
429 needsComma = super._printOn(sb, needsComma); | |
430 if (box != null) { | |
431 if (needsComma) { | |
432 sb.write(','); | |
433 } | |
434 sb.write('box=${box}'); | |
435 needsComma = true; | |
436 } | |
437 if (boxedVariables.isNotEmpty) { | |
438 if (needsComma) { | |
439 sb.write(','); | |
440 } | |
441 sb.write('boxedVariables=${boxedVariables}'); | |
442 needsComma = true; | |
443 } | |
444 return needsComma; | |
445 } | |
446 | |
447 String toString() { | |
448 StringBuffer sb = new StringBuffer(); | |
449 sb.write('JsCapturedScope('); | |
450 _printOn(sb, false); | |
451 sb.write(')'); | |
452 return sb.toString(); | |
453 } | |
353 } | 454 } |
354 | 455 |
355 class KernelCapturedLoopScope extends KernelCapturedScope { | 456 class KernelCapturedLoopScope extends KernelCapturedScope { |
356 final List<ir.VariableDeclaration> boxedLoopVariables; | 457 final List<ir.VariableDeclaration> boxedLoopVariables; |
357 | 458 |
358 KernelCapturedLoopScope( | 459 KernelCapturedLoopScope( |
359 ir.TreeNode node, | 460 ir.TreeNode node, |
360 NodeBox box, | 461 NodeBox box, |
361 Set<ir.VariableDeclaration> boxedVariables, | 462 Set<ir.VariableDeclaration> boxedVariables, |
362 this.boxedLoopVariables, | 463 this.boxedLoopVariables, |
363 Set<ir.VariableDeclaration> localsUsedInTryOrSync, | 464 Set<ir.VariableDeclaration> localsUsedInTryOrSync, |
364 Set<ir.VariableDeclaration> freeVariables, | 465 Set<ir.VariableDeclaration> freeVariables, |
365 bool hasThisLocal) | 466 bool hasThisLocal) |
366 : super(node, box, boxedVariables, localsUsedInTryOrSync, freeVariables, | 467 : super(node, box, boxedVariables, localsUsedInTryOrSync, freeVariables, |
367 hasThisLocal); | 468 hasThisLocal); |
368 | 469 |
369 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; | 470 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; |
471 | |
472 bool _printOn(StringBuffer sb, bool needsComma) { | |
473 needsComma = super._printOn(sb, needsComma); | |
474 if (boxedLoopVariables.isNotEmpty) { | |
475 if (needsComma) { | |
476 sb.write(','); | |
477 } | |
478 sb.write('boxedLoopVariables={${boxedLoopVariables.join(',')}}'); | |
479 needsComma = true; | |
480 } | |
481 return needsComma; | |
482 } | |
483 | |
484 String toString() { | |
485 StringBuffer sb = new StringBuffer(); | |
486 sb.write('KernelCapturedLoopScope('); | |
487 _printOn(sb, false); | |
488 sb.write(')'); | |
489 return sb.toString(); | |
490 } | |
370 } | 491 } |
371 | 492 |
372 class JsCapturedLoopScope extends JsCapturedScope implements CapturedLoopScope { | 493 class JsCapturedLoopScope extends JsCapturedScope implements CapturedLoopScope { |
373 final List<Local> boxedLoopVariables; | 494 final List<Local> boxedLoopVariables; |
374 | 495 |
375 JsCapturedLoopScope.from( | 496 JsCapturedLoopScope.from( |
376 KernelCapturedLoopScope capturedScope, | 497 KernelCapturedLoopScope capturedScope, |
377 KernelToLocalsMap localsMap, | 498 KernelToLocalsMap localsMap, |
378 Local box, | 499 Local box, |
379 Map<Local, FieldEntity> boxedVariables) | 500 Map<Local, FieldEntity> boxedVariables) |
380 : this.boxedLoopVariables = capturedScope.boxedLoopVariables | 501 : this.boxedLoopVariables = capturedScope.boxedLoopVariables |
381 .map(localsMap.getLocalVariable) | 502 .map(localsMap.getLocalVariable) |
382 .toList(), | 503 .toList(), |
383 super.from(capturedScope, localsMap, box, boxedVariables); | 504 super.from(capturedScope, localsMap, box, boxedVariables); |
384 | 505 |
385 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; | 506 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; |
507 | |
508 bool _printOn(StringBuffer sb, bool needsComma) { | |
509 needsComma = super._printOn(sb, needsComma); | |
510 if (boxedLoopVariables.isNotEmpty) { | |
511 if (needsComma) { | |
512 sb.write(','); | |
513 } | |
514 sb.write('boxedLoopVariables={${boxedLoopVariables.join(',')}}'); | |
515 needsComma = true; | |
516 } | |
517 return needsComma; | |
518 } | |
519 | |
520 String toString() { | |
521 StringBuffer sb = new StringBuffer(); | |
522 sb.write('JsCapturedLoopScope('); | |
523 _printOn(sb, false); | |
524 sb.write(')'); | |
525 return sb.toString(); | |
526 } | |
386 } | 527 } |
387 | 528 |
388 // TODO(johnniwinther): Add unittest for the computed [ClosureClass]. | 529 // TODO(johnniwinther): Add unittest for the computed [ClosureClass]. |
389 class KernelClosureClass extends JsScopeInfo | 530 class KernelClosureClass extends JsScopeInfo |
390 implements ClosureRepresentationInfo { | 531 implements ClosureRepresentationInfo { |
391 JFunction callMethod; | 532 JFunction callMethod; |
392 final Local closureEntity; | 533 final Local closureEntity; |
393 final Local thisLocal; | 534 final Local thisLocal; |
394 final JClass closureClassEntity; | 535 final JClass closureClassEntity; |
395 final Map<Local, FieldEntity> boxedVariables; | 536 final Map<Local, FieldEntity> boxedVariables; |
(...skipping 19 matching lines...) Expand all Loading... | |
415 : localsMap.getLocalFunction(closureSourceNode.parent), | 556 : localsMap.getLocalFunction(closureSourceNode.parent), |
416 thisLocal = | 557 thisLocal = |
417 info.hasThisLocal ? new ThisLocal(localsMap.currentMember) : null, | 558 info.hasThisLocal ? new ThisLocal(localsMap.currentMember) : null, |
418 super.from(info, localsMap); | 559 super.from(info, localsMap); |
419 | 560 |
420 List<Local> get createdFieldEntities => localToFieldMap.keys.toList(); | 561 List<Local> get createdFieldEntities => localToFieldMap.keys.toList(); |
421 | 562 |
422 FieldEntity get thisFieldEntity => localToFieldMap[thisLocal]; | 563 FieldEntity get thisFieldEntity => localToFieldMap[thisLocal]; |
423 | 564 |
424 void forEachBoxedVariable(f(Local from, FieldEntity to)) { | 565 void forEachBoxedVariable(f(Local from, FieldEntity to)) { |
425 boxedVariables.forEach(f); | 566 throw new UnsupportedError('KernelClosureClass.forEachBoxedVariable'); |
426 } | 567 } |
427 | 568 |
428 void forEachCapturedVariable(f(Local from, FieldEntity to)) { | 569 void forEachCapturedVariable(f(Local from, FieldEntity to)) { |
429 for (Local local in localToFieldMap.keys) { | 570 throw new UnsupportedError('KernelClosureClass.forEachCapturedVariable'); |
430 FieldEntity field = localToFieldMap[local]; | |
431 if (local is! BoxLocal) f(local, field); | |
432 } | |
433 boxedVariables.forEach(f); | |
434 } | 571 } |
435 | 572 |
436 void forEachFreeVariable(f(Local variable, FieldEntity field)) { | 573 void forEachFreeVariable(f(Local variable, FieldEntity field)) { |
437 localToFieldMap.forEach(f); | 574 localToFieldMap.forEach(f); |
438 boxedVariables.forEach(f); | 575 boxedVariables.forEach(f); |
439 } | 576 } |
440 | 577 |
441 bool isVariableBoxed(Local variable) => boxedVariables.containsKey(variable); | 578 bool isVariableBoxed(Local variable) { |
579 throw new UnsupportedError('KernelClosureClass.isVariableBoxed'); | |
580 } | |
442 | 581 |
443 bool get isClosure => true; | 582 bool get isClosure => true; |
444 } | 583 } |
445 | 584 |
446 /// A local variable to disambiguate between a variable that has been captured | 585 /// A local variable to disambiguate between a variable that has been captured |
447 /// from one scope to another. This is the ir.Node version that corresponds to | 586 /// from one scope to another. This is the ir.Node version that corresponds to |
448 /// [BoxLocal]. | 587 /// [BoxLocal]. |
449 class NodeBox { | 588 class NodeBox { |
450 final String name; | 589 final String name; |
451 | 590 |
452 NodeBox(this.name); | 591 NodeBox(this.name); |
592 | |
593 String toString() => 'NodeBox(name=$name)'; | |
453 } | 594 } |
454 | 595 |
455 class JClosureClass extends JClass { | 596 class JClosureClass extends JClass { |
456 JClosureClass(JLibrary library, int classIndex, String name) | 597 JClosureClass(JLibrary library, int classIndex, String name) |
457 : super(library, classIndex, name, isAbstract: false); | 598 : super(library, classIndex, name, isAbstract: false); |
458 | 599 |
459 @override | 600 @override |
460 bool get isClosure => true; | 601 bool get isClosure => true; |
461 | 602 |
462 String toString() => '${jsElementPrefix}closure_class($name)'; | 603 String toString() => '${jsElementPrefix}closure_class($name)'; |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
604 KernelScopeInfo scopeInfo; | 745 KernelScopeInfo scopeInfo; |
605 | 746 |
606 /// Collected [CapturedScope] data for nodes. | 747 /// Collected [CapturedScope] data for nodes. |
607 Map<ir.Node, KernelCapturedScope> capturedScopesMap = | 748 Map<ir.Node, KernelCapturedScope> capturedScopesMap = |
608 <ir.Node, KernelCapturedScope>{}; | 749 <ir.Node, KernelCapturedScope>{}; |
609 | 750 |
610 /// Collected [ScopeInfo] data for nodes. | 751 /// Collected [ScopeInfo] data for nodes. |
611 Map<ir.TreeNode, KernelScopeInfo> closuresToGenerate = | 752 Map<ir.TreeNode, KernelScopeInfo> closuresToGenerate = |
612 <ir.TreeNode, KernelScopeInfo>{}; | 753 <ir.TreeNode, KernelScopeInfo>{}; |
613 } | 754 } |
OLD | NEW |