Chromium Code Reviews| 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 |