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