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 |