| 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 |