Chromium Code Reviews| 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 static int __id = 0; | |
|
Jennifer Messerly
2015/05/12 16:23:46
this helped for debugging, I could remove it thoug
| |
| 18 final int _id = ++__id; | |
| 17 TemporaryId(String name) : super(name); | 19 TemporaryId(String name) : super(name); |
| 20 toString() => super.toString() + ' $_id'; | |
| 21 } | |
| 22 | |
| 23 /// Creates a qualified identifier, without determining for sure if it needs to | |
| 24 /// be qualified until [setQualified] is called. | |
| 25 /// | |
| 26 /// This expression is transparent to visiting after [setQualified]. | |
| 27 class MaybeQualifiedId extends Expression { | |
| 28 Expression _expr; | |
| 29 | |
| 30 final Identifier qualifier; | |
| 31 final Expression name; | |
| 32 | |
| 33 MaybeQualifiedId(this.qualifier, this.name) { | |
| 34 _expr = new PropertyAccess(qualifier, name); | |
| 35 } | |
| 36 | |
| 37 /// Helper to create an [Identifier] from something that starts as a property. | |
| 38 static identifier(LiteralString propertyName) => | |
| 39 new Identifier(propertyName.valueWithoutQuotes); | |
| 40 | |
| 41 void setQualified(bool qualified) { | |
| 42 if (!qualified && name is LiteralString) { | |
| 43 _expr = identifier(name); | |
| 44 } | |
| 45 } | |
| 46 | |
| 47 int get precedenceLevel => _expr.precedenceLevel; | |
| 48 | |
| 49 accept(NodeVisitor visitor) => _expr.accept(visitor); | |
| 50 | |
| 51 void visitChildren(NodeVisitor visitor) => _expr.visitChildren(visitor); | |
| 18 } | 52 } |
| 19 | 53 |
| 20 /// This class has two purposes: | 54 /// This class has two purposes: |
| 21 /// | 55 /// |
| 22 /// * rename JS identifiers to avoid keywords. | 56 /// * rename JS identifiers to avoid keywords. |
| 23 /// * rename temporary variables to avoid colliding with user-specified names, | 57 /// * rename temporary variables to avoid colliding with user-specified names, |
| 24 /// or other temporaries | 58 /// or other temporaries |
| 25 /// | 59 /// |
| 26 /// Each instance of [JSTemporary] is treated as a unique variable, with its | 60 /// 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 | 61 /// `name` field simply the suggestion of what name to use. By contrast |
| 28 /// [Identifiers] are never renamed unless they are an invalid identifier, like | 62 /// [Identifiers] are never renamed unless they are an invalid identifier, like |
| 29 /// `function` or `instanceof`, and their `name` field controls whether they | 63 /// `function` or `instanceof`, and their `name` field controls whether they |
| 30 /// refer to the same variable. | 64 /// refer to the same variable. |
| 31 class TemporaryNamer extends LocalNamer { | 65 class TemporaryNamer extends LocalNamer { |
| 32 final Map<Object, String> renames; | 66 final Map<Object, String> renames; |
| 33 | 67 |
| 34 TemporaryNamer(Node node) : renames = new _RenameVisitor.build(node).renames; | 68 TemporaryNamer(Node node) : renames = new _RenameVisitor.build(node).renames; |
| 35 | 69 |
| 36 String getName(Identifier node) { | 70 String getName(Identifier node) { |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 63 | 97 |
| 64 /// Nested functions, these are visited after everything else so the names | 98 /// Nested functions, these are visited after everything else so the names |
| 65 /// they might need are in scope. | 99 /// they might need are in scope. |
| 66 final functions = new List<FunctionExpression>(); | 100 final functions = new List<FunctionExpression>(); |
| 67 | 101 |
| 68 _FunctionScope(this.parent); | 102 _FunctionScope(this.parent); |
| 69 } | 103 } |
| 70 | 104 |
| 71 /// Collects all names used in the visited tree. | 105 /// Collects all names used in the visited tree. |
| 72 class _RenameVisitor extends VariableDeclarationVisitor { | 106 class _RenameVisitor extends VariableDeclarationVisitor { |
| 73 final pendingRenames = new Map<Object, Set<_FunctionScope>>(); | 107 final pendingRenames = new Map<Object, Set<_FunctionScope>>.identity(); |
| 74 final renames = new HashMap<Object, String>(); | 108 final renames = new HashMap<Object, String>.identity(); |
| 75 | 109 |
| 76 final _FunctionScope rootScope = new _FunctionScope(null); | 110 final _FunctionScope rootScope = new _FunctionScope(null); |
| 77 _FunctionScope scope; | 111 _FunctionScope scope; |
| 78 | 112 |
| 79 _RenameVisitor.build(Node root) { | 113 _RenameVisitor.build(Node root) { |
| 80 scope = rootScope; | 114 scope = rootScope; |
| 81 root.accept(this); | 115 root.accept(this); |
| 82 _finishFunctions(); | 116 _finishFunctions(); |
| 83 _finishNames(); | 117 _finishNames(); |
| 84 } | 118 } |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 108 _markUsed(node, id, declScope); | 142 _markUsed(node, id, declScope); |
| 109 } | 143 } |
| 110 | 144 |
| 111 _markUsed(Identifier node, Object id, _FunctionScope declScope) { | 145 _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 | 146 // 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. | 147 // will record all scopes it is visible in. |
| 114 Set<_FunctionScope> usedIn = null; | 148 Set<_FunctionScope> usedIn = null; |
| 115 if (needsRename(node)) { | 149 if (needsRename(node)) { |
| 116 usedIn = pendingRenames.putIfAbsent(id, () => new HashSet()); | 150 usedIn = pendingRenames.putIfAbsent(id, () => new HashSet()); |
| 117 } | 151 } |
| 118 | |
| 119 for (var s = scope, end = declScope.parent; s != end; s = s.parent) { | 152 for (var s = scope, end = declScope.parent; s != end; s = s.parent) { |
| 120 if (usedIn != null) { | 153 if (usedIn != null) { |
| 121 usedIn.add(s); | 154 usedIn.add(s); |
| 122 } else { | 155 } else { |
| 123 s.used.add(node.name); | 156 s.used.add(node.name); |
| 124 } | 157 } |
| 125 } | 158 } |
| 126 } | 159 } |
| 127 | 160 |
| 128 visitFunctionExpression(FunctionExpression node) { | 161 visitFunctionExpression(FunctionExpression node) { |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 177 candidate = '${name}\$$i'; | 210 candidate = '${name}\$$i'; |
| 178 } | 211 } |
| 179 } | 212 } |
| 180 return candidate; | 213 return candidate; |
| 181 } | 214 } |
| 182 } | 215 } |
| 183 | 216 |
| 184 bool needsRename(Identifier node) => | 217 bool needsRename(Identifier node) => |
| 185 node is TemporaryId || node.allowRename && invalidVariableName(node.name); | 218 node is TemporaryId || node.allowRename && invalidVariableName(node.name); |
| 186 | 219 |
| 187 Object /*String|JSTemporary*/ identifierKey(Identifier node) => | 220 Object /*String|TemporaryId*/ identifierKey(Identifier node) => |
| 188 node is TemporaryId ? node : node.name; | 221 node is TemporaryId ? node : node.name; |
| 189 | 222 |
| 190 /// Returns true for invalid JS variable names, such as keywords. | 223 /// Returns true for invalid JS variable names, such as keywords. |
| 191 /// Also handles invalid variable names in strict mode, like "arguments". | 224 /// Also handles invalid variable names in strict mode, like "arguments". |
| 192 bool invalidVariableName(String keyword, {bool strictMode: true}) { | 225 bool invalidVariableName(String keyword, {bool strictMode: true}) { |
| 193 switch (keyword) { | 226 switch (keyword) { |
| 194 case "break": | 227 case "break": |
| 195 case "case": | 228 case "case": |
| 196 case "catch": | 229 case "catch": |
| 197 case "class": | 230 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. | 271 /// In particular, "caller" "callee" and "arguments" cannot be used. |
| 239 bool invalidStaticFieldName(String name) { | 272 bool invalidStaticFieldName(String name) { |
| 240 switch (name) { | 273 switch (name) { |
| 241 case "arguments": | 274 case "arguments": |
| 242 case "caller": | 275 case "caller": |
| 243 case "callee": | 276 case "callee": |
| 244 return true; | 277 return true; |
| 245 } | 278 } |
| 246 return false; | 279 return false; |
| 247 } | 280 } |
| OLD | NEW |