Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(320)

Side by Side Diff: gen.dart

Issue 8437043: Fixes a few different issues related to block scope. (Closed) Base URL: https://dart.googlecode.com/svn/experimental/frog
Patch Set: Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « frogsh ('k') | tests/frog/frog.status » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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 }
OLDNEW
« no previous file with comments | « frogsh ('k') | tests/frog/frog.status » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698