| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 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. |
| 4 |
| 5 part of js_backend; |
| 6 |
| 7 abstract class _MinifiedFieldNamer implements Namer { |
| 8 _FieldNamingRegistry get fieldRegistry; |
| 9 |
| 10 // Returns a minimal name for the field that is globally unique along |
| 11 // the given element's class inheritance chain. |
| 12 // |
| 13 // The inheritance scope based naming might not yield a name. For instance, |
| 14 // this could be because the field belongs to a mixin. In such a case this |
| 15 // will return `null` and a normal field name has to be used. |
| 16 jsAst.Name _minifiedInstanceFieldPropertyName(Element element) { |
| 17 if (element.hasFixedBackendName) { |
| 18 return new StringBackedName(element.fixedBackendName); |
| 19 } |
| 20 |
| 21 _FieldNamingScope names; |
| 22 if (element is BoxFieldElement) { |
| 23 names = new _FieldNamingScope.forBox(element.box, fieldRegistry); |
| 24 } else { |
| 25 ClassElement cls = element is ClosureFieldElement |
| 26 ? element.closureClass |
| 27 : element.enclosingClass; |
| 28 names = |
| 29 new _FieldNamingScope.forClass(cls, compiler.world, fieldRegistry); |
| 30 } |
| 31 |
| 32 if (names.containsField(element)) { |
| 33 return names[element]; |
| 34 } |
| 35 return null; |
| 36 } |
| 37 } |
| 38 |
| 39 /** |
| 40 * Encapsulates the global state of field naming. |
| 41 * |
| 42 * The field naming registry allocates names to be used along a path in the |
| 43 * inheritance hierarchy of fields, starting with the object class. The actual |
| 44 * hierarchy is encoded using instances of [_FieldNamingScope]. |
| 45 */ |
| 46 class _FieldNamingRegistry { |
| 47 final MinifyNamer namer; |
| 48 |
| 49 final Map<Entity, _FieldNamingScope> scopes = |
| 50 new Map<Entity, _FieldNamingScope>(); |
| 51 |
| 52 final Map<Entity, jsAst.Name> globalNames = new Map<Entity, jsAst.Name>(); |
| 53 |
| 54 int globalCount = 0; |
| 55 |
| 56 final List<jsAst.Name> nameStore = new List<jsAst.Name>(); |
| 57 |
| 58 _FieldNamingRegistry(this.namer); |
| 59 |
| 60 // Returns the name to be used for a field with distance [index] from the |
| 61 // root of the object hierarchy. The distance thereby is computed as the |
| 62 // number of fields preceding the current field in its classes inheritance |
| 63 // chain. |
| 64 // |
| 65 // The implementation assumes that names are requedsted in order, that is the |
| 66 // name at position i+1 is requested after the name at position i was |
| 67 // requested. |
| 68 jsAst.Name getName(int index) { |
| 69 if (index >= nameStore.length) { |
| 70 // The namer usually does not use certain names as they clash with |
| 71 // existing properties on JS objects (see [_reservedNativeProperties]). |
| 72 // However, some of them are really short and safe to use for fields. |
| 73 // Thus, we shortcut the namer to use those first. |
| 74 assert(index == nameStore.length + 1); |
| 75 if (index < MinifyNamer._reservedNativeProperties.length && |
| 76 MinifyNamer._reservedNativeProperties[index].length <= 2) { |
| 77 nameStore.add( |
| 78 new StringBackedName(MinifyNamer._reservedNativeProperties[index])); |
| 79 } else { |
| 80 nameStore.add(namer.getFreshName("field$index", namer.usedInstanceNames, |
| 81 namer.suggestedInstanceNames)); |
| 82 } |
| 83 } |
| 84 |
| 85 return nameStore[index]; |
| 86 } |
| 87 } |
| 88 |
| 89 /** |
| 90 * A [_FieldNamingScope] encodes a node in the inheritance tree of the current |
| 91 * class hierarchy. The root node typically is the node corresponding to the |
| 92 * `Object` class. It is used to assign a unique name to each field of a class. |
| 93 * Unique here means unique wrt. all fields along the path back to the root. |
| 94 * This is achieved at construction time via the [_fieldNameCounter] field that |
| 95 * counts the number of fields on the path to the root node that have been |
| 96 * encountered so far. |
| 97 * |
| 98 * Obviously, this only works if no fields are added to a parent node after its |
| 99 * children have added their first field. |
| 100 */ |
| 101 class _FieldNamingScope { |
| 102 final _FieldNamingScope superScope; |
| 103 final Entity container; |
| 104 final Map<Element, jsAst.Name> names = new Maplet<Element, jsAst.Name>(); |
| 105 final _FieldNamingRegistry registry; |
| 106 |
| 107 /// Naming counter used for fields of ordinary classes. |
| 108 int _fieldNameCounter; |
| 109 |
| 110 /// The number of fields along the superclass chain that use inheritance |
| 111 /// based naming, including the ones allocated for this scope. |
| 112 int get inheritanceBasedFieldNameCounter => _fieldNameCounter; |
| 113 |
| 114 /// The number of locally used fields. Depending on the naming source |
| 115 /// (e.g. inheritance based or globally unique for mixixns) this |
| 116 /// might be different from [inheritanceBasedFieldNameCounter]. |
| 117 int get _localFieldNameCounter => _fieldNameCounter; |
| 118 void set _localFieldNameCounter(int val) { |
| 119 _fieldNameCounter = val; |
| 120 } |
| 121 |
| 122 factory _FieldNamingScope.forClass( |
| 123 ClassElement cls, ClassWorld world, _FieldNamingRegistry registry) { |
| 124 _FieldNamingScope result = registry.scopes[cls]; |
| 125 if (result != null) return result; |
| 126 |
| 127 if (world.isUsedAsMixin(cls)) { |
| 128 result = new _MixinFieldNamingScope.mixin(cls, registry); |
| 129 } else { |
| 130 if (cls.superclass == null) { |
| 131 result = new _FieldNamingScope.rootScope(cls, registry); |
| 132 } else { |
| 133 _FieldNamingScope superScope = |
| 134 new _FieldNamingScope.forClass(cls.superclass, world, registry); |
| 135 if (cls.isMixinApplication) { |
| 136 result = |
| 137 new _MixinFieldNamingScope.mixedIn(cls, superScope, registry); |
| 138 } else { |
| 139 result = new _FieldNamingScope.inherit(cls, superScope, registry); |
| 140 } |
| 141 } |
| 142 } |
| 143 |
| 144 cls.forEachInstanceField((cls, field) => result.add(field)); |
| 145 |
| 146 registry.scopes[cls] = result; |
| 147 return result; |
| 148 } |
| 149 |
| 150 factory _FieldNamingScope.forBox(Local box, _FieldNamingRegistry registry) { |
| 151 return registry.scopes.putIfAbsent( |
| 152 box, () => new _BoxFieldNamingScope(box, registry)); |
| 153 } |
| 154 |
| 155 _FieldNamingScope.rootScope(this.container, this.registry) |
| 156 : superScope = null, |
| 157 _fieldNameCounter = 0; |
| 158 |
| 159 _FieldNamingScope.inherit(this.container, this.superScope, this.registry) { |
| 160 _fieldNameCounter = superScope.inheritanceBasedFieldNameCounter; |
| 161 } |
| 162 |
| 163 /** |
| 164 * Checks whether [name] is already used in the current scope chain. |
| 165 */ |
| 166 _isNameUnused(jsAst.Name name) { |
| 167 return !names.values.contains(name) && |
| 168 ((superScope == null) || superScope._isNameUnused(name)); |
| 169 } |
| 170 |
| 171 jsAst.Name _nextName() => registry.getName(_localFieldNameCounter++); |
| 172 |
| 173 jsAst.Name operator [](Element field) { |
| 174 jsAst.Name name = names[field]; |
| 175 if (name == null && superScope != null) return superScope[field]; |
| 176 return name; |
| 177 } |
| 178 |
| 179 void add(Element field) { |
| 180 if (names.containsKey(field)) return; |
| 181 |
| 182 jsAst.Name value = _nextName(); |
| 183 assert(invariant(field, _isNameUnused(value))); |
| 184 names[field] = value; |
| 185 } |
| 186 |
| 187 bool containsField(Element field) => names.containsKey(field); |
| 188 } |
| 189 |
| 190 /** |
| 191 * Field names for mixins have two constraints: They need to be unique in the |
| 192 * hierarchy of each application of a mixin and they need to be the same for |
| 193 * all applications of a mixin. To achieve this, we use global naming for |
| 194 * mixins from the same name pool as fields and add a `$` at the end to ensure |
| 195 * they do not collide with normal field names. The `$` sign is typically used |
| 196 * as a separator between method names and argument counts and does not appear |
| 197 * in generated names themselves. |
| 198 */ |
| 199 class _MixinFieldNamingScope extends _FieldNamingScope { |
| 200 int get _localFieldNameCounter => registry.globalCount; |
| 201 void set _localFieldNameCounter(int val) { |
| 202 registry.globalCount = val; |
| 203 } |
| 204 |
| 205 @override |
| 206 Map<Entity, jsAst.Name> get names => registry.globalNames; |
| 207 |
| 208 _MixinFieldNamingScope.mixin(ClassElement cls, _FieldNamingRegistry registry) |
| 209 : super.rootScope(cls, registry); |
| 210 |
| 211 _MixinFieldNamingScope.mixedIn(MixinApplicationElement container, |
| 212 _FieldNamingScope superScope, _FieldNamingRegistry registry) |
| 213 : super.inherit(container, superScope, registry); |
| 214 |
| 215 jsAst.Name _nextName() { |
| 216 jsAst.Name proposed = super._nextName(); |
| 217 return new CompoundName([proposed, Namer._literalDollar]); |
| 218 } |
| 219 } |
| 220 |
| 221 /** |
| 222 * [BoxFieldElement] fields work differently in that they do not belong to an |
| 223 * actual class but an anonymous box associated to a [Local]. As there is no |
| 224 * inheritance chain, we do not need to compute fields a priori but can assign |
| 225 * names on the fly. |
| 226 */ |
| 227 class _BoxFieldNamingScope extends _FieldNamingScope { |
| 228 _BoxFieldNamingScope(Local box, _FieldNamingRegistry registry) |
| 229 : super.rootScope(box, registry); |
| 230 |
| 231 @override |
| 232 bool containsField(_) => true; |
| 233 |
| 234 jsAst.Name operator [](Element field) { |
| 235 if (!names.containsKey(field)) add(field); |
| 236 return names[field]; |
| 237 } |
| 238 } |
| OLD | NEW |