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 |
11 /// across the entire file. Different instances will be named differently | 11 /// across the entire file. Different instances will be named differently |
12 /// even if they have the same name, this makes it safe to use in code | 12 /// even if they have the same name, this makes it safe to use in code |
13 /// generation without needing global knowledge. See [JSNamer]. | 13 /// generation without needing global knowledge. See [JSNamer]. |
14 /// | 14 /// |
15 // TODO(jmesserly): move into js_ast? add a boolean to Identifier? | 15 // TODO(jmesserly): move into js_ast? add a boolean to Identifier? |
16 class TemporaryId extends Identifier { | 16 class TemporaryId extends Identifier { |
17 TemporaryId(String name) : super(name); | 17 TemporaryId(String name) : super(name); |
18 } | 18 } |
19 | 19 |
| 20 /// Creates a qualified identifier, without determining for sure if it needs to |
| 21 /// be qualified until [setQualified] is called. |
| 22 /// |
| 23 /// This expression is transparent to visiting after [setQualified]. |
| 24 class MaybeQualifiedId extends Expression { |
| 25 Expression _expr; |
| 26 |
| 27 final Identifier qualifier; |
| 28 final Expression name; |
| 29 |
| 30 MaybeQualifiedId(this.qualifier, this.name) { |
| 31 _expr = new PropertyAccess(qualifier, name); |
| 32 } |
| 33 |
| 34 /// Helper to create an [Identifier] from something that starts as a property. |
| 35 static identifier(LiteralString propertyName) => |
| 36 new Identifier(propertyName.valueWithoutQuotes); |
| 37 |
| 38 void setQualified(bool qualified) { |
| 39 if (!qualified && name is LiteralString) { |
| 40 _expr = identifier(name); |
| 41 } |
| 42 } |
| 43 |
| 44 int get precedenceLevel => _expr.precedenceLevel; |
| 45 |
| 46 accept(NodeVisitor visitor) => _expr.accept(visitor); |
| 47 |
| 48 void visitChildren(NodeVisitor visitor) => _expr.visitChildren(visitor); |
| 49 } |
| 50 |
20 /// This class has two purposes: | 51 /// This class has two purposes: |
21 /// | 52 /// |
22 /// * rename JS identifiers to avoid keywords. | 53 /// * rename JS identifiers to avoid keywords. |
23 /// * rename temporary variables to avoid colliding with user-specified names, | 54 /// * rename temporary variables to avoid colliding with user-specified names, |
24 /// or other temporaries | 55 /// or other temporaries |
25 /// | 56 /// |
26 /// Each instance of [JSTemporary] is treated as a unique variable, with its | 57 /// Each instance of [TemporaryId] is treated as a unique variable, with its |
27 /// `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 |
28 /// [Identifiers] are never renamed unless they are an invalid identifier, like | 59 /// [Identifiers] are never renamed unless they are an invalid identifier, like |
29 /// `function` or `instanceof`, and their `name` field controls whether they | 60 /// `function` or `instanceof`, and their `name` field controls whether they |
30 /// refer to the same variable. | 61 /// refer to the same variable. |
31 class TemporaryNamer extends LocalNamer { | 62 class TemporaryNamer extends LocalNamer { |
32 final Map<Object, String> renames; | 63 final Map<Object, String> renames; |
33 | 64 |
34 TemporaryNamer(Node node) : renames = new _RenameVisitor.build(node).renames; | 65 TemporaryNamer(Node node) : renames = new _RenameVisitor.build(node).renames; |
35 | 66 |
36 String getName(Identifier node) { | 67 String getName(Identifier node) { |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
108 _markUsed(node, id, declScope); | 139 _markUsed(node, id, declScope); |
109 } | 140 } |
110 | 141 |
111 _markUsed(Identifier node, Object id, _FunctionScope declScope) { | 142 _markUsed(Identifier node, Object id, _FunctionScope declScope) { |
112 // If it needs rename, we can't add it to the used name set yet, instead we | 143 // If it needs rename, we can't add it to the used name set yet, instead we |
113 // will record all scopes it is visible in. | 144 // will record all scopes it is visible in. |
114 Set<_FunctionScope> usedIn = null; | 145 Set<_FunctionScope> usedIn = null; |
115 if (needsRename(node)) { | 146 if (needsRename(node)) { |
116 usedIn = pendingRenames.putIfAbsent(id, () => new HashSet()); | 147 usedIn = pendingRenames.putIfAbsent(id, () => new HashSet()); |
117 } | 148 } |
118 | |
119 for (var s = scope, end = declScope.parent; s != end; s = s.parent) { | 149 for (var s = scope, end = declScope.parent; s != end; s = s.parent) { |
120 if (usedIn != null) { | 150 if (usedIn != null) { |
121 usedIn.add(s); | 151 usedIn.add(s); |
122 } else { | 152 } else { |
123 s.used.add(node.name); | 153 s.used.add(node.name); |
124 } | 154 } |
125 } | 155 } |
126 } | 156 } |
127 | 157 |
128 visitFunctionExpression(FunctionExpression node) { | 158 visitFunctionExpression(FunctionExpression node) { |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
177 candidate = '${name}\$$i'; | 207 candidate = '${name}\$$i'; |
178 } | 208 } |
179 } | 209 } |
180 return candidate; | 210 return candidate; |
181 } | 211 } |
182 } | 212 } |
183 | 213 |
184 bool needsRename(Identifier node) => | 214 bool needsRename(Identifier node) => |
185 node is TemporaryId || node.allowRename && invalidVariableName(node.name); | 215 node is TemporaryId || node.allowRename && invalidVariableName(node.name); |
186 | 216 |
187 Object /*String|JSTemporary*/ identifierKey(Identifier node) => | 217 Object /*String|TemporaryId*/ identifierKey(Identifier node) => |
188 node is TemporaryId ? node : node.name; | 218 node is TemporaryId ? node : node.name; |
189 | 219 |
190 /// Returns true for invalid JS variable names, such as keywords. | 220 /// Returns true for invalid JS variable names, such as keywords. |
191 /// Also handles invalid variable names in strict mode, like "arguments". | 221 /// Also handles invalid variable names in strict mode, like "arguments". |
192 bool invalidVariableName(String keyword, {bool strictMode: true}) { | 222 bool invalidVariableName(String keyword, {bool strictMode: true}) { |
193 switch (keyword) { | 223 switch (keyword) { |
194 case "break": | 224 case "break": |
195 case "case": | 225 case "case": |
196 case "catch": | 226 case "catch": |
197 case "class": | 227 case "class": |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
238 /// In particular, "caller" "callee" and "arguments" cannot be used. | 268 /// In particular, "caller" "callee" and "arguments" cannot be used. |
239 bool invalidStaticFieldName(String name) { | 269 bool invalidStaticFieldName(String name) { |
240 switch (name) { | 270 switch (name) { |
241 case "arguments": | 271 case "arguments": |
242 case "caller": | 272 case "caller": |
243 case "callee": | 273 case "callee": |
244 return true; | 274 return true; |
245 } | 275 } |
246 return false; | 276 return false; |
247 } | 277 } |
OLD | NEW |