Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library kernel.transformations.closure.converter; | |
| 6 | |
| 7 import '../../ast.dart' show | |
| 8 Arguments, | |
| 9 Block, | |
| 10 Catch, | |
| 11 Class, | |
| 12 Constructor, | |
| 13 ConstructorInvocation, | |
| 14 DartType, | |
| 15 EmptyStatement, | |
| 16 Expression, | |
| 17 ExpressionStatement, | |
| 18 Field, | |
| 19 FieldInitializer, | |
| 20 ForInStatement, | |
| 21 ForStatement, | |
| 22 FunctionDeclaration, | |
| 23 FunctionExpression, | |
| 24 FunctionNode, | |
| 25 InferredValue, | |
| 26 Initializer, | |
| 27 InterfaceType, | |
| 28 InvalidExpression, | |
| 29 InvocationExpression, | |
| 30 Library, | |
| 31 LocalInitializer, | |
| 32 Member, | |
| 33 MethodInvocation, | |
| 34 Name, | |
| 35 NamedExpression, | |
| 36 NullLiteral, | |
| 37 Procedure, | |
| 38 ProcedureKind, | |
| 39 PropertyGet, | |
| 40 ReturnStatement, | |
| 41 Statement, | |
| 42 StaticGet, | |
| 43 StaticInvocation, | |
| 44 StringLiteral, | |
| 45 Supertype, | |
| 46 ThisExpression, | |
| 47 Transformer, | |
| 48 TreeNode, | |
| 49 TypeParameter, | |
| 50 TypeParameterType, | |
| 51 VariableDeclaration, | |
| 52 VariableGet, | |
| 53 VariableSet, | |
| 54 transformList; | |
| 55 | |
| 56 import '../../frontend/accessors.dart' show | |
| 57 VariableAccessor; | |
| 58 | |
| 59 import '../../clone.dart' show | |
| 60 CloneVisitor; | |
| 61 | |
| 62 import '../../core_types.dart' show | |
| 63 CoreTypes; | |
| 64 | |
| 65 import '../../type_algebra.dart' show | |
| 66 substitute; | |
| 67 | |
| 68 import 'clone_without_body.dart' show | |
| 69 CloneWithoutBody; | |
| 70 | |
| 71 import 'context.dart' show | |
| 72 Context, | |
| 73 NoContext; | |
| 74 | |
| 75 import 'info.dart' show | |
| 76 ClosureInfo; | |
| 77 | |
| 78 class ClosureConverter extends Transformer { | |
| 79 final CoreTypes coreTypes; | |
| 80 final Class contextClass; | |
| 81 final Set<VariableDeclaration> capturedVariables; | |
| 82 final Map<FunctionNode, Set<TypeParameter>> capturedTypeVariables; | |
| 83 final Map<FunctionNode, VariableDeclaration> thisAccess; | |
| 84 final Map<FunctionNode, String> localNames; | |
| 85 | |
| 86 /// Records place-holders for cloning contexts. See [visitForStatement]. | |
| 87 final Set<InvalidExpression> contextClonePlaceHolders = | |
| 88 new Set<InvalidExpression>(); | |
| 89 | |
| 90 /// Maps the names of all instance methods that may be torn off (aka | |
| 91 /// implicitly closurized) to `${name.name}#get`. | |
| 92 final Map<Name, Name> tearOffGetterNames; | |
| 93 | |
| 94 final CloneVisitor cloner = new CloneWithoutBody(); | |
| 95 | |
| 96 /// New members to add to [currentLibrary] after it has been | |
| 97 /// transformed. These members will not be transformed themselves. | |
| 98 final List<TreeNode> newLibraryMembers = <TreeNode>[]; | |
| 99 | |
| 100 /// New members to add to [currentClass] after it has been transformed. These | |
| 101 /// members will not be transformed themselves. | |
| 102 final List<Member> newClassMembers = <Member>[]; | |
| 103 | |
| 104 Library currentLibrary; | |
| 105 | |
| 106 Class currentClass; | |
| 107 | |
| 108 Member currentMember; | |
| 109 | |
| 110 FunctionNode currentMemberFunction; | |
| 111 | |
| 112 FunctionNode currentFunction; | |
| 113 | |
| 114 Block _currentBlock; | |
| 115 | |
| 116 int _insertionIndex = 0; | |
| 117 | |
| 118 Context context; | |
| 119 | |
| 120 /// Maps original type variable (aka type parameter) to a hoisted type | |
| 121 /// variable type. | |
| 122 /// | |
| 123 /// For example, consider: | |
| 124 /// | |
| 125 /// class C<T> { | |
| 126 /// f() => (x) => x is T; | |
| 127 /// } | |
| 128 /// | |
| 129 /// This is currently converted to: | |
| 130 /// | |
| 131 /// class C<T> { | |
| 132 /// f() => new Closure#0<T>(); | |
| 133 /// } | |
| 134 /// class Closure#0<T_> implements Function { | |
| 135 /// call(x) => x is T_; | |
| 136 /// } | |
| 137 /// | |
| 138 /// In this example, `typeSubstitution[T].parameter == T_` when transforming | |
| 139 /// the closure in `f`. | |
| 140 Map<TypeParameter, DartType> typeSubstitution = | |
| 141 const <TypeParameter, DartType>{}; | |
| 142 | |
| 143 ClosureConverter( | |
| 144 this.coreTypes, ClosureInfo info, this.contextClass) | |
| 145 : this.capturedVariables = info.variables, | |
| 146 this.capturedTypeVariables = info.typeVariables, | |
| 147 this.thisAccess = info.thisAccess, | |
| 148 this.localNames = info.localNames, | |
| 149 this.tearOffGetterNames = info.tearOffGetterNames; | |
| 150 | |
| 151 bool get isOuterMostContext { | |
| 152 return currentFunction == null || currentMemberFunction == currentFunction; | |
| 153 } | |
| 154 | |
| 155 String get currentFileUri { | |
| 156 if (currentMember is Constructor) return currentClass.fileUri; | |
| 157 if (currentMember is Field) return (currentMember as Field).fileUri; | |
| 158 if (currentMember is Procedure) return (currentMember as Procedure).fileUri; | |
| 159 throw "No file uri"; | |
| 160 } | |
| 161 | |
| 162 void insert(Statement statement) { | |
| 163 _currentBlock.statements.insert(_insertionIndex++, statement); | |
| 164 statement.parent = _currentBlock; | |
| 165 } | |
| 166 | |
| 167 TreeNode saveContext(TreeNode f()) { | |
| 168 Block savedBlock = _currentBlock; | |
| 169 int savedIndex = _insertionIndex; | |
| 170 Context savedContext = context; | |
| 171 try { | |
| 172 return f(); | |
| 173 } finally { | |
| 174 _currentBlock = savedBlock; | |
| 175 _insertionIndex = savedIndex; | |
| 176 context = savedContext; | |
| 177 } | |
| 178 } | |
| 179 | |
| 180 TreeNode visitLibrary(Library node) { | |
| 181 assert(newLibraryMembers.isEmpty); | |
| 182 if (node == contextClass.enclosingLibrary) return node; | |
| 183 currentLibrary = node; | |
| 184 node = super.visitLibrary(node); | |
| 185 for (TreeNode member in newLibraryMembers) { | |
| 186 if (member is Class) { | |
| 187 node.addClass(member); | |
| 188 } else { | |
| 189 node.addMember(member); | |
| 190 } | |
| 191 } | |
| 192 newLibraryMembers.clear(); | |
| 193 currentLibrary = null; | |
| 194 return node; | |
| 195 } | |
| 196 | |
| 197 TreeNode visitClass(Class node) { | |
| 198 assert(newClassMembers.isEmpty); | |
| 199 currentClass = node; | |
| 200 node = super.visitClass(node); | |
| 201 newClassMembers.forEach(node.addMember); | |
| 202 newClassMembers.clear(); | |
| 203 currentClass = null; | |
| 204 return node; | |
| 205 } | |
| 206 | |
| 207 TreeNode visitConstructor(Constructor node) { | |
| 208 // TODO(ahe): Convert closures in constructors as well. | |
| 209 return node; | |
| 210 } | |
| 211 | |
| 212 Expression handleLocalFunction(FunctionNode function) { | |
| 213 FunctionNode enclosingFunction = currentFunction; | |
| 214 Map<TypeParameter, DartType> enclosingTypeSubstitution = | |
| 215 typeSubstitution; | |
| 216 currentFunction = function; | |
| 217 Statement body = function.body; | |
| 218 assert(body != null); | |
| 219 | |
| 220 if (body is Block) { | |
| 221 _currentBlock = body; | |
| 222 } else { | |
| 223 _currentBlock = new Block(<Statement>[body]); | |
| 224 function.body = body.parent = _currentBlock; | |
| 225 } | |
| 226 _insertionIndex = 0; | |
| 227 | |
| 228 VariableDeclaration contextVariable = new VariableDeclaration( | |
| 229 "#contextParameter", type: contextClass.rawType, isFinal: true); | |
| 230 Context parent = context; | |
| 231 context = context.toNestedContext(new VariableAccessor(contextVariable)); | |
| 232 | |
| 233 Set<TypeParameter> captured = capturedTypeVariables[currentFunction]; | |
| 234 if (captured != null) { | |
| 235 typeSubstitution = copyTypeVariables(captured); | |
| 236 } else { | |
| 237 typeSubstitution = const <TypeParameter, DartType>{}; | |
| 238 } | |
| 239 | |
| 240 function.transformChildren(this); | |
| 241 | |
| 242 Expression result = addClosure(function, contextVariable, parent.expression, | |
| 243 typeSubstitution, enclosingTypeSubstitution); | |
| 244 currentFunction = enclosingFunction; | |
| 245 typeSubstitution = enclosingTypeSubstitution; | |
| 246 return result; | |
| 247 } | |
| 248 | |
| 249 TreeNode visitFunctionDeclaration(FunctionDeclaration node) { | |
| 250 /// Is this closure itself captured by a closure? | |
| 251 bool isCaptured = capturedVariables.contains(node.variable); | |
| 252 if (isCaptured) { | |
| 253 context.extend(node.variable, new InvalidExpression()); | |
| 254 } | |
| 255 Context parent = context; | |
| 256 return saveContext(() { | |
| 257 Expression expression = handleLocalFunction(node.function); | |
| 258 | |
| 259 if (isCaptured) { | |
| 260 parent.update(node.variable, expression); | |
| 261 return null; | |
| 262 } else { | |
| 263 node.variable.initializer = expression; | |
| 264 expression.parent = node.variable; | |
| 265 return node.variable; | |
| 266 } | |
| 267 }); | |
| 268 } | |
| 269 | |
| 270 TreeNode visitFunctionExpression(FunctionExpression node) => saveContext(() { | |
| 271 return handleLocalFunction(node.function); | |
| 272 }); | |
| 273 | |
| 274 /// Add a new class to the current library that looks like this: | |
| 275 /// | |
| 276 /// class Closure#0 extends core::Object implements core::Function { | |
| 277 /// field _in::Context context; | |
| 278 /// constructor •(final _in::Context #t1) → dynamic | |
| 279 /// : self::Closure 0::context = #t1 | |
| 280 /// ; | |
| 281 /// method call(/* The parameters of [function] */) → dynamic { | |
| 282 /// /// #t2 is [contextVariable]. | |
| 283 /// final _in::Context #t2 = this.{self::Closure#0::context}; | |
| 284 /// /* The body of [function]. */ | |
| 285 /// } | |
| 286 /// } | |
| 287 /// | |
| 288 /// Returns a constructor call to invoke the above constructor. | |
| 289 /// | |
| 290 /// TODO(ahe): We shouldn't create a class for each closure. Instead we turn | |
| 291 /// [function] into a top-level function and use the Dart VM's mechnism for | |
| 292 /// closures. | |
| 293 Expression addClosure( | |
| 294 FunctionNode function, | |
| 295 VariableDeclaration contextVariable, | |
| 296 Expression accessContext, | |
| 297 Map<TypeParameter, DartType> substitution, | |
| 298 Map<TypeParameter, DartType> enclosingTypeSubstitution) { | |
| 299 Field contextField = new Field( | |
| 300 // TODO(ahe): Rename to #context. | |
| 301 new Name("context"), type: contextClass.rawType, | |
| 302 fileUri: currentFileUri); | |
| 303 Class closureClass = createClosureClass(function, fields: [contextField], | |
| 304 substitution: substitution); | |
| 305 closureClass.addMember( | |
| 306 new Procedure(new Name("call"), ProcedureKind.Method, function, | |
| 307 fileUri: currentFileUri)); | |
| 308 newLibraryMembers.add(closureClass); | |
| 309 Statement note = new ExpressionStatement(new StringLiteral( | |
| 310 "This is a temporary solution. " | |
| 311 "In the VM, this will become an additional parameter.")); | |
| 312 List<Statement> statements = <Statement>[note, contextVariable]; | |
| 313 Statement body = function.body; | |
| 314 if (body is Block) { | |
| 315 statements.addAll(body.statements); | |
| 316 } else { | |
| 317 statements.add(body); | |
| 318 } | |
| 319 function.body = new Block(statements); | |
| 320 function.body.parent = function; | |
| 321 contextVariable.initializer = new PropertyGet(new ThisExpression(), | |
| 322 contextField.name, contextField); | |
| 323 contextVariable.initializer.parent = contextVariable; | |
| 324 return new ConstructorInvocation(closureClass.constructors.single, | |
| 325 new Arguments( | |
| 326 <Expression>[accessContext], | |
| 327 types: new List<DartType>.from( | |
| 328 substitution.keys.map( | |
| 329 (TypeParameter t) { | |
| 330 return substitute( | |
| 331 new TypeParameterType(t), enclosingTypeSubstitution); | |
| 332 })))); | |
| 333 } | |
| 334 | |
| 335 TreeNode visitField(Field node) { | |
| 336 currentMember = node; | |
| 337 context = new NoContext(this); | |
| 338 if (node.isInstanceMember) { | |
| 339 Name tearOffName = tearOffGetterNames[node.name]; | |
| 340 if (tearOffName != null) { | |
| 341 // TODO(ahe): If we rewrite setters, we can rename the field to avoid | |
| 342 // an indirection in most cases. | |
| 343 addFieldForwarder(tearOffName, node); | |
| 344 } | |
| 345 } | |
| 346 node = super.visitField(node); | |
| 347 context = null; | |
| 348 currentMember = null; | |
| 349 return node; | |
| 350 } | |
| 351 | |
| 352 TreeNode visitProcedure(Procedure node) { | |
| 353 currentMember = node; | |
| 354 assert(_currentBlock == null); | |
| 355 assert(_insertionIndex == 0); | |
| 356 assert(context == null); | |
| 357 | |
| 358 Statement body = node.function.body; | |
| 359 | |
| 360 if (node.isInstanceMember) { | |
| 361 Name tearOffName = tearOffGetterNames[node.name]; | |
| 362 if (tearOffName != null) { | |
| 363 if (node.isGetter) { | |
| 364 // We rename the getter to avoid an indirection in most cases. | |
| 365 Name oldName = node.name; | |
| 366 node.name = tearOffName; | |
| 367 addGetterForwarder(oldName, node); | |
| 368 } else if (node.kind == ProcedureKind.Method) { | |
| 369 addTearOffGetter(tearOffName, node); | |
| 370 } | |
| 371 } | |
| 372 } | |
| 373 | |
| 374 if (body == null) return node; | |
| 375 | |
| 376 currentMemberFunction = node.function; | |
| 377 | |
| 378 // Ensure that the body is a block which becomes the current block. | |
| 379 if (body is Block) { | |
| 380 _currentBlock = body; | |
| 381 } else { | |
| 382 _currentBlock = new Block(<Statement>[body]); | |
| 383 node.function.body = body.parent = _currentBlock; | |
| 384 } | |
| 385 _insertionIndex = 0; | |
| 386 | |
| 387 // Start with no context. This happens after setting up _currentBlock | |
| 388 // so statements can be emitted into _currentBlock if necessary. | |
| 389 context = new NoContext(this); | |
| 390 | |
| 391 VariableDeclaration self = thisAccess[currentMemberFunction]; | |
| 392 if (self != null) { | |
| 393 context.extend(self, new ThisExpression()); | |
| 394 } | |
| 395 | |
| 396 node.transformChildren(this); | |
| 397 | |
| 398 _currentBlock = null; | |
| 399 _insertionIndex = 0; | |
| 400 context = null; | |
| 401 currentMemberFunction = null; | |
| 402 currentMember = null; | |
| 403 return node; | |
| 404 } | |
| 405 | |
| 406 TreeNode visitLocalInitializer(LocalInitializer node) { | |
| 407 assert(!capturedVariables.contains(node.variable)); | |
| 408 node.transformChildren(this); | |
| 409 return node; | |
| 410 } | |
| 411 | |
| 412 TreeNode visitFunctionNode(FunctionNode node) { | |
| 413 transformList(node.typeParameters, this, node); | |
| 414 | |
| 415 void extend(VariableDeclaration parameter) { | |
| 416 context.extend(parameter, new VariableGet(parameter)); | |
| 417 } | |
| 418 // TODO: Can parameters contain initializers (e.g., for optional ones) that | |
| 419 // need to be closure converted? | |
| 420 node.positionalParameters.where(capturedVariables.contains).forEach(extend); | |
| 421 node.namedParameters.where(capturedVariables.contains).forEach(extend); | |
| 422 | |
| 423 assert(node.body != null); | |
| 424 node.body = node.body.accept(this); | |
| 425 node.body.parent = node; | |
| 426 return node; | |
| 427 } | |
| 428 | |
| 429 TreeNode visitBlock(Block node) => saveContext(() { | |
| 430 if (_currentBlock != node) { | |
| 431 _currentBlock = node; | |
| 432 _insertionIndex = 0; | |
| 433 } | |
| 434 | |
| 435 while (_insertionIndex < _currentBlock.statements.length) { | |
| 436 assert(_currentBlock == node); | |
| 437 | |
| 438 var original = _currentBlock.statements[_insertionIndex]; | |
| 439 var transformed = original.accept(this); | |
| 440 assert(_currentBlock.statements[_insertionIndex] == original); | |
| 441 if (transformed == null) { | |
| 442 _currentBlock.statements.removeAt(_insertionIndex); | |
| 443 } else { | |
| 444 _currentBlock.statements[_insertionIndex++] = transformed; | |
| 445 transformed.parent = _currentBlock; | |
| 446 } | |
| 447 } | |
| 448 | |
| 449 return node; | |
| 450 }); | |
| 451 | |
| 452 TreeNode visitVariableDeclaration(VariableDeclaration node) { | |
| 453 node.transformChildren(this); | |
| 454 | |
| 455 if (!capturedVariables.contains(node)) return node; | |
| 456 context.extend(node, node.initializer ?? new NullLiteral()); | |
| 457 | |
| 458 if (node.parent == currentFunction) return node; | |
| 459 if (node.parent is Block) { | |
| 460 // When returning null, the parent block will remove this node from its | |
| 461 // list of statements. | |
| 462 // TODO(ahe): I'd like to avoid testing on the parent pointer. | |
| 463 return null; | |
| 464 } | |
| 465 throw "Unexpected parent for $node: ${node.parent.parent}"; | |
| 466 } | |
| 467 | |
| 468 TreeNode visitVariableGet(VariableGet node) { | |
| 469 return capturedVariables.contains(node.variable) | |
| 470 ? context.lookup(node.variable) | |
| 471 : node; | |
| 472 } | |
| 473 | |
| 474 TreeNode visitVariableSet(VariableSet node) { | |
| 475 node.transformChildren(this); | |
| 476 | |
| 477 return capturedVariables.contains(node.variable) | |
| 478 ? context.assign(node.variable, node.value, | |
| 479 voidContext: isInVoidContext(node)) | |
| 480 : node; | |
| 481 } | |
| 482 | |
| 483 bool isInVoidContext(Expression node) { | |
| 484 TreeNode parent = node.parent; | |
| 485 return parent is ExpressionStatement || | |
| 486 parent is ForStatement && parent.condition != node; | |
| 487 } | |
| 488 | |
| 489 DartType visitDartType(DartType node) { | |
| 490 return substitute(node, typeSubstitution); | |
| 491 } | |
| 492 | |
| 493 VariableDeclaration getReplacementLoopVariable(VariableDeclaration variable) { | |
| 494 VariableDeclaration newVariable = new VariableDeclaration( | |
| 495 variable.name, initializer: variable.initializer, | |
| 496 type: variable.type) | |
| 497 ..flags = variable.flags; | |
| 498 variable.initializer = new VariableGet(newVariable); | |
| 499 variable.initializer.parent = variable; | |
| 500 return newVariable; | |
| 501 } | |
| 502 | |
| 503 Expression cloneContext() { | |
| 504 InvalidExpression placeHolder = new InvalidExpression(); | |
| 505 contextClonePlaceHolders.add(placeHolder); | |
| 506 return placeHolder; | |
| 507 } | |
| 508 | |
| 509 TreeNode visitInvalidExpression(InvalidExpression node) { | |
| 510 return contextClonePlaceHolders.remove(node) ? context.clone() : node; | |
| 511 } | |
| 512 | |
| 513 TreeNode visitForStatement(ForStatement node) { | |
| 514 if (node.variables.any(capturedVariables.contains)) { | |
| 515 // In Dart, loop variables are new variables on each iteration of the | |
| 516 // loop. This is only observable when a loop variable is captured by a | |
| 517 // closure, which is the situation we're in here. So we transform the | |
| 518 // loop. | |
| 519 // | |
| 520 // Consider the following example, where `x` is `node.variables.first`, | |
| 521 // and `#t1` is a temporary variable: | |
| 522 // | |
| 523 // for (var x = 0; x < 10; x++) body; | |
| 524 // | |
| 525 // This is transformed to: | |
| 526 // | |
| 527 // { | |
| 528 // var x = 0; | |
| 529 // for (; x < 10; clone-context, x++) body; | |
| 530 // } | |
| 531 // | |
| 532 // `clone-context` is a place-holder that will later be replaced by an | |
| 533 // expression that clones the current closure context (see | |
| 534 // [visitInvalidExpression]). | |
| 535 return saveContext(() { | |
| 536 context = context.toNestedContext(); | |
| 537 List<Statement> statements = <Statement>[]; | |
| 538 statements.addAll(node.variables); | |
| 539 statements.add(node); | |
| 540 node.variables.clear(); | |
| 541 node.updates.insert(0, cloneContext()); | |
| 542 _currentBlock = new Block(statements); | |
| 543 _insertionIndex = 0; | |
| 544 return _currentBlock.accept(this); | |
| 545 }); | |
| 546 } | |
| 547 return super.visitForStatement(node); | |
| 548 } | |
| 549 | |
| 550 TreeNode visitForInStatement(ForInStatement node) { | |
| 551 if (capturedVariables.contains(node.variable)) { | |
| 552 // In Dart, loop variables are new variables on each iteration of the | |
| 553 // loop. This is only observable when the loop variable is captured by a | |
| 554 // closure, so we need to transform the for-in loop when `node.variable` | |
| 555 // is captured. | |
| 556 // | |
| 557 // Consider the following example, where `x` is `node.variable`, and | |
| 558 // `#t1` is a temporary variable: | |
| 559 // | |
| 560 // for (var x in expr) body; | |
| 561 // | |
| 562 // Notice that we can assume that `x` doesn't have an initializer based | |
| 563 // on invariants in the Kernel AST. This is transformed to: | |
| 564 // | |
| 565 // for (var #t1 in expr) { var x = #t1; body; } | |
| 566 // | |
| 567 // After this, we call super to apply the normal closure conversion to | |
| 568 // the transformed for-in loop. | |
| 569 VariableDeclaration variable = node.variable; | |
| 570 VariableDeclaration newVariable = getReplacementLoopVariable(variable); | |
| 571 node.variable = newVariable; | |
| 572 newVariable.parent = node; | |
| 573 node.body = new Block(<Statement>[variable, node.body]); | |
| 574 node.body.parent = node; | |
| 575 } | |
| 576 return super.visitForInStatement(node); | |
| 577 } | |
| 578 | |
| 579 TreeNode visitThisExpression(ThisExpression node) { | |
| 580 return isOuterMostContext | |
| 581 ? node : context.lookup(thisAccess[currentMemberFunction]); | |
| 582 } | |
| 583 | |
| 584 TreeNode visitStaticGet(StaticGet node) { | |
| 585 Member target = node.target; | |
| 586 if (target is Procedure && target.kind == ProcedureKind.Method) { | |
| 587 Expression expression = getTearOffExpression(node.target); | |
| 588 expression.transformChildren(this); | |
| 589 return expression; | |
| 590 } | |
| 591 return super.visitStaticGet(node); | |
| 592 } | |
| 593 | |
| 594 TreeNode visitPropertyGet(PropertyGet node) { | |
| 595 Name tearOffName = tearOffGetterNames[node.name]; | |
| 596 if (tearOffName != null) { | |
| 597 node.name = tearOffName; | |
| 598 } | |
| 599 return super.visitPropertyGet(node); | |
| 600 } | |
| 601 | |
| 602 TreeNode visitCatch(Catch node) { | |
| 603 VariableDeclaration exception = node.exception; | |
| 604 VariableDeclaration stackTrace = node.stackTrace; | |
| 605 if (stackTrace != null && capturedVariables.contains(stackTrace)) { | |
| 606 Block block = node.body = ensureBlock(node.body); | |
| 607 block.parent = node; | |
| 608 node.stackTrace = new VariableDeclaration(null); | |
| 609 node.stackTrace.parent = node; | |
| 610 stackTrace.initializer = new VariableGet(node.stackTrace); | |
| 611 block.statements.insert(0, stackTrace); | |
| 612 stackTrace.parent = block; | |
| 613 } | |
| 614 if (exception != null && capturedVariables.contains(exception)) { | |
| 615 Block block = node.body = ensureBlock(node.body); | |
| 616 block.parent = node; | |
| 617 node.exception = new VariableDeclaration(null); | |
| 618 node.exception.parent = node; | |
| 619 exception.initializer = new VariableGet(node.exception); | |
| 620 block.statements.insert(0, exception); | |
| 621 exception.parent = block; | |
| 622 } | |
| 623 return super.visitCatch(node); | |
| 624 } | |
| 625 | |
| 626 Block ensureBlock(Statement statement) { | |
| 627 return statement is Block ? statement : new Block(<Statement>[statement]); | |
| 628 } | |
| 629 | |
| 630 /// Creates a closure that will invoke [procedure] and return an expression | |
| 631 /// that instantiates that closure. | |
| 632 Expression getTearOffExpression(Procedure procedure) { | |
| 633 Map<TypeParameter, DartType> substitution = procedure.isInstanceMember | |
| 634 // Note: we do not attempt to avoid copying type variables that aren't | |
| 635 // used in the signature of [procedure]. It might be more economical to | |
| 636 // only copy type variables that are used. However, we assume that | |
| 637 // passing type arguments that match the enclosing class' type | |
| 638 // variables will be handled most efficiently. | |
| 639 ? copyTypeVariables(procedure.enclosingClass.typeParameters) | |
| 640 : const <TypeParameter, DartType>{}; | |
| 641 Expression receiver = null; | |
| 642 List<Field> fields = null; | |
| 643 if (procedure.isInstanceMember) { | |
| 644 // TODO(ahe): Rename to #self. | |
| 645 Field self = new Field(new Name("self"), fileUri: currentFileUri); | |
| 646 self.type = substitute(procedure.enclosingClass.thisType, substitution); | |
| 647 fields = <Field>[self]; | |
| 648 receiver = new PropertyGet(new ThisExpression(), self.name, self); | |
| 649 } | |
| 650 Class closureClass = createClosureClass(procedure.function, fields: fields, | |
| 651 substitution: substitution); | |
| 652 closureClass.addMember( | |
| 653 new Procedure(new Name("call"), ProcedureKind.Method, | |
| 654 forwardFunction(procedure, receiver, substitution), | |
| 655 fileUri: currentFileUri)); | |
| 656 newLibraryMembers.add(closureClass); | |
| 657 Arguments constructorArguments = procedure.isInstanceMember | |
| 658 ? new Arguments(<Expression>[new ThisExpression()]) | |
| 659 : new Arguments.empty(); | |
| 660 if (substitution.isNotEmpty) { | |
| 661 constructorArguments.types.addAll( | |
| 662 procedure.enclosingClass.thisType.typeArguments); | |
| 663 } | |
| 664 return new ConstructorInvocation( | |
| 665 closureClass.constructors.single, constructorArguments); | |
| 666 } | |
| 667 | |
| 668 /// Creates a function that has the same signature as `procedure.function` | |
| 669 /// and which forwards all arguments to `procedure`. | |
| 670 FunctionNode forwardFunction(Procedure procedure, Expression receiver, | |
| 671 Map<TypeParameter, DartType> substitution) { | |
| 672 CloneVisitor cloner = substitution.isEmpty | |
| 673 ? this.cloner | |
| 674 : new CloneWithoutBody(typeSubstitution: substitution); | |
| 675 FunctionNode function = procedure.function; | |
| 676 List<TypeParameter> typeParameters = | |
| 677 function.typeParameters.map(cloner.clone).toList(); | |
| 678 List<VariableDeclaration> positionalParameters = | |
| 679 function.positionalParameters.map(cloner.clone).toList(); | |
| 680 List<VariableDeclaration> namedParameters = | |
| 681 function.namedParameters.map(cloner.clone).toList(); | |
| 682 // TODO(ahe): Clone or copy inferredReturnValue? | |
| 683 InferredValue inferredReturnValue = null; | |
| 684 | |
| 685 List<DartType> types = typeParameters.map( | |
| 686 (TypeParameter parameter) => new TypeParameterType(parameter)).toList(); | |
| 687 List<Expression> positional = positionalParameters.map( | |
| 688 (VariableDeclaration parameter) => new VariableGet(parameter)).toList(); | |
| 689 List<NamedExpression> named = namedParameters.map( | |
| 690 (VariableDeclaration parameter) { | |
| 691 return new NamedExpression( | |
| 692 parameter.name, new VariableGet(parameter)); | |
| 693 }).toList(); | |
| 694 | |
| 695 Arguments arguments = new Arguments(positional, types: types, named: named); | |
| 696 InvocationExpression invocation = procedure.isInstanceMember | |
| 697 ? new MethodInvocation(receiver, procedure.name, arguments, procedure) | |
| 698 : new StaticInvocation(procedure, arguments); | |
| 699 return new FunctionNode( | |
| 700 new ReturnStatement(invocation), | |
| 701 typeParameters: typeParameters, | |
| 702 positionalParameters: positionalParameters, | |
| 703 namedParameters: namedParameters, | |
| 704 requiredParameterCount: function.requiredParameterCount, | |
| 705 returnType: substitute(function.returnType, substitution), | |
| 706 inferredReturnValue: inferredReturnValue); | |
| 707 } | |
| 708 | |
| 709 /// Creates copies of the type variables in [original] and returns a | |
| 710 /// substitution that can be passed to [substitute] to substitute all uses of | |
| 711 /// [original] with their copies. | |
| 712 Map<TypeParameter, DartType> copyTypeVariables( | |
| 713 Iterable<TypeParameter> original) { | |
| 714 if (original.isEmpty) return const <TypeParameter, DartType>{}; | |
| 715 Map<TypeParameter, DartType> substitution = <TypeParameter, DartType>{}; | |
| 716 for (TypeParameter t in original) { | |
| 717 substitution[t] = new TypeParameterType(new TypeParameter(t.name)); | |
| 718 } | |
| 719 substitution.forEach((TypeParameter t, TypeParameterType copy) { | |
| 720 copy.parameter.bound = substitute(t.bound, substitution); | |
| 721 }); | |
| 722 return substitution; | |
| 723 } | |
| 724 | |
| 725 Class createClosureClass(FunctionNode function, | |
| 726 {List<Field> fields, Map<TypeParameter, DartType> substitution}) { | |
| 727 List<TypeParameter> typeParameters = new List<TypeParameter>.from( | |
| 728 substitution.values.map((TypeParameterType t) => t.parameter)); | |
| 729 Class closureClass = new Class( | |
| 730 name: 'Closure#${localNames[function]}', | |
| 731 supertype: new Supertype(coreTypes.objectClass, const <DartType>[]), | |
| 732 typeParameters: typeParameters, | |
| 733 implementedTypes: <Supertype>[new Supertype(coreTypes.functionClass, con st <DartType>[])], | |
|
asgerf
2016/12/09 10:57:58
Long line.
Could you please dartfmt the whole thi
| |
| 734 fileUri: currentFileUri); | |
| 735 addClosureClassNote(closureClass); | |
| 736 | |
| 737 List<VariableDeclaration> parameters = <VariableDeclaration>[]; | |
| 738 List<Initializer> initializers = <Initializer>[]; | |
| 739 for (Field field in fields ?? const <Field>[]) { | |
| 740 closureClass.addMember(field); | |
| 741 VariableDeclaration parameter = new VariableDeclaration( | |
| 742 field.name.name, type: field.type, isFinal: true); | |
| 743 parameters.add(parameter); | |
| 744 initializers.add(new FieldInitializer(field, new VariableGet(parameter))); | |
| 745 } | |
| 746 | |
| 747 closureClass.addMember( | |
| 748 new Constructor( | |
| 749 new FunctionNode( | |
| 750 new EmptyStatement(), positionalParameters: parameters), | |
| 751 name: new Name(""), | |
| 752 initializers: initializers)); | |
| 753 | |
| 754 return closureClass; | |
| 755 } | |
| 756 | |
| 757 Statement forwardToThisProperty(Member node) { | |
| 758 assert(node is Field || (node is Procedure && node.isGetter)); | |
| 759 return new ReturnStatement( | |
| 760 new PropertyGet(new ThisExpression(), node.name, node)); | |
| 761 } | |
| 762 | |
| 763 void addFieldForwarder(Name name, Field field) { | |
| 764 newClassMembers.add( | |
| 765 new Procedure(name, ProcedureKind.Getter, | |
| 766 new FunctionNode(forwardToThisProperty(field)), | |
| 767 fileUri: currentFileUri)); | |
| 768 } | |
| 769 | |
| 770 Procedure copyWithBody(Procedure procedure, Statement body) { | |
| 771 Procedure copy = cloner.clone(procedure); | |
| 772 copy.function.body = body; | |
| 773 copy.function.body.parent = copy.function; | |
| 774 return copy; | |
| 775 } | |
| 776 | |
| 777 void addGetterForwarder(Name name, Procedure getter) { | |
| 778 assert(getter.isGetter); | |
| 779 newClassMembers.add( | |
| 780 copyWithBody(getter, forwardToThisProperty(getter))..name = name); | |
| 781 } | |
| 782 | |
| 783 void addTearOffGetter(Name name, Procedure procedure) { | |
| 784 newClassMembers.add( | |
| 785 new Procedure(name, ProcedureKind.Getter, | |
| 786 new FunctionNode( | |
| 787 new ReturnStatement(getTearOffExpression(procedure))), | |
| 788 fileUri: currentFileUri)); | |
| 789 } | |
| 790 | |
| 791 // TODO(ahe): Remove this method when we don't generate closure classes | |
| 792 // anymore. | |
| 793 void addClosureClassNote(Class closureClass) { | |
| 794 closureClass.addMember( | |
| 795 new Field(new Name("note"), type: coreTypes.stringClass.rawType, | |
| 796 initializer: new StringLiteral( | |
| 797 "This is temporary. The VM doesn't need closure classes."), | |
| 798 fileUri: currentFileUri)); | |
| 799 } | |
| 800 } | |
| OLD | NEW |