| 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, |
| (...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 140 this.tearOffGetterNames = info.tearOffGetterNames; | 140 this.tearOffGetterNames = info.tearOffGetterNames; |
| 141 | 141 |
| 142 bool get isOuterMostContext { | 142 bool get isOuterMostContext { |
| 143 return currentFunction == null || currentMemberFunction == currentFunction; | 143 return currentFunction == null || currentMemberFunction == currentFunction; |
| 144 } | 144 } |
| 145 | 145 |
| 146 String get currentFileUri { | 146 String get currentFileUri { |
| 147 if (currentMember is Constructor) return currentClass.fileUri; | 147 if (currentMember is Constructor) return currentClass.fileUri; |
| 148 if (currentMember is Field) return (currentMember as Field).fileUri; | 148 if (currentMember is Field) return (currentMember as Field).fileUri; |
| 149 if (currentMember is Procedure) return (currentMember as Procedure).fileUri; | 149 if (currentMember is Procedure) return (currentMember as Procedure).fileUri; |
| 150 throw "No file uri"; | 150 throw "No file uri for ${currentMember.runtimeType}"; |
| 151 } | 151 } |
| 152 | 152 |
| 153 void insert(Statement statement) { | 153 void insert(Statement statement) { |
| 154 _currentBlock.statements.insert(_insertionIndex++, statement); | 154 _currentBlock.statements.insert(_insertionIndex++, statement); |
| 155 statement.parent = _currentBlock; | 155 statement.parent = _currentBlock; |
| 156 } | 156 } |
| 157 | 157 |
| 158 TreeNode saveContext(TreeNode f()) { | 158 TreeNode saveContext(TreeNode f()) { |
| 159 Block savedBlock = _currentBlock; | 159 Block savedBlock = _currentBlock; |
| 160 int savedIndex = _insertionIndex; | 160 int savedIndex = _insertionIndex; |
| 161 Context savedContext = context; | 161 Context savedContext = context; |
| 162 try { | 162 try { |
| 163 return f(); | 163 return f(); |
| 164 } finally { | 164 } finally { |
| 165 _currentBlock = savedBlock; | 165 _currentBlock = savedBlock; |
| 166 _insertionIndex = savedIndex; | 166 _insertionIndex = savedIndex; |
| 167 context = savedContext; | 167 context = savedContext; |
| 168 } | 168 } |
| 169 } | 169 } |
| 170 | 170 |
| 171 TreeNode visitLibrary(Library node) { | 171 TreeNode visitLibrary(Library node) { |
| 172 assert(newLibraryMembers.isEmpty); | 172 assert(newLibraryMembers.isEmpty); |
| 173 if (node == contextClass.enclosingLibrary) return node; | 173 if (node == contextClass.enclosingLibrary) return node; |
| 174 |
| 174 currentLibrary = node; | 175 currentLibrary = node; |
| 175 node = super.visitLibrary(node); | 176 node = super.visitLibrary(node); |
| 176 for (TreeNode member in newLibraryMembers) { | 177 for (TreeNode member in newLibraryMembers) { |
| 177 if (member is Class) { | 178 if (member is Class) { |
| 178 node.addClass(member); | 179 node.addClass(member); |
| 179 } else { | 180 } else { |
| 180 node.addMember(member); | 181 node.addMember(member); |
| 181 } | 182 } |
| 182 } | 183 } |
| 183 newLibraryMembers.clear(); | 184 newLibraryMembers.clear(); |
| 184 currentLibrary = null; | 185 currentLibrary = null; |
| 185 return node; | 186 return node; |
| 186 } | 187 } |
| 187 | 188 |
| 188 TreeNode visitClass(Class node) { | 189 TreeNode visitClass(Class node) { |
| 189 assert(newClassMembers.isEmpty); | 190 assert(newClassMembers.isEmpty); |
| 190 currentClass = node; | 191 currentClass = node; |
| 191 node = super.visitClass(node); | 192 node = super.visitClass(node); |
| 192 newClassMembers.forEach(node.addMember); | 193 newClassMembers.forEach(node.addMember); |
| 193 newClassMembers.clear(); | 194 newClassMembers.clear(); |
| 194 currentClass = null; | 195 currentClass = null; |
| 195 return node; | 196 return node; |
| 196 } | 197 } |
| 197 | 198 |
| 198 TreeNode visitConstructor(Constructor node) { | 199 TreeNode visitConstructor(Constructor node) { |
| 199 // TODO(ahe): Convert closures in constructors as well. | 200 assert(isEmptyContext); |
| 201 |
| 202 currentMember = node; |
| 203 |
| 204 FunctionNode function = node.function; |
| 205 if (function.body != null && function.body is! EmptyStatement) { |
| 206 setupContextForFunctionBody(function); |
| 207 VariableDeclaration self = thisAccess[currentMemberFunction]; |
| 208 // TODO(karlklose): transform initializers |
| 209 if (self != null) { |
| 210 context.extend(self, new ThisExpression()); |
| 211 } |
| 212 node.function.accept(this); |
| 213 resetContext(); |
| 214 } |
| 215 |
| 200 return node; | 216 return node; |
| 201 } | 217 } |
| 202 | 218 |
| 203 Expression handleLocalFunction(FunctionNode function) { | 219 Expression handleLocalFunction(FunctionNode function) { |
| 204 FunctionNode enclosingFunction = currentFunction; | 220 FunctionNode enclosingFunction = currentFunction; |
| 205 Map<TypeParameter, DartType> enclosingTypeSubstitution = typeSubstitution; | 221 Map<TypeParameter, DartType> enclosingTypeSubstitution = typeSubstitution; |
| 206 currentFunction = function; | 222 currentFunction = function; |
| 207 Statement body = function.body; | 223 Statement body = function.body; |
| 208 assert(body != null); | 224 assert(body != null); |
| 209 | 225 |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 334 addFieldForwarder(tearOffName, node); | 350 addFieldForwarder(tearOffName, node); |
| 335 } | 351 } |
| 336 } | 352 } |
| 337 node = super.visitField(node); | 353 node = super.visitField(node); |
| 338 context = null; | 354 context = null; |
| 339 currentMember = null; | 355 currentMember = null; |
| 340 return node; | 356 return node; |
| 341 } | 357 } |
| 342 | 358 |
| 343 TreeNode visitProcedure(Procedure node) { | 359 TreeNode visitProcedure(Procedure node) { |
| 360 assert(isEmptyContext); |
| 361 |
| 344 currentMember = node; | 362 currentMember = node; |
| 345 assert(_currentBlock == null); | |
| 346 assert(_insertionIndex == 0); | |
| 347 assert(context == null); | |
| 348 | |
| 349 Statement body = node.function.body; | |
| 350 | 363 |
| 351 if (node.isInstanceMember) { | 364 if (node.isInstanceMember) { |
| 352 Name tearOffName = tearOffGetterNames[node.name]; | 365 Name tearOffName = tearOffGetterNames[node.name]; |
| 353 if (tearOffName != null) { | 366 if (tearOffName != null) { |
| 354 if (node.isGetter) { | 367 if (node.isGetter) { |
| 355 // We rename the getter to avoid an indirection in most cases. | 368 // We rename the getter to avoid an indirection in most cases. |
| 356 Name oldName = node.name; | 369 Name oldName = node.name; |
| 357 node.name = tearOffName; | 370 node.name = tearOffName; |
| 358 addGetterForwarder(oldName, node); | 371 addGetterForwarder(oldName, node); |
| 359 } else if (node.kind == ProcedureKind.Method) { | 372 } else if (node.kind == ProcedureKind.Method) { |
| 360 addTearOffGetter(tearOffName, node); | 373 addTearOffGetter(tearOffName, node); |
| 361 } | 374 } |
| 362 } | 375 } |
| 363 } | 376 } |
| 364 | 377 |
| 365 if (body == null) return node; | 378 FunctionNode function = node.function; |
| 379 if (function.body != null) { |
| 380 setupContextForFunctionBody(function); |
| 381 VariableDeclaration self = thisAccess[currentMemberFunction]; |
| 382 if (self != null) { |
| 383 context.extend(self, new ThisExpression()); |
| 384 } |
| 385 node.transformChildren(this); |
| 386 resetContext(); |
| 387 } |
| 366 | 388 |
| 367 currentMemberFunction = node.function; | 389 return node; |
| 390 } |
| 368 | 391 |
| 392 void setupContextForFunctionBody(FunctionNode function) { |
| 393 Statement body = function.body; |
| 394 assert(body != null); |
| 395 currentMemberFunction = function; |
| 369 // Ensure that the body is a block which becomes the current block. | 396 // Ensure that the body is a block which becomes the current block. |
| 370 if (body is Block) { | 397 if (body is Block) { |
| 371 _currentBlock = body; | 398 _currentBlock = body; |
| 372 } else { | 399 } else { |
| 373 _currentBlock = new Block(<Statement>[body]); | 400 _currentBlock = new Block(<Statement>[body]); |
| 374 node.function.body = body.parent = _currentBlock; | 401 function.body = body.parent = _currentBlock; |
| 375 } | 402 } |
| 376 _insertionIndex = 0; | 403 _insertionIndex = 0; |
| 377 | |
| 378 // Start with no context. This happens after setting up _currentBlock | 404 // Start with no context. This happens after setting up _currentBlock |
| 379 // so statements can be emitted into _currentBlock if necessary. | 405 // so statements can be emitted into _currentBlock if necessary. |
| 380 context = new NoContext(this); | 406 context = new NoContext(this); |
| 407 } |
| 381 | 408 |
| 382 VariableDeclaration self = thisAccess[currentMemberFunction]; | 409 void resetContext() { |
| 383 if (self != null) { | |
| 384 context.extend(self, new ThisExpression()); | |
| 385 } | |
| 386 | |
| 387 node.transformChildren(this); | |
| 388 | |
| 389 _currentBlock = null; | 410 _currentBlock = null; |
| 390 _insertionIndex = 0; | 411 _insertionIndex = 0; |
| 391 context = null; | 412 context = null; |
| 392 currentMemberFunction = null; | 413 currentMemberFunction = null; |
| 393 currentMember = null; | 414 currentMember = null; |
| 394 return node; | 415 } |
| 416 |
| 417 bool get isEmptyContext { |
| 418 return _currentBlock == null && _insertionIndex == 0 && context == null; |
| 395 } | 419 } |
| 396 | 420 |
| 397 TreeNode visitLocalInitializer(LocalInitializer node) { | 421 TreeNode visitLocalInitializer(LocalInitializer node) { |
| 398 assert(!capturedVariables.contains(node.variable)); | 422 assert(!capturedVariables.contains(node.variable)); |
| 399 node.transformChildren(this); | 423 node.transformChildren(this); |
| 400 return node; | 424 return node; |
| 401 } | 425 } |
| 402 | 426 |
| 403 TreeNode visitFunctionNode(FunctionNode node) { | 427 TreeNode visitFunctionNode(FunctionNode node) { |
| 404 transformList(node.typeParameters, this, node); | 428 transformList(node.typeParameters, this, node); |
| 405 | 429 |
| 406 void extend(VariableDeclaration parameter) { | 430 void extend(VariableDeclaration parameter) { |
| 407 context.extend(parameter, new VariableGet(parameter)); | 431 context.extend(parameter, new VariableGet(parameter)); |
| 408 } | 432 } |
| 409 | 433 |
| 410 // TODO: Can parameters contain initializers (e.g., for optional ones) that | 434 // TODO: Can parameters contain initializers (e.g., for optional ones) that |
| 411 // need to be closure converted? | 435 // need to be closure converted? |
| 412 node.positionalParameters.where(capturedVariables.contains).forEach(extend); | 436 node.positionalParameters.where(capturedVariables.contains).forEach(extend); |
| 413 node.namedParameters.where(capturedVariables.contains).forEach(extend); | 437 node.namedParameters.where(capturedVariables.contains).forEach(extend); |
| 414 | 438 |
| 415 assert(node.body != null); | 439 assert(node.body != null); |
| 416 node.body = node.body.accept(this); | 440 node.body = node.body.accept(this); |
| 417 node.body.parent = node; | 441 node.body.parent = node; |
| 418 return node; | 442 return node; |
| 419 } | 443 } |
| 420 | 444 |
| 421 TreeNode visitBlock(Block node) => saveContext(() { | 445 TreeNode visitBlock(Block node) { |
| 422 if (_currentBlock != node) { | 446 return saveContext(() { |
| 423 _currentBlock = node; | 447 if (_currentBlock != node) { |
| 424 _insertionIndex = 0; | 448 _currentBlock = node; |
| 449 _insertionIndex = 0; |
| 450 } |
| 451 |
| 452 while (_insertionIndex < _currentBlock.statements.length) { |
| 453 assert(_currentBlock == node); |
| 454 |
| 455 var original = _currentBlock.statements[_insertionIndex]; |
| 456 var transformed = original.accept(this); |
| 457 assert(_currentBlock.statements[_insertionIndex] == original); |
| 458 if (transformed == null) { |
| 459 _currentBlock.statements.removeAt(_insertionIndex); |
| 460 } else { |
| 461 _currentBlock.statements[_insertionIndex++] = transformed; |
| 462 transformed.parent = _currentBlock; |
| 425 } | 463 } |
| 464 } |
| 426 | 465 |
| 427 while (_insertionIndex < _currentBlock.statements.length) { | 466 return node; |
| 428 assert(_currentBlock == node); | 467 }); |
| 429 | 468 } |
| 430 var original = _currentBlock.statements[_insertionIndex]; | |
| 431 var transformed = original.accept(this); | |
| 432 assert(_currentBlock.statements[_insertionIndex] == original); | |
| 433 if (transformed == null) { | |
| 434 _currentBlock.statements.removeAt(_insertionIndex); | |
| 435 } else { | |
| 436 _currentBlock.statements[_insertionIndex++] = transformed; | |
| 437 transformed.parent = _currentBlock; | |
| 438 } | |
| 439 } | |
| 440 | |
| 441 return node; | |
| 442 }); | |
| 443 | 469 |
| 444 TreeNode visitVariableDeclaration(VariableDeclaration node) { | 470 TreeNode visitVariableDeclaration(VariableDeclaration node) { |
| 445 node.transformChildren(this); | 471 node.transformChildren(this); |
| 446 | 472 |
| 447 if (!capturedVariables.contains(node)) return node; | 473 if (!capturedVariables.contains(node)) return node; |
| 448 context.extend(node, node.initializer ?? new NullLiteral()); | 474 context.extend(node, node.initializer ?? new NullLiteral()); |
| 449 | 475 |
| 450 if (node.parent == currentFunction) return node; | 476 if (node.parent == currentFunction) { |
| 451 if (node.parent is Block) { | 477 return node; |
| 478 } else { |
| 479 assert(node.parent is Block); |
| 452 // When returning null, the parent block will remove this node from its | 480 // When returning null, the parent block will remove this node from its |
| 453 // list of statements. | 481 // list of statements. |
| 454 // TODO(ahe): I'd like to avoid testing on the parent pointer. | |
| 455 return null; | 482 return null; |
| 456 } | 483 } |
| 457 throw "Unexpected parent for $node: ${node.parent.parent}"; | |
| 458 } | 484 } |
| 459 | 485 |
| 460 TreeNode visitVariableGet(VariableGet node) { | 486 TreeNode visitVariableGet(VariableGet node) { |
| 461 return capturedVariables.contains(node.variable) | 487 return capturedVariables.contains(node.variable) |
| 462 ? context.lookup(node.variable) | 488 ? context.lookup(node.variable) |
| 463 : node; | 489 : node; |
| 464 } | 490 } |
| 465 | 491 |
| 466 TreeNode visitVariableSet(VariableSet node) { | 492 TreeNode visitVariableSet(VariableSet node) { |
| 467 node.transformChildren(this); | 493 node.transformChildren(this); |
| (...skipping 315 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 783 // TODO(ahe): Remove this method when we don't generate closure classes | 809 // TODO(ahe): Remove this method when we don't generate closure classes |
| 784 // anymore. | 810 // anymore. |
| 785 void addClosureClassNote(Class closureClass) { | 811 void addClosureClassNote(Class closureClass) { |
| 786 closureClass.addMember(new Field(new Name("note"), | 812 closureClass.addMember(new Field(new Name("note"), |
| 787 type: coreTypes.stringClass.rawType, | 813 type: coreTypes.stringClass.rawType, |
| 788 initializer: new StringLiteral( | 814 initializer: new StringLiteral( |
| 789 "This is temporary. The VM doesn't need closure classes."), | 815 "This is temporary. The VM doesn't need closure classes."), |
| 790 fileUri: currentFileUri)); | 816 fileUri: currentFileUri)); |
| 791 } | 817 } |
| 792 } | 818 } |
| OLD | NEW |