Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, 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 part of js_backend; | 5 part of js_backend; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * Assigns JavaScript identifiers to Dart variables, class-names and members. | 8 * Assigns JavaScript identifiers to Dart variables, class-names and members. |
| 9 */ | 9 */ |
| 10 class MinifyNamer extends Namer { | 10 class MinifyNamer extends Namer { |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 28 // counterpart it will never return the proposedName as the new fresh name. | 28 // counterpart it will never return the proposedName as the new fresh name. |
| 29 String getFreshName(String proposedName, | 29 String getFreshName(String proposedName, |
| 30 Set<String> usedNames, | 30 Set<String> usedNames, |
| 31 Map<String, String> suggestedNames, | 31 Map<String, String> suggestedNames, |
| 32 {bool ensureSafe: true}) { | 32 {bool ensureSafe: true}) { |
| 33 var freshName; | 33 var freshName; |
| 34 var suggestion = suggestedNames[proposedName]; | 34 var suggestion = suggestedNames[proposedName]; |
| 35 if (suggestion != null && !usedNames.contains(suggestion)) { | 35 if (suggestion != null && !usedNames.contains(suggestion)) { |
| 36 freshName = suggestion; | 36 freshName = suggestion; |
| 37 } else { | 37 } else { |
| 38 freshName = _getUnusedName(proposedName, usedNames); | 38 freshName = _getUnusedName(proposedName, usedNames, |
| 39 suggestedNames.values); | |
| 39 } | 40 } |
| 40 usedNames.add(freshName); | 41 usedNames.add(freshName); |
| 41 return freshName; | 42 return freshName; |
| 42 } | 43 } |
| 43 | 44 |
| 44 String getClosureVariableName(String name, int id) { | 45 String getClosureVariableName(String name, int id) { |
| 45 if (id < ALPHABET_CHARACTERS) { | 46 if (id < ALPHABET_CHARACTERS) { |
| 46 return new String.fromCharCodes([_letterNumber(id)]); | 47 return new String.fromCharCodes([_letterNumber(id)]); |
| 47 } | 48 } |
| 48 return "${getMappedInstanceName('closure')}_$id"; | 49 return "${getMappedInstanceName('closure')}_$id"; |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 89 | 90 |
| 90 // These popular names are present in most programs and deserve | 91 // These popular names are present in most programs and deserve |
| 91 // single character minified names. We could determine the popular names | 92 // single character minified names. We could determine the popular names |
| 92 // individually per program, but that would mean that the output of the | 93 // individually per program, but that would mean that the output of the |
| 93 // minifier was less stable from version to version of the program being | 94 // minifier was less stable from version to version of the program being |
| 94 // minified. | 95 // minified. |
| 95 _populateSuggestedNames( | 96 _populateSuggestedNames( |
| 96 suggestedInstanceNames, | 97 suggestedInstanceNames, |
| 97 usedInstanceNames, | 98 usedInstanceNames, |
| 98 const <String>[ | 99 const <String>[ |
| 99 r'$add', r'add$1', r'$and', r'codeUnitAt$1', r'$or', | 100 r'$add', r'add$1', r'$and', |
| 101 r'$or', | |
| 100 r'current', r'$shr', r'$eq', r'$ne', | 102 r'current', r'$shr', r'$eq', r'$ne', |
| 101 r'getPrototypeOf', r'hasOwnProperty', r'$index', r'$indexSet', | 103 r'$index', r'$indexSet', |
| 102 r'$isJavaScriptIndexingBehavior', r'$xor', | 104 r'$xor', r'clone$0', |
| 103 r'iterator', r'length', r'$lt', r'$gt', r'$le', r'$ge', | 105 r'iterator', r'length', |
| 106 r'$lt', r'$gt', r'$le', r'$ge', | |
| 104 r'moveNext$0', r'node', r'on', r'$negate', r'push', r'self', | 107 r'moveNext$0', r'node', r'on', r'$negate', r'push', r'self', |
| 105 r'start', r'target', r'$shl', r'value', r'width', r'style', | 108 r'start', r'target', r'$shl', r'value', r'width', r'style', |
| 106 r'noSuchMethod$1', r'$mul', r'$div', r'$sub', r'$not', r'$mod', | 109 r'noSuchMethod$1', r'$mul', r'$div', r'$sub', r'$not', r'$mod', |
| 107 r'$tdiv']); | 110 r'$tdiv', r'toString$0']); |
| 108 | 111 |
| 109 _populateSuggestedNames( | 112 _populateSuggestedNames( |
| 110 suggestedGlobalNames, | 113 suggestedGlobalNames, |
| 111 usedGlobalNames, | 114 usedGlobalNames, |
| 112 const <String>[ | 115 const <String>[ |
| 113 r'Object', 'wrapException', r'$eq', r'S', r'ioore', | 116 r'Object', 'wrapException', r'$eq', r'S', r'ioore', |
| 114 r'UnsupportedError$', r'length', r'$sub', | 117 r'UnsupportedError$', r'length', r'$sub', |
| 115 r'getInterceptor$JSArrayJSString', r'$add', | 118 r'$add', r'$gt', r'$ge', r'$lt', r'$le', r'add', |
| 116 r'$gt', r'$ge', r'$lt', r'$le', r'add', r'getInterceptor$JSNumber', | 119 r'iae', |
| 117 r'iterator', r'$index', r'iae', r'getInterceptor$JSArray', | 120 r'ArgumentError$', r'BoundClosure', r'Closure', r'StateError$', |
| 118 r'ArgumentError$', r'BoundClosure', r'StateError$', | 121 r'getInterceptor', r'max', r'$mul', |
| 119 r'getInterceptor', r'max', r'$mul', r'List_List', r'Map_Map', | 122 r'Map', r'Key_Key', r'$div', |
| 120 r'getInterceptor$JSString', r'$div', r'$indexSet', | 123 r'List_List$from', |
| 121 r'List_List$from', r'Set_Set$from', r'toString', r'toInt', r'min', | 124 r'LinkedHashMap_LinkedHashMap$_empty', |
| 122 r'StringBuffer_StringBuffer', r'contains1', r'WhereIterable$', | 125 r'LinkedHashMap_LinkedHashMap$_literal', |
| 126 r'min', | |
| 123 r'RangeError$value', r'JSString', r'JSNumber', | 127 r'RangeError$value', r'JSString', r'JSNumber', |
| 124 r'JSArray', r'createInvocationMirror' | 128 r'JSArray', r'createInvocationMirror', r'String', |
| 129 r'setRuntimeTypeInfo', r'createRuntimeType' | |
| 125 ]); | 130 ]); |
| 126 } | 131 } |
| 127 | 132 |
| 128 void _populateSuggestedNames(Map<String, String> suggestionMap, | 133 void _populateSuggestedNames(Map<String, String> suggestionMap, |
| 129 Set<String> used, | 134 Set<String> used, |
| 130 List<String> suggestions) { | 135 List<String> suggestions) { |
| 131 int c = $a - 1; | 136 int c = $a - 1; |
| 132 String letter; | 137 String letter; |
| 133 for (String name in suggestions) { | 138 for (String name in suggestions) { |
| 134 do { | 139 do { |
| 135 assert(c != $Z); | 140 assert(c != $Z); |
| 136 c = (c == $z) ? $A : c + 1; | 141 c = (c == $z) ? $A : c + 1; |
| 137 letter = new String.fromCharCodes([c]); | 142 letter = new String.fromCharCodes([c]); |
| 138 } while (used.contains(letter)); | 143 } while (used.contains(letter)); |
| 139 assert(suggestionMap[name] == null); | 144 assert(suggestionMap[name] == null); |
| 140 suggestionMap[name] = letter; | 145 suggestionMap[name] = letter; |
| 141 } | 146 } |
| 142 } | 147 } |
| 143 | 148 |
| 144 | 149 |
| 145 // This gets a minified name based on a hash of the proposed name. This | 150 // This gets a minified name based on a hash of the proposed name. This |
| 146 // is slightly less efficient than just getting the next name in a series, | 151 // is slightly less efficient than just getting the next name in a series, |
| 147 // but it means that small changes in the input program will give smallish | 152 // but it means that small changes in the input program will give smallish |
| 148 // changes in the output, which can be useful for diffing etc. | 153 // changes in the output, which can be useful for diffing etc. |
| 149 String _getUnusedName(String proposedName, Set<String> usedNames) { | 154 String _getUnusedName(String proposedName, Set<String> usedNames, |
| 155 Iterable<String> suggestions) { | |
| 150 int hash = _calculateHash(proposedName); | 156 int hash = _calculateHash(proposedName); |
| 151 // Avoid very small hashes that won't try many names. | 157 // Avoid very small hashes that won't try many names. |
| 152 hash = hash < 1000 ? hash * 314159 : hash; // Yes, it's prime. | 158 hash = hash < 1000 ? hash * 314159 : hash; // Yes, it's prime. |
| 153 | 159 |
| 154 // Try other n-character names based on the hash. We try one to three | 160 // Try other n-character names based on the hash. We try one to three |
| 155 // character identifiers. For each length we try around 10 different names | 161 // character identifiers. For each length we try around 10 different names |
| 156 // in a predictable order determined by the proposed name. This is in order | 162 // in a predictable order determined by the proposed name. This is in order |
| 157 // to make the renamer stable: small changes in the input should nornally | 163 // to make the renamer stable: small changes in the input should nornally |
| 158 // result in relatively small changes in the output. | 164 // result in relatively small changes in the output. |
| 159 for (var n = 2; n <= 3; n++) { | 165 for (var n = 1; n <= 3; n++) { |
| 160 int h = hash; | 166 int h = hash; |
| 161 while (h > 10) { | 167 while (h > 10) { |
| 162 var codes = <int>[_letterNumber(h)]; | 168 var codes = <int>[_letterNumber(h)]; |
| 163 int h2 = h ~/ ALPHABET_CHARACTERS; | 169 int h2 = h ~/ ALPHABET_CHARACTERS; |
| 164 for (var i = 1; i < n; i++) { | 170 for (var i = 1; i < n; i++) { |
| 165 codes.add(_alphaNumericNumber(h2)); | 171 codes.add(_alphaNumericNumber(h2)); |
| 166 h2 ~/= ALPHANUMERIC_CHARACTERS; | 172 h2 ~/= ALPHANUMERIC_CHARACTERS; |
| 167 } | 173 } |
| 168 final candidate = new String.fromCharCodes(codes); | 174 final candidate = new String.fromCharCodes(codes); |
| 169 if (!usedNames.contains(candidate) && | 175 if (!usedNames.contains(candidate) && |
| 170 !jsReserved.contains(candidate) && | 176 !jsReserved.contains(candidate) && |
| 171 !_hasBannedPrefix(candidate)) { | 177 !_hasBannedPrefix(candidate) && |
| 178 (n != 1 || !suggestions.contains(candidate))) { | |
|
sra1
2014/11/13 23:52:25
Iterable.contains is not particularly fast.
herhut
2014/11/14 08:44:42
I know but it is not called that often. Only for o
| |
| 172 return candidate; | 179 return candidate; |
| 173 } | 180 } |
| 174 // Try again with a slightly different hash. After around 10 turns | 181 // Try again with a slightly different hash. After around 10 turns |
| 175 // around this loop h is zero and we try a longer name. | 182 // around this loop h is zero and we try a longer name. |
| 176 h ~/= 7; | 183 h ~/= 7; |
| 177 } | 184 } |
| 178 } | 185 } |
| 179 | 186 return _badName(hash, usedNames); |
| 180 // If we can't find a hash based name in the three-letter space, then base | |
| 181 // the name on a letter and a counter. | |
| 182 var startLetter = new String.fromCharCodes([_letterNumber(hash)]); | |
| 183 var i = 0; | |
| 184 while (usedNames.contains("$startLetter$i")) { | |
| 185 i++; | |
| 186 } | |
| 187 // We don't need to check for banned prefix because the name is in the form | |
| 188 // xnnn, where nnn is a number. There can be no getter or setter called | |
| 189 // gnnn since that would imply a numeric field name. | |
| 190 return "$startLetter$i"; | |
| 191 } | 187 } |
| 192 | 188 |
| 193 /// Instance members starting with g and s are reserved for getters and | 189 /// Instance members starting with g and s are reserved for getters and |
| 194 /// setters. | 190 /// setters. |
| 195 bool _hasBannedPrefix(String name) { | 191 bool _hasBannedPrefix(String name) { |
| 196 int code = name.codeUnitAt(0); | 192 int code = name.codeUnitAt(0); |
| 197 return code == $g || code == $s; | 193 return code == $g || code == $s; |
| 198 } | 194 } |
| 199 | 195 |
| 200 int _calculateHash(String name) { | 196 int _calculateHash(String name) { |
| 201 int h = 0; | 197 int h = 0; |
| 202 for (int i = 0; i < name.length; i++) { | 198 for (int i = 0; i < name.length; i++) { |
| 203 h += name.codeUnitAt(i); | 199 h += name.codeUnitAt(i); |
| 204 h &= 0xffffffff; | 200 h &= 0xffffffff; |
| 205 h += h << 10; | 201 h += h << 10; |
| 206 h &= 0xffffffff; | 202 h &= 0xffffffff; |
| 207 h ^= h >> 6; | 203 h ^= h >> 6; |
| 208 h &= 0xffffffff; | 204 h &= 0xffffffff; |
| 209 } | 205 } |
| 210 return h; | 206 return h; |
| 211 } | 207 } |
| 212 | 208 |
| 209 /// If we can't find a hash based name in the three-letter space, then base | |
| 210 /// the name on a letter and a counter. | |
| 211 String _badName(int hash, Set<String> usedNames) { | |
| 212 var startLetter = new String.fromCharCodes([_letterNumber(hash)]); | |
| 213 var name; | |
| 214 var i = 0; | |
| 215 do { | |
| 216 name = "$startLetter${i++}"; | |
| 217 } while (usedNames.contains(name)); | |
| 218 // We don't need to check for banned prefix because the name is in the form | |
| 219 // xnnn, where nnn is a number. There can be no getter or setter called | |
| 220 // gnnn since that would imply a numeric field name. | |
| 221 return name; | |
| 222 } | |
| 223 | |
| 213 int _letterNumber(int x) { | 224 int _letterNumber(int x) { |
| 214 if (x >= ALPHABET_CHARACTERS) x %= ALPHABET_CHARACTERS; | 225 if (x >= ALPHABET_CHARACTERS) x %= ALPHABET_CHARACTERS; |
| 215 if (x < 26) return $a + x; | 226 if (x < 26) return $a + x; |
| 216 return $A + x - 26; | 227 return $A + x - 26; |
| 217 } | 228 } |
| 218 | 229 |
| 219 int _alphaNumericNumber(int x) { | 230 int _alphaNumericNumber(int x) { |
| 220 if (x >= ALPHANUMERIC_CHARACTERS) x %= ALPHANUMERIC_CHARACTERS; | 231 if (x >= ALPHANUMERIC_CHARACTERS) x %= ALPHANUMERIC_CHARACTERS; |
| 221 if (x < 26) return $a + x; | 232 if (x < 26) return $a + x; |
| 222 if (x < 52) return $A + x - 26; | 233 if (x < 52) return $A + x - 26; |
| 223 return $0 + x - 52; | 234 return $0 + x - 52; |
| 224 } | 235 } |
| 225 | 236 |
| 226 } | 237 } |
| OLD | NEW |