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

Unified Diff: lib/src/codegen/js_names.dart

Issue 1099813006: fixes #146, JS globals were not understood by the renamer (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 years, 8 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 | « lib/runtime/dart/core.js ('k') | lib/src/js/printer.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/src/codegen/js_names.dart
diff --git a/lib/src/codegen/js_names.dart b/lib/src/codegen/js_names.dart
index 2002e283fae953cd4a9233b48415251e6d83fd67..ea3eed336713814fcd80e24c90e60caa6b5261f4 100644
--- a/lib/src/codegen/js_names.dart
+++ b/lib/src/codegen/js_names.dart
@@ -34,7 +34,7 @@ class JSNamer extends LocalNamer {
JSNamer(Node node) : renames = new _RenameVisitor.build(node).renames;
String getName(Identifier node) {
- var rename = renames[renameKey(node)];
+ var rename = renames[identifierKey(node)];
if (rename != null) return rename;
assert(!needsRename(node));
@@ -45,81 +45,108 @@ class JSNamer extends LocalNamer {
void leaveScope() {}
}
+/// Represents a complete function scope in JS.
+///
+/// We don't currently track ES6 block scopes, because we don't represent them
+/// in js_ast yet.
class _FunctionScope {
+ /// The parent scope.
final _FunctionScope parent;
- final names = new HashSet<String>();
+
+ /// All names declared in this scope.
+ final declared = new HashSet<Object>();
+
+ /// All names [declared] in this scope or its [parent]s, that is used in this
+ /// scope and/or children. This is exactly the set of variable names we must
+ /// not collide with inside this scope.
+ final used = new HashSet<String>();
+
+ /// Nested functions, these are visited after everything else so the names
+ /// they might need are in scope.
+ final functions = new List<FunctionExpression>();
+
_FunctionScope(this.parent);
}
/// Collects all names used in the visited tree.
-class _RenameVisitor extends BaseVisitor {
+class _RenameVisitor extends VariableDeclarationVisitor {
final pendingRenames = new Map<Object, Set<_FunctionScope>>();
final renames = new HashMap<Object, String>();
- _FunctionScope scope = new _FunctionScope(null);
+ final _FunctionScope rootScope = new _FunctionScope(null);
+ _FunctionScope scope;
_RenameVisitor.build(Node root) {
+ scope = rootScope;
root.accept(this);
+ _finishFunctions();
_finishNames();
}
+ declare(Identifier node) {
+ var id = identifierKey(node);
+ scope.declared.add(id);
+ _markUsed(node, id, scope);
+ }
+
visitIdentifier(Identifier node) {
Jennifer Messerly 2015/04/22 16:19:53 previously, we simply recorded usage of a name, an
+ var id = identifierKey(node);
+
+ // Find where the node was declared.
+ var declScope = scope;
+ while (declScope != null && !declScope.declared.contains(id)) {
+ declScope = declScope.parent;
+ }
+ if (declScope == null) {
+ // Assume it comes from the global scope.
+ declScope = rootScope;
+ declScope.declared.add(id);
+ }
+ _markUsed(node, id, declScope);
+ }
+
+ _markUsed(Identifier node, Object id, _FunctionScope declScope) {
+ // If it needs rename, we can't add it to the used name set yet, instead we
+ // will record all scopes it is visible in.
+ Set<_FunctionScope> usedIn = null;
if (needsRename(node)) {
- // We can't assign the name yet, but we can add it to the list of things
- // that need a name.
- var id = renameKey(node);
- pendingRenames.putIfAbsent(id, () => new HashSet()).add(scope);
- } else {
- scope.names.add(node.name);
+ usedIn = pendingRenames.putIfAbsent(id, () => new HashSet());
+ }
+
+ for (var s = scope, end = declScope.parent; s != end; s = s.parent) {
+ if (usedIn != null) {
+ usedIn.add(s);
+ } else {
+ s.used.add(node.name);
+ }
}
}
visitFunctionExpression(FunctionExpression node) {
- scope = new _FunctionScope(scope);
- super.visitFunctionExpression(node);
- scope = scope.parent;
+ // Visit nested functions after all identifiers are declared.
+ scope.functions.add(node);
+ }
+
+ void _finishFunctions() {
+ for (var f in scope.functions) {
+ scope = new _FunctionScope(scope);
+ super.visitFunctionExpression(f);
+ _finishFunctions();
+ scope = scope.parent;
+ }
}
void _finishNames() {
+ var allNames = new Set<String>();
pendingRenames.forEach((id, scopes) {
- var name = _findName(id, _allNamesInScope(scopes));
- renames[id] = name;
- for (var s in scopes) s.names.add(name);
- });
- }
+ allNames.clear();
+ for (var s in scopes) allNames.addAll(s.used);
- // Given a set of scopes, populates [allNames] to include all names in those
- // scopes as well as intermediate scopes. Returns the common parent of
- // all scopes. For example:
- //
- // function outer(t) {
- // function middle(x) {
- // function inner() { return t; }
- // foo(x);
- // }
- // }
- //
- // Here `t` is used in `inner` and `outer` but we need to include `middle`
- // as well, so we know the rename of `t` to `x` is not valid.
- static Set<String> _allNamesInScope(Set<_FunctionScope> scopes) {
- // As we iterate, we'll add more scopes. We don't need to consider these
- // as intermediate scopes can't introduce new intermediates.
- var candidates = [];
- var allScopes = scopes.toSet();
- for (var scope in scopes) {
- for (var p = scope.parent; p != null; p = p.parent) {
- if (allScopes.contains(p)) {
- allScopes.addAll(candidates);
- break;
- }
- candidates.add(p);
- }
- // Discard these, we already added them or we didn't find a parent scope.
- candidates.clear();
- }
+ var name = _findName(id, allNames);
+ renames[id] = name;
- // Now collect all names found.
- return allScopes.expand((s) => s.names).toSet();
+ for (var s in scopes) s.used.add(name);
+ });
}
static String _findName(Object id, Set<String> usedNames) {
@@ -154,7 +181,7 @@ class _RenameVisitor extends BaseVisitor {
bool needsRename(Identifier node) =>
node is JSTemporary || node.allowRename && invalidJSVariableName(node.name);
-Object /*String|JSTemporary*/ renameKey(Identifier node) =>
+Object /*String|JSTemporary*/ identifierKey(Identifier node) =>
node is JSTemporary ? node : node.name;
/// Returns true for invalid JS variable names, such as keywords.
« no previous file with comments | « lib/runtime/dart/core.js ('k') | lib/src/js/printer.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698