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

Side by Side Diff: pkg/kernel/lib/transformations/closure/converter.dart

Issue 2778223002: Add primitive to create closures and use it for closure conversion (Closed)
Patch Set: Skip context param in closure type construction, rather than remove it Created 3 years, 8 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) 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,
11 Catch, 11 Catch,
12 Class, 12 Class,
13 ClosureCreation,
13 Constructor, 14 Constructor,
14 ConstructorInvocation,
15 DartType, 15 DartType,
16 DynamicType,
16 EmptyStatement, 17 EmptyStatement,
17 Expression, 18 Expression,
18 ExpressionStatement, 19 ExpressionStatement,
19 Field, 20 Field,
20 FieldInitializer, 21 FieldInitializer,
21 ForInStatement, 22 ForInStatement,
22 ForStatement, 23 ForStatement,
23 FunctionDeclaration, 24 FunctionDeclaration,
24 FunctionExpression, 25 FunctionExpression,
25 FunctionNode, 26 FunctionNode,
27 FunctionType,
26 Initializer, 28 Initializer,
29 InterfaceType,
27 InvalidExpression, 30 InvalidExpression,
28 InvocationExpression, 31 InvocationExpression,
29 Let, 32 Let,
30 Library, 33 Library,
31 LocalInitializer, 34 LocalInitializer,
32 Member, 35 Member,
33 MethodInvocation, 36 MethodInvocation,
34 Name, 37 Name,
35 NamedExpression, 38 NamedExpression,
39 NamedType,
36 NullLiteral, 40 NullLiteral,
37 Procedure, 41 Procedure,
38 ProcedureKind, 42 ProcedureKind,
39 PropertyGet, 43 PropertyGet,
40 ReturnStatement, 44 ReturnStatement,
41 Statement, 45 Statement,
42 StaticGet, 46 StaticGet,
43 StaticInvocation, 47 StaticInvocation,
44 StringLiteral,
45 Supertype,
46 ThisExpression, 48 ThisExpression,
47 Transformer, 49 Transformer,
48 TreeNode, 50 TreeNode,
49 TypeParameter, 51 TypeParameter,
50 TypeParameterType, 52 TypeParameterType,
51 VariableDeclaration, 53 VariableDeclaration,
52 VariableGet, 54 VariableGet,
53 VariableSet, 55 VariableSet,
54 VectorType, 56 VectorType,
57 getMemberReference,
55 transformList; 58 transformList;
56 59
57 import '../../frontend/accessors.dart' show VariableAccessor; 60 import '../../frontend/accessors.dart' show VariableAccessor;
58 61
59 import '../../clone.dart' show CloneVisitor; 62 import '../../clone.dart' show CloneVisitor;
60 63
61 import '../../core_types.dart' show CoreTypes; 64 import '../../core_types.dart' show CoreTypes;
62 65
63 import '../../type_algebra.dart' show substitute; 66 import '../../type_algebra.dart' show substitute;
64 67
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
103 Member currentMember; 106 Member currentMember;
104 107
105 FunctionNode currentMemberFunction; 108 FunctionNode currentMemberFunction;
106 109
107 FunctionNode currentFunction; 110 FunctionNode currentFunction;
108 111
109 Context context; 112 Context context;
110 113
111 AstRewriter rewriter; 114 AstRewriter rewriter;
112 115
116 /// TODO(29181): update this comment when the type variables are restored.
113 /// Maps original type variable (aka type parameter) to a hoisted type 117 /// Maps original type variable (aka type parameter) to a hoisted type
114 /// variable type. 118 /// variable type.
115 /// 119 ///
116 /// For example, consider: 120 /// For example, consider:
117 /// 121 ///
118 /// class C<T> { 122 /// class C<T> {
119 /// f() => (x) => x is T; 123 /// f() => (x) => x is T;
120 /// } 124 /// }
121 /// 125 ///
122 /// This is currently converted to: 126 /// This is currently converted to:
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after
245 249
246 AstRewriter makeRewriterForBody(FunctionNode function) { 250 AstRewriter makeRewriterForBody(FunctionNode function) {
247 Statement body = function.body; 251 Statement body = function.body;
248 if (body is! Block) { 252 if (body is! Block) {
249 body = new Block(<Statement>[body]); 253 body = new Block(<Statement>[body]);
250 function.body = function.body.parent = body; 254 function.body = function.body.parent = body;
251 } 255 }
252 return new BlockRewriter(body); 256 return new BlockRewriter(body);
253 } 257 }
254 258
259 bool isObject(DartType type) {
260 return type is InterfaceType && type.classNode.supertype == null;
261 }
262
255 Expression handleLocalFunction(FunctionNode function) { 263 Expression handleLocalFunction(FunctionNode function) {
256 FunctionNode enclosingFunction = currentFunction; 264 FunctionNode enclosingFunction = currentFunction;
257 Map<TypeParameter, DartType> enclosingTypeSubstitution = typeSubstitution; 265 Map<TypeParameter, DartType> enclosingTypeSubstitution = typeSubstitution;
258 currentFunction = function; 266 currentFunction = function;
259 Statement body = function.body; 267 Statement body = function.body;
260 assert(body != null); 268 assert(body != null);
261 269
262 rewriter = makeRewriterForBody(function); 270 rewriter = makeRewriterForBody(function);
263 271
264 VariableDeclaration contextVariable = new VariableDeclaration( 272 VariableDeclaration contextVariable =
265 "#contextParameter", 273 new VariableDeclaration("#contextParameter", type: const VectorType());
266 type: const VectorType(),
267 isFinal: true);
268 Context parent = context; 274 Context parent = context;
269 context = context.toNestedContext( 275 context = context.toNestedContext(
270 new VariableAccessor(contextVariable, null, TreeNode.noOffset)); 276 new VariableAccessor(contextVariable, null, TreeNode.noOffset));
271 277
272 Set<TypeParameter> captured = capturedTypeVariables[currentFunction]; 278 Set<TypeParameter> captured = capturedTypeVariables[currentFunction];
273 if (captured != null) { 279 if (captured != null) {
274 typeSubstitution = copyTypeVariables(captured); 280 typeSubstitution = copyTypeVariables(captured);
275 } else { 281 } else {
276 typeSubstitution = const <TypeParameter, DartType>{}; 282 typeSubstitution = const <TypeParameter, DartType>{};
277 } 283 }
278 284
285 // TODO(29181): remove replacementTypeSubstitution variable and its usages.
286 // All the type variables used in this function body are replaced with
287 // either dynamic or their bounds. This is to temporarily remove the type
288 // variables from closure conversion. They should be returned after the VM
289 // changes are done to support vectors and closure creation. See #29181.
290 Map<TypeParameter, DartType> replacementTypeSubstitution =
291 <TypeParameter, DartType>{};
292 for (TypeParameter parameter in typeSubstitution.keys) {
293 replacementTypeSubstitution[parameter] = const DynamicType();
294 }
295 for (TypeParameter parameter in typeSubstitution.keys) {
296 if (!isObject(parameter.bound)) {
297 replacementTypeSubstitution[parameter] =
298 substitute(parameter.bound, replacementTypeSubstitution);
299 }
300 }
301 typeSubstitution = replacementTypeSubstitution;
279 function.transformChildren(this); 302 function.transformChildren(this);
280 303
304 // TODO(29181): don't replace typeSubstitution with an empty map.
305 // Information about captured type variables is deleted from the closure
306 // class, because the type variables in this function body are already
307 // replaced with either dynamic or their bounds. This change should be
308 // undone after the VM support for vectors and closure creation is
309 // implemented. See #29181.
310 typeSubstitution = <TypeParameter, DartType>{};
281 Expression result = addClosure(function, contextVariable, parent.expression, 311 Expression result = addClosure(function, contextVariable, parent.expression,
282 typeSubstitution, enclosingTypeSubstitution); 312 typeSubstitution, enclosingTypeSubstitution);
283 currentFunction = enclosingFunction; 313 currentFunction = enclosingFunction;
284 typeSubstitution = enclosingTypeSubstitution; 314 typeSubstitution = enclosingTypeSubstitution;
285 return result; 315 return result;
286 } 316 }
287 317
288 TreeNode visitFunctionDeclaration(FunctionDeclaration node) { 318 TreeNode visitFunctionDeclaration(FunctionDeclaration node) {
289 /// Is this closure itself captured by a closure? 319 /// Is this closure itself captured by a closure?
290 bool isCaptured = capturedVariables.contains(node.variable); 320 bool isCaptured = capturedVariables.contains(node.variable);
(...skipping 14 matching lines...) Expand all
305 } 335 }
306 }); 336 });
307 } 337 }
308 338
309 TreeNode visitFunctionExpression(FunctionExpression node) { 339 TreeNode visitFunctionExpression(FunctionExpression node) {
310 return saveContext(() { 340 return saveContext(() {
311 return handleLocalFunction(node.function); 341 return handleLocalFunction(node.function);
312 }); 342 });
313 } 343 }
314 344
315 /// Add a new class to the current library that looks like this: 345 /// Add a new procedure to the current library that looks like this:
316 /// 346 ///
317 /// class Closure#0 extends core::Object implements core::Function { 347 /// static method closure#0(Vector #c, /* Parameters of [function]. */)
318 /// field Vector context; 348 /// → dynamic {
319 /// constructor •(final Vector #t1) → dynamic 349 ///
320 /// : self::Closure#0::context = #t1 350 /// /* Context is represented by #c. */
321 /// ; 351 ///
322 /// method call(/* The parameters of [function] */) → dynamic { 352 /// /* Body of [function]. */
323 /// /// #t2 is [contextVariable]. 353 ///
324 /// final Vector #t2 = this.{self::Closure#0::context};
325 /// /* The body of [function]. */
326 /// }
327 /// } 354 /// }
328 /// 355 ///
329 /// Returns a constructor call to invoke the above constructor. 356 /// Returns an invocation of the closure creation primitive that binds the
330 /// 357 /// above top-level function to a context represented as Vector.
331 /// TODO(ahe): We shouldn't create a class for each closure. Instead we turn
332 /// [function] into a top-level function and use the Dart VM's mechnism for
333 /// closures.
334 Expression addClosure( 358 Expression addClosure(
335 FunctionNode function, 359 FunctionNode function,
336 VariableDeclaration contextVariable, 360 VariableDeclaration contextVariable,
337 Expression accessContext, 361 Expression accessContext,
338 Map<TypeParameter, DartType> substitution, 362 Map<TypeParameter, DartType> substitution,
339 Map<TypeParameter, DartType> enclosingTypeSubstitution) { 363 Map<TypeParameter, DartType> enclosingTypeSubstitution) {
340 Field contextField = new Field( 364 function.positionalParameters.insert(0, contextVariable);
341 // TODO(ahe): Rename to #context. 365 ++function.requiredParameterCount;
342 new Name("context"), 366 Procedure closedTopLevelFunction = new Procedure(
343 type: const VectorType(), 367 new Name(createNameForClosedTopLevelFunction(function)),
368 ProcedureKind.Method,
369 function,
370 isStatic: true,
344 fileUri: currentFileUri); 371 fileUri: currentFileUri);
345 Class closureClass = createClosureClass(function, 372 newLibraryMembers.add(closedTopLevelFunction);
346 fields: [contextField], substitution: substitution); 373
347 closureClass.addMember(new Procedure( 374 FunctionType closureType = new FunctionType(
348 new Name("call"), ProcedureKind.Method, function, 375 function.positionalParameters
349 fileUri: currentFileUri)); 376 .skip(1)
350 newLibraryMembers.add(closureClass); 377 .map((VariableDeclaration decl) => decl.type)
351 Statement note = new ExpressionStatement( 378 .toList(),
352 new StringLiteral("This is a temporary solution. " 379 function.returnType,
353 "In the VM, this will become an additional parameter.")); 380 namedParameters: function.namedParameters
354 List<Statement> statements = <Statement>[note, contextVariable]; 381 .map((VariableDeclaration decl) =>
355 Statement body = function.body; 382 new NamedType(decl.name, decl.type))
356 if (body is Block) { 383 .toList(),
357 statements.addAll(body.statements); 384 typeParameters: function.typeParameters,
358 } else { 385 requiredParameterCount: function.requiredParameterCount - 1);
359 statements.add(body); 386
360 } 387 return new ClosureCreation(
361 function.body = new Block(statements); 388 getMemberReference(closedTopLevelFunction), accessContext, closureType);
362 function.body.parent = function;
363 contextVariable.initializer =
364 new PropertyGet(new ThisExpression(), contextField.name, contextField);
365 contextVariable.initializer.parent = contextVariable;
366 return new ConstructorInvocation(
367 closureClass.constructors.single,
368 new Arguments(<Expression>[accessContext], types:
369 new List<DartType>.from(substitution.keys.map((TypeParameter t) {
370 return substitute(
371 new TypeParameterType(t), enclosingTypeSubstitution);
372 }))));
373 } 389 }
374 390
375 TreeNode visitField(Field node) { 391 TreeNode visitField(Field node) {
376 currentMember = node; 392 currentMember = node;
377 context = new NoContext(this); 393 context = new NoContext(this);
378 if (node.isInstanceMember) { 394 if (node.isInstanceMember) {
379 Name tearOffName = tearOffGetterNames[node.name]; 395 Name tearOffName = tearOffGetterNames[node.name];
380 if (tearOffName != null) { 396 if (tearOffName != null) {
381 // TODO(ahe): If we rewrite setters, we can rename the field to avoid 397 // TODO(ahe): If we rewrite setters, we can rename the field to avoid
382 // an indirection in most cases. 398 // an indirection in most cases.
(...skipping 13 matching lines...) Expand all
396 412
397 if (node.isInstanceMember) { 413 if (node.isInstanceMember) {
398 Name tearOffName = tearOffGetterNames[node.name]; 414 Name tearOffName = tearOffGetterNames[node.name];
399 if (tearOffName != null) { 415 if (tearOffName != null) {
400 if (node.isGetter) { 416 if (node.isGetter) {
401 // We rename the getter to avoid an indirection in most cases. 417 // We rename the getter to avoid an indirection in most cases.
402 Name oldName = node.name; 418 Name oldName = node.name;
403 node.name = tearOffName; 419 node.name = tearOffName;
404 addGetterForwarder(oldName, node); 420 addGetterForwarder(oldName, node);
405 } else if (node.kind == ProcedureKind.Method) { 421 } else if (node.kind == ProcedureKind.Method) {
406 addTearOffGetter(tearOffName, node); 422 addTearOffMethod(tearOffName, node);
407 } 423 }
408 } 424 }
409 } 425 }
410 426
411 FunctionNode function = node.function; 427 FunctionNode function = node.function;
412 if (function.body != null) { 428 if (function.body != null) {
413 setupContextForFunctionBody(function); 429 setupContextForFunctionBody(function);
414 VariableDeclaration self = thisAccess[currentMemberFunction]; 430 VariableDeclaration self = thisAccess[currentMemberFunction];
415 if (self != null) { 431 if (self != null) {
416 context.extend(self, new ThisExpression()); 432 context.extend(self, new ThisExpression());
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
471 BlockRewriter blockRewriter = rewriter = rewriter.forNestedBlock(node); 487 BlockRewriter blockRewriter = rewriter = rewriter.forNestedBlock(node);
472 blockRewriter.transformStatements(node, this); 488 blockRewriter.transformStatements(node, this);
473 return node; 489 return node;
474 }); 490 });
475 } 491 }
476 492
477 TreeNode visitVariableDeclaration(VariableDeclaration node) { 493 TreeNode visitVariableDeclaration(VariableDeclaration node) {
478 node.transformChildren(this); 494 node.transformChildren(this);
479 495
480 if (!capturedVariables.contains(node)) return node; 496 if (!capturedVariables.contains(node)) return node;
481 context.extend(node, node.initializer ?? new NullLiteral()); 497 if (node.initializer == null && node.parent is FunctionNode) {
498 // If the variable is a function parameter and doesn't have an
499 // initializer, just use this variable name to put it into the context.
500 context.extend(node, new VariableGet(node));
501 } else {
502 context.extend(node, node.initializer ?? new NullLiteral());
503 }
482 504
483 if (node.parent == currentFunction) { 505 if (node.parent == currentFunction) {
484 return node; 506 return node;
485 } else { 507 } else {
486 assert(node.parent is Block); 508 assert(node.parent is Block);
487 // When returning null, the parent block will remove this node from its 509 // When returning null, the parent block will remove this node from its
488 // list of statements. 510 // list of statements.
489 return null; 511 return null;
490 } 512 }
491 } 513 }
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after
602 624
603 TreeNode visitThisExpression(ThisExpression node) { 625 TreeNode visitThisExpression(ThisExpression node) {
604 return isOuterMostContext 626 return isOuterMostContext
605 ? node 627 ? node
606 : context.lookup(thisAccess[currentMemberFunction]); 628 : context.lookup(thisAccess[currentMemberFunction]);
607 } 629 }
608 630
609 TreeNode visitStaticGet(StaticGet node) { 631 TreeNode visitStaticGet(StaticGet node) {
610 Member target = node.target; 632 Member target = node.target;
611 if (target is Procedure && target.kind == ProcedureKind.Method) { 633 if (target is Procedure && target.kind == ProcedureKind.Method) {
612 Expression expression = getTearOffExpression(node.target); 634 VariableDeclaration contextVariable = new VariableDeclaration(
635 "#contextParameter",
636 type: const VectorType());
637 Expression expression = getTearOffExpression(
638 null, node.target, contextVariable, new NullLiteral());
613 expression.transformChildren(this); 639 expression.transformChildren(this);
614 return expression; 640 return expression;
615 } 641 }
616 return super.visitStaticGet(node); 642 return super.visitStaticGet(node);
617 } 643 }
618 644
619 TreeNode visitPropertyGet(PropertyGet node) { 645 TreeNode visitPropertyGet(PropertyGet node) {
620 Name tearOffName = tearOffGetterNames[node.name]; 646 Name tearOffName = tearOffGetterNames[node.name];
621 if (tearOffName != null) { 647 if (tearOffName != null) {
622 node.name = tearOffName; 648 MethodInvocation replacement = new MethodInvocation(
649 node.receiver, tearOffName, new Arguments(<Expression>[]));
650 return super.visitMethodInvocation(replacement);
623 } 651 }
624 return super.visitPropertyGet(node); 652 return super.visitPropertyGet(node);
625 } 653 }
626 654
627 TreeNode visitCatch(Catch node) { 655 TreeNode visitCatch(Catch node) {
628 VariableDeclaration exception = node.exception; 656 VariableDeclaration exception = node.exception;
629 VariableDeclaration stackTrace = node.stackTrace; 657 VariableDeclaration stackTrace = node.stackTrace;
630 if (stackTrace != null && capturedVariables.contains(stackTrace)) { 658 if (stackTrace != null && capturedVariables.contains(stackTrace)) {
631 Block block = node.body = ensureBlock(node.body); 659 Block block = node.body = ensureBlock(node.body);
632 block.parent = node; 660 block.parent = node;
(...skipping 12 matching lines...) Expand all
645 block.statements.insert(0, exception); 673 block.statements.insert(0, exception);
646 exception.parent = block; 674 exception.parent = block;
647 } 675 }
648 return super.visitCatch(node); 676 return super.visitCatch(node);
649 } 677 }
650 678
651 Block ensureBlock(Statement statement) { 679 Block ensureBlock(Statement statement) {
652 return statement is Block ? statement : new Block(<Statement>[statement]); 680 return statement is Block ? statement : new Block(<Statement>[statement]);
653 } 681 }
654 682
655 /// Creates a closure that will invoke [procedure] and return an expression 683 /// Creates a closure that will invoke method [procedure] of [receiver] and
656 /// that instantiates that closure. 684 /// return an expression that instantiates that closure.
657 Expression getTearOffExpression(Procedure procedure) { 685 Expression getTearOffExpression(
686 VariableDeclaration receiver,
687 Procedure procedure,
688 VariableDeclaration contextVariable,
689 Expression accessContext) {
658 Map<TypeParameter, DartType> substitution = procedure.isInstanceMember 690 Map<TypeParameter, DartType> substitution = procedure.isInstanceMember
659 // Note: we do not attempt to avoid copying type variables that aren't 691 // Note: we do not attempt to avoid copying type variables that aren't
660 // used in the signature of [procedure]. It might be more economical to 692 // used in the signature of [procedure]. It might be more economical to
661 // only copy type variables that are used. However, we assume that 693 // only copy type variables that are used. However, we assume that
662 // passing type arguments that match the enclosing class' type 694 // passing type arguments that match the enclosing class' type
663 // variables will be handled most efficiently. 695 // variables will be handled most efficiently.
664 ? copyTypeVariables(procedure.enclosingClass.typeParameters) 696 ? copyTypeVariables(procedure.enclosingClass.typeParameters)
665 : const <TypeParameter, DartType>{}; 697 : const <TypeParameter, DartType>{};
666 Expression receiver = null; 698
667 List<Field> fields = null; 699 // TODO(29181): remove variable `dynamicSubstitution` and replace its usages
668 if (procedure.isInstanceMember) { 700 // with `substitution`.
669 // TODO(ahe): Rename to #self. 701
670 Field self = new Field(new Name("self"), fileUri: currentFileUri); 702 Map<TypeParameter, DartType> dynamicSubstitution =
671 self.type = substitute(procedure.enclosingClass.thisType, substitution); 703 <TypeParameter, DartType>{};
672 fields = <Field>[self]; 704 for (TypeParameter parameter in substitution.keys) {
673 receiver = new PropertyGet(new ThisExpression(), self.name, self); 705 dynamicSubstitution[parameter] = const DynamicType();
706 }
707 for (TypeParameter parameter in substitution.keys) {
708 if (!isObject(parameter.bound)) {
709 dynamicSubstitution[parameter] =
710 substitute(parameter.bound, dynamicSubstitution);
711 }
674 } 712 }
675 713
676 // Find the closure class for the function. If there isn't one, create it. 714 // Find the closure class for the function. If there isn't one, create it.
677 String closureClassName = createNameForClosureClass(procedure.function); 715 String closedTopLevelFunctionName =
678 Class closureClass = null; 716 createNameForClosedTopLevelFunction(procedure.function);
717 Procedure closedTopLevelFunction = null;
679 for (TreeNode node in newLibraryMembers) { 718 for (TreeNode node in newLibraryMembers) {
680 if (node is Class && node.name == closureClassName) { 719 if (node is Procedure && node.name.name == closedTopLevelFunctionName) {
681 closureClass = node; 720 closedTopLevelFunction = node;
682 } 721 }
683 } 722 }
684 if (closureClass == null) { 723 if (closedTopLevelFunction == null) {
685 closureClass = createClosureClass(procedure.function, 724 closedTopLevelFunction = new Procedure(
686 fields: fields, substitution: substitution); 725 new Name(closedTopLevelFunctionName),
687 closureClass.addMember(new Procedure(
688 new Name("call"),
689 ProcedureKind.Method, 726 ProcedureKind.Method,
690 forwardFunction(procedure, receiver, substitution), 727 forwardFunction(
691 fileUri: currentFileUri)); 728 procedure, receiver, contextVariable, dynamicSubstitution),
692 newLibraryMembers.add(closureClass); 729 isStatic: true,
730 fileUri: currentFileUri);
731 newLibraryMembers.add(closedTopLevelFunction);
693 } 732 }
694 733
695 Arguments constructorArguments = procedure.isInstanceMember 734 return new ClosureCreation(getMemberReference(closedTopLevelFunction),
696 ? new Arguments(<Expression>[new ThisExpression()]) 735 accessContext, procedure.function.functionType);
697 : new Arguments.empty();
698 if (substitution.isNotEmpty) {
699 constructorArguments.types
700 .addAll(procedure.enclosingClass.thisType.typeArguments);
701 }
702 return new ConstructorInvocation(
703 closureClass.constructors.single, constructorArguments);
704 } 736 }
705 737
706 /// Creates a function that has the same signature as `procedure.function` 738 /// Creates a function that has the same signature as `procedure.function`
707 /// and which forwards all arguments to `procedure`. 739 /// and which forwards all arguments to `procedure`.
708 FunctionNode forwardFunction(Procedure procedure, Expression receiver, 740 FunctionNode forwardFunction(
741 Procedure procedure,
742 VariableDeclaration receiver,
743 VariableDeclaration contextVariable,
709 Map<TypeParameter, DartType> substitution) { 744 Map<TypeParameter, DartType> substitution) {
710 CloneVisitor cloner = substitution.isEmpty 745 CloneVisitor cloner = substitution.isEmpty
711 ? this.cloner 746 ? this.cloner
712 : new CloneWithoutBody(typeSubstitution: substitution); 747 : new CloneWithoutBody(typeSubstitution: substitution);
713 FunctionNode function = procedure.function; 748 FunctionNode function = procedure.function;
714 List<TypeParameter> typeParameters = 749 List<TypeParameter> typeParameters =
715 function.typeParameters.map(cloner.clone).toList(); 750 function.typeParameters.map(cloner.clone).toList();
716 List<VariableDeclaration> positionalParameters = 751 List<VariableDeclaration> positionalParameters =
717 function.positionalParameters.map(cloner.clone).toList(); 752 function.positionalParameters.map(cloner.clone).toList();
753 if (contextVariable != null) {
754 positionalParameters.insert(0, contextVariable);
755 }
718 List<VariableDeclaration> namedParameters = 756 List<VariableDeclaration> namedParameters =
719 function.namedParameters.map(cloner.clone).toList(); 757 function.namedParameters.map(cloner.clone).toList();
720 758
721 List<DartType> types = typeParameters 759 List<DartType> types = typeParameters
722 .map((TypeParameter parameter) => new TypeParameterType(parameter)) 760 .map((TypeParameter parameter) => new TypeParameterType(parameter))
723 .toList(); 761 .toList();
724 List<Expression> positional = positionalParameters 762 List<Expression> positional = positionalParameters
725 .map((VariableDeclaration parameter) => new VariableGet(parameter)) 763 .map((VariableDeclaration parameter) => new VariableGet(parameter))
726 .toList(); 764 .toList();
765 if (contextVariable != null) {
766 positional.removeAt(0);
767 }
727 List<NamedExpression> named = 768 List<NamedExpression> named =
728 namedParameters.map((VariableDeclaration parameter) { 769 namedParameters.map((VariableDeclaration parameter) {
729 return new NamedExpression(parameter.name, new VariableGet(parameter)); 770 return new NamedExpression(parameter.name, new VariableGet(parameter));
730 }).toList(); 771 }).toList();
731 772
732 Arguments arguments = new Arguments(positional, types: types, named: named); 773 Arguments arguments = new Arguments(positional, types: types, named: named);
733 InvocationExpression invocation = procedure.isInstanceMember 774 InvocationExpression invocation = procedure.isInstanceMember
734 ? new MethodInvocation(receiver, procedure.name, arguments, procedure) 775 ? new MethodInvocation(
776 context.lookup(receiver), procedure.name, arguments, procedure)
735 : new StaticInvocation(procedure, arguments); 777 : new StaticInvocation(procedure, arguments);
778 int requiredParameterCount = function.requiredParameterCount;
779 if (contextVariable != null) {
780 ++requiredParameterCount;
781 }
736 return new FunctionNode(new ReturnStatement(invocation), 782 return new FunctionNode(new ReturnStatement(invocation),
737 typeParameters: typeParameters, 783 typeParameters: typeParameters,
738 positionalParameters: positionalParameters, 784 positionalParameters: positionalParameters,
739 namedParameters: namedParameters, 785 namedParameters: namedParameters,
740 requiredParameterCount: function.requiredParameterCount, 786 requiredParameterCount: requiredParameterCount,
741 returnType: substitute(function.returnType, cloner.typeSubstitution)); 787 returnType: substitute(function.returnType, cloner.typeSubstitution));
742 } 788 }
743 789
744 /// Creates copies of the type variables in [original] and returns a 790 /// Creates copies of the type variables in [original] and returns a
745 /// substitution that can be passed to [substitute] to substitute all uses of 791 /// substitution that can be passed to [substitute] to substitute all uses of
746 /// [original] with their copies. 792 /// [original] with their copies.
747 Map<TypeParameter, DartType> copyTypeVariables( 793 Map<TypeParameter, DartType> copyTypeVariables(
748 Iterable<TypeParameter> original) { 794 Iterable<TypeParameter> original) {
749 if (original.isEmpty) return const <TypeParameter, DartType>{}; 795 if (original.isEmpty) return const <TypeParameter, DartType>{};
750 Map<TypeParameter, DartType> substitution = <TypeParameter, DartType>{}; 796 Map<TypeParameter, DartType> substitution = <TypeParameter, DartType>{};
751 for (TypeParameter t in original) { 797 for (TypeParameter t in original) {
752 substitution[t] = new TypeParameterType(new TypeParameter(t.name)); 798 substitution[t] = new TypeParameterType(new TypeParameter(t.name));
753 } 799 }
754 substitution.forEach((TypeParameter t, DartType copy) { 800 substitution.forEach((TypeParameter t, DartType copy) {
755 if (copy is TypeParameterType) { 801 if (copy is TypeParameterType) {
756 copy.parameter.bound = substitute(t.bound, substitution); 802 copy.parameter.bound = substitute(t.bound, substitution);
757 } 803 }
758 }); 804 });
759 return substitution; 805 return substitution;
760 } 806 }
761 807
762 String createNameForClosureClass(FunctionNode function) { 808 String createNameForClosedTopLevelFunction(FunctionNode function) {
763 return 'Closure#${localNames[function]}'; 809 return 'closure#${localNames[function]}';
764 }
765
766 Class createClosureClass(FunctionNode function,
767 {List<Field> fields, Map<TypeParameter, DartType> substitution}) {
768 List<TypeParameter> typeParameters = new List<TypeParameter>.from(
769 substitution.values
770 .map((DartType t) => (t as TypeParameterType).parameter));
771 Class closureClass = new Class(
772 name: createNameForClosureClass(function),
773 supertype: new Supertype(coreTypes.objectClass, const <DartType>[]),
774 typeParameters: typeParameters,
775 implementedTypes: <Supertype>[
776 new Supertype(coreTypes.functionClass, const <DartType>[])
777 ],
778 fileUri: currentFileUri);
779 addClosureClassNote(closureClass);
780
781 List<VariableDeclaration> parameters = <VariableDeclaration>[];
782 List<Initializer> initializers = <Initializer>[];
783 for (Field field in fields ?? const <Field>[]) {
784 closureClass.addMember(field);
785 VariableDeclaration parameter = new VariableDeclaration(field.name.name,
786 type: field.type, isFinal: true);
787 parameters.add(parameter);
788 initializers.add(new FieldInitializer(field, new VariableGet(parameter)));
789 }
790
791 closureClass.addMember(new Constructor(
792 new FunctionNode(new EmptyStatement(),
793 positionalParameters: parameters),
794 name: new Name(""),
795 initializers: initializers));
796
797 return closureClass;
798 } 810 }
799 811
800 Statement forwardToThisProperty(Member node) { 812 Statement forwardToThisProperty(Member node) {
801 assert(node is Field || (node is Procedure && node.isGetter)); 813 assert(node is Field || (node is Procedure && node.isGetter));
802 return new ReturnStatement( 814 return new ReturnStatement(
803 new PropertyGet(new ThisExpression(), node.name, node)); 815 new PropertyGet(new ThisExpression(), node.name, node));
804 } 816 }
805 817
806 void addFieldForwarder(Name name, Field field) { 818 void addFieldForwarder(Name name, Field field) {
807 newClassMembers.add(new Procedure(name, ProcedureKind.Getter, 819 newClassMembers.add(new Procedure(name, ProcedureKind.Getter,
808 new FunctionNode(forwardToThisProperty(field)), 820 new FunctionNode(forwardToThisProperty(field)),
809 fileUri: currentFileUri)); 821 fileUri: currentFileUri));
810 } 822 }
811 823
812 Procedure copyWithBody(Procedure procedure, Statement body) { 824 Procedure copyWithBody(Procedure procedure, Statement body) {
813 Procedure copy = cloner.clone(procedure); 825 Procedure copy = cloner.clone(procedure);
814 copy.function.body = body; 826 copy.function.body = body;
815 copy.function.body.parent = copy.function; 827 copy.function.body.parent = copy.function;
816 return copy; 828 return copy;
817 } 829 }
818 830
819 void addGetterForwarder(Name name, Procedure getter) { 831 void addGetterForwarder(Name name, Procedure getter) {
820 assert(getter.isGetter); 832 assert(getter.isGetter);
821 newClassMembers 833 newClassMembers
822 .add(copyWithBody(getter, forwardToThisProperty(getter))..name = name); 834 .add(copyWithBody(getter, forwardToThisProperty(getter))..name = name);
823 } 835 }
824 836
825 void addTearOffGetter(Name name, Procedure procedure) { 837 void addTearOffMethod(Name name, Procedure procedure) {
826 newClassMembers.add(new Procedure(name, ProcedureKind.Getter, 838 // [addTearOffMethod] generates a method along with a context that captures
827 new FunctionNode(new ReturnStatement(getTearOffExpression(procedure))), 839 // `this`. The work with contexts is typically done using the data gathered
828 fileUri: currentFileUri)); 840 // by a [ClosureInfo] instance. In absence of this information, we need to
829 } 841 // create some variables, like `#self` and `#context`, and manipulate
842 // contexts directly in some cases.
843 //
844 // Also, the tear-off method is generated during a visit to the AST node
845 // of the procedure being torn off, so we need to save and restore some
846 // auxiliary variables like `currentMember` and `currentMemberFunction`
847 // and use [saveContext], so that those variables have proper values when
848 // the procedure itself is being transformed.
849 Member oldCurrentMember = currentMember;
850 FunctionNode oldCurrentMemberFunction = currentMemberFunction;
851 try {
852 saveContext(() {
853 Block body = new Block(<Statement>[]);
854 FunctionNode tearOffMethodFunction = new FunctionNode(body);
855 setupContextForFunctionBody(tearOffMethodFunction);
830 856
831 // TODO(ahe): Remove this method when we don't generate closure classes 857 // We need a variable that refers to `this` to put it into the context.
832 // anymore. 858 VariableDeclaration self = new VariableDeclaration("#self",
833 void addClosureClassNote(Class closureClass) { 859 type: procedure.enclosingClass.rawType);
834 closureClass.addMember(new Field(new Name("note"), 860 context.extend(self, new ThisExpression());
835 type: coreTypes.stringClass.rawType, 861
836 initializer: new StringLiteral( 862 // The `#context` variable is used to access the context in the closed
837 "This is temporary. The VM doesn't need closure classes."), 863 // top-level function that represents the closure and is generated in
838 fileUri: currentFileUri)); 864 // [getTearOffExpression].
865 VariableDeclaration contextVariable = new VariableDeclaration(
866 "#contextParameter",
867 type: const VectorType());
868 Context parent = context;
869 context = context.toNestedContext(
870 new VariableAccessor(contextVariable, null, TreeNode.noOffset));
871
872 body.addStatement(new ReturnStatement(getTearOffExpression(
873 self, procedure, contextVariable, parent.expression)));
874
875 Procedure tearOffMethod = new Procedure(
876 name, ProcedureKind.Method, tearOffMethodFunction,
877 fileUri: currentFileUri);
878 newClassMembers.add(tearOffMethod);
879
880 resetContext();
881 });
882 } finally {
883 currentMember = oldCurrentMember;
884 currentMemberFunction = oldCurrentMemberFunction;
885 }
839 } 886 }
840 } 887 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698