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 |
| 74 // This map pairs variables that are captured with flags indicating whether | |
| 75 // they are captured inside or outside an initializer. | |
|
Dmitry Stefantsov
2017/08/01 09:36:13
Please, add a reference to the field in [ClosureIn
sjindel
2017/08/01 14:58:25
Done.
| |
| 76 final Map<VariableDeclaration, int> capturedVariables; | |
| 77 | |
| 73 final Map<FunctionNode, Set<TypeParameter>> capturedTypeVariables; | 78 final Map<FunctionNode, Set<TypeParameter>> capturedTypeVariables; |
| 74 final Map<FunctionNode, VariableDeclaration> thisAccess; | 79 final Map<FunctionNode, VariableDeclaration> thisAccess; |
| 75 final Map<FunctionNode, String> localNames; | 80 final Map<FunctionNode, String> localNames; |
| 76 | 81 |
| 77 /// Records place-holders for cloning contexts. See [visitForStatement]. | 82 /// Records place-holders for cloning contexts. See [visitForStatement]. |
| 78 final Set<InvalidExpression> contextClonePlaceHolders = | 83 final Set<InvalidExpression> contextClonePlaceHolders = |
| 79 new Set<InvalidExpression>(); | 84 new Set<InvalidExpression>(); |
| 80 | 85 |
| 81 final CloneVisitor cloner = new CloneWithoutBody(); | 86 final CloneVisitor cloner = new CloneWithoutBody(); |
| 82 | 87 |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 174 TreeNode visitClass(Class node) { | 179 TreeNode visitClass(Class node) { |
| 175 assert(newClassMembers.isEmpty); | 180 assert(newClassMembers.isEmpty); |
| 176 currentClass = node; | 181 currentClass = node; |
| 177 node = super.visitClass(node); | 182 node = super.visitClass(node); |
| 178 newClassMembers.forEach(node.addMember); | 183 newClassMembers.forEach(node.addMember); |
| 179 newClassMembers.clear(); | 184 newClassMembers.clear(); |
| 180 currentClass = null; | 185 currentClass = null; |
| 181 return node; | 186 return node; |
| 182 } | 187 } |
| 183 | 188 |
| 184 void extendContextWith(VariableDeclaration parameter) { | 189 extendContextConditionally({bool inInitializer}) { |
| 185 context.extend(parameter, new VariableGet(parameter)); | 190 return (VariableDeclaration parameter) { |
| 191 int flags = capturedVariables[parameter]; | |
| 192 | |
| 193 // When moving variables into the context while scanning initializers, | |
| 194 // we need to add the variable if it's captured in an initializer, | |
| 195 // whether or not it's used/captured in the body. However, in the body, | |
| 196 // we only need to add the variable into the context if it's *not* | |
| 197 // captured in an initializer. | |
| 198 if (flags != null && inInitializer | |
| 199 ? (flags & ClosureInfo.INSIDE_INITIALIZER) > 0 | |
| 200 : flags == ClosureInfo.OUTSIDE_INITIALIZER) { | |
|
Dmitry Stefantsov
2017/08/01 09:36:13
How about breaking this condition into many, for e
| |
| 201 context.extend(parameter, new VariableGet(parameter)); | |
| 202 } | |
| 203 return flags ?? 0; | |
| 204 }; | |
| 186 } | 205 } |
| 187 | 206 |
| 188 TreeNode visitConstructor(Constructor node) { | 207 TreeNode visitConstructor(Constructor node) { |
| 189 assert(isEmptyContext); | 208 assert(isEmptyContext); |
| 190 currentMember = node; | 209 currentMember = node; |
| 191 | 210 |
| 211 // If we created a context for the initializers, we need to re-use that | |
| 212 // context in the body of the function. Unfortunately, the context is | |
| 213 // declared in a local initializer and local initializers aren't visible | |
| 214 // in the body of the constructor. To work around this issue, we move the | |
| 215 // body into a new constructor and make this constructor redirect to that | |
| 216 // one, passing the context as an argument to the new constructor. | |
| 217 var movingCtor = false; | |
| 218 | |
| 192 // Transform initializers. | 219 // Transform initializers. |
| 193 if (node.initializers.length > 0) { | 220 if (node.initializers.length > 0) { |
| 194 var initRewriter = new InitializerListRewriter(node); | 221 var initRewriter = new InitializerListRewriter(node); |
| 195 rewriter = initRewriter; | 222 rewriter = initRewriter; |
| 196 context = new NoContext(this); | 223 context = new NoContext(this); |
| 197 | 224 |
| 225 int capturedBoth = | |
|
Dmitry Stefantsov
2017/08/01 09:36:13
I think using `final` modifier makes sense for thi
sjindel
2017/08/01 14:58:25
Done.
| |
| 226 ClosureInfo.OUTSIDE_INITIALIZER | ClosureInfo.INSIDE_INITIALIZER; | |
| 227 | |
| 198 // TODO(karlklose): add a fine-grained analysis of captured parameters. | 228 // TODO(karlklose): add a fine-grained analysis of captured parameters. |
| 199 node.function.positionalParameters | 229 movingCtor = node.function.positionalParameters |
| 200 .where(capturedVariables.contains) | 230 .map(extendContextConditionally(inInitializer: true)) |
| 201 .forEach(extendContextWith); | 231 .any((flags) => flags == capturedBoth) || |
| 202 node.function.namedParameters | 232 node.function.namedParameters |
| 203 .where(capturedVariables.contains) | 233 .map(extendContextConditionally(inInitializer: true)) |
| 204 .forEach(extendContextWith); | 234 .any((flags) => flags == capturedBoth); |
| 205 | 235 |
| 206 transformList(node.initializers, this, node); | 236 transformList(node.initializers, this, node); |
| 207 node.initializers.insertAll(0, initRewriter.prefix); | 237 node.initializers.insertAll(0, initRewriter.prefix); |
| 208 context = rewriter = null; | 238 rewriter = null; |
| 209 } | 239 } |
| 210 | 240 |
| 211 // Transform constructor body. | 241 // Transform constructor body. |
| 212 FunctionNode function = node.function; | 242 FunctionNode function = node.function; |
| 213 if (function.body != null && function.body is! EmptyStatement) { | 243 if (function.body != null && function.body is! EmptyStatement) { |
| 214 setupContextForFunctionBody(function); | 244 setupRewriterForFunctionBody(function); |
| 245 if (!movingCtor) context = new NoContext(this); | |
| 215 VariableDeclaration self = thisAccess[currentMemberFunction]; | 246 VariableDeclaration self = thisAccess[currentMemberFunction]; |
| 216 if (self != null) { | 247 if (self != null) { |
| 217 context.extend(self, new ThisExpression()); | 248 context.extend(self, new ThisExpression()); |
| 218 } | 249 } |
| 219 node.function.accept(this); | 250 node.function.accept(this); |
| 220 resetContext(); | 251 |
| 252 if (movingCtor) { | |
| 253 var contextDecl = (context as LocalContext).self; | |
| 254 var newCtorName = new Name("${node.name.name}#redir"); | |
| 255 var newCtor = new Constructor(node.function, name: newCtorName); | |
| 256 newClassMembers.add(newCtor); | |
| 257 | |
| 258 LocalInitializer contextDeclInit = null; | |
| 259 for (var init in node.initializers) { | |
| 260 if (init is LocalInitializer && init.variable == contextDecl) { | |
| 261 contextDeclInit = init; | |
| 262 } else { | |
| 263 newCtor.initializers.add(init); | |
| 264 } | |
| 265 } | |
| 266 | |
| 267 node.initializers = node.initializers.take(1).toList(); | |
|
Dmitry Stefantsov
2017/08/01 09:36:13
Here we still treat the first initializer as speci
sjindel
2017/08/01 14:58:25
Done.
| |
| 268 | |
| 269 var cv = new CloneVisitor(); | |
| 270 var oldCtorParams = function.positionalParameters | |
| 271 .map(cv.visitVariableDeclaration) | |
| 272 .toList(); | |
| 273 var oldCtorNamedParams = | |
| 274 function.namedParameters.map(cv.visitVariableDeclaration).toList(); | |
| 275 | |
| 276 function.positionalParameters.addAll(function.namedParameters); | |
| 277 function.namedParameters = []; | |
| 278 | |
| 279 var args = <Expression>[]; | |
| 280 assert(function.typeParameters.length == 0); | |
|
Dmitry Stefantsov
2017/08/01 09:36:13
If we are now sure that the [FunctionNode] of the
sjindel
2017/08/01 14:58:25
Done.
| |
| 281 args.addAll(oldCtorParams.map((decl) => new VariableGet(decl))); | |
| 282 args.addAll(oldCtorNamedParams.map((decl) => new VariableGet(decl))); | |
| 283 | |
| 284 node.function = new FunctionNode(new EmptyStatement(), | |
| 285 typeParameters: [], | |
| 286 positionalParameters: oldCtorParams, | |
| 287 namedParameters: oldCtorNamedParams, | |
| 288 requiredParameterCount: function.requiredParameterCount, | |
| 289 returnType: function.returnType, | |
| 290 asyncMarker: function.asyncMarker, | |
| 291 dartAsyncMarker: function.dartAsyncMarker); | |
| 292 node.function.parent = node; | |
| 293 | |
| 294 var oldCtorDecl = cv.visitVariableDeclaration(contextDecl); | |
| 295 contextDecl.initializer = null; | |
| 296 function.positionalParameters.add(contextDecl); | |
| 297 function.requiredParameterCount++; | |
| 298 | |
| 299 contextDeclInit.variable = oldCtorDecl; | |
| 300 oldCtorDecl.parent = contextDeclInit; | |
| 301 | |
| 302 args.add(new VariableGet(oldCtorDecl)); | |
| 303 var redirInit = | |
| 304 new RedirectingInitializer(newCtor, new Arguments(args)); | |
| 305 node.initializers.add(redirInit); | |
| 306 } | |
| 221 } | 307 } |
| 308 resetContext(); | |
| 222 return node; | 309 return node; |
| 223 } | 310 } |
| 224 | 311 |
| 225 AstRewriter makeRewriterForBody(FunctionNode function) { | 312 AstRewriter makeRewriterForBody(FunctionNode function) { |
| 226 Statement body = function.body; | 313 Statement body = function.body; |
| 227 if (body is! Block) { | 314 if (body is! Block) { |
| 228 body = new Block(<Statement>[body]); | 315 body = new Block(<Statement>[body]); |
| 229 function.body = function.body.parent = body; | 316 function.body = function.body.parent = body; |
| 230 } | 317 } |
| 231 return new BlockRewriter(body); | 318 return new BlockRewriter(body); |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 267 | 354 |
| 268 Expression result = addClosure(function, contextVariable, parent.expression, | 355 Expression result = addClosure(function, contextVariable, parent.expression, |
| 269 typeSubstitution, enclosingTypeSubstitution); | 356 typeSubstitution, enclosingTypeSubstitution); |
| 270 currentFunction = enclosingFunction; | 357 currentFunction = enclosingFunction; |
| 271 typeSubstitution = enclosingTypeSubstitution; | 358 typeSubstitution = enclosingTypeSubstitution; |
| 272 return result; | 359 return result; |
| 273 } | 360 } |
| 274 | 361 |
| 275 TreeNode visitFunctionDeclaration(FunctionDeclaration node) { | 362 TreeNode visitFunctionDeclaration(FunctionDeclaration node) { |
| 276 /// Is this closure itself captured by a closure? | 363 /// Is this closure itself captured by a closure? |
| 277 bool isCaptured = capturedVariables.contains(node.variable); | 364 bool isCaptured = capturedVariables.containsKey(node.variable); |
| 278 if (isCaptured) { | 365 if (isCaptured) { |
| 279 context.extend(node.variable, new InvalidExpression()); | 366 context.extend(node.variable, new InvalidExpression()); |
| 280 } | 367 } |
| 281 Context parent = context; | 368 Context parent = context; |
| 282 return saveContext(() { | 369 return saveContext(() { |
| 283 Expression expression = handleLocalFunction(node.function); | 370 Expression expression = handleLocalFunction(node.function); |
| 284 | 371 |
| 285 if (isCaptured) { | 372 if (isCaptured) { |
| 286 parent.update(node.variable, expression); | 373 parent.update(node.variable, expression); |
| 287 return null; | 374 return null; |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 361 closedTopLevelFunction, accessContext, closureType, fnTypeArgs); | 448 closedTopLevelFunction, accessContext, closureType, fnTypeArgs); |
| 362 } | 449 } |
| 363 | 450 |
| 364 TreeNode visitProcedure(Procedure node) { | 451 TreeNode visitProcedure(Procedure node) { |
| 365 assert(isEmptyContext); | 452 assert(isEmptyContext); |
| 366 | 453 |
| 367 currentMember = node; | 454 currentMember = node; |
| 368 | 455 |
| 369 FunctionNode function = node.function; | 456 FunctionNode function = node.function; |
| 370 if (function.body != null) { | 457 if (function.body != null) { |
| 371 setupContextForFunctionBody(function); | 458 setupRewriterForFunctionBody(function); |
| 459 // Start with no context. This happens after setting up _currentBlock | |
| 460 // so statements can be emitted into _currentBlock if necessary. | |
| 461 context = new NoContext(this); | |
| 462 | |
| 372 VariableDeclaration self = thisAccess[currentMemberFunction]; | 463 VariableDeclaration self = thisAccess[currentMemberFunction]; |
| 373 if (self != null) { | 464 if (self != null) { |
| 374 context.extend(self, new ThisExpression()); | 465 context.extend(self, new ThisExpression()); |
| 375 } | 466 } |
| 376 node.transformChildren(this); | 467 node.transformChildren(this); |
| 377 resetContext(); | 468 resetContext(); |
| 378 } | 469 } |
| 379 | 470 |
| 380 return node; | 471 return node; |
| 381 } | 472 } |
| 382 | 473 |
| 383 void setupContextForFunctionBody(FunctionNode function) { | 474 void setupRewriterForFunctionBody(FunctionNode function) { |
| 384 Statement body = function.body; | 475 Statement body = function.body; |
| 385 assert(body != null); | 476 assert(body != null); |
| 386 currentMemberFunction = function; | 477 currentMemberFunction = function; |
| 387 // Ensure that the body is a block which becomes the current block. | 478 // Ensure that the body is a block which becomes the current block. |
| 388 rewriter = makeRewriterForBody(function); | 479 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 } | 480 } |
| 393 | 481 |
| 394 void resetContext() { | 482 void resetContext() { |
| 395 rewriter = null; | 483 rewriter = null; |
| 396 context = null; | 484 context = null; |
| 397 currentMemberFunction = null; | 485 currentMemberFunction = null; |
| 398 currentMember = null; | 486 currentMember = null; |
| 399 } | 487 } |
| 400 | 488 |
| 401 bool get isEmptyContext { | 489 bool get isEmptyContext { |
| 402 return rewriter == null && context == null; | 490 return rewriter == null && context == null; |
| 403 } | 491 } |
| 404 | 492 |
| 405 TreeNode visitLocalInitializer(LocalInitializer node) { | 493 TreeNode visitLocalInitializer(LocalInitializer node) { |
| 406 assert(!capturedVariables.contains(node.variable)); | 494 assert(!capturedVariables.containsKey(node.variable)); |
| 407 node.transformChildren(this); | 495 node.transformChildren(this); |
| 408 return node; | 496 return node; |
| 409 } | 497 } |
| 410 | 498 |
| 411 TreeNode visitFunctionNode(FunctionNode node) { | 499 TreeNode visitFunctionNode(FunctionNode node) { |
| 412 transformList(node.typeParameters, this, node); | 500 transformList(node.typeParameters, this, node); |
| 413 // TODO: Can parameters contain initializers (e.g., for optional ones) that | 501 // Initializers for optional parameters must be compile-time constants, |
| 414 // need to be closure converted? | 502 // which excludes closures. |
|
Dmitry Stefantsov
2017/08/01 09:36:13
Looks like the cause for this comment was lost dur
sjindel
2017/08/01 14:58:25
Done.
| |
| 415 node.positionalParameters | 503 node.positionalParameters |
| 416 .where(capturedVariables.contains) | 504 .forEach(extendContextConditionally(inInitializer: false)); |
| 417 .forEach(extendContextWith); | |
| 418 node.namedParameters | 505 node.namedParameters |
| 419 .where(capturedVariables.contains) | 506 .forEach(extendContextConditionally(inInitializer: false)); |
| 420 .forEach(extendContextWith); | |
| 421 assert(node.body != null); | 507 assert(node.body != null); |
| 422 node.body = node.body.accept(this); | 508 node.body = node.body.accept(this); |
| 423 node.body.parent = node; | 509 node.body.parent = node; |
| 424 return node; | 510 return node; |
| 425 } | 511 } |
| 426 | 512 |
| 427 TreeNode visitBlock(Block node) { | 513 TreeNode visitBlock(Block node) { |
| 428 return saveContext(() { | 514 return saveContext(() { |
| 429 BlockRewriter blockRewriter = rewriter = rewriter.forNestedBlock(node); | 515 BlockRewriter blockRewriter = rewriter = rewriter.forNestedBlock(node); |
| 430 blockRewriter.transformStatements(this); | 516 blockRewriter.transformStatements(this); |
| 431 return node; | 517 return node; |
| 432 }); | 518 }); |
| 433 } | 519 } |
| 434 | 520 |
| 435 TreeNode visitVariableDeclaration(VariableDeclaration node) { | 521 TreeNode visitVariableDeclaration(VariableDeclaration node) { |
| 436 node.transformChildren(this); | 522 node.transformChildren(this); |
| 437 | 523 |
| 438 if (!capturedVariables.contains(node)) return node; | 524 if (!capturedVariables.containsKey(node)) return node; |
| 439 if (node.initializer == null && node.parent is FunctionNode) { | 525 if (node.initializer == null && node.parent is FunctionNode) { |
| 440 // If the variable is a function parameter and doesn't have an | 526 // 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. | 527 // initializer, just use this variable name to put it into the context. |
| 442 context.extend(node, new VariableGet(node)); | 528 context.extend(node, new VariableGet(node)); |
| 443 } else { | 529 } else { |
| 444 context.extend(node, node.initializer ?? new NullLiteral()); | 530 context.extend(node, node.initializer ?? new NullLiteral()); |
| 445 } | 531 } |
| 446 | 532 |
| 447 if (node.parent == currentFunction) { | 533 if (node.parent == currentFunction) { |
| 448 return node; | 534 return node; |
| 449 } else { | 535 } else { |
| 450 assert(node.parent is Block); | 536 assert(node.parent is Block); |
| 451 // When returning null, the parent block will remove | 537 // When returning null, the parent block will remove |
| 452 // this node from its list of statements. | 538 // this node from its list of statements. |
| 453 return null; | 539 return null; |
| 454 } | 540 } |
| 455 } | 541 } |
| 456 | 542 |
| 457 TreeNode visitVariableGet(VariableGet node) { | 543 TreeNode visitVariableGet(VariableGet node) { |
| 458 return capturedVariables.contains(node.variable) | 544 return capturedVariables.containsKey(node.variable) |
| 459 ? context.lookup(node.variable) | 545 ? context.lookup(node.variable) |
| 460 : node; | 546 : node; |
| 461 } | 547 } |
| 462 | 548 |
| 463 TreeNode visitVariableSet(VariableSet node) { | 549 TreeNode visitVariableSet(VariableSet node) { |
| 464 node.transformChildren(this); | 550 node.transformChildren(this); |
| 465 | 551 |
| 466 return capturedVariables.contains(node.variable) | 552 return capturedVariables.containsKey(node.variable) |
| 467 ? context.assign(node.variable, node.value, | 553 ? context.assign(node.variable, node.value, |
| 468 voidContext: isInVoidContext(node)) | 554 voidContext: isInVoidContext(node)) |
| 469 : node; | 555 : node; |
| 470 } | 556 } |
| 471 | 557 |
| 472 bool isInVoidContext(Expression node) { | 558 bool isInVoidContext(Expression node) { |
| 473 TreeNode parent = node.parent; | 559 TreeNode parent = node.parent; |
| 474 return parent is ExpressionStatement || | 560 return parent is ExpressionStatement || |
| 475 parent is ForStatement && parent.condition != node; | 561 parent is ForStatement && parent.condition != node; |
| 476 } | 562 } |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 492 InvalidExpression placeHolder = new InvalidExpression(); | 578 InvalidExpression placeHolder = new InvalidExpression(); |
| 493 contextClonePlaceHolders.add(placeHolder); | 579 contextClonePlaceHolders.add(placeHolder); |
| 494 return placeHolder; | 580 return placeHolder; |
| 495 } | 581 } |
| 496 | 582 |
| 497 TreeNode visitInvalidExpression(InvalidExpression node) { | 583 TreeNode visitInvalidExpression(InvalidExpression node) { |
| 498 return contextClonePlaceHolders.remove(node) ? context.clone() : node; | 584 return contextClonePlaceHolders.remove(node) ? context.clone() : node; |
| 499 } | 585 } |
| 500 | 586 |
| 501 TreeNode visitForStatement(ForStatement node) { | 587 TreeNode visitForStatement(ForStatement node) { |
| 502 if (node.variables.any(capturedVariables.contains)) { | 588 if (node.variables.any(capturedVariables.containsKey)) { |
| 503 // In Dart, loop variables are new variables on each iteration of the | 589 // 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 | 590 // 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 | 591 // closure, which is the situation we're in here. So we transform the |
| 506 // loop. | 592 // loop. |
| 507 // | 593 // |
| 508 // Consider the following example, where `x` is `node.variables.first`, | 594 // Consider the following example, where `x` is `node.variables.first`, |
| 509 // and `#t1` is a temporary variable: | 595 // and `#t1` is a temporary variable: |
| 510 // | 596 // |
| 511 // for (var x = 0; x < 10; x++) body; | 597 // for (var x = 0; x < 10; x++) body; |
| 512 // | 598 // |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 529 node.updates.insert(0, cloneContext()); | 615 node.updates.insert(0, cloneContext()); |
| 530 Block block = new Block(statements); | 616 Block block = new Block(statements); |
| 531 rewriter = new BlockRewriter(block); | 617 rewriter = new BlockRewriter(block); |
| 532 return block.accept(this); | 618 return block.accept(this); |
| 533 }); | 619 }); |
| 534 } | 620 } |
| 535 return super.visitForStatement(node); | 621 return super.visitForStatement(node); |
| 536 } | 622 } |
| 537 | 623 |
| 538 TreeNode visitForInStatement(ForInStatement node) { | 624 TreeNode visitForInStatement(ForInStatement node) { |
| 539 if (capturedVariables.contains(node.variable)) { | 625 if (capturedVariables.containsKey(node.variable)) { |
| 540 // In Dart, loop variables are new variables on each iteration of the | 626 // 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 | 627 // 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` | 628 // closure, so we need to transform the for-in loop when `node.variable` |
| 543 // is captured. | 629 // is captured. |
| 544 // | 630 // |
| 545 // Consider the following example, where `x` is `node.variable`, and | 631 // Consider the following example, where `x` is `node.variable`, and |
| 546 // `#t1` is a temporary variable: | 632 // `#t1` is a temporary variable: |
| 547 // | 633 // |
| 548 // for (var x in expr) body; | 634 // for (var x in expr) body; |
| 549 // | 635 // |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 566 | 652 |
| 567 TreeNode visitThisExpression(ThisExpression node) { | 653 TreeNode visitThisExpression(ThisExpression node) { |
| 568 return isOuterMostContext | 654 return isOuterMostContext |
| 569 ? node | 655 ? node |
| 570 : context.lookup(thisAccess[currentMemberFunction]); | 656 : context.lookup(thisAccess[currentMemberFunction]); |
| 571 } | 657 } |
| 572 | 658 |
| 573 TreeNode visitCatch(Catch node) { | 659 TreeNode visitCatch(Catch node) { |
| 574 VariableDeclaration exception = node.exception; | 660 VariableDeclaration exception = node.exception; |
| 575 VariableDeclaration stackTrace = node.stackTrace; | 661 VariableDeclaration stackTrace = node.stackTrace; |
| 576 if (stackTrace != null && capturedVariables.contains(stackTrace)) { | 662 if (stackTrace != null && capturedVariables.containsKey(stackTrace)) { |
| 577 Block block = node.body = ensureBlock(node.body); | 663 Block block = node.body = ensureBlock(node.body); |
| 578 block.parent = node; | 664 block.parent = node; |
| 579 node.stackTrace = new VariableDeclaration(null); | 665 node.stackTrace = new VariableDeclaration(null); |
| 580 node.stackTrace.parent = node; | 666 node.stackTrace.parent = node; |
| 581 stackTrace.initializer = new VariableGet(node.stackTrace); | 667 stackTrace.initializer = new VariableGet(node.stackTrace); |
| 582 block.statements.insert(0, stackTrace); | 668 block.statements.insert(0, stackTrace); |
| 583 stackTrace.parent = block; | 669 stackTrace.parent = block; |
| 584 } | 670 } |
| 585 if (exception != null && capturedVariables.contains(exception)) { | 671 if (exception != null && capturedVariables.containsKey(exception)) { |
| 586 Block block = node.body = ensureBlock(node.body); | 672 Block block = node.body = ensureBlock(node.body); |
| 587 block.parent = node; | 673 block.parent = node; |
| 588 node.exception = new VariableDeclaration(null); | 674 node.exception = new VariableDeclaration(null); |
| 589 node.exception.parent = node; | 675 node.exception.parent = node; |
| 590 exception.initializer = new VariableGet(node.exception); | 676 exception.initializer = new VariableGet(node.exception); |
| 591 block.statements.insert(0, exception); | 677 block.statements.insert(0, exception); |
| 592 exception.parent = block; | 678 exception.parent = block; |
| 593 } | 679 } |
| 594 return super.visitCatch(node); | 680 return super.visitCatch(node); |
| 595 } | 681 } |
| (...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 695 copy.function.body.parent = copy.function; | 781 copy.function.body.parent = copy.function; |
| 696 return copy; | 782 return copy; |
| 697 } | 783 } |
| 698 | 784 |
| 699 void addGetterForwarder(Name name, Procedure getter) { | 785 void addGetterForwarder(Name name, Procedure getter) { |
| 700 assert(getter.isGetter); | 786 assert(getter.isGetter); |
| 701 newClassMembers | 787 newClassMembers |
| 702 .add(copyWithBody(getter, forwardToThisProperty(getter))..name = name); | 788 .add(copyWithBody(getter, forwardToThisProperty(getter))..name = name); |
| 703 } | 789 } |
| 704 } | 790 } |
| OLD | NEW |