| OLD | NEW |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, 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 library kernel.transformations.closure.converter; | 5 library kernel.transformations.closure.converter; |
| 6 | 6 |
| 7 import '../../ast.dart' | 7 import '../../ast.dart' |
| 8 show | 8 show |
| 9 Arguments, | 9 Arguments, |
| 10 Block, | 10 Block, |
| (...skipping 22 matching lines...) Expand all Loading... |
| 33 MethodInvocation, | 33 MethodInvocation, |
| 34 Name, | 34 Name, |
| 35 NamedExpression, | 35 NamedExpression, |
| 36 NamedType, | 36 NamedType, |
| 37 NullLiteral, | 37 NullLiteral, |
| 38 Procedure, | 38 Procedure, |
| 39 ProcedureKind, | 39 ProcedureKind, |
| 40 PropertyGet, | 40 PropertyGet, |
| 41 ReturnStatement, | 41 ReturnStatement, |
| 42 Statement, | 42 Statement, |
| 43 StaticGet, | |
| 44 StaticInvocation, | 43 StaticInvocation, |
| 45 ThisExpression, | 44 ThisExpression, |
| 46 Transformer, | 45 Transformer, |
| 47 TreeNode, | 46 TreeNode, |
| 48 TypeParameter, | 47 TypeParameter, |
| 49 TypeParameterType, | 48 TypeParameterType, |
| 50 VariableDeclaration, | 49 VariableDeclaration, |
| 51 VariableGet, | 50 VariableGet, |
| 52 VariableSet, | 51 VariableSet, |
| 53 VectorType, | 52 VectorType, |
| (...skipping 19 matching lines...) Expand all Loading... |
| 73 final CoreTypes coreTypes; | 72 final CoreTypes coreTypes; |
| 74 final Set<VariableDeclaration> capturedVariables; | 73 final Set<VariableDeclaration> capturedVariables; |
| 75 final Map<FunctionNode, Set<TypeParameter>> capturedTypeVariables; | 74 final Map<FunctionNode, Set<TypeParameter>> capturedTypeVariables; |
| 76 final Map<FunctionNode, VariableDeclaration> thisAccess; | 75 final Map<FunctionNode, VariableDeclaration> thisAccess; |
| 77 final Map<FunctionNode, String> localNames; | 76 final Map<FunctionNode, String> localNames; |
| 78 | 77 |
| 79 /// Records place-holders for cloning contexts. See [visitForStatement]. | 78 /// Records place-holders for cloning contexts. See [visitForStatement]. |
| 80 final Set<InvalidExpression> contextClonePlaceHolders = | 79 final Set<InvalidExpression> contextClonePlaceHolders = |
| 81 new Set<InvalidExpression>(); | 80 new Set<InvalidExpression>(); |
| 82 | 81 |
| 83 /// Maps the names of all instance methods that may be torn off (aka | |
| 84 /// implicitly closurized) to `${name.name}#get`. | |
| 85 final Map<Name, Name> tearOffGetterNames; | |
| 86 | |
| 87 final CloneVisitor cloner = new CloneWithoutBody(); | 82 final CloneVisitor cloner = new CloneWithoutBody(); |
| 88 | 83 |
| 89 /// New members to add to [currentLibrary] after it has been | 84 /// New members to add to [currentLibrary] after it has been |
| 90 /// transformed. These members will not be transformed themselves. | 85 /// transformed. These members will not be transformed themselves. |
| 91 final List<TreeNode> newLibraryMembers = <TreeNode>[]; | 86 final List<TreeNode> newLibraryMembers = <TreeNode>[]; |
| 92 | 87 |
| 93 /// New members to add to [currentClass] after it has been transformed. These | 88 /// New members to add to [currentClass] after it has been transformed. These |
| 94 /// members will not be transformed themselves. | 89 /// members will not be transformed themselves. |
| 95 final List<Member> newClassMembers = <Member>[]; | 90 final List<Member> newClassMembers = <Member>[]; |
| 96 | 91 |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 129 /// | 124 /// |
| 130 /// In this example, `typeSubstitution[T].parameter == T_` when transforming | 125 /// In this example, `typeSubstitution[T].parameter == T_` when transforming |
| 131 /// the closure in `f`. | 126 /// the closure in `f`. |
| 132 Map<TypeParameter, DartType> typeSubstitution = | 127 Map<TypeParameter, DartType> typeSubstitution = |
| 133 const <TypeParameter, DartType>{}; | 128 const <TypeParameter, DartType>{}; |
| 134 | 129 |
| 135 ClosureConverter(this.coreTypes, ClosureInfo info) | 130 ClosureConverter(this.coreTypes, ClosureInfo info) |
| 136 : this.capturedVariables = info.variables, | 131 : this.capturedVariables = info.variables, |
| 137 this.capturedTypeVariables = info.typeVariables, | 132 this.capturedTypeVariables = info.typeVariables, |
| 138 this.thisAccess = info.thisAccess, | 133 this.thisAccess = info.thisAccess, |
| 139 this.localNames = info.localNames, | 134 this.localNames = info.localNames; |
| 140 this.tearOffGetterNames = info.tearOffGetterNames; | |
| 141 | 135 |
| 142 bool get isOuterMostContext { | 136 bool get isOuterMostContext { |
| 143 return currentFunction == null || currentMemberFunction == currentFunction; | 137 return currentFunction == null || currentMemberFunction == currentFunction; |
| 144 } | 138 } |
| 145 | 139 |
| 146 String get currentFileUri { | 140 String get currentFileUri { |
| 147 if (currentMember is Constructor) return currentClass.fileUri; | 141 if (currentMember is Constructor) return currentClass.fileUri; |
| 148 if (currentMember is Field) return (currentMember as Field).fileUri; | 142 if (currentMember is Field) return (currentMember as Field).fileUri; |
| 149 if (currentMember is Procedure) return (currentMember as Procedure).fileUri; | 143 if (currentMember is Procedure) return (currentMember as Procedure).fileUri; |
| 150 throw "No file uri for ${currentMember.runtimeType}"; | 144 throw "No file uri for ${currentMember.runtimeType}"; |
| (...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 363 .map((VariableDeclaration decl) => | 357 .map((VariableDeclaration decl) => |
| 364 new NamedType(decl.name, decl.type)) | 358 new NamedType(decl.name, decl.type)) |
| 365 .toList(), | 359 .toList(), |
| 366 typeParameters: function.typeParameters, | 360 typeParameters: function.typeParameters, |
| 367 requiredParameterCount: function.requiredParameterCount - 1); | 361 requiredParameterCount: function.requiredParameterCount - 1); |
| 368 | 362 |
| 369 return new ClosureCreation( | 363 return new ClosureCreation( |
| 370 closedTopLevelFunction, accessContext, closureType); | 364 closedTopLevelFunction, accessContext, closureType); |
| 371 } | 365 } |
| 372 | 366 |
| 373 TreeNode visitField(Field node) { | |
| 374 currentMember = node; | |
| 375 context = new NoContext(this); | |
| 376 if (node.isInstanceMember) { | |
| 377 Name tearOffName = tearOffGetterNames[node.name]; | |
| 378 if (tearOffName != null) { | |
| 379 // TODO(ahe): If we rewrite setters, we can rename the field to avoid | |
| 380 // an indirection in most cases. | |
| 381 addFieldForwarder(tearOffName, node); | |
| 382 } | |
| 383 } | |
| 384 node = super.visitField(node); | |
| 385 context = null; | |
| 386 currentMember = null; | |
| 387 return node; | |
| 388 } | |
| 389 | |
| 390 TreeNode visitProcedure(Procedure node) { | 367 TreeNode visitProcedure(Procedure node) { |
| 391 assert(isEmptyContext); | 368 assert(isEmptyContext); |
| 392 | 369 |
| 393 currentMember = node; | 370 currentMember = node; |
| 394 | 371 |
| 395 if (node.isInstanceMember) { | |
| 396 Name tearOffName = tearOffGetterNames[node.name]; | |
| 397 if (tearOffName != null) { | |
| 398 if (node.isGetter) { | |
| 399 // We rename the getter to avoid an indirection in most cases. | |
| 400 Name oldName = node.name; | |
| 401 node.name = tearOffName; | |
| 402 node.canonicalName?.unbind(); | |
| 403 addGetterForwarder(oldName, node); | |
| 404 } else if (node.kind == ProcedureKind.Method) { | |
| 405 addTearOffMethod(tearOffName, node); | |
| 406 } | |
| 407 } | |
| 408 } | |
| 409 | |
| 410 FunctionNode function = node.function; | 372 FunctionNode function = node.function; |
| 411 if (function.body != null) { | 373 if (function.body != null) { |
| 412 setupContextForFunctionBody(function); | 374 setupContextForFunctionBody(function); |
| 413 VariableDeclaration self = thisAccess[currentMemberFunction]; | 375 VariableDeclaration self = thisAccess[currentMemberFunction]; |
| 414 if (self != null) { | 376 if (self != null) { |
| 415 context.extend(self, new ThisExpression()); | 377 context.extend(self, new ThisExpression()); |
| 416 } | 378 } |
| 417 node.transformChildren(this); | 379 node.transformChildren(this); |
| 418 resetContext(); | 380 resetContext(); |
| 419 } | 381 } |
| (...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 604 } | 566 } |
| 605 return super.visitForInStatement(node); | 567 return super.visitForInStatement(node); |
| 606 } | 568 } |
| 607 | 569 |
| 608 TreeNode visitThisExpression(ThisExpression node) { | 570 TreeNode visitThisExpression(ThisExpression node) { |
| 609 return isOuterMostContext | 571 return isOuterMostContext |
| 610 ? node | 572 ? node |
| 611 : context.lookup(thisAccess[currentMemberFunction]); | 573 : context.lookup(thisAccess[currentMemberFunction]); |
| 612 } | 574 } |
| 613 | 575 |
| 614 TreeNode visitStaticGet(StaticGet node) { | |
| 615 Member target = node.target; | |
| 616 if (target is Procedure && target.kind == ProcedureKind.Method) { | |
| 617 VariableDeclaration contextVariable = new VariableDeclaration( | |
| 618 "#contextParameter", | |
| 619 type: const VectorType()); | |
| 620 Expression expression = getTearOffExpression( | |
| 621 null, node.target, contextVariable, new NullLiteral()); | |
| 622 expression.transformChildren(this); | |
| 623 return expression; | |
| 624 } | |
| 625 return super.visitStaticGet(node); | |
| 626 } | |
| 627 | |
| 628 TreeNode visitPropertyGet(PropertyGet node) { | |
| 629 Name tearOffName = tearOffGetterNames[node.name]; | |
| 630 if (tearOffName != null) { | |
| 631 MethodInvocation replacement = new MethodInvocation( | |
| 632 node.receiver, tearOffName, new Arguments(<Expression>[])); | |
| 633 return super.visitMethodInvocation(replacement); | |
| 634 } | |
| 635 return super.visitPropertyGet(node); | |
| 636 } | |
| 637 | |
| 638 TreeNode visitCatch(Catch node) { | 576 TreeNode visitCatch(Catch node) { |
| 639 VariableDeclaration exception = node.exception; | 577 VariableDeclaration exception = node.exception; |
| 640 VariableDeclaration stackTrace = node.stackTrace; | 578 VariableDeclaration stackTrace = node.stackTrace; |
| 641 if (stackTrace != null && capturedVariables.contains(stackTrace)) { | 579 if (stackTrace != null && capturedVariables.contains(stackTrace)) { |
| 642 Block block = node.body = ensureBlock(node.body); | 580 Block block = node.body = ensureBlock(node.body); |
| 643 block.parent = node; | 581 block.parent = node; |
| 644 node.stackTrace = new VariableDeclaration(null); | 582 node.stackTrace = new VariableDeclaration(null); |
| 645 node.stackTrace.parent = node; | 583 node.stackTrace.parent = node; |
| 646 stackTrace.initializer = new VariableGet(node.stackTrace); | 584 stackTrace.initializer = new VariableGet(node.stackTrace); |
| 647 block.statements.insert(0, stackTrace); | 585 block.statements.insert(0, stackTrace); |
| 648 stackTrace.parent = block; | 586 stackTrace.parent = block; |
| 649 } | 587 } |
| 650 if (exception != null && capturedVariables.contains(exception)) { | 588 if (exception != null && capturedVariables.contains(exception)) { |
| 651 Block block = node.body = ensureBlock(node.body); | 589 Block block = node.body = ensureBlock(node.body); |
| 652 block.parent = node; | 590 block.parent = node; |
| 653 node.exception = new VariableDeclaration(null); | 591 node.exception = new VariableDeclaration(null); |
| 654 node.exception.parent = node; | 592 node.exception.parent = node; |
| 655 exception.initializer = new VariableGet(node.exception); | 593 exception.initializer = new VariableGet(node.exception); |
| 656 block.statements.insert(0, exception); | 594 block.statements.insert(0, exception); |
| 657 exception.parent = block; | 595 exception.parent = block; |
| 658 } | 596 } |
| 659 return super.visitCatch(node); | 597 return super.visitCatch(node); |
| 660 } | 598 } |
| 661 | 599 |
| 662 Block ensureBlock(Statement statement) { | 600 Block ensureBlock(Statement statement) { |
| 663 return statement is Block ? statement : new Block(<Statement>[statement]); | 601 return statement is Block ? statement : new Block(<Statement>[statement]); |
| 664 } | 602 } |
| 665 | 603 |
| 666 /// Creates a closure that will invoke method [procedure] of [receiver] and | |
| 667 /// return an expression that instantiates that closure. | |
| 668 Expression getTearOffExpression( | |
| 669 VariableDeclaration receiver, | |
| 670 Procedure procedure, | |
| 671 VariableDeclaration contextVariable, | |
| 672 Expression accessContext) { | |
| 673 Map<TypeParameter, DartType> substitution = procedure.isInstanceMember | |
| 674 // Note: we do not attempt to avoid copying type variables that aren't | |
| 675 // used in the signature of [procedure]. It might be more economical to | |
| 676 // only copy type variables that are used. However, we assume that | |
| 677 // passing type arguments that match the enclosing class' type | |
| 678 // variables will be handled most efficiently. | |
| 679 ? copyTypeVariables(procedure.enclosingClass.typeParameters) | |
| 680 : const <TypeParameter, DartType>{}; | |
| 681 | |
| 682 // TODO(29181): remove variable `dynamicSubstitution` and replace its usages | |
| 683 // with `substitution`. | |
| 684 | |
| 685 Map<TypeParameter, DartType> dynamicSubstitution = | |
| 686 <TypeParameter, DartType>{}; | |
| 687 for (TypeParameter parameter in substitution.keys) { | |
| 688 dynamicSubstitution[parameter] = const DynamicType(); | |
| 689 } | |
| 690 for (TypeParameter parameter in substitution.keys) { | |
| 691 if (!isObject(parameter.bound)) { | |
| 692 dynamicSubstitution[parameter] = | |
| 693 substitute(parameter.bound, dynamicSubstitution); | |
| 694 } | |
| 695 } | |
| 696 | |
| 697 // Find the closure class for the function. If there isn't one, create it. | |
| 698 String closedTopLevelFunctionName = | |
| 699 createNameForClosedTopLevelFunction(procedure.function); | |
| 700 Procedure closedTopLevelFunction = null; | |
| 701 for (TreeNode node in newLibraryMembers) { | |
| 702 if (node is Procedure && node.name.name == closedTopLevelFunctionName) { | |
| 703 closedTopLevelFunction = node; | |
| 704 } | |
| 705 } | |
| 706 if (closedTopLevelFunction == null) { | |
| 707 closedTopLevelFunction = new Procedure( | |
| 708 new Name(closedTopLevelFunctionName), | |
| 709 ProcedureKind.Method, | |
| 710 forwardFunction( | |
| 711 procedure, receiver, contextVariable, dynamicSubstitution), | |
| 712 isStatic: true, | |
| 713 fileUri: currentFileUri); | |
| 714 newLibraryMembers.add(closedTopLevelFunction); | |
| 715 } | |
| 716 | |
| 717 return new ClosureCreation( | |
| 718 closedTopLevelFunction, accessContext, procedure.function.functionType); | |
| 719 } | |
| 720 | |
| 721 /// Creates a function that has the same signature as `procedure.function` | 604 /// Creates a function that has the same signature as `procedure.function` |
| 722 /// and which forwards all arguments to `procedure`. | 605 /// and which forwards all arguments to `procedure`. |
| 723 FunctionNode forwardFunction( | 606 FunctionNode forwardFunction( |
| 724 Procedure procedure, | 607 Procedure procedure, |
| 725 VariableDeclaration receiver, | 608 VariableDeclaration receiver, |
| 726 VariableDeclaration contextVariable, | 609 VariableDeclaration contextVariable, |
| 727 Map<TypeParameter, DartType> substitution) { | 610 Map<TypeParameter, DartType> substitution) { |
| 728 CloneVisitor cloner = substitution.isEmpty | 611 CloneVisitor cloner = substitution.isEmpty |
| 729 ? this.cloner | 612 ? this.cloner |
| 730 : new CloneWithoutBody(typeSubstitution: substitution); | 613 : new CloneWithoutBody(typeSubstitution: substitution); |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 809 copy.function.body = body; | 692 copy.function.body = body; |
| 810 copy.function.body.parent = copy.function; | 693 copy.function.body.parent = copy.function; |
| 811 return copy; | 694 return copy; |
| 812 } | 695 } |
| 813 | 696 |
| 814 void addGetterForwarder(Name name, Procedure getter) { | 697 void addGetterForwarder(Name name, Procedure getter) { |
| 815 assert(getter.isGetter); | 698 assert(getter.isGetter); |
| 816 newClassMembers | 699 newClassMembers |
| 817 .add(copyWithBody(getter, forwardToThisProperty(getter))..name = name); | 700 .add(copyWithBody(getter, forwardToThisProperty(getter))..name = name); |
| 818 } | 701 } |
| 819 | |
| 820 void addTearOffMethod(Name name, Procedure procedure) { | |
| 821 // [addTearOffMethod] generates a method along with a context that captures | |
| 822 // `this`. The work with contexts is typically done using the data gathered | |
| 823 // by a [ClosureInfo] instance. In absence of this information, we need to | |
| 824 // create some variables, like `#self` and `#context`, and manipulate | |
| 825 // contexts directly in some cases. | |
| 826 // | |
| 827 // Also, the tear-off method is generated during a visit to the AST node | |
| 828 // of the procedure being torn off, so we need to save and restore some | |
| 829 // auxiliary variables like `currentMember` and `currentMemberFunction` | |
| 830 // and use [saveContext], so that those variables have proper values when | |
| 831 // the procedure itself is being transformed. | |
| 832 Member oldCurrentMember = currentMember; | |
| 833 FunctionNode oldCurrentMemberFunction = currentMemberFunction; | |
| 834 try { | |
| 835 saveContext(() { | |
| 836 Block body = new Block(<Statement>[]); | |
| 837 FunctionNode tearOffMethodFunction = new FunctionNode(body); | |
| 838 setupContextForFunctionBody(tearOffMethodFunction); | |
| 839 | |
| 840 // We need a variable that refers to `this` to put it into the context. | |
| 841 VariableDeclaration self = new VariableDeclaration("#self", | |
| 842 type: procedure.enclosingClass.rawType); | |
| 843 context.extend(self, new ThisExpression()); | |
| 844 | |
| 845 // The `#context` variable is used to access the context in the closed | |
| 846 // top-level function that represents the closure and is generated in | |
| 847 // [getTearOffExpression]. | |
| 848 VariableDeclaration contextVariable = new VariableDeclaration( | |
| 849 "#contextParameter", | |
| 850 type: const VectorType()); | |
| 851 Context parent = context; | |
| 852 context = context.toNestedContext( | |
| 853 new VariableAccessor(contextVariable, null, TreeNode.noOffset)); | |
| 854 | |
| 855 body.addStatement(new ReturnStatement(getTearOffExpression( | |
| 856 self, procedure, contextVariable, parent.expression))); | |
| 857 | |
| 858 Procedure tearOffMethod = new Procedure( | |
| 859 name, ProcedureKind.Method, tearOffMethodFunction, | |
| 860 fileUri: currentFileUri); | |
| 861 newClassMembers.add(tearOffMethod); | |
| 862 | |
| 863 resetContext(); | |
| 864 }); | |
| 865 } finally { | |
| 866 currentMember = oldCurrentMember; | |
| 867 currentMemberFunction = oldCurrentMemberFunction; | |
| 868 } | |
| 869 } | |
| 870 } | 702 } |
| OLD | NEW |