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 |