Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(89)

Side by Side Diff: pkg/compiler/lib/src/js_backend/minify_namer.dart

Issue 1428853004: Use better names for captured variables in closures. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Address comments Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 with _MinifiedFieldNamer, 10 class MinifyNamer extends Namer with _MinifiedFieldNamer,
(...skipping 16 matching lines...) Expand all
27 final ALPHABET_CHARACTERS = 52; // a-zA-Z. 27 final ALPHABET_CHARACTERS = 52; // a-zA-Z.
28 final ALPHANUMERIC_CHARACTERS = 62; // a-zA-Z0-9. 28 final ALPHANUMERIC_CHARACTERS = 62; // a-zA-Z0-9.
29 29
30 /// You can pass an invalid identifier to this and unlike its non-minifying 30 /// You can pass an invalid identifier to this and unlike its non-minifying
31 /// counterpart it will never return the proposedName as the new fresh name. 31 /// counterpart it will never return the proposedName as the new fresh name.
32 /// 32 ///
33 /// [sanitizeForNatives] and [sanitizeForAnnotations] are ignored because the 33 /// [sanitizeForNatives] and [sanitizeForAnnotations] are ignored because the
34 /// minified names will always avoid clashing with annotated names or natives. 34 /// minified names will always avoid clashing with annotated names or natives.
35 @override 35 @override
36 String _generateFreshStringForName(String proposedName, 36 String _generateFreshStringForName(String proposedName,
37 Set<String> usedNames, 37 NamingScope scope,
38 Map<String, String> suggestedNames,
39 {bool sanitizeForNatives: false, 38 {bool sanitizeForNatives: false,
40 bool sanitizeForAnnotations: false}) { 39 bool sanitizeForAnnotations: false}) {
41 String freshName; 40 String freshName;
42 String suggestion = suggestedNames[proposedName]; 41 String suggestion = scope.suggestName(proposedName);
43 if (suggestion != null && !usedNames.contains(suggestion)) { 42 if (suggestion != null && scope.isUnused(suggestion)) {
44 freshName = suggestion; 43 freshName = suggestion;
45 } else { 44 } else {
46 freshName = _getUnusedName(proposedName, usedNames, 45 freshName = _getUnusedName(proposedName, scope);
47 suggestedNames.values);
48 } 46 }
49 usedNames.add(freshName); 47 scope.registerUse(freshName);
50 return freshName; 48 return freshName;
51 } 49 }
52 50
53 // From issue 7554. These should not be used on objects (as instance 51 // From issue 7554. These should not be used on objects (as instance
54 // variables) because they clash with names from the DOM. However, it is 52 // variables) because they clash with names from the DOM. However, it is
55 // OK to use them as fields, as we only access fields directly if we know 53 // OK to use them as fields, as we only access fields directly if we know
56 // the receiver type. 54 // the receiver type.
57 static const List<String> _reservedNativeProperties = const <String>[ 55 static const List<String> _reservedNativeProperties = const <String>[
58 'a', 'b', 'c', 'd', 'e', 'f', 'r', 'x', 'y', 'z', 'Q', 56 'a', 'b', 'c', 'd', 'e', 'f', 'r', 'x', 'y', 'z', 'Q',
59 // 2-letter: 57 // 2-letter:
(...skipping 18 matching lines...) Expand all
78 'show', 'SINE', 'size', 'span', 'stat', 'step', 'stop', 'tags', 'text', 76 'show', 'SINE', 'size', 'span', 'stat', 'step', 'stop', 'tags', 'text',
79 'Text', 'time', 'type', 'view', 'warn', 'wrap', 'ZERO']; 77 'Text', 'time', 'type', 'view', 'warn', 'wrap', 'ZERO'];
80 78
81 void reserveBackendNames() { 79 void reserveBackendNames() {
82 for (String name in _reservedNativeProperties) { 80 for (String name in _reservedNativeProperties) {
83 if (name.length < 2) { 81 if (name.length < 2) {
84 // Ensure the 1-letter names are disambiguated to the same name. 82 // Ensure the 1-letter names are disambiguated to the same name.
85 String disambiguatedName = name; 83 String disambiguatedName = name;
86 reservePublicMemberName(name, disambiguatedName); 84 reservePublicMemberName(name, disambiguatedName);
87 } 85 }
88 usedInstanceNames.add(name); 86 instanceScope.registerUse(name);
89 // Getter and setter names are autogenerated by prepending 'g' and 's' to 87 // Getter and setter names are autogenerated by prepending 'g' and 's' to
90 // field names. Therefore there are some field names we don't want to 88 // field names. Therefore there are some field names we don't want to
91 // use. It is implicit in the next line that the banned prefix is 89 // use. It is implicit in the next line that the banned prefix is
92 // only one character. 90 // only one character.
93 if (_hasBannedPrefix(name)) usedInstanceNames.add(name.substring(1)); 91 if (_hasBannedPrefix(name)) {
92 instanceScope.registerUse(name.substring(1));
93 }
94 } 94 }
95 95
96 // These popular names are present in most programs and deserve 96 // These popular names are present in most programs and deserve
97 // single character minified names. We could determine the popular names 97 // single character minified names. We could determine the popular names
98 // individually per program, but that would mean that the output of the 98 // individually per program, but that would mean that the output of the
99 // minifier was less stable from version to version of the program being 99 // minifier was less stable from version to version of the program being
100 // minified. 100 // minified.
101 _populateSuggestedNames( 101 _populateSuggestedNames(
102 suggestedInstanceNames, 102 instanceScope,
103 usedInstanceNames,
104 const <String>[ 103 const <String>[
105 r'$add', r'add$1', r'$and', 104 r'$add', r'add$1', r'$and',
106 r'$or', 105 r'$or',
107 r'current', r'$shr', r'$eq', r'$ne', 106 r'current', r'$shr', r'$eq', r'$ne',
108 r'$index', r'$indexSet', 107 r'$index', r'$indexSet',
109 r'$xor', r'clone$0', 108 r'$xor', r'clone$0',
110 r'iterator', r'length', 109 r'iterator', r'length',
111 r'$lt', r'$gt', r'$le', r'$ge', 110 r'$lt', r'$gt', r'$le', r'$ge',
112 r'moveNext$0', r'node', r'on', r'$negate', r'push', r'self', 111 r'moveNext$0', r'node', r'on', r'$negate', r'push', r'self',
113 r'start', r'target', r'$shl', r'value', r'width', r'style', 112 r'start', r'target', r'$shl', r'value', r'width', r'style',
114 r'noSuchMethod$1', r'$mul', r'$div', r'$sub', r'$not', r'$mod', 113 r'noSuchMethod$1', r'$mul', r'$div', r'$sub', r'$not', r'$mod',
115 r'$tdiv', r'toString$0']); 114 r'$tdiv', r'toString$0']);
116 115
117 _populateSuggestedNames( 116 _populateSuggestedNames(
118 suggestedGlobalNames, 117 globalScope,
119 usedGlobalNames,
120 const <String>[ 118 const <String>[
121 r'Object', 'wrapException', r'$eq', r'S', r'ioore', 119 r'Object', 'wrapException', r'$eq', r'S', r'ioore',
122 r'UnsupportedError$', r'length', r'$sub', 120 r'UnsupportedError$', r'length', r'$sub',
123 r'$add', r'$gt', r'$ge', r'$lt', r'$le', r'add', 121 r'$add', r'$gt', r'$ge', r'$lt', r'$le', r'add',
124 r'iae', 122 r'iae',
125 r'ArgumentError$', r'BoundClosure', r'Closure', r'StateError$', 123 r'ArgumentError$', r'BoundClosure', r'Closure', r'StateError$',
126 r'getInterceptor', r'max', r'$mul', 124 r'getInterceptor', r'max', r'$mul',
127 r'Map', r'Key_Key', r'$div', 125 r'Map', r'Key_Key', r'$div',
128 r'List_List$from', 126 r'List_List$from',
129 r'LinkedHashMap_LinkedHashMap$_empty', 127 r'LinkedHashMap_LinkedHashMap$_empty',
130 r'LinkedHashMap_LinkedHashMap$_literal', 128 r'LinkedHashMap_LinkedHashMap$_literal',
131 r'min', 129 r'min',
132 r'RangeError$value', r'JSString', r'JSNumber', 130 r'RangeError$value', r'JSString', r'JSNumber',
133 r'JSArray', r'createInvocationMirror', r'String', 131 r'JSArray', r'createInvocationMirror', r'String',
134 r'setRuntimeTypeInfo', r'createRuntimeType' 132 r'setRuntimeTypeInfo', r'createRuntimeType'
135 ]); 133 ]);
136 } 134 }
137 135
138 void _populateSuggestedNames(Map<String, String> suggestionMap, 136 void _populateSuggestedNames(NamingScope scope,
139 Set<String> used,
140 List<String> suggestions) { 137 List<String> suggestions) {
141 int c = $a - 1; 138 int c = $a - 1;
142 String letter; 139 String letter;
143 for (String name in suggestions) { 140 for (String name in suggestions) {
144 do { 141 do {
145 assert(c != $Z); 142 assert(c != $Z);
146 c = (c == $z) ? $A : c + 1; 143 c = (c == $z) ? $A : c + 1;
147 letter = new String.fromCharCodes([c]); 144 letter = new String.fromCharCodes([c]);
148 } while (_hasBannedPrefix(letter) || used.contains(letter)); 145 } while (_hasBannedPrefix(letter) || scope.isUsed(letter));
149 assert(suggestionMap[name] == null); 146 assert(!scope.hasSuggestion(name));
150 suggestionMap[name] = letter; 147 scope.addSuggestion(name, letter);
151 } 148 }
152 } 149 }
153 150
154 151
155 // This gets a minified name based on a hash of the proposed name. This 152 // This gets a minified name based on a hash of the proposed name. This
156 // is slightly less efficient than just getting the next name in a series, 153 // is slightly less efficient than just getting the next name in a series,
157 // but it means that small changes in the input program will give smallish 154 // but it means that small changes in the input program will give smallish
158 // changes in the output, which can be useful for diffing etc. 155 // changes in the output, which can be useful for diffing etc.
159 String _getUnusedName(String proposedName, Set<String> usedNames, 156 String _getUnusedName(String proposedName, NamingScope scope) {
160 Iterable<String> suggestions) {
161 int hash = _calculateHash(proposedName); 157 int hash = _calculateHash(proposedName);
162 // Avoid very small hashes that won't try many names. 158 // Avoid very small hashes that won't try many names.
163 hash = hash < 1000 ? hash * 314159 : hash; // Yes, it's prime. 159 hash = hash < 1000 ? hash * 314159 : hash; // Yes, it's prime.
164 160
165 // Try other n-character names based on the hash. We try one to three 161 // Try other n-character names based on the hash. We try one to three
166 // character identifiers. For each length we try around 10 different names 162 // character identifiers. For each length we try around 10 different names
167 // in a predictable order determined by the proposed name. This is in order 163 // in a predictable order determined by the proposed name. This is in order
168 // to make the renamer stable: small changes in the input should nornally 164 // to make the renamer stable: small changes in the input should nornally
169 // result in relatively small changes in the output. 165 // result in relatively small changes in the output.
170 for (int n = 1; n <= 3; n++) { 166 for (int n = 1; n <= 3; n++) {
171 int h = hash; 167 int h = hash;
172 while (h > 10) { 168 while (h > 10) {
173 List<int> codes = <int>[_letterNumber(h)]; 169 List<int> codes = <int>[_letterNumber(h)];
174 int h2 = h ~/ ALPHABET_CHARACTERS; 170 int h2 = h ~/ ALPHABET_CHARACTERS;
175 for (int i = 1; i < n; i++) { 171 for (int i = 1; i < n; i++) {
176 codes.add(_alphaNumericNumber(h2)); 172 codes.add(_alphaNumericNumber(h2));
177 h2 ~/= ALPHANUMERIC_CHARACTERS; 173 h2 ~/= ALPHANUMERIC_CHARACTERS;
178 } 174 }
179 final candidate = new String.fromCharCodes(codes); 175 final candidate = new String.fromCharCodes(codes);
180 if (!usedNames.contains(candidate) && 176 if (scope.isUnused(candidate) &&
181 !jsReserved.contains(candidate) && 177 !jsReserved.contains(candidate) &&
182 !_hasBannedPrefix(candidate) && 178 !_hasBannedPrefix(candidate) &&
183 (n != 1 || !suggestions.contains(candidate))) { 179 (n != 1 || scope.isSuggestion(candidate))) {
184 return candidate; 180 return candidate;
185 } 181 }
186 // Try again with a slightly different hash. After around 10 turns 182 // Try again with a slightly different hash. After around 10 turns
187 // around this loop h is zero and we try a longer name. 183 // around this loop h is zero and we try a longer name.
188 h ~/= 7; 184 h ~/= 7;
189 } 185 }
190 } 186 }
191 return _badName(hash, usedNames); 187 return _badName(hash, scope);
192 } 188 }
193 189
194 /// Instance members starting with g and s are reserved for getters and 190 /// Instance members starting with g and s are reserved for getters and
195 /// setters. 191 /// setters.
196 static bool _hasBannedPrefix(String name) { 192 static bool _hasBannedPrefix(String name) {
197 int code = name.codeUnitAt(0); 193 int code = name.codeUnitAt(0);
198 return code == $g || code == $s; 194 return code == $g || code == $s;
199 } 195 }
200 196
201 int _calculateHash(String name) { 197 int _calculateHash(String name) {
202 int h = 0; 198 int h = 0;
203 for (int i = 0; i < name.length; i++) { 199 for (int i = 0; i < name.length; i++) {
204 h += name.codeUnitAt(i); 200 h += name.codeUnitAt(i);
205 h &= 0xffffffff; 201 h &= 0xffffffff;
206 h += h << 10; 202 h += h << 10;
207 h &= 0xffffffff; 203 h &= 0xffffffff;
208 h ^= h >> 6; 204 h ^= h >> 6;
209 h &= 0xffffffff; 205 h &= 0xffffffff;
210 } 206 }
211 return h; 207 return h;
212 } 208 }
213 209
214 /// Remember bad hashes to avoid using a the same character with long numbers 210 /// Remember bad hashes to avoid using a the same character with long numbers
215 /// for frequent hashes. For example, `closure` is a very common name. 211 /// for frequent hashes. For example, `closure` is a very common name.
216 Map<int, int> _badNames = new Map<int, int>(); 212 Map<int, int> _badNames = new Map<int, int>();
217 213
218 /// If we can't find a hash based name in the three-letter space, then base 214 /// If we can't find a hash based name in the three-letter space, then base
219 /// the name on a letter and a counter. 215 /// the name on a letter and a counter.
220 String _badName(int hash, Set<String> usedNames) { 216 String _badName(int hash, NamingScope scope) {
221 int count = _badNames.putIfAbsent(hash, () => 0); 217 int count = _badNames.putIfAbsent(hash, () => 0);
222 String startLetter = 218 String startLetter =
223 new String.fromCharCodes([_letterNumber(hash + count)]); 219 new String.fromCharCodes([_letterNumber(hash + count)]);
224 _badNames[hash] = count + 1; 220 _badNames[hash] = count + 1;
225 String name; 221 String name;
226 int i = 0; 222 int i = 0;
227 do { 223 do {
228 name = "$startLetter${i++}"; 224 name = "$startLetter${i++}";
229 } while (usedNames.contains(name)); 225 } while (scope.isUsed(name));
230 // We don't need to check for banned prefix because the name is in the form 226 // We don't need to check for banned prefix because the name is in the form
231 // xnnn, where nnn is a number. There can be no getter or setter called 227 // xnnn, where nnn is a number. There can be no getter or setter called
232 // gnnn since that would imply a numeric field name. 228 // gnnn since that would imply a numeric field name.
233 return name; 229 return name;
234 } 230 }
235 231
236 int _letterNumber(int x) { 232 int _letterNumber(int x) {
237 if (x >= ALPHABET_CHARACTERS) x %= ALPHABET_CHARACTERS; 233 if (x >= ALPHABET_CHARACTERS) x %= ALPHABET_CHARACTERS;
238 if (x < 26) return $a + x; 234 if (x < 26) return $a + x;
239 return $A + x - 26; 235 return $A + x - 26;
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after
344 : selector.isSetter ? r"$set" : ""; 340 : selector.isSetter ? r"$set" : "";
345 String callSuffix = 341 String callSuffix =
346 selector.isCall ? callSuffixForStructure(selector.callStructure).join() 342 selector.isCall ? callSuffixForStructure(selector.callStructure).join()
347 : ""; 343 : "";
348 String suffix = suffixForGetInterceptor(classes); 344 String suffix = suffixForGetInterceptor(classes);
349 String fullName = "\$intercepted$prefix\$$root$callSuffix\$$suffix"; 345 String fullName = "\$intercepted$prefix\$$root$callSuffix\$$suffix";
350 return _disambiguateInternalGlobal(fullName); 346 return _disambiguateInternalGlobal(fullName);
351 } 347 }
352 } 348 }
353 349
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/js_backend/frequency_namer.dart ('k') | pkg/compiler/lib/src/js_backend/namer.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698