Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, 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 /** | 5 /** |
| 6 * Top level generator object for writing code and keeping track of | 6 * Top level generator object for writing code and keeping track of |
| 7 * dependencies. | 7 * dependencies. |
| 8 * | 8 * |
| 9 * Should have two compilation models, but only one implemented so far. | 9 * Should have two compilation models, but only one implemented so far. |
| 10 * | 10 * |
| (...skipping 339 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 350 return factType; | 350 return factType; |
| 351 } | 351 } |
| 352 } | 352 } |
| 353 | 353 |
| 354 | 354 |
| 355 class BlockScope { | 355 class BlockScope { |
| 356 MethodGenerator enclosingMethod; | 356 MethodGenerator enclosingMethod; |
| 357 BlockScope parent; | 357 BlockScope parent; |
| 358 Map<String, Value> _vars; // TODO(jimhug): Using a list may improve perf. | 358 Map<String, Value> _vars; // TODO(jimhug): Using a list may improve perf. |
| 359 | 359 |
| 360 /** | |
| 361 * Variables in this method that have been captured by lambdas. | |
| 362 * Don't reuse the names in child blocks. | |
| 363 */ | |
| 364 Set<String> _closedOver; | |
| 365 | |
| 360 /** If we are in a catch block, this is the exception variable to rethrow. */ | 366 /** If we are in a catch block, this is the exception variable to rethrow. */ |
| 361 Value rethrow; | 367 Value rethrow; |
| 362 | 368 |
| 363 /** | 369 /** |
| 364 * True if the block is reentrant while the current method is executing. | 370 * True if the block is reentrant while the current method is executing. |
| 365 * This is only used for the blocks within loops. | 371 * This is only used for the blocks within loops. |
| 366 */ | 372 */ |
| 367 bool reentrant; | 373 bool reentrant; |
| 368 | 374 |
| 369 BlockScope(this.enclosingMethod, this.parent, [this.reentrant = false]) | 375 BlockScope(this.enclosingMethod, this.parent, [this.reentrant = false]) |
| 370 : _vars = {} { | 376 : _vars = {} { |
| 371 | 377 |
| 372 // Blocks within a reentrant block are also reentrant. | 378 if (isMethodScope) { |
| 373 if (parent != null && parent.enclosingMethod == enclosingMethod) { | 379 _closedOver = new Set<String>(); |
| 380 } else { | |
| 381 // Blocks within a reentrant block are also reentrant. | |
| 374 reentrant = reentrant || parent.reentrant; | 382 reentrant = reentrant || parent.reentrant; |
| 375 } | 383 } |
| 376 } | 384 } |
| 377 | 385 |
| 386 /** True if this is the top level scope of the method. */ | |
| 387 bool get isMethodScope() { | |
| 388 return parent == null || parent.enclosingMethod != enclosingMethod; | |
| 389 } | |
| 390 | |
| 391 /** | |
| 392 * Gets the method scope associated with this block scope (possibly itself). | |
| 393 */ | |
| 394 BlockScope get methodScope() { | |
| 395 var s = this; | |
| 396 while (!s.isMethodScope) s = s.parent; | |
| 397 return s; | |
| 398 } | |
| 399 | |
| 378 lookup(String name) { | 400 lookup(String name) { |
| 379 var ret = _vars[name]; | 401 var ret = _vars[name]; |
| 380 if (ret != null) return ret; | 402 if (ret != null) return ret; |
| 381 | 403 |
| 382 for (var s = parent; s != null; s = s.parent) { | 404 for (var s = parent; s != null; s = s.parent) { |
| 383 ret = s._vars[name]; | 405 ret = s._vars[name]; |
| 384 if (ret != null) { | 406 if (ret != null) { |
| 385 // If we're in a lambda and this variable is from a different method | 407 // If this variable is from a different method, it means we closed over |
| 386 // and the scope we found this variable in is reentrant, put this | 408 // it in the child lambda. Time for some bookeeping! |
| 387 // variable in the list of variables that we're going to capture | 409 if (s.enclosingMethod != enclosingMethod) { |
| 388 // with Function.bind | 410 // Make sure the parent method doesn't reuse this variable to mean |
| 389 if (enclosingMethod.captures != null && s.reentrant && | 411 // something else. |
| 390 s.enclosingMethod != enclosingMethod) { | 412 s.methodScope._closedOver.add(ret.code); |
| 391 enclosingMethod.captures.add(name); | 413 |
| 414 // If the scope we found this variable in is reentrant, remember the | |
| 415 // variable. The lambda we're in will capture it with Function.bind. | |
| 416 if (enclosingMethod.captures != null && s.reentrant) { | |
| 417 enclosingMethod.captures.add(ret.code); | |
| 418 } | |
| 392 } | 419 } |
| 420 | |
| 393 return ret; | 421 return ret; |
| 394 } | 422 } |
| 395 } | 423 } |
| 396 } | 424 } |
| 397 | 425 |
| 398 bool _isDefined(String name) { | 426 /** |
| 399 if (_vars.containsKey(name)) return true; | 427 * Returns true if we can't use this name because we would be shadowing |
| 400 if (parent != null) return parent._isDefined(name); | 428 * another name in the JS that we might need to access later. |
| 429 */ | |
| 430 bool _isDefinedInParent(String name) { | |
| 431 if (isMethodScope && _closedOver.contains(name)) return true; | |
| 432 | |
| 433 for (var s = parent; s != null; s = s.parent) { | |
| 434 if (s._vars.containsKey(name)) return true; | |
| 435 // Don't reuse a name that's been closed over | |
| 436 if (s.isMethodScope && s._closedOver.contains(name)) return true; | |
| 437 } | |
| 438 | |
| 439 // Ensure that we don't shadow another name from the global scope. | |
| 440 final type = enclosingMethod.method.declaringType; | |
| 441 if (type.resolveMember(name) != null) return true; | |
| 442 | |
| 443 // This lookup might report errors, which is a bit strange. | |
| 444 // But probably harmless since we have to pay for the lookup anyway. | |
| 445 if (type.library.lookup(name, null) != null) return true; | |
| 446 | |
| 447 // Nobody else needs this name. It's safe to reuse. | |
| 401 return false; | 448 return false; |
| 402 } | 449 } |
| 403 | 450 |
| 404 Value create(String name, Type type, Node location) { | 451 Value create(String name, Type type, Node location) { |
| 405 var jsName = world.toJsIdentifier(name); | 452 var jsName = world.toJsIdentifier(name); |
| 406 if (_vars.containsKey(name)) { | 453 if (_vars.containsKey(name)) { |
| 407 if (location != null) { | 454 if (location != null) { |
| 408 world.error('duplicate name "$name"', location.span); | 455 world.error('duplicate name "$name"', location.span); |
| 409 } else { | 456 } else { |
| 410 world.internalError('conflict with temporary name "$name"'); | 457 world.internalError('conflict with temporary name "$name"'); |
| 411 } | 458 } |
| 412 } | 459 } |
| 413 | 460 |
| 414 if (parent != null) { | 461 int index = 0; |
| 415 int index = 0; | 462 while (_isDefinedInParent(jsName)) { |
| 416 // TODO(jmesserly): we also need to check that no scope in the method | 463 jsName = '$name${index++}'; |
|
Jennifer Messerly
2011/11/02 03:23:19
this TODO is one of the issues addressed
| |
| 417 // declares the same name, if we want closures to work right. | |
| 418 // In other words, if another sibling block in the method declares a | |
| 419 // variable with the same name, it shouldn't be able to mutate the closed | |
| 420 //over value. | |
| 421 while (parent._isDefined(jsName)) { | |
| 422 jsName = '$name${index++}'; | |
| 423 } | |
| 424 } | 464 } |
| 465 | |
| 425 var ret = new Value(type, jsName, false, false); // TODO: needsTemp:false); | 466 var ret = new Value(type, jsName, false, false); // TODO: needsTemp:false); |
| 426 _vars[name] = ret; | 467 _vars[name] = ret; |
| 427 return ret; | 468 return ret; |
| 428 } | 469 } |
| 429 | 470 |
| 430 /** Declares a variable in the current scope for this identifier. */ | 471 /** Declares a variable in the current scope for this identifier. */ |
| 431 Value declare(DeclaredIdentifier id) { | 472 Value declare(DeclaredIdentifier id) { |
| 432 var type = enclosingMethod.method.resolveType(id.type, false); | 473 var type = enclosingMethod.method.resolveType(id.type, false); |
| 433 return create(id.name.name, type, id); | 474 return create(id.name.name, type, id); |
| 434 } | 475 } |
| (...skipping 1741 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2176 result.add(new Value(world.varType, '\$$i', false, /*needsTemp:*/false)); | 2217 result.add(new Value(world.varType, '\$$i', false, /*needsTemp:*/false)); |
| 2177 } | 2218 } |
| 2178 for (int i = bareCount; i < length; i++) { | 2219 for (int i = bareCount; i < length; i++) { |
| 2179 var name = getName(i); | 2220 var name = getName(i); |
| 2180 if (name == null) name = '\$$i'; | 2221 if (name == null) name = '\$$i'; |
| 2181 result.add(new Value(world.varType, name, false, /*needsTemp:*/false)); | 2222 result.add(new Value(world.varType, name, false, /*needsTemp:*/false)); |
| 2182 } | 2223 } |
| 2183 return new Arguments(nodes, result); | 2224 return new Arguments(nodes, result); |
| 2184 } | 2225 } |
| 2185 } | 2226 } |
| OLD | NEW |