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

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

Issue 1198293002: dart2js: Use an abstract Name class for names in the generated JavaScript ast. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: fix tests Created 5 years, 5 months 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 { 10 class MinifyNamer extends Namer {
(...skipping 13 matching lines...) Expand all
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698