Chromium Code Reviews| Index: pkg/compiler/lib/src/js_backend/field_naming_mixin.dart |
| diff --git a/pkg/compiler/lib/src/js_backend/field_naming_mixin.dart b/pkg/compiler/lib/src/js_backend/field_naming_mixin.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..872b383ebbfdfeba8d9a05f92ef6de784a24a59b |
| --- /dev/null |
| +++ b/pkg/compiler/lib/src/js_backend/field_naming_mixin.dart |
| @@ -0,0 +1,225 @@ |
| +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| +// for details. All rights reserved. Use of this source code is governed by a |
| +// BSD-style license that can be found in the LICENSE file. |
| + |
| +part of js_backend; |
| + |
| +abstract class _MinifiedFieldNamer implements Namer { |
| + _FieldNamingRegistry get fieldRegistry; |
| + |
| + // Returns a minimal name for the field that is globally unique along |
| + // the given element's class inheritance chain. |
| + // |
| + // The inheritance scope based naming might not yield a name. For instance, |
| + // this could be because the field belongs to a mixin. In such a case this |
| + // will return `null` and a normal field name has to be used. |
| + jsAst.Name _minifiedInstanceFieldPropertyName(Element element) { |
| + if (element.hasFixedBackendName) { |
| + return new StringBackedName(element.fixedBackendName); |
| + } |
| + |
| + _FieldNamingScope names; |
| + if (element is BoxFieldElement) { |
| + names = new _FieldNamingScope.forBox(element.box, fieldRegistry); |
| + } else { |
| + ClassElement cls = element is ClosureFieldElement |
| + ? element.closureClass |
| + : element.enclosingClass; |
| + names = |
| + new _FieldNamingScope.forClass(cls, compiler.world, fieldRegistry); |
| + } |
| + |
| + if (names.containsField(element)) { |
| + return names[element]; |
| + } |
| + return null; |
| + } |
| +} |
| + |
| +/** |
| + * Encapsulates the global state of field naming. |
| + */ |
| +class _FieldNamingRegistry { |
| + final MinifyNamer namer; |
| + |
| + final Map<Entity, _FieldNamingScope> scopes = |
| + new Map<Entity, _FieldNamingScope>(); |
| + |
| + final Map<Entity, jsAst.Name> globalNames = new Map<Entity, jsAst.Name>(); |
| + |
| + int globalCount = 0; |
| + |
| + final List<jsAst.Name> nameStore = new List<jsAst.Name>(); |
| + |
| + _FieldNamingRegistry(this.namer); |
| + |
| + jsAst.Name getName(int count) { |
|
Johnni Winther
2015/06/26 10:03:35
Document the significance of [count]. Maybe rename
herhut
2015/06/29 12:19:31
Done.
|
| + if (count >= nameStore.length) { |
| + // The namer usually does not use certain names as they clash with |
| + // existing properties on JS objects (see [_reservedNativeProperties]). |
| + // However, some of them are really short and safe to use for fields. |
| + // Thus, we shortcut the namer to use those first. |
| + if (count < MinifyNamer._reservedNativeProperties.length && |
| + MinifyNamer._reservedNativeProperties[count].length <= 2) { |
| + nameStore.add( |
| + new StringBackedName(MinifyNamer._reservedNativeProperties[count])); |
| + } else { |
| + nameStore.add(namer.getFreshName("field$count", namer.usedInstanceNames, |
| + namer.suggestedInstanceNames)); |
| + } |
| + } |
| + |
| + return nameStore[count]; |
| + } |
| +} |
| + |
| +/** |
| + * A [_FieldNamingScope] encodes a node in the inheritance tree of the current |
| + * class hierarchy. The root node typically is the node corresponding to the |
| + * `Object` class. It is used to assign a unique name to each field of a class. |
| + * Unique here means unique wrt. all fields along the path back to the root. |
| + * This is achieved at construction time via the [_fieldNameCounter] field that |
| + * counts the number of fields on the path to the root node that have been |
| + * encountered so far. |
| + * |
| + * Obviously, this only works if no fields are added to a parent node after its |
| + * children have added their first field. |
| + */ |
| +class _FieldNamingScope { |
| + final _FieldNamingScope superScope; |
| + final Entity container; |
| + final Map<Element, jsAst.Name> names = new Maplet<Element, jsAst.Name>(); |
| + final _FieldNamingRegistry registry; |
| + |
| + /// Naming counter used for fields of ordinary classes. |
| + int _fieldNameCounter; |
| + |
| + /// The number of fields along the superclass chain that use inheritance |
| + /// based naming, including the ones allocated for this scope. |
| + int get inheritanceBasedFieldNameCounter => _fieldNameCounter; |
| + |
| + /// The number of locally used fields. Depending on the naming source |
| + /// (e.g. inheritance based or globally unique for mixixns) this |
| + /// might be different from [inheritanceBasedFieldNameCounter]. |
| + int get _localFieldNameCounter => _fieldNameCounter; |
| + void set _localFieldNameCounter(int val) { |
| + _fieldNameCounter = val; |
| + } |
| + |
| + factory _FieldNamingScope.forClass( |
| + ClassElement cls, ClassWorld world, _FieldNamingRegistry registry) { |
| + _FieldNamingScope result = registry.scopes[cls]; |
| + if (result != null) return result; |
| + |
| + if (world.isUsedAsMixin(cls)) { |
| + result = new _MixinFieldNamingScope.mixin(cls, registry); |
| + } else { |
| + if (cls.superclass == null) { |
| + result = new _FieldNamingScope.rootScope(cls, registry); |
| + } else { |
| + _FieldNamingScope superScope = |
| + new _FieldNamingScope.forClass(cls.superclass, world, registry); |
| + if (cls.isMixinApplication) { |
| + result = |
| + new _MixinFieldNamingScope.mixedIn(cls, superScope, registry); |
| + } else { |
| + result = new _FieldNamingScope.inherit(cls, superScope, registry); |
| + } |
| + } |
| + } |
| + |
| + cls.forEachInstanceField((cls, field) => result.add(field)); |
| + |
| + registry.scopes[cls] = result; |
| + return result; |
| + } |
| + |
| + factory _FieldNamingScope.forBox(Local box, _FieldNamingRegistry registry) { |
| + return registry.scopes.putIfAbsent( |
| + box, () => new _BoxFieldNamingScope(box, registry)); |
| + } |
| + |
| + _FieldNamingScope.rootScope(this.container, this.registry) |
| + : superScope = null, |
| + _fieldNameCounter = 0; |
| + |
| + _FieldNamingScope.inherit(this.container, this.superScope, this.registry) { |
| + _fieldNameCounter = superScope.inheritanceBasedFieldNameCounter; |
| + } |
| + |
| + /** |
| + * Checks whether [name] is already used in the current scope chain. |
| + */ |
| + _isNameUnused(jsAst.Name name) { |
| + return !names.values.contains(name) && |
| + ((superScope == null) || superScope._isNameUnused(name)); |
| + } |
| + |
| + jsAst.Name _nextName() => registry.getName(_localFieldNameCounter++); |
| + |
| + jsAst.Name operator [](Element field) { |
| + jsAst.Name name = names[field]; |
| + if (name == null && superScope != null) return superScope[field]; |
| + return name; |
| + } |
| + |
| + void add(Element field) { |
| + if (names.containsKey(field)) return; |
| + |
| + jsAst.Name value = _nextName(); |
| + assert(invariant(field, _isNameUnused(value))); |
| + names[field] = value; |
| + } |
| + |
| + bool containsField(Element field) => names.containsKey(field); |
| +} |
| + |
| +/** |
| + * Field names for mixins have two constraints: They need to be unique in the |
| + * hierarchy of each application of a mixin and they need to be the same for |
| + * all applications of a mixin. To achieve this, we use global naming for |
| + * mixins from the same name pool as fields and add a `$` at the end to ensure |
| + * they do not collide with normal field names. The `$` sign is typically used |
| + * as a separator between method names and argument counts and does not appear |
| + * in generated names themselves. |
| + */ |
| +class _MixinFieldNamingScope extends _FieldNamingScope { |
| + int get _localFieldNameCounter => registry.globalCount; |
| + void set _localFieldNameCounter(int val) { |
| + registry.globalCount = val; |
| + } |
| + |
| + @override |
| + Map<Entity, jsAst.Name> get names => registry.globalNames; |
| + |
| + _MixinFieldNamingScope.mixin(ClassElement cls, _FieldNamingRegistry registry) |
| + : super.rootScope(cls, registry); |
| + |
| + _MixinFieldNamingScope.mixedIn(MixinApplicationElement container, |
| + _FieldNamingScope superScope, _FieldNamingRegistry registry) |
| + : super.inherit(container, superScope, registry); |
| + |
| + jsAst.Name _nextName() { |
| + jsAst.Name proposed = super._nextName(); |
| + return new CompoundName([proposed, Namer._literalDollar]); |
| + } |
| +} |
| + |
| +/** |
| + * [BoxFieldElement] fields work differently in that they do not belong to an |
| + * actual class but an anonymous box associated to a [Local]. As there is no |
| + * inheritance chain, we do not need to compute fields a priori but can assign |
| + * names on the fly. |
| + */ |
| +class _BoxFieldNamingScope extends _FieldNamingScope { |
| + _BoxFieldNamingScope(Local box, _FieldNamingRegistry registry) |
| + : super.rootScope(box, registry); |
| + |
| + @override |
| + bool containsField(_) => true; |
| + |
| + jsAst.Name operator [](Element field) { |
| + if (!names.containsKey(field)) add(field); |
| + return names[field]; |
| + } |
| +} |