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

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: Follow common pattern for AST nodes with References in ClosureCreation 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
« no previous file with comments | « pkg/kernel/lib/text/ast_to_text.dart ('k') | pkg/kernel/lib/type_checker.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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,
55 transformList; 57 transformList;
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « pkg/kernel/lib/text/ast_to_text.dart ('k') | pkg/kernel/lib/type_checker.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698