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

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

Issue 1209973002: dart2js: Extract minified field naming into mixin. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 5 years, 6 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 with _MinifiedFieldNamer {
11 MinifyNamer(Compiler compiler) : super(compiler) { 11 MinifyNamer(Compiler compiler) : super(compiler) {
12 reserveBackendNames(); 12 reserveBackendNames();
13 fieldRegistry = new _FieldNamingRegistry(this); 13 fieldRegistry = new _FieldNamingRegistry(this);
14 } 14 }
15 15
16 _FieldNamingRegistry fieldRegistry;
17
16 String get isolateName => 'I'; 18 String get isolateName => 'I';
17 String get isolatePropertiesName => 'p'; 19 String get isolatePropertiesName => 'p';
18 bool get shouldMinify => true; 20 bool get shouldMinify => true;
19 21
20 final String getterPrefix = 'g'; 22 final String getterPrefix = 'g';
21 final String setterPrefix = 's'; 23 final String setterPrefix = 's';
22 final String callPrefix = ''; // this will create function names $<n> 24 final String callPrefix = ''; // this will create function names $<n>
23 25
24 final ALPHABET_CHARACTERS = 52; // a-zA-Z. 26 final ALPHABET_CHARACTERS = 52; // a-zA-Z.
25 final ALPHANUMERIC_CHARACTERS = 62; // a-zA-Z0-9. 27 final ALPHANUMERIC_CHARACTERS = 62; // a-zA-Z0-9.
26 28
27 _FieldNamingRegistry fieldRegistry;
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 @override
34 jsAst.Name getFreshName(String proposedName, 35 jsAst.Name getFreshName(String proposedName,
35 Set<String> usedNames, 36 Set<String> usedNames,
Johnni Winther 2015/06/26 10:03:35 Identation is off.
herhut 2015/06/29 12:19:31 Done.
36 Map<String, String> suggestedNames, 37 Map<String, String> suggestedNames,
37 {bool sanitizeForNatives: false, 38 {bool sanitizeForNatives: false,
38 bool sanitizeForAnnotations: false}) { 39 bool sanitizeForAnnotations: false}) {
39 String freshName; 40 String freshName;
40 String suggestion = suggestedNames[proposedName]; 41 String suggestion = suggestedNames[proposedName];
41 if (suggestion != null && !usedNames.contains(suggestion)) { 42 if (suggestion != null && !usedNames.contains(suggestion)) {
42 freshName = suggestion; 43 freshName = suggestion;
43 } else { 44 } else {
44 freshName = _getUnusedName(proposedName, usedNames, 45 freshName = _getUnusedName(proposedName, usedNames,
45 suggestedNames.values); 46 suggestedNames.values);
(...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after
237 return $A + x - 26; 238 return $A + x - 26;
238 } 239 }
239 240
240 int _alphaNumericNumber(int x) { 241 int _alphaNumericNumber(int x) {
241 if (x >= ALPHANUMERIC_CHARACTERS) x %= ALPHANUMERIC_CHARACTERS; 242 if (x >= ALPHANUMERIC_CHARACTERS) x %= ALPHANUMERIC_CHARACTERS;
242 if (x < 26) return $a + x; 243 if (x < 26) return $a + x;
243 if (x < 52) return $A + x - 26; 244 if (x < 52) return $A + x - 26;
244 return $0 + x - 52; 245 return $0 + x - 52;
245 } 246 }
246 247
248 @override
247 jsAst.Name instanceFieldPropertyName(Element element) { 249 jsAst.Name instanceFieldPropertyName(Element element) {
248 if (element.hasFixedBackendName) { 250 jsAst.Name proposed = _minifiedInstanceFieldPropertyName(element);
249 return new StringBackedName(element.fixedBackendName); 251 if (proposed != null) {
252 return proposed;
250 } 253 }
251 254 return super.instanceFieldPropertyName(element);
252 _FieldNamingScope names;
253 if (element is BoxFieldElement) {
254 names = new _FieldNamingScope.forBox(element.box, fieldRegistry);
255 } else {
256 ClassElement cls = element is ClosureFieldElement
257 ? element.closureClass : element.enclosingClass;
258 names = new _FieldNamingScope.forClass(cls, compiler.world,
259 fieldRegistry);
260 }
261
262 // The inheritance scope based naming did not yield a name. For instance,
263 // this could be because the field belongs to a mixin.
264 if (!names.containsField(element)) {
265 return super.instanceFieldPropertyName(element);
266 }
267
268 return names[element];
269 } 255 }
270 } 256 }
271 257
272 /**
273 * Encapsulates the global state of field naming.
274 */
275 class _FieldNamingRegistry {
276 final MinifyNamer namer;
277
278 final Map<Entity, _FieldNamingScope> scopes =
279 new Map<Entity, _FieldNamingScope>();
280
281 final Map<Entity, jsAst.Name> globalNames = new Map<Entity, jsAst.Name>();
282
283 int globalCount = 0;
284
285 final List<jsAst.Name> nameStore = new List<jsAst.Name>();
286
287 _FieldNamingRegistry(this.namer);
288
289 jsAst.Name getName(int count) {
290 if (count >= nameStore.length) {
291 // The namer usually does not use certain names as they clash with
292 // existing properties on JS objects (see [_reservedNativeProperties]).
293 // However, some of them are really short and safe to use for fields.
294 // Thus, we shortcut the namer to use those first.
295 if (count < MinifyNamer._reservedNativeProperties.length &&
296 MinifyNamer._reservedNativeProperties[count].length <= 2) {
297 nameStore.add(new StringBackedName(
298 MinifyNamer._reservedNativeProperties[count]));
299 } else {
300 nameStore.add(namer.getFreshName("field$count",
301 namer.usedInstanceNames, namer.suggestedInstanceNames));
302 }
303 }
304
305 return nameStore[count];
306 }
307 }
308
309 /**
310 * A [_FieldNamingScope] encodes a node in the inheritance tree of the current
311 * class hierarchy. The root node typically is the node corresponding to the
312 * `Object` class. It is used to assign a unique name to each field of a class.
313 * Unique here means unique wrt. all fields along the path back to the root.
314 * This is achieved at construction time via the [_fieldNameCounter] field that
315 * counts the number of fields on the path to the root node that have been
316 * encountered so far.
317 *
318 * Obviously, this only works if no fields are added to a parent node after its
319 * children have added their first field.
320 */
321 class _FieldNamingScope {
322 final _FieldNamingScope superScope;
323 final Entity container;
324 final Map<Element, jsAst.Name> names = new Maplet<Element, jsAst.Name>();
325 final _FieldNamingRegistry registry;
326
327 /// Naming counter used for fields of ordinary classes.
328 int _fieldNameCounter;
329
330 /// The number of fields along the superclass chain that use inheritance
331 /// based naming, including the ones allocated for this scope.
332 int get inheritanceBasedFieldNameCounter => _fieldNameCounter;
333
334 /// The number of locally used fields. Depending on the naming source
335 /// (e.g. inheritance based or globally unique for mixixns) this
336 /// might be different from [inheritanceBasedFieldNameCounter].
337 int get _localFieldNameCounter => _fieldNameCounter;
338 void set _localFieldNameCounter(int val) { _fieldNameCounter = val; }
339
340 factory _FieldNamingScope.forClass(ClassElement cls, ClassWorld world,
341 _FieldNamingRegistry registry) {
342 _FieldNamingScope result = registry.scopes[cls];
343 if (result != null) return result;
344
345 if (world.isUsedAsMixin(cls)) {
346 result = new _MixinFieldNamingScope.mixin(cls, registry);
347 } else {
348 if (cls.superclass == null) {
349 result = new _FieldNamingScope.rootScope(cls, registry);
350 } else {
351 _FieldNamingScope superScope = new _FieldNamingScope.forClass(
352 cls.superclass, world, registry);
353 if (cls.isMixinApplication) {
354 result = new _MixinFieldNamingScope.mixedIn(cls, superScope,
355 registry);
356 } else {
357 result = new _FieldNamingScope.inherit(cls, superScope, registry);
358 }
359 }
360 }
361
362 cls.forEachInstanceField((cls, field) => result.add(field));
363
364 registry.scopes[cls] = result;
365 return result;
366 }
367
368 factory _FieldNamingScope.forBox(Local box, _FieldNamingRegistry registry) {
369 return registry.scopes.putIfAbsent(box,
370 () => new _BoxFieldNamingScope(box, registry));
371 }
372
373 _FieldNamingScope.rootScope(this.container, this.registry)
374 : superScope = null,
375 _fieldNameCounter = 0;
376
377 _FieldNamingScope.inherit(this.container, this.superScope, this.registry) {
378 _fieldNameCounter = superScope.inheritanceBasedFieldNameCounter;
379 }
380
381 /**
382 * Checks whether [name] is already used in the current scope chain.
383 */
384 _isNameUnused(jsAst.Name name) {
385 return !names.values.contains(name) &&
386 ((superScope == null) || superScope._isNameUnused(name));
387 }
388
389 jsAst.Name _nextName() => registry.getName(_localFieldNameCounter++);
390
391 jsAst.Name operator[](Element field) {
392 jsAst.Name name = names[field];
393 if (name == null && superScope != null) return superScope[field];
394 return name;
395 }
396
397 void add(Element field) {
398 if (names.containsKey(field)) return;
399
400 jsAst.Name value = _nextName();
401 assert(invariant(field, _isNameUnused(value)));
402 names[field] = value;
403 }
404
405 bool containsField(Element field) => names.containsKey(field);
406 }
407
408 /**
409 * Field names for mixins have two constraints: They need to be unique in the
410 * hierarchy of each application of a mixin and they need to be the same for
411 * all applications of a mixin. To achieve this, we use global naming for
412 * mixins from the same name pool as fields and add a `$` at the end to ensure
413 * they do not collide with normal field names. The `$` sign is typically used
414 * as a separator between method names and argument counts and does not appear
415 * in generated names themselves.
416 */
417 class _MixinFieldNamingScope extends _FieldNamingScope {
418 int get _localFieldNameCounter => registry.globalCount;
419 void set _localFieldNameCounter(int val) { registry.globalCount = val; }
420
421 Map<Entity, jsAst.Name> get names => registry.globalNames;
422
423 _MixinFieldNamingScope.mixin(ClassElement cls, _FieldNamingRegistry registry)
424 : super.rootScope(cls, registry);
425
426 _MixinFieldNamingScope.mixedIn(MixinApplicationElement container,
427 _FieldNamingScope superScope, _FieldNamingRegistry registry)
428 : super.inherit(container, superScope, registry);
429
430 jsAst.Name _nextName() {
431 jsAst.Name proposed = super._nextName();
432 return new CompoundName([proposed, Namer._literalDollar]);
433 }
434 }
435
436 /**
437 * [BoxFieldElement] fields work differently in that they do not belong to an
438 * actual class but an anonymous box associated to a [Local]. As there is no
439 * inheritance chain, we do not need to compute fields a priori but can assign
440 * names on the fly.
441 */
442 class _BoxFieldNamingScope extends _FieldNamingScope {
443 _BoxFieldNamingScope(Local box, _FieldNamingRegistry registry) :
444 super.rootScope(box, registry);
445
446 bool containsField(_) => true;
447
448 jsAst.Name operator[](Element field) {
449 if (!names.containsKey(field)) add(field);
450 return names[field];
451 }
452 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698