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 |