Chromium Code Reviews| 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 10 matching lines...) Expand all Loading... | |
| 21 ForStatement, | 21 ForStatement, |
| 22 FunctionDeclaration, | 22 FunctionDeclaration, |
| 23 FunctionExpression, | 23 FunctionExpression, |
| 24 FunctionNode, | 24 FunctionNode, |
| 25 FunctionType, | 25 FunctionType, |
| 26 InterfaceType, | 26 InterfaceType, |
| 27 InvalidExpression, | 27 InvalidExpression, |
| 28 InvocationExpression, | 28 InvocationExpression, |
| 29 Library, | 29 Library, |
| 30 LocalInitializer, | 30 LocalInitializer, |
| 31 RedirectingInitializer, | |
| 31 Member, | 32 Member, |
| 32 MethodInvocation, | 33 MethodInvocation, |
| 33 Name, | 34 Name, |
| 34 NamedExpression, | 35 NamedExpression, |
| 35 NamedType, | 36 NamedType, |
| 36 NullLiteral, | 37 NullLiteral, |
| 37 Procedure, | 38 Procedure, |
| 38 ProcedureKind, | 39 ProcedureKind, |
| 39 PropertyGet, | 40 PropertyGet, |
| 40 ReturnStatement, | 41 ReturnStatement, |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 54 import '../../frontend/accessors.dart' show VariableAccessor; | 55 import '../../frontend/accessors.dart' show VariableAccessor; |
| 55 | 56 |
| 56 import '../../clone.dart' show CloneVisitor; | 57 import '../../clone.dart' show CloneVisitor; |
| 57 | 58 |
| 58 import '../../core_types.dart' show CoreTypes; | 59 import '../../core_types.dart' show CoreTypes; |
| 59 | 60 |
| 60 import '../../type_algebra.dart' show substitute; | 61 import '../../type_algebra.dart' show substitute; |
| 61 | 62 |
| 62 import 'clone_without_body.dart' show CloneWithoutBody; | 63 import 'clone_without_body.dart' show CloneWithoutBody; |
| 63 | 64 |
| 64 import 'context.dart' show Context, NoContext; | 65 import 'context.dart' show Context, NoContext, LocalContext; |
| 65 | 66 |
| 66 import 'info.dart' show ClosureInfo; | 67 import 'info.dart' show ClosureInfo; |
| 67 | 68 |
| 68 import 'rewriter.dart' show AstRewriter, BlockRewriter, InitializerListRewriter; | 69 import 'rewriter.dart' show AstRewriter, BlockRewriter, InitializerListRewriter; |
| 69 | 70 |
| 70 class ClosureConverter extends Transformer { | 71 class ClosureConverter extends Transformer { |
| 71 final CoreTypes coreTypes; | 72 final CoreTypes coreTypes; |
| 72 final Set<VariableDeclaration> capturedVariables; | 73 final Map<VariableDeclaration, int /*capture flags -- see info.dart*/ > |
|
Dmitry Stefantsov
2017/07/31 15:05:35
Please, add put this comment on its own line above
sjindel
2017/07/31 15:32:16
Done.
| |
| 74 capturedVariables; | |
| 73 final Map<FunctionNode, Set<TypeParameter>> capturedTypeVariables; | 75 final Map<FunctionNode, Set<TypeParameter>> capturedTypeVariables; |
| 74 final Map<FunctionNode, VariableDeclaration> thisAccess; | 76 final Map<FunctionNode, VariableDeclaration> thisAccess; |
| 75 final Map<FunctionNode, String> localNames; | 77 final Map<FunctionNode, String> localNames; |
| 76 | 78 |
| 77 /// Records place-holders for cloning contexts. See [visitForStatement]. | 79 /// Records place-holders for cloning contexts. See [visitForStatement]. |
| 78 final Set<InvalidExpression> contextClonePlaceHolders = | 80 final Set<InvalidExpression> contextClonePlaceHolders = |
| 79 new Set<InvalidExpression>(); | 81 new Set<InvalidExpression>(); |
| 80 | 82 |
| 81 final CloneVisitor cloner = new CloneWithoutBody(); | 83 final CloneVisitor cloner = new CloneWithoutBody(); |
| 82 | 84 |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 174 TreeNode visitClass(Class node) { | 176 TreeNode visitClass(Class node) { |
| 175 assert(newClassMembers.isEmpty); | 177 assert(newClassMembers.isEmpty); |
| 176 currentClass = node; | 178 currentClass = node; |
| 177 node = super.visitClass(node); | 179 node = super.visitClass(node); |
| 178 newClassMembers.forEach(node.addMember); | 180 newClassMembers.forEach(node.addMember); |
| 179 newClassMembers.clear(); | 181 newClassMembers.clear(); |
| 180 currentClass = null; | 182 currentClass = null; |
| 181 return node; | 183 return node; |
| 182 } | 184 } |
| 183 | 185 |
| 184 void extendContextWith(VariableDeclaration parameter) { | 186 extendContextConditionally(bool inInitializer) => |
|
Dmitry Stefantsov
2017/07/31 15:05:35
As Karl pointed out, the arrow notation is usually
Dmitry Stefantsov
2017/07/31 15:05:35
I would suggest to make [inInitializer] a named pa
sjindel
2017/07/31 15:32:16
Done.
sjindel
2017/07/31 15:32:16
Done.
| |
| 185 context.extend(parameter, new VariableGet(parameter)); | 187 (VariableDeclaration parameter) { |
| 186 } | 188 var captured = capturedVariables[parameter]; |
|
Dmitry Stefantsov
2017/07/31 15:05:35
Also I think that the name [captured] is a bit mis
Dmitry Stefantsov
2017/07/31 15:05:35
I think using type annotation `int` here helps wit
sjindel
2017/07/31 15:32:16
Done.
sjindel
2017/07/31 15:32:16
Done.
| |
| 189 | |
| 190 // When moving variables into the context while scanning initializers, | |
| 191 // we need to add the variable if it's captured in an initializer, | |
| 192 // whether or not it's used/captured in the body. However, in the body, | |
| 193 // we only need to add the variable into the context if it's *not* | |
| 194 // captured in an initializer. | |
| 195 if (captured != null && inInitializer | |
| 196 ? (captured & ClosureInfo.INSIDE_INITIALIZER) > 0 | |
| 197 : captured == ClosureInfo.OUTSIDE_INITIALIZER) { | |
| 198 context.extend(parameter, new VariableGet(parameter)); | |
| 199 } | |
| 200 return captured ?? 0; | |
| 201 }; | |
| 187 | 202 |
| 188 TreeNode visitConstructor(Constructor node) { | 203 TreeNode visitConstructor(Constructor node) { |
| 189 assert(isEmptyContext); | 204 assert(isEmptyContext); |
| 190 currentMember = node; | 205 currentMember = node; |
| 191 | 206 |
| 207 // If we created a context for the initializers, we need to re-use that | |
| 208 // context in the body of the function. Unfortunately, the context is | |
| 209 // declared in a local initializer and local initializers aren't visible | |
| 210 // in the body of the constructor. To work around this issue, we move the | |
| 211 // body into a new constructor and make this constructor redirect to that | |
| 212 // one, passing the context as an argument to the new constructor. | |
| 213 var movingCtor = false; | |
| 214 | |
| 192 // Transform initializers. | 215 // Transform initializers. |
| 193 if (node.initializers.length > 0) { | 216 if (node.initializers.length > 0) { |
| 194 var initRewriter = new InitializerListRewriter(node); | 217 var initRewriter = new InitializerListRewriter(node); |
| 195 rewriter = initRewriter; | 218 rewriter = initRewriter; |
| 196 context = new NoContext(this); | 219 context = new NoContext(this); |
| 197 | 220 |
| 221 int capturedBoth = | |
| 222 ClosureInfo.OUTSIDE_INITIALIZER | ClosureInfo.INSIDE_INITIALIZER; | |
| 223 | |
| 198 // TODO(karlklose): add a fine-grained analysis of captured parameters. | 224 // TODO(karlklose): add a fine-grained analysis of captured parameters. |
| 199 node.function.positionalParameters | 225 movingCtor = node.function.positionalParameters |
| 200 .where(capturedVariables.contains) | 226 .map(extendContextConditionally(true)) |
| 201 .forEach(extendContextWith); | 227 .any((flags) => flags == capturedBoth) || |
| 202 node.function.namedParameters | 228 node.function.namedParameters |
| 203 .where(capturedVariables.contains) | 229 .map(extendContextConditionally(true)) |
| 204 .forEach(extendContextWith); | 230 .any((flags) => flags == capturedBoth); |
| 205 | 231 |
| 206 transformList(node.initializers, this, node); | 232 transformList(node.initializers, this, node); |
| 207 node.initializers.insertAll(0, initRewriter.prefix); | 233 node.initializers.insertAll(0, initRewriter.prefix); |
| 208 context = rewriter = null; | 234 rewriter = null; |
| 209 } | 235 } |
| 210 | 236 |
| 211 // Transform constructor body. | 237 // Transform constructor body. |
| 212 FunctionNode function = node.function; | 238 FunctionNode function = node.function; |
| 213 if (function.body != null && function.body is! EmptyStatement) { | 239 if (function.body != null && function.body is! EmptyStatement) { |
| 214 setupContextForFunctionBody(function); | 240 setupContextForFunctionBody(function); |
| 241 if (!movingCtor) context = new NoContext(this); | |
| 215 VariableDeclaration self = thisAccess[currentMemberFunction]; | 242 VariableDeclaration self = thisAccess[currentMemberFunction]; |
| 216 if (self != null) { | 243 if (self != null) { |
| 217 context.extend(self, new ThisExpression()); | 244 context.extend(self, new ThisExpression()); |
| 218 } | 245 } |
| 219 node.function.accept(this); | 246 node.function.accept(this); |
| 220 resetContext(); | 247 |
| 248 if (movingCtor) { | |
| 249 var contextDecl = (context as LocalContext).self; | |
| 250 var newCtorName = new Name("${node.name.name}#redir"); | |
| 251 var newCtor = new Constructor(node.function, name: newCtorName); | |
| 252 newClassMembers.add(newCtor); | |
| 253 | |
| 254 LocalInitializer contextDeclInit = null; | |
| 255 for (var init in node.initializers) { | |
|
Dmitry Stefantsov
2017/07/31 15:05:35
This version is definitely better. I was thinking
sjindel
2017/07/31 15:32:16
True, but I feel that this approach keeps the logi
Dmitry Stefantsov
2017/08/01 09:36:13
But it's not completely isolated. I think that su
| |
| 256 if (init is LocalInitializer && init.variable == contextDecl) { | |
| 257 contextDeclInit = init; | |
| 258 } else { | |
| 259 newCtor.initializers.add(init); | |
| 260 } | |
| 261 } | |
| 262 | |
| 263 node.initializers = node.initializers.take(1).toList(); | |
| 264 | |
| 265 var cv = new CloneVisitor(); | |
| 266 var oldCtorParams = function.positionalParameters | |
| 267 .map(cv.visitVariableDeclaration) | |
| 268 .toList(); | |
| 269 var oldCtorNamedParams = | |
| 270 function.namedParameters.map(cv.visitVariableDeclaration).toList(); | |
| 271 | |
| 272 function.positionalParameters.addAll(function.namedParameters); | |
| 273 function.namedParameters = []; | |
| 274 | |
| 275 var args = <Expression>[]; | |
| 276 assert(function.typeParameters.length == 0); | |
| 277 args.addAll(oldCtorParams.map((decl) => new VariableGet(decl))); | |
| 278 args.addAll(oldCtorNamedParams.map((decl) => new VariableGet(decl))); | |
| 279 | |
| 280 node.function = new FunctionNode(new EmptyStatement(), | |
| 281 typeParameters: [], | |
| 282 positionalParameters: oldCtorParams, | |
| 283 namedParameters: oldCtorNamedParams, | |
| 284 requiredParameterCount: function.requiredParameterCount, | |
| 285 returnType: function.returnType, | |
| 286 asyncMarker: function.asyncMarker, | |
| 287 dartAsyncMarker: function.dartAsyncMarker); | |
| 288 node.function.parent = node; | |
| 289 | |
| 290 var oldCtorDecl = cv.visitVariableDeclaration(contextDecl); | |
| 291 contextDecl.initializer = null; | |
| 292 function.positionalParameters.add(contextDecl); | |
| 293 function.requiredParameterCount++; | |
| 294 | |
| 295 contextDeclInit.variable = oldCtorDecl; | |
| 296 oldCtorDecl.parent = contextDeclInit; | |
| 297 | |
| 298 args.add(new VariableGet(oldCtorDecl)); | |
| 299 var redirInit = | |
| 300 new RedirectingInitializer(newCtor, new Arguments(args)); | |
| 301 node.initializers.add(redirInit); | |
| 302 } | |
| 221 } | 303 } |
| 304 resetContext(); | |
| 222 return node; | 305 return node; |
| 223 } | 306 } |
| 224 | 307 |
| 225 AstRewriter makeRewriterForBody(FunctionNode function) { | 308 AstRewriter makeRewriterForBody(FunctionNode function) { |
| 226 Statement body = function.body; | 309 Statement body = function.body; |
| 227 if (body is! Block) { | 310 if (body is! Block) { |
| 228 body = new Block(<Statement>[body]); | 311 body = new Block(<Statement>[body]); |
| 229 function.body = function.body.parent = body; | 312 function.body = function.body.parent = body; |
| 230 } | 313 } |
| 231 return new BlockRewriter(body); | 314 return new BlockRewriter(body); |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 267 | 350 |
| 268 Expression result = addClosure(function, contextVariable, parent.expression, | 351 Expression result = addClosure(function, contextVariable, parent.expression, |
| 269 typeSubstitution, enclosingTypeSubstitution); | 352 typeSubstitution, enclosingTypeSubstitution); |
| 270 currentFunction = enclosingFunction; | 353 currentFunction = enclosingFunction; |
| 271 typeSubstitution = enclosingTypeSubstitution; | 354 typeSubstitution = enclosingTypeSubstitution; |
| 272 return result; | 355 return result; |
| 273 } | 356 } |
| 274 | 357 |
| 275 TreeNode visitFunctionDeclaration(FunctionDeclaration node) { | 358 TreeNode visitFunctionDeclaration(FunctionDeclaration node) { |
| 276 /// Is this closure itself captured by a closure? | 359 /// Is this closure itself captured by a closure? |
| 277 bool isCaptured = capturedVariables.contains(node.variable); | 360 bool isCaptured = capturedVariables.containsKey(node.variable); |
| 278 if (isCaptured) { | 361 if (isCaptured) { |
| 279 context.extend(node.variable, new InvalidExpression()); | 362 context.extend(node.variable, new InvalidExpression()); |
| 280 } | 363 } |
| 281 Context parent = context; | 364 Context parent = context; |
| 282 return saveContext(() { | 365 return saveContext(() { |
| 283 Expression expression = handleLocalFunction(node.function); | 366 Expression expression = handleLocalFunction(node.function); |
| 284 | 367 |
| 285 if (isCaptured) { | 368 if (isCaptured) { |
| 286 parent.update(node.variable, expression); | 369 parent.update(node.variable, expression); |
| 287 return null; | 370 return null; |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 362 } | 445 } |
| 363 | 446 |
| 364 TreeNode visitProcedure(Procedure node) { | 447 TreeNode visitProcedure(Procedure node) { |
| 365 assert(isEmptyContext); | 448 assert(isEmptyContext); |
| 366 | 449 |
| 367 currentMember = node; | 450 currentMember = node; |
| 368 | 451 |
| 369 FunctionNode function = node.function; | 452 FunctionNode function = node.function; |
| 370 if (function.body != null) { | 453 if (function.body != null) { |
| 371 setupContextForFunctionBody(function); | 454 setupContextForFunctionBody(function); |
| 455 // Start with no context. This happens after setting up _currentBlock | |
| 456 // so statements can be emitted into _currentBlock if necessary. | |
| 457 context = new NoContext(this); | |
|
Dmitry Stefantsov
2017/07/31 15:05:35
Why was [context] initialization moved here?
sjindel
2017/07/31 15:32:16
It didn't really make sense to do it in the functi
Dmitry Stefantsov
2017/08/01 09:36:13
If we had a special kind of rewriter for the case
| |
| 458 | |
| 372 VariableDeclaration self = thisAccess[currentMemberFunction]; | 459 VariableDeclaration self = thisAccess[currentMemberFunction]; |
| 373 if (self != null) { | 460 if (self != null) { |
| 374 context.extend(self, new ThisExpression()); | 461 context.extend(self, new ThisExpression()); |
| 375 } | 462 } |
| 376 node.transformChildren(this); | 463 node.transformChildren(this); |
| 377 resetContext(); | 464 resetContext(); |
| 378 } | 465 } |
| 379 | 466 |
| 380 return node; | 467 return node; |
| 381 } | 468 } |
| 382 | 469 |
| 383 void setupContextForFunctionBody(FunctionNode function) { | 470 void setupContextForFunctionBody(FunctionNode function) { |
|
Dmitry Stefantsov
2017/07/31 15:05:35
Now this function name is misleading, after actual
sjindel
2017/07/31 15:32:16
Done.
| |
| 384 Statement body = function.body; | 471 Statement body = function.body; |
| 385 assert(body != null); | 472 assert(body != null); |
| 386 currentMemberFunction = function; | 473 currentMemberFunction = function; |
| 387 // Ensure that the body is a block which becomes the current block. | 474 // Ensure that the body is a block which becomes the current block. |
| 388 rewriter = makeRewriterForBody(function); | 475 rewriter = makeRewriterForBody(function); |
| 389 // Start with no context. This happens after setting up _currentBlock | |
| 390 // so statements can be emitted into _currentBlock if necessary. | |
| 391 context = new NoContext(this); | |
| 392 } | 476 } |
| 393 | 477 |
| 394 void resetContext() { | 478 void resetContext() { |
| 395 rewriter = null; | 479 rewriter = null; |
| 396 context = null; | 480 context = null; |
| 397 currentMemberFunction = null; | 481 currentMemberFunction = null; |
| 398 currentMember = null; | 482 currentMember = null; |
| 399 } | 483 } |
| 400 | 484 |
| 401 bool get isEmptyContext { | 485 bool get isEmptyContext { |
| 402 return rewriter == null && context == null; | 486 return rewriter == null && context == null; |
| 403 } | 487 } |
| 404 | 488 |
| 405 TreeNode visitLocalInitializer(LocalInitializer node) { | 489 TreeNode visitLocalInitializer(LocalInitializer node) { |
| 406 assert(!capturedVariables.contains(node.variable)); | 490 assert(!capturedVariables.containsKey(node.variable)); |
| 407 node.transformChildren(this); | 491 node.transformChildren(this); |
| 408 return node; | 492 return node; |
| 409 } | 493 } |
| 410 | 494 |
| 411 TreeNode visitFunctionNode(FunctionNode node) { | 495 TreeNode visitFunctionNode(FunctionNode node) { |
| 412 transformList(node.typeParameters, this, node); | 496 transformList(node.typeParameters, this, node); |
| 413 // TODO: Can parameters contain initializers (e.g., for optional ones) that | 497 // Initializers for optional parameters must be compile-time constants, |
| 414 // need to be closure converted? | 498 // which excludes closures. |
| 415 node.positionalParameters | 499 node.positionalParameters.forEach(extendContextConditionally(false)); |
| 416 .where(capturedVariables.contains) | 500 node.namedParameters.forEach(extendContextConditionally(false)); |
| 417 .forEach(extendContextWith); | |
| 418 node.namedParameters | |
| 419 .where(capturedVariables.contains) | |
| 420 .forEach(extendContextWith); | |
| 421 assert(node.body != null); | 501 assert(node.body != null); |
| 422 node.body = node.body.accept(this); | 502 node.body = node.body.accept(this); |
| 423 node.body.parent = node; | 503 node.body.parent = node; |
| 424 return node; | 504 return node; |
| 425 } | 505 } |
| 426 | 506 |
| 427 TreeNode visitBlock(Block node) { | 507 TreeNode visitBlock(Block node) { |
| 428 return saveContext(() { | 508 return saveContext(() { |
| 429 BlockRewriter blockRewriter = rewriter = rewriter.forNestedBlock(node); | 509 BlockRewriter blockRewriter = rewriter = rewriter.forNestedBlock(node); |
| 430 blockRewriter.transformStatements(this); | 510 blockRewriter.transformStatements(this); |
| 431 return node; | 511 return node; |
| 432 }); | 512 }); |
| 433 } | 513 } |
| 434 | 514 |
| 435 TreeNode visitVariableDeclaration(VariableDeclaration node) { | 515 TreeNode visitVariableDeclaration(VariableDeclaration node) { |
| 436 node.transformChildren(this); | 516 node.transformChildren(this); |
| 437 | 517 |
| 438 if (!capturedVariables.contains(node)) return node; | 518 if (!capturedVariables.containsKey(node)) return node; |
| 439 if (node.initializer == null && node.parent is FunctionNode) { | 519 if (node.initializer == null && node.parent is FunctionNode) { |
| 440 // If the variable is a function parameter and doesn't have an | 520 // If the variable is a function parameter and doesn't have an |
| 441 // initializer, just use this variable name to put it into the context. | 521 // initializer, just use this variable name to put it into the context. |
| 442 context.extend(node, new VariableGet(node)); | 522 context.extend(node, new VariableGet(node)); |
| 443 } else { | 523 } else { |
| 444 context.extend(node, node.initializer ?? new NullLiteral()); | 524 context.extend(node, node.initializer ?? new NullLiteral()); |
| 445 } | 525 } |
| 446 | 526 |
| 447 if (node.parent == currentFunction) { | 527 if (node.parent == currentFunction) { |
| 448 return node; | 528 return node; |
| 449 } else { | 529 } else { |
| 450 assert(node.parent is Block); | 530 assert(node.parent is Block); |
| 451 // When returning null, the parent block will remove | 531 // When returning null, the parent block will remove |
| 452 // this node from its list of statements. | 532 // this node from its list of statements. |
| 453 return null; | 533 return null; |
| 454 } | 534 } |
| 455 } | 535 } |
| 456 | 536 |
| 457 TreeNode visitVariableGet(VariableGet node) { | 537 TreeNode visitVariableGet(VariableGet node) { |
| 458 return capturedVariables.contains(node.variable) | 538 return capturedVariables.containsKey(node.variable) |
| 459 ? context.lookup(node.variable) | 539 ? context.lookup(node.variable) |
| 460 : node; | 540 : node; |
| 461 } | 541 } |
| 462 | 542 |
| 463 TreeNode visitVariableSet(VariableSet node) { | 543 TreeNode visitVariableSet(VariableSet node) { |
| 464 node.transformChildren(this); | 544 node.transformChildren(this); |
| 465 | 545 |
| 466 return capturedVariables.contains(node.variable) | 546 return capturedVariables.containsKey(node.variable) |
| 467 ? context.assign(node.variable, node.value, | 547 ? context.assign(node.variable, node.value, |
| 468 voidContext: isInVoidContext(node)) | 548 voidContext: isInVoidContext(node)) |
| 469 : node; | 549 : node; |
| 470 } | 550 } |
| 471 | 551 |
| 472 bool isInVoidContext(Expression node) { | 552 bool isInVoidContext(Expression node) { |
| 473 TreeNode parent = node.parent; | 553 TreeNode parent = node.parent; |
| 474 return parent is ExpressionStatement || | 554 return parent is ExpressionStatement || |
| 475 parent is ForStatement && parent.condition != node; | 555 parent is ForStatement && parent.condition != node; |
| 476 } | 556 } |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 492 InvalidExpression placeHolder = new InvalidExpression(); | 572 InvalidExpression placeHolder = new InvalidExpression(); |
| 493 contextClonePlaceHolders.add(placeHolder); | 573 contextClonePlaceHolders.add(placeHolder); |
| 494 return placeHolder; | 574 return placeHolder; |
| 495 } | 575 } |
| 496 | 576 |
| 497 TreeNode visitInvalidExpression(InvalidExpression node) { | 577 TreeNode visitInvalidExpression(InvalidExpression node) { |
| 498 return contextClonePlaceHolders.remove(node) ? context.clone() : node; | 578 return contextClonePlaceHolders.remove(node) ? context.clone() : node; |
| 499 } | 579 } |
| 500 | 580 |
| 501 TreeNode visitForStatement(ForStatement node) { | 581 TreeNode visitForStatement(ForStatement node) { |
| 502 if (node.variables.any(capturedVariables.contains)) { | 582 if (node.variables.any(capturedVariables.containsKey)) { |
| 503 // In Dart, loop variables are new variables on each iteration of the | 583 // In Dart, loop variables are new variables on each iteration of the |
| 504 // loop. This is only observable when a loop variable is captured by a | 584 // loop. This is only observable when a loop variable is captured by a |
| 505 // closure, which is the situation we're in here. So we transform the | 585 // closure, which is the situation we're in here. So we transform the |
| 506 // loop. | 586 // loop. |
| 507 // | 587 // |
| 508 // Consider the following example, where `x` is `node.variables.first`, | 588 // Consider the following example, where `x` is `node.variables.first`, |
| 509 // and `#t1` is a temporary variable: | 589 // and `#t1` is a temporary variable: |
| 510 // | 590 // |
| 511 // for (var x = 0; x < 10; x++) body; | 591 // for (var x = 0; x < 10; x++) body; |
| 512 // | 592 // |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 529 node.updates.insert(0, cloneContext()); | 609 node.updates.insert(0, cloneContext()); |
| 530 Block block = new Block(statements); | 610 Block block = new Block(statements); |
| 531 rewriter = new BlockRewriter(block); | 611 rewriter = new BlockRewriter(block); |
| 532 return block.accept(this); | 612 return block.accept(this); |
| 533 }); | 613 }); |
| 534 } | 614 } |
| 535 return super.visitForStatement(node); | 615 return super.visitForStatement(node); |
| 536 } | 616 } |
| 537 | 617 |
| 538 TreeNode visitForInStatement(ForInStatement node) { | 618 TreeNode visitForInStatement(ForInStatement node) { |
| 539 if (capturedVariables.contains(node.variable)) { | 619 if (capturedVariables.containsKey(node.variable)) { |
| 540 // In Dart, loop variables are new variables on each iteration of the | 620 // In Dart, loop variables are new variables on each iteration of the |
| 541 // loop. This is only observable when the loop variable is captured by a | 621 // loop. This is only observable when the loop variable is captured by a |
| 542 // closure, so we need to transform the for-in loop when `node.variable` | 622 // closure, so we need to transform the for-in loop when `node.variable` |
| 543 // is captured. | 623 // is captured. |
| 544 // | 624 // |
| 545 // Consider the following example, where `x` is `node.variable`, and | 625 // Consider the following example, where `x` is `node.variable`, and |
| 546 // `#t1` is a temporary variable: | 626 // `#t1` is a temporary variable: |
| 547 // | 627 // |
| 548 // for (var x in expr) body; | 628 // for (var x in expr) body; |
| 549 // | 629 // |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 566 | 646 |
| 567 TreeNode visitThisExpression(ThisExpression node) { | 647 TreeNode visitThisExpression(ThisExpression node) { |
| 568 return isOuterMostContext | 648 return isOuterMostContext |
| 569 ? node | 649 ? node |
| 570 : context.lookup(thisAccess[currentMemberFunction]); | 650 : context.lookup(thisAccess[currentMemberFunction]); |
| 571 } | 651 } |
| 572 | 652 |
| 573 TreeNode visitCatch(Catch node) { | 653 TreeNode visitCatch(Catch node) { |
| 574 VariableDeclaration exception = node.exception; | 654 VariableDeclaration exception = node.exception; |
| 575 VariableDeclaration stackTrace = node.stackTrace; | 655 VariableDeclaration stackTrace = node.stackTrace; |
| 576 if (stackTrace != null && capturedVariables.contains(stackTrace)) { | 656 if (stackTrace != null && capturedVariables.containsKey(stackTrace)) { |
| 577 Block block = node.body = ensureBlock(node.body); | 657 Block block = node.body = ensureBlock(node.body); |
| 578 block.parent = node; | 658 block.parent = node; |
| 579 node.stackTrace = new VariableDeclaration(null); | 659 node.stackTrace = new VariableDeclaration(null); |
| 580 node.stackTrace.parent = node; | 660 node.stackTrace.parent = node; |
| 581 stackTrace.initializer = new VariableGet(node.stackTrace); | 661 stackTrace.initializer = new VariableGet(node.stackTrace); |
| 582 block.statements.insert(0, stackTrace); | 662 block.statements.insert(0, stackTrace); |
| 583 stackTrace.parent = block; | 663 stackTrace.parent = block; |
| 584 } | 664 } |
| 585 if (exception != null && capturedVariables.contains(exception)) { | 665 if (exception != null && capturedVariables.containsKey(exception)) { |
| 586 Block block = node.body = ensureBlock(node.body); | 666 Block block = node.body = ensureBlock(node.body); |
| 587 block.parent = node; | 667 block.parent = node; |
| 588 node.exception = new VariableDeclaration(null); | 668 node.exception = new VariableDeclaration(null); |
| 589 node.exception.parent = node; | 669 node.exception.parent = node; |
| 590 exception.initializer = new VariableGet(node.exception); | 670 exception.initializer = new VariableGet(node.exception); |
| 591 block.statements.insert(0, exception); | 671 block.statements.insert(0, exception); |
| 592 exception.parent = block; | 672 exception.parent = block; |
| 593 } | 673 } |
| 594 return super.visitCatch(node); | 674 return super.visitCatch(node); |
| 595 } | 675 } |
| (...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 695 copy.function.body.parent = copy.function; | 775 copy.function.body.parent = copy.function; |
| 696 return copy; | 776 return copy; |
| 697 } | 777 } |
| 698 | 778 |
| 699 void addGetterForwarder(Name name, Procedure getter) { | 779 void addGetterForwarder(Name name, Procedure getter) { |
| 700 assert(getter.isGetter); | 780 assert(getter.isGetter); |
| 701 newClassMembers | 781 newClassMembers |
| 702 .add(copyWithBody(getter, forwardToThisProperty(getter))..name = name); | 782 .add(copyWithBody(getter, forwardToThisProperty(getter))..name = name); |
| 703 } | 783 } |
| 704 } | 784 } |
| OLD | NEW |