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 13 matching lines...) Expand all Loading... |
24 final ALPHABET_CHARACTERS = 52; // a-zA-Z. | 24 final ALPHABET_CHARACTERS = 52; // a-zA-Z. |
25 final ALPHANUMERIC_CHARACTERS = 62; // a-zA-Z0-9. | 25 final ALPHANUMERIC_CHARACTERS = 62; // a-zA-Z0-9. |
26 | 26 |
27 _FieldNamingRegistry fieldRegistry; | 27 _FieldNamingRegistry fieldRegistry; |
28 | 28 |
29 /// You can pass an invalid identifier to this and unlike its non-minifying | 29 /// You can pass an invalid identifier to this and unlike its non-minifying |
30 /// counterpart it will never return the proposedName as the new fresh name. | 30 /// counterpart it will never return the proposedName as the new fresh name. |
31 /// | 31 /// |
32 /// [sanitizeForNatives] and [sanitizeForAnnotations] are ignored because the | 32 /// [sanitizeForNatives] and [sanitizeForAnnotations] are ignored because the |
33 /// minified names will always avoid clashing with annotated names or natives. | 33 /// minified names will always avoid clashing with annotated names or natives. |
34 String getFreshName(String proposedName, | 34 jsAst.Name getFreshName(String proposedName, |
35 Set<String> usedNames, | 35 Set<String> usedNames, |
36 Map<String, String> suggestedNames, | 36 Map<String, String> suggestedNames, |
37 {bool sanitizeForNatives: false, | 37 {bool sanitizeForNatives: false, |
38 bool sanitizeForAnnotations: false}) { | 38 bool sanitizeForAnnotations: false}) { |
39 String freshName; | 39 String freshName; |
40 String suggestion = suggestedNames[proposedName]; | 40 String suggestion = suggestedNames[proposedName]; |
41 if (suggestion != null && !usedNames.contains(suggestion)) { | 41 if (suggestion != null && !usedNames.contains(suggestion)) { |
42 freshName = suggestion; | 42 freshName = suggestion; |
43 } else { | 43 } else { |
44 freshName = _getUnusedName(proposedName, usedNames, | 44 freshName = _getUnusedName(proposedName, usedNames, |
45 suggestedNames.values); | 45 suggestedNames.values); |
46 } | 46 } |
47 usedNames.add(freshName); | 47 usedNames.add(freshName); |
48 return freshName; | 48 return new StringBackedName(freshName); |
49 } | 49 } |
50 | 50 |
51 // 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 |
52 // 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 |
53 // 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 |
54 // the receiver type. | 54 // the receiver type. |
55 static const List<String> _reservedNativeProperties = const <String>[ | 55 static const List<String> _reservedNativeProperties = const <String>[ |
56 'Q', 'a', 'b', 'c', 'd', 'e', 'f', 'r', 'x', 'y', 'z', | 56 'Q', 'a', 'b', 'c', 'd', 'e', 'f', 'r', 'x', 'y', 'z', |
57 // 2-letter: | 57 // 2-letter: |
58 'ch', 'cx', 'cy', 'db', 'dx', 'dy', 'fr', 'fx', 'fy', 'go', 'id', 'k1', | 58 'ch', 'cx', 'cy', 'db', 'dx', 'dy', 'fr', 'fx', 'fy', 'go', 'id', 'k1', |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
136 void _populateSuggestedNames(Map<String, String> suggestionMap, | 136 void _populateSuggestedNames(Map<String, String> suggestionMap, |
137 Set<String> used, | 137 Set<String> used, |
138 List<String> suggestions) { | 138 List<String> suggestions) { |
139 int c = $a - 1; | 139 int c = $a - 1; |
140 String letter; | 140 String letter; |
141 for (String name in suggestions) { | 141 for (String name in suggestions) { |
142 do { | 142 do { |
143 assert(c != $Z); | 143 assert(c != $Z); |
144 c = (c == $z) ? $A : c + 1; | 144 c = (c == $z) ? $A : c + 1; |
145 letter = new String.fromCharCodes([c]); | 145 letter = new String.fromCharCodes([c]); |
146 } while (used.contains(letter)); | 146 } while (_hasBannedPrefix(letter) || used.contains(letter)); |
147 assert(suggestionMap[name] == null); | 147 assert(suggestionMap[name] == null); |
148 suggestionMap[name] = letter; | 148 suggestionMap[name] = letter; |
149 } | 149 } |
150 } | 150 } |
151 | 151 |
152 | 152 |
153 // This gets a minified name based on a hash of the proposed name. This | 153 // This gets a minified name based on a hash of the proposed name. This |
154 // is slightly less efficient than just getting the next name in a series, | 154 // is slightly less efficient than just getting the next name in a series, |
155 // but it means that small changes in the input program will give smallish | 155 // but it means that small changes in the input program will give smallish |
156 // changes in the output, which can be useful for diffing etc. | 156 // changes in the output, which can be useful for diffing etc. |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
237 return $A + x - 26; | 237 return $A + x - 26; |
238 } | 238 } |
239 | 239 |
240 int _alphaNumericNumber(int x) { | 240 int _alphaNumericNumber(int x) { |
241 if (x >= ALPHANUMERIC_CHARACTERS) x %= ALPHANUMERIC_CHARACTERS; | 241 if (x >= ALPHANUMERIC_CHARACTERS) x %= ALPHANUMERIC_CHARACTERS; |
242 if (x < 26) return $a + x; | 242 if (x < 26) return $a + x; |
243 if (x < 52) return $A + x - 26; | 243 if (x < 52) return $A + x - 26; |
244 return $0 + x - 52; | 244 return $0 + x - 52; |
245 } | 245 } |
246 | 246 |
247 String instanceFieldPropertyName(Element element) { | 247 jsAst.Name instanceFieldPropertyName(Element element) { |
248 if (element.hasFixedBackendName) { | 248 if (element.hasFixedBackendName) { |
249 return element.fixedBackendName; | 249 return new StringBackedName(element.fixedBackendName); |
250 } | 250 } |
251 | 251 |
252 _FieldNamingScope names; | 252 _FieldNamingScope names; |
253 if (element is BoxFieldElement) { | 253 if (element is BoxFieldElement) { |
254 names = new _FieldNamingScope.forBox(element.box, fieldRegistry); | 254 names = new _FieldNamingScope.forBox(element.box, fieldRegistry); |
255 } else { | 255 } else { |
256 ClassElement cls = element is ClosureFieldElement | 256 ClassElement cls = element is ClosureFieldElement |
257 ? element.closureClass : element.enclosingClass; | 257 ? element.closureClass : element.enclosingClass; |
258 names = new _FieldNamingScope.forClass(cls, compiler.world, | 258 names = new _FieldNamingScope.forClass(cls, compiler.world, |
259 fieldRegistry); | 259 fieldRegistry); |
(...skipping 11 matching lines...) Expand all Loading... |
271 | 271 |
272 /** | 272 /** |
273 * Encapsulates the global state of field naming. | 273 * Encapsulates the global state of field naming. |
274 */ | 274 */ |
275 class _FieldNamingRegistry { | 275 class _FieldNamingRegistry { |
276 final MinifyNamer namer; | 276 final MinifyNamer namer; |
277 | 277 |
278 final Map<Entity, _FieldNamingScope> scopes = | 278 final Map<Entity, _FieldNamingScope> scopes = |
279 new Map<Entity, _FieldNamingScope>(); | 279 new Map<Entity, _FieldNamingScope>(); |
280 | 280 |
281 final Map<Entity, String> globalNames = new Map<Entity, String>(); | 281 final Map<Entity, jsAst.Name> globalNames = new Map<Entity, jsAst.Name>(); |
282 | 282 |
283 int globalCount = 0; | 283 int globalCount = 0; |
284 | 284 |
285 final List<String> nameStore = new List<String>(); | 285 final List<jsAst.Name> nameStore = new List<jsAst.Name>(); |
286 | 286 |
287 _FieldNamingRegistry(this.namer); | 287 _FieldNamingRegistry(this.namer); |
288 | 288 |
289 String getName(int count) { | 289 jsAst.Name getName(int count) { |
290 if (count >= nameStore.length) { | 290 if (count >= nameStore.length) { |
291 // The namer usually does not use certain names as they clash with | 291 // The namer usually does not use certain names as they clash with |
292 // existing properties on JS objects (see [_reservedNativeProperties]). | 292 // existing properties on JS objects (see [_reservedNativeProperties]). |
293 // However, some of them are really short and safe to use for fields. | 293 // However, some of them are really short and safe to use for fields. |
294 // Thus, we shortcut the namer to use those first. | 294 // Thus, we shortcut the namer to use those first. |
295 if (count < MinifyNamer._reservedNativeProperties.length && | 295 if (count < MinifyNamer._reservedNativeProperties.length && |
296 MinifyNamer._reservedNativeProperties[count].length <= 2) { | 296 MinifyNamer._reservedNativeProperties[count].length <= 2) { |
297 nameStore.add(MinifyNamer._reservedNativeProperties[count]); | 297 nameStore.add(new StringBackedName( |
| 298 MinifyNamer._reservedNativeProperties[count])); |
298 } else { | 299 } else { |
299 nameStore.add(namer.getFreshName("field$count", | 300 nameStore.add(namer.getFreshName("field$count", |
300 namer.usedInstanceNames, namer.suggestedInstanceNames)); | 301 namer.usedInstanceNames, namer.suggestedInstanceNames)); |
301 } | 302 } |
302 } | 303 } |
303 | 304 |
304 return nameStore[count]; | 305 return nameStore[count]; |
305 } | 306 } |
306 } | 307 } |
307 | 308 |
308 /** | 309 /** |
309 * A [_FieldNamingScope] encodes a node in the inheritance tree of the current | 310 * A [_FieldNamingScope] encodes a node in the inheritance tree of the current |
310 * class hierarchy. The root node typically is the node corresponding to the | 311 * class hierarchy. The root node typically is the node corresponding to the |
311 * `Object` class. It is used to assign a unique name to each field of a class. | 312 * `Object` class. It is used to assign a unique name to each field of a class. |
312 * Unique here means unique wrt. all fields along the path back to the root. | 313 * Unique here means unique wrt. all fields along the path back to the root. |
313 * This is achieved at construction time via the [_fieldNameCounter] field that
counts the | 314 * This is achieved at construction time via the [_fieldNameCounter] field that |
314 * number of fields on the path to the root node that have been encountered so | 315 * counts the number of fields on the path to the root node that have been |
315 * far. | 316 * encountered so far. |
316 * | 317 * |
317 * Obviously, this only works if no fields are added to a parent node after its | 318 * Obviously, this only works if no fields are added to a parent node after its |
318 * children have added their first field. | 319 * children have added their first field. |
319 */ | 320 */ |
320 class _FieldNamingScope { | 321 class _FieldNamingScope { |
321 final _FieldNamingScope superScope; | 322 final _FieldNamingScope superScope; |
322 final Entity container; | 323 final Entity container; |
323 final Map<Element, String> names = new Maplet<Element, String>(); | 324 final Map<Element, jsAst.Name> names = new Maplet<Element, jsAst.Name>(); |
324 final _FieldNamingRegistry registry; | 325 final _FieldNamingRegistry registry; |
325 | 326 |
326 /// Naming counter used for fields of ordinary classes. | 327 /// Naming counter used for fields of ordinary classes. |
327 int _fieldNameCounter; | 328 int _fieldNameCounter; |
328 | 329 |
329 /// The number of fields along the superclass chain that use inheritance | 330 /// The number of fields along the superclass chain that use inheritance |
330 /// based naming, including the ones allocated for this scope. | 331 /// based naming, including the ones allocated for this scope. |
331 int get inheritanceBasedFieldNameCounter => _fieldNameCounter; | 332 int get inheritanceBasedFieldNameCounter => _fieldNameCounter; |
332 | 333 |
333 /// The number of locally used fields. Depending on the naming source | 334 /// The number of locally used fields. Depending on the naming source |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
373 : superScope = null, | 374 : superScope = null, |
374 _fieldNameCounter = 0; | 375 _fieldNameCounter = 0; |
375 | 376 |
376 _FieldNamingScope.inherit(this.container, this.superScope, this.registry) { | 377 _FieldNamingScope.inherit(this.container, this.superScope, this.registry) { |
377 _fieldNameCounter = superScope.inheritanceBasedFieldNameCounter; | 378 _fieldNameCounter = superScope.inheritanceBasedFieldNameCounter; |
378 } | 379 } |
379 | 380 |
380 /** | 381 /** |
381 * Checks whether [name] is already used in the current scope chain. | 382 * Checks whether [name] is already used in the current scope chain. |
382 */ | 383 */ |
383 _isNameUnused(String name) { | 384 _isNameUnused(jsAst.Name name) { |
384 return !names.values.contains(name) && | 385 return !names.values.contains(name) && |
385 ((superScope == null) || superScope._isNameUnused(name)); | 386 ((superScope == null) || superScope._isNameUnused(name)); |
386 } | 387 } |
387 | 388 |
388 String _nextName() => registry.getName(_localFieldNameCounter++); | 389 jsAst.Name _nextName() => registry.getName(_localFieldNameCounter++); |
389 | 390 |
390 String operator[](Element field) { | 391 jsAst.Name operator[](Element field) { |
391 String name = names[field]; | 392 jsAst.Name name = names[field]; |
392 if (name == null && superScope != null) return superScope[field]; | 393 if (name == null && superScope != null) return superScope[field]; |
393 return name; | 394 return name; |
394 } | 395 } |
395 | 396 |
396 void add(Element field) { | 397 void add(Element field) { |
397 if (names.containsKey(field)) return; | 398 if (names.containsKey(field)) return; |
398 | 399 |
399 String value = _nextName(); | 400 jsAst.Name value = _nextName(); |
400 assert(invariant(field, _isNameUnused(value))); | 401 assert(invariant(field, _isNameUnused(value))); |
401 names[field] = value; | 402 names[field] = value; |
402 } | 403 } |
403 | 404 |
404 bool containsField(Element field) => names.containsKey(field); | 405 bool containsField(Element field) => names.containsKey(field); |
405 } | 406 } |
406 | 407 |
407 /** | 408 /** |
408 * Field names for mixins have two constraints: They need to be unique in the | 409 * Field names for mixins have two constraints: They need to be unique in the |
409 * hierarchy of each application of a mixin and they need to be the same for | 410 * hierarchy of each application of a mixin and they need to be the same for |
410 * all applications of a mixin. To achieve this, we use global naming for | 411 * all applications of a mixin. To achieve this, we use global naming for |
411 * mixins from the same name pool as fields and add a `$` at the end to ensure | 412 * mixins from the same name pool as fields and add a `$` at the end to ensure |
412 * they do not collide with normal field names. The `$` sign is typically used | 413 * they do not collide with normal field names. The `$` sign is typically used |
413 * as a separator between method names and argument counts and does not appear | 414 * as a separator between method names and argument counts and does not appear |
414 * in generated names themselves. | 415 * in generated names themselves. |
415 */ | 416 */ |
416 class _MixinFieldNamingScope extends _FieldNamingScope { | 417 class _MixinFieldNamingScope extends _FieldNamingScope { |
417 int get _localFieldNameCounter => registry.globalCount; | 418 int get _localFieldNameCounter => registry.globalCount; |
418 void set _localFieldNameCounter(int val) { registry.globalCount = val; } | 419 void set _localFieldNameCounter(int val) { registry.globalCount = val; } |
419 | 420 |
420 Map<Entity, String> get names => registry.globalNames; | 421 Map<Entity, jsAst.Name> get names => registry.globalNames; |
421 | 422 |
422 _MixinFieldNamingScope.mixin(ClassElement cls, _FieldNamingRegistry registry) | 423 _MixinFieldNamingScope.mixin(ClassElement cls, _FieldNamingRegistry registry) |
423 : super.rootScope(cls, registry); | 424 : super.rootScope(cls, registry); |
424 | 425 |
425 _MixinFieldNamingScope.mixedIn(MixinApplicationElement container, | 426 _MixinFieldNamingScope.mixedIn(MixinApplicationElement container, |
426 _FieldNamingScope superScope, _FieldNamingRegistry registry) | 427 _FieldNamingScope superScope, _FieldNamingRegistry registry) |
427 : super.inherit(container, superScope, registry); | 428 : super.inherit(container, superScope, registry); |
428 | 429 |
429 String _nextName() { | 430 jsAst.Name _nextName() { |
430 String proposed = super._nextName(); | 431 jsAst.Name proposed = super._nextName(); |
431 return proposed + r'$'; | 432 return new CompoundName([proposed, Namer._literalDollar]); |
432 } | 433 } |
433 } | 434 } |
434 | 435 |
435 /** | 436 /** |
436 * [BoxFieldElement] fields work differently in that they do not belong to an | 437 * [BoxFieldElement] fields work differently in that they do not belong to an |
437 * actual class but an anonymous box associated to a [Local]. As there is no | 438 * actual class but an anonymous box associated to a [Local]. As there is no |
438 * inheritance chain, we do not need to compute fields a priori but can assign | 439 * inheritance chain, we do not need to compute fields a priori but can assign |
439 * names on the fly. | 440 * names on the fly. |
440 */ | 441 */ |
441 class _BoxFieldNamingScope extends _FieldNamingScope { | 442 class _BoxFieldNamingScope extends _FieldNamingScope { |
442 _BoxFieldNamingScope(Local box, _FieldNamingRegistry registry) : | 443 _BoxFieldNamingScope(Local box, _FieldNamingRegistry registry) : |
443 super.rootScope(box, registry); | 444 super.rootScope(box, registry); |
444 | 445 |
445 bool containsField(_) => true; | 446 bool containsField(_) => true; |
446 | 447 |
447 String operator[](Element field) { | 448 jsAst.Name operator[](Element field) { |
448 if (!names.containsKey(field)) add(field); | 449 if (!names.containsKey(field)) add(field); |
449 return names[field]; | 450 return names[field]; |
450 } | 451 } |
451 } | 452 } |
OLD | NEW |