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

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

Powered by Google App Engine
This is Rietveld 408576698