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

Unified 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, 2 months 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « frogsh ('k') | tests/frog/frog.status » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: gen.dart
diff --git a/gen.dart b/gen.dart
index 3e4983982ad60144c3b44954032142e473ccf67f..97ad438d86bf4ff241d373641eea8b6ce0bc34c7 100644
--- a/gen.dart
+++ b/gen.dart
@@ -357,6 +357,12 @@ class BlockScope {
BlockScope parent;
Map<String, Value> _vars; // TODO(jimhug): Using a list may improve perf.
+ /**
+ * Variables in this method that have been captured by lambdas.
+ * Don't reuse the names in child blocks.
+ */
+ Set<String> _closedOver;
+
/** If we are in a catch block, this is the exception variable to rethrow. */
Value rethrow;
@@ -369,12 +375,28 @@ class BlockScope {
BlockScope(this.enclosingMethod, this.parent, [this.reentrant = false])
: _vars = {} {
- // Blocks within a reentrant block are also reentrant.
- if (parent != null && parent.enclosingMethod == enclosingMethod) {
+ if (isMethodScope) {
+ _closedOver = new Set<String>();
+ } else {
+ // Blocks within a reentrant block are also reentrant.
reentrant = reentrant || parent.reentrant;
}
}
+ /** True if this is the top level scope of the method. */
+ bool get isMethodScope() {
+ return parent == null || parent.enclosingMethod != enclosingMethod;
+ }
+
+ /**
+ * Gets the method scope associated with this block scope (possibly itself).
+ */
+ BlockScope get methodScope() {
+ var s = this;
+ while (!s.isMethodScope) s = s.parent;
+ return s;
+ }
+
lookup(String name) {
var ret = _vars[name];
if (ret != null) return ret;
@@ -382,22 +404,47 @@ class BlockScope {
for (var s = parent; s != null; s = s.parent) {
ret = s._vars[name];
if (ret != null) {
- // If we're in a lambda and this variable is from a different method
- // and the scope we found this variable in is reentrant, put this
- // variable in the list of variables that we're going to capture
- // with Function.bind
- if (enclosingMethod.captures != null && s.reentrant &&
- s.enclosingMethod != enclosingMethod) {
- enclosingMethod.captures.add(name);
+ // If this variable is from a different method, it means we closed over
+ // it in the child lambda. Time for some bookeeping!
+ if (s.enclosingMethod != enclosingMethod) {
+ // Make sure the parent method doesn't reuse this variable to mean
+ // something else.
+ s.methodScope._closedOver.add(ret.code);
+
+ // If the scope we found this variable in is reentrant, remember the
+ // variable. The lambda we're in will capture it with Function.bind.
+ if (enclosingMethod.captures != null && s.reentrant) {
+ enclosingMethod.captures.add(ret.code);
+ }
}
+
return ret;
}
}
}
- bool _isDefined(String name) {
- if (_vars.containsKey(name)) return true;
- if (parent != null) return parent._isDefined(name);
+ /**
+ * Returns true if we can't use this name because we would be shadowing
+ * another name in the JS that we might need to access later.
+ */
+ bool _isDefinedInParent(String name) {
+ if (isMethodScope && _closedOver.contains(name)) return true;
+
+ for (var s = parent; s != null; s = s.parent) {
+ if (s._vars.containsKey(name)) return true;
+ // Don't reuse a name that's been closed over
+ if (s.isMethodScope && s._closedOver.contains(name)) return true;
+ }
+
+ // Ensure that we don't shadow another name from the global scope.
+ final type = enclosingMethod.method.declaringType;
+ if (type.resolveMember(name) != null) return true;
+
+ // This lookup might report errors, which is a bit strange.
+ // But probably harmless since we have to pay for the lookup anyway.
+ if (type.library.lookup(name, null) != null) return true;
+
+ // Nobody else needs this name. It's safe to reuse.
return false;
}
@@ -411,17 +458,11 @@ class BlockScope {
}
}
- if (parent != null) {
- int index = 0;
- // TODO(jmesserly): we also need to check that no scope in the method
Jennifer Messerly 2011/11/02 03:23:19 this TODO is one of the issues addressed
- // declares the same name, if we want closures to work right.
- // In other words, if another sibling block in the method declares a
- // variable with the same name, it shouldn't be able to mutate the closed
- //over value.
- while (parent._isDefined(jsName)) {
- jsName = '$name${index++}';
- }
+ int index = 0;
+ while (_isDefinedInParent(jsName)) {
+ jsName = '$name${index++}';
}
+
var ret = new Value(type, jsName, false, false); // TODO: needsTemp:false);
_vars[name] = ret;
return ret;
« 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