OLD | NEW |
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 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
53 /// * rename JS identifiers to avoid keywords. | 53 /// * rename JS identifiers to avoid keywords. |
54 /// * rename temporary variables to avoid colliding with user-specified names, | 54 /// * rename temporary variables to avoid colliding with user-specified names, |
55 /// or other temporaries | 55 /// or other temporaries |
56 /// | 56 /// |
57 /// Each instance of [TemporaryId] is treated as a unique variable, with its | 57 /// Each instance of [TemporaryId] is treated as a unique variable, with its |
58 /// `name` field simply the suggestion of what name to use. By contrast | 58 /// `name` field simply the suggestion of what name to use. By contrast |
59 /// [Identifiers] are never renamed unless they are an invalid identifier, like | 59 /// [Identifiers] are never renamed unless they are an invalid identifier, like |
60 /// `function` or `instanceof`, and their `name` field controls whether they | 60 /// `function` or `instanceof`, and their `name` field controls whether they |
61 /// refer to the same variable. | 61 /// refer to the same variable. |
62 class TemporaryNamer extends LocalNamer { | 62 class TemporaryNamer extends LocalNamer { |
63 final Map<Object, String> renames; | 63 _FunctionScope scope; |
64 | 64 |
65 TemporaryNamer(Node node) : renames = new _RenameVisitor.build(node).renames; | 65 TemporaryNamer(Node node) : scope = new _RenameVisitor.build(node).rootScope; |
66 | 66 |
67 String getName(Identifier node) { | 67 String getName(Identifier node) { |
68 var rename = renames[identifierKey(node)]; | 68 var rename = scope.renames[identifierKey(node)]; |
69 if (rename != null) return rename; | 69 if (rename != null) return rename; |
70 | |
71 assert(!needsRename(node)); | |
72 return node.name; | 70 return node.name; |
73 } | 71 } |
74 | 72 |
75 void enterScope(FunctionExpression node) {} | 73 void enterScope(FunctionExpression node) { |
76 void leaveScope() {} | 74 scope = scope.functions[node]; |
| 75 } |
| 76 void leaveScope() { |
| 77 scope = scope.parent; |
| 78 } |
77 } | 79 } |
78 | 80 |
79 /// Represents a complete function scope in JS. | 81 /// Represents a complete function scope in JS. |
80 /// | 82 /// |
81 /// We don't currently track ES6 block scopes, because we don't represent them | 83 /// We don't currently track ES6 block scopes, because we don't represent them |
82 /// in js_ast yet. | 84 /// in js_ast yet. |
83 class _FunctionScope { | 85 class _FunctionScope { |
84 /// The parent scope. | 86 /// The parent scope. |
85 final _FunctionScope parent; | 87 final _FunctionScope parent; |
86 | 88 |
87 /// All names declared in this scope. | 89 /// All names declared in this scope. |
88 final declared = new HashSet<Object>(); | 90 final declared = new HashSet<Object>(); |
89 | 91 |
90 /// All names [declared] in this scope or its [parent]s, that is used in this | 92 /// All names [declared] in this scope or its [parent]s, that is used in this |
91 /// scope and/or children. This is exactly the set of variable names we must | 93 /// scope and/or children. This is exactly the set of variable names we must |
92 /// not collide with inside this scope. | 94 /// not collide with inside this scope. |
93 final used = new HashSet<String>(); | 95 final used = new HashSet<String>(); |
94 | 96 |
95 /// Nested functions, these are visited after everything else so the names | 97 /// Nested functions, these are visited after everything else so the names |
96 /// they might need are in scope. | 98 /// they might need are in scope. |
97 final functions = new List<FunctionExpression>(); | 99 final functions = new Map<FunctionExpression, _FunctionScope>(); |
| 100 |
| 101 /// New names assigned for temps and identifiers. |
| 102 final renames = new HashMap<Object, String>(); |
98 | 103 |
99 _FunctionScope(this.parent); | 104 _FunctionScope(this.parent); |
100 } | 105 } |
101 | 106 |
102 /// Collects all names used in the visited tree. | 107 /// Collects all names used in the visited tree. |
103 class _RenameVisitor extends VariableDeclarationVisitor { | 108 class _RenameVisitor extends VariableDeclarationVisitor { |
104 final pendingRenames = new Map<Object, Set<_FunctionScope>>(); | 109 final pendingRenames = new Map<Object, Set<_FunctionScope>>(); |
105 final renames = new HashMap<Object, String>(); | |
106 | 110 |
107 final _FunctionScope rootScope = new _FunctionScope(null); | 111 final _FunctionScope rootScope = new _FunctionScope(null); |
108 _FunctionScope scope; | 112 _FunctionScope scope; |
109 | 113 |
110 _RenameVisitor.build(Node root) { | 114 _RenameVisitor.build(Node root) { |
111 scope = rootScope; | 115 scope = rootScope; |
112 root.accept(this); | 116 root.accept(this); |
113 _finishFunctions(); | 117 _finishFunctions(); |
114 _finishNames(); | 118 _finishNames(); |
115 } | 119 } |
(...skipping 20 matching lines...) Expand all Loading... |
136 declScope = rootScope; | 140 declScope = rootScope; |
137 declScope.declared.add(id); | 141 declScope.declared.add(id); |
138 } | 142 } |
139 _markUsed(node, id, declScope); | 143 _markUsed(node, id, declScope); |
140 } | 144 } |
141 | 145 |
142 _markUsed(Identifier node, Object id, _FunctionScope declScope) { | 146 _markUsed(Identifier node, Object id, _FunctionScope declScope) { |
143 // If it needs rename, we can't add it to the used name set yet, instead we | 147 // If it needs rename, we can't add it to the used name set yet, instead we |
144 // will record all scopes it is visible in. | 148 // will record all scopes it is visible in. |
145 Set<_FunctionScope> usedIn = null; | 149 Set<_FunctionScope> usedIn = null; |
146 if (needsRename(node)) { | 150 var rename = declScope != rootScope && needsRename(node); |
| 151 if (rename) { |
147 usedIn = pendingRenames.putIfAbsent(id, () => new HashSet()); | 152 usedIn = pendingRenames.putIfAbsent(id, () => new HashSet()); |
148 } | 153 } |
149 for (var s = scope, end = declScope.parent; s != end; s = s.parent) { | 154 for (var s = scope, end = declScope.parent; s != end; s = s.parent) { |
150 if (usedIn != null) { | 155 if (usedIn != null) { |
151 usedIn.add(s); | 156 usedIn.add(s); |
152 } else { | 157 } else { |
153 s.used.add(node.name); | 158 s.used.add(node.name); |
154 } | 159 } |
155 } | 160 } |
156 } | 161 } |
157 | 162 |
158 visitFunctionExpression(FunctionExpression node) { | 163 visitFunctionExpression(FunctionExpression node) { |
159 // Visit nested functions after all identifiers are declared. | 164 // Visit nested functions after all identifiers are declared. |
160 scope.functions.add(node); | 165 scope.functions[node] = new _FunctionScope(scope); |
161 } | 166 } |
162 | 167 |
163 void _finishFunctions() { | 168 void _finishFunctions() { |
164 for (var f in scope.functions) { | 169 scope.functions.forEach((FunctionExpression f, _FunctionScope s) { |
165 scope = new _FunctionScope(scope); | 170 scope = s; |
166 super.visitFunctionExpression(f); | 171 super.visitFunctionExpression(f); |
167 _finishFunctions(); | 172 _finishFunctions(); |
168 scope = scope.parent; | 173 scope = scope.parent; |
169 } | 174 }); |
170 } | 175 } |
171 | 176 |
172 void _finishNames() { | 177 void _finishNames() { |
173 var allNames = new Set<String>(); | 178 var allNames = new Set<String>(); |
174 pendingRenames.forEach((id, scopes) { | 179 pendingRenames.forEach((id, scopes) { |
175 allNames.clear(); | 180 allNames.clear(); |
176 for (var s in scopes) allNames.addAll(s.used); | 181 for (var s in scopes) allNames.addAll(s.used); |
177 | 182 |
178 var name = _findName(id, allNames); | 183 var name = _findName(id, allNames); |
179 renames[id] = name; | 184 for (var s in scopes) { |
180 | 185 s.used.add(name); |
181 for (var s in scopes) s.used.add(name); | 186 s.renames[id] = name; |
| 187 } |
182 }); | 188 }); |
183 } | 189 } |
184 | 190 |
185 static String _findName(Object id, Set<String> usedNames) { | 191 static String _findName(Object id, Set<String> usedNames) { |
186 String name; | 192 String name; |
187 bool valid; | 193 bool valid; |
188 if (id is TemporaryId) { | 194 if (id is TemporaryId) { |
189 name = id.name; | 195 name = id.name; |
190 valid = !invalidVariableName(name); | 196 valid = !invalidVariableName(name); |
191 } else { | 197 } else { |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
269 /// In particular, "caller" "callee" and "arguments" cannot be used. | 275 /// In particular, "caller" "callee" and "arguments" cannot be used. |
270 bool invalidStaticFieldName(String name) { | 276 bool invalidStaticFieldName(String name) { |
271 switch (name) { | 277 switch (name) { |
272 case "arguments": | 278 case "arguments": |
273 case "caller": | 279 case "caller": |
274 case "callee": | 280 case "callee": |
275 return true; | 281 return true; |
276 } | 282 } |
277 return false; | 283 return false; |
278 } | 284 } |
OLD | NEW |