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

Side by Side 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 unified diff | Download patch
« no previous file with comments | « lib/runtime/dart/core.js ('k') | lib/src/js/printer.dart » ('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) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, 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 dev_compiler.src.codegen.js_names; 5 library dev_compiler.src.codegen.js_names;
6 6
7 import 'dart:collection'; 7 import 'dart:collection';
8 import 'package:dev_compiler/src/js/js_ast.dart'; 8 import 'package:dev_compiler/src/js/js_ast.dart';
9 9
10 /// Unique instance for temporary variables. Will be renamed consistently 10 /// Unique instance for temporary variables. Will be renamed consistently
(...skipping 16 matching lines...) Expand all
27 /// `name` field simply the suggestion of what name to use. By contrast 27 /// `name` field simply the suggestion of what name to use. By contrast
28 /// [Identifiers] are never renamed unless they are an invalid identifier, like 28 /// [Identifiers] are never renamed unless they are an invalid identifier, like
29 /// `function` or `instanceof`, and their `name` field controls whether they 29 /// `function` or `instanceof`, and their `name` field controls whether they
30 /// refer to the same variable. 30 /// refer to the same variable.
31 class JSNamer extends LocalNamer { 31 class JSNamer extends LocalNamer {
32 final Map<Object, String> renames; 32 final Map<Object, String> renames;
33 33
34 JSNamer(Node node) : renames = new _RenameVisitor.build(node).renames; 34 JSNamer(Node node) : renames = new _RenameVisitor.build(node).renames;
35 35
36 String getName(Identifier node) { 36 String getName(Identifier node) {
37 var rename = renames[renameKey(node)]; 37 var rename = renames[identifierKey(node)];
38 if (rename != null) return rename; 38 if (rename != null) return rename;
39 39
40 assert(!needsRename(node)); 40 assert(!needsRename(node));
41 return node.name; 41 return node.name;
42 } 42 }
43 43
44 void enterScope(FunctionExpression node) {} 44 void enterScope(FunctionExpression node) {}
45 void leaveScope() {} 45 void leaveScope() {}
46 } 46 }
47 47
48 /// Represents a complete function scope in JS.
49 ///
50 /// We don't currently track ES6 block scopes, because we don't represent them
51 /// in js_ast yet.
48 class _FunctionScope { 52 class _FunctionScope {
53 /// The parent scope.
49 final _FunctionScope parent; 54 final _FunctionScope parent;
50 final names = new HashSet<String>(); 55
56 /// All names declared in this scope.
57 final declared = new HashSet<Object>();
58
59 /// All names [declared] in this scope or its [parent]s, that is used in this
60 /// scope and/or children. This is exactly the set of variable names we must
61 /// not collide with inside this scope.
62 final used = new HashSet<String>();
63
64 /// Nested functions, these are visited after everything else so the names
65 /// they might need are in scope.
66 final functions = new List<FunctionExpression>();
67
51 _FunctionScope(this.parent); 68 _FunctionScope(this.parent);
52 } 69 }
53 70
54 /// Collects all names used in the visited tree. 71 /// Collects all names used in the visited tree.
55 class _RenameVisitor extends BaseVisitor { 72 class _RenameVisitor extends VariableDeclarationVisitor {
56 final pendingRenames = new Map<Object, Set<_FunctionScope>>(); 73 final pendingRenames = new Map<Object, Set<_FunctionScope>>();
57 final renames = new HashMap<Object, String>(); 74 final renames = new HashMap<Object, String>();
58 75
59 _FunctionScope scope = new _FunctionScope(null); 76 final _FunctionScope rootScope = new _FunctionScope(null);
77 _FunctionScope scope;
60 78
61 _RenameVisitor.build(Node root) { 79 _RenameVisitor.build(Node root) {
80 scope = rootScope;
62 root.accept(this); 81 root.accept(this);
82 _finishFunctions();
63 _finishNames(); 83 _finishNames();
64 } 84 }
65 85
86 declare(Identifier node) {
87 var id = identifierKey(node);
88 scope.declared.add(id);
89 _markUsed(node, id, scope);
90 }
91
66 visitIdentifier(Identifier node) { 92 visitIdentifier(Identifier node) {
Jennifer Messerly 2015/04/22 16:19:53 previously, we simply recorded usage of a name, an
93 var id = identifierKey(node);
94
95 // Find where the node was declared.
96 var declScope = scope;
97 while (declScope != null && !declScope.declared.contains(id)) {
98 declScope = declScope.parent;
99 }
100 if (declScope == null) {
101 // Assume it comes from the global scope.
102 declScope = rootScope;
103 declScope.declared.add(id);
104 }
105 _markUsed(node, id, declScope);
106 }
107
108 _markUsed(Identifier node, Object id, _FunctionScope declScope) {
109 // If it needs rename, we can't add it to the used name set yet, instead we
110 // will record all scopes it is visible in.
111 Set<_FunctionScope> usedIn = null;
67 if (needsRename(node)) { 112 if (needsRename(node)) {
68 // We can't assign the name yet, but we can add it to the list of things 113 usedIn = pendingRenames.putIfAbsent(id, () => new HashSet());
69 // that need a name. 114 }
70 var id = renameKey(node); 115
71 pendingRenames.putIfAbsent(id, () => new HashSet()).add(scope); 116 for (var s = scope, end = declScope.parent; s != end; s = s.parent) {
72 } else { 117 if (usedIn != null) {
73 scope.names.add(node.name); 118 usedIn.add(s);
119 } else {
120 s.used.add(node.name);
121 }
74 } 122 }
75 } 123 }
76 124
77 visitFunctionExpression(FunctionExpression node) { 125 visitFunctionExpression(FunctionExpression node) {
78 scope = new _FunctionScope(scope); 126 // Visit nested functions after all identifiers are declared.
79 super.visitFunctionExpression(node); 127 scope.functions.add(node);
80 scope = scope.parent; 128 }
129
130 void _finishFunctions() {
131 for (var f in scope.functions) {
132 scope = new _FunctionScope(scope);
133 super.visitFunctionExpression(f);
134 _finishFunctions();
135 scope = scope.parent;
136 }
81 } 137 }
82 138
83 void _finishNames() { 139 void _finishNames() {
140 var allNames = new Set<String>();
84 pendingRenames.forEach((id, scopes) { 141 pendingRenames.forEach((id, scopes) {
85 var name = _findName(id, _allNamesInScope(scopes)); 142 allNames.clear();
143 for (var s in scopes) allNames.addAll(s.used);
144
145 var name = _findName(id, allNames);
86 renames[id] = name; 146 renames[id] = name;
87 for (var s in scopes) s.names.add(name); 147
148 for (var s in scopes) s.used.add(name);
88 }); 149 });
89 } 150 }
90 151
91 // Given a set of scopes, populates [allNames] to include all names in those
92 // scopes as well as intermediate scopes. Returns the common parent of
93 // all scopes. For example:
94 //
95 // function outer(t) {
96 // function middle(x) {
97 // function inner() { return t; }
98 // foo(x);
99 // }
100 // }
101 //
102 // Here `t` is used in `inner` and `outer` but we need to include `middle`
103 // as well, so we know the rename of `t` to `x` is not valid.
104 static Set<String> _allNamesInScope(Set<_FunctionScope> scopes) {
105 // As we iterate, we'll add more scopes. We don't need to consider these
106 // as intermediate scopes can't introduce new intermediates.
107 var candidates = [];
108 var allScopes = scopes.toSet();
109 for (var scope in scopes) {
110 for (var p = scope.parent; p != null; p = p.parent) {
111 if (allScopes.contains(p)) {
112 allScopes.addAll(candidates);
113 break;
114 }
115 candidates.add(p);
116 }
117 // Discard these, we already added them or we didn't find a parent scope.
118 candidates.clear();
119 }
120
121 // Now collect all names found.
122 return allScopes.expand((s) => s.names).toSet();
123 }
124
125 static String _findName(Object id, Set<String> usedNames) { 152 static String _findName(Object id, Set<String> usedNames) {
126 String name; 153 String name;
127 bool valid; 154 bool valid;
128 if (id is JSTemporary) { 155 if (id is JSTemporary) {
129 name = id.name; 156 name = id.name;
130 valid = !invalidJSVariableName(name); 157 valid = !invalidJSVariableName(name);
131 } else { 158 } else {
132 name = id; 159 name = id;
133 valid = false; 160 valid = false;
134 } 161 }
(...skipping 12 matching lines...) Expand all
147 candidate = '${name}\$$i'; 174 candidate = '${name}\$$i';
148 } 175 }
149 } 176 }
150 return candidate; 177 return candidate;
151 } 178 }
152 } 179 }
153 180
154 bool needsRename(Identifier node) => 181 bool needsRename(Identifier node) =>
155 node is JSTemporary || node.allowRename && invalidJSVariableName(node.name); 182 node is JSTemporary || node.allowRename && invalidJSVariableName(node.name);
156 183
157 Object /*String|JSTemporary*/ renameKey(Identifier node) => 184 Object /*String|JSTemporary*/ identifierKey(Identifier node) =>
158 node is JSTemporary ? node : node.name; 185 node is JSTemporary ? node : node.name;
159 186
160 /// Returns true for invalid JS variable names, such as keywords. 187 /// Returns true for invalid JS variable names, such as keywords.
161 /// Also handles invalid variable names in strict mode, like "arguments". 188 /// Also handles invalid variable names in strict mode, like "arguments".
162 bool invalidJSVariableName(String keyword, {bool strictMode: true}) { 189 bool invalidJSVariableName(String keyword, {bool strictMode: true}) {
163 switch (keyword) { 190 switch (keyword) {
164 case "break": 191 case "break":
165 case "case": 192 case "case":
166 case "catch": 193 case "catch":
167 case "class": 194 case "class":
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
208 /// In particular, "caller" "callee" and "arguments" cannot be used. 235 /// In particular, "caller" "callee" and "arguments" cannot be used.
209 bool invalidJSStaticMethodName(String name) { 236 bool invalidJSStaticMethodName(String name) {
210 switch (name) { 237 switch (name) {
211 case "arguments": 238 case "arguments":
212 case "caller": 239 case "caller":
213 case "callee": 240 case "callee":
214 return true; 241 return true;
215 } 242 }
216 return false; 243 return false;
217 } 244 }
OLDNEW
« 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