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