| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2015, the Dartino 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.md file. | |
| 4 | |
| 5 library fletchc.fletch_class_builder; | |
| 6 | |
| 7 import 'package:compiler/src/dart_types.dart'; | |
| 8 import 'package:compiler/src/elements/elements.dart'; | |
| 9 import 'package:compiler/src/universe/selector.dart'; | |
| 10 import 'package:persistent/persistent.dart'; | |
| 11 | |
| 12 import 'fletch_function_builder.dart'; | |
| 13 import 'fletch_context.dart'; | |
| 14 import 'fletch_backend.dart'; | |
| 15 | |
| 16 import '../fletch_system.dart'; | |
| 17 import '../vm_commands.dart'; | |
| 18 | |
| 19 // TODO(ahe): Remove this import. | |
| 20 import '../incremental/fletchc_incremental.dart' show | |
| 21 IncrementalCompilationFailed; | |
| 22 | |
| 23 abstract class FletchClassBuilder { | |
| 24 int get classId; | |
| 25 ClassElement get element; | |
| 26 FletchClassBuilder get superclass; | |
| 27 int get fields; | |
| 28 | |
| 29 /** | |
| 30 * Returns the number of instance fields of all the super classes of this | |
| 31 * class. | |
| 32 * | |
| 33 * If this class has no super class (if it's Object), 0 is returned. | |
| 34 */ | |
| 35 int get superclassFields => hasSuperClass ? superclass.fields : 0; | |
| 36 | |
| 37 bool get hasSuperClass => superclass != null; | |
| 38 | |
| 39 void addToMethodTable(int selector, FletchFunctionBase functionBase); | |
| 40 void removeFromMethodTable(FletchFunctionBase function); | |
| 41 | |
| 42 void addField(FieldElement field); | |
| 43 void removeField(FieldElement field); | |
| 44 | |
| 45 // Add a selector for is-tests. The selector is only to be hit with the | |
| 46 // InvokeTest bytecode, as the function is not guraranteed to be valid. | |
| 47 void addIsSelector(int selector); | |
| 48 void createIsFunctionEntry(FletchBackend backend, int arity); | |
| 49 void updateImplicitAccessors(FletchBackend backend); | |
| 50 | |
| 51 FletchClass finalizeClass( | |
| 52 FletchContext context, | |
| 53 List<VmCommand> commands); | |
| 54 | |
| 55 // The method table for a class is a mapping from Fletch's integer | |
| 56 // selectors to method ids. It contains all methods defined for a | |
| 57 // class including the implicit accessors. The returned map is not sorted. | |
| 58 // TODO(ajohnsen): Remove once not used by feature_test anymore. | |
| 59 PersistentMap<int, int> computeMethodTable(); | |
| 60 | |
| 61 bool computeSchemaChange(List<VmCommand> commands) { | |
| 62 return false; | |
| 63 } | |
| 64 } | |
| 65 | |
| 66 void forEachField(ClassElement c, void action(FieldElement field)) { | |
| 67 List classes = []; | |
| 68 while (c != null) { | |
| 69 classes.add(c); | |
| 70 c = c.superclass; | |
| 71 } | |
| 72 for (int i = classes.length - 1; i >= 0; i--) { | |
| 73 classes[i].implementation.forEachInstanceField((_, FieldElement field) { | |
| 74 action(field); | |
| 75 }); | |
| 76 } | |
| 77 } | |
| 78 | |
| 79 class FletchNewClassBuilder extends FletchClassBuilder { | |
| 80 final int classId; | |
| 81 final ClassElement element; | |
| 82 final FletchClassBuilder superclass; | |
| 83 final bool isBuiltin; | |
| 84 | |
| 85 // The extra fields are synthetic fields not represented in any Dart source | |
| 86 // code. They are used for the synthetic closure classes that are introduced | |
| 87 // behind the scenes. | |
| 88 final int extraFields; | |
| 89 | |
| 90 final Map<int, int> _implicitAccessorTable = <int, int>{}; | |
| 91 final Map<int, FletchFunctionBase> _methodTable = <int, FletchFunctionBase>{}; | |
| 92 | |
| 93 FletchNewClassBuilder( | |
| 94 this.classId, | |
| 95 this.element, | |
| 96 this.superclass, | |
| 97 this.isBuiltin, | |
| 98 this.extraFields); | |
| 99 | |
| 100 int get fields { | |
| 101 int count = superclassFields + extraFields; | |
| 102 if (element != null) { | |
| 103 // TODO(kasperl): Once we change compiled class to be immutable, we | |
| 104 // should cache the field count. | |
| 105 element.implementation.forEachInstanceField((_, __) { count++; }); | |
| 106 } | |
| 107 return count; | |
| 108 } | |
| 109 | |
| 110 void addToMethodTable(int selector, FletchFunctionBase functionBase) { | |
| 111 _methodTable[selector] = functionBase; | |
| 112 } | |
| 113 | |
| 114 void addField(FieldElement field) { | |
| 115 throw new StateError("Fields should not be added to a new class."); | |
| 116 } | |
| 117 | |
| 118 void removeField(FieldElement field) { | |
| 119 // TODO(ahe): Change this to a StateError when bug in incremental compiler | |
| 120 // is fixed (tested by super_is_parameter). | |
| 121 throw new IncrementalCompilationFailed( | |
| 122 "Can't remove a field ($field) from a new class ($element)"); | |
| 123 } | |
| 124 | |
| 125 void removeFromMethodTable(FletchFunctionBase function) { | |
| 126 throw new StateError("Methods should not be removed from a new class."); | |
| 127 } | |
| 128 | |
| 129 void addIsSelector(int selector) { | |
| 130 // TODO(ajohnsen): 'null' is a placeholder. Generate dummy function? | |
| 131 _methodTable[selector] = null; | |
| 132 } | |
| 133 | |
| 134 PersistentMap<int, int> computeMethodTable() { | |
| 135 PersistentMap<int, int> result = new PersistentMap<int, int>(); | |
| 136 List<int> selectors = _implicitAccessorTable.keys.toList() | |
| 137 ..addAll(_methodTable.keys); | |
| 138 for (int selector in selectors) { | |
| 139 if (_methodTable.containsKey(selector)) { | |
| 140 FletchFunctionBase function = _methodTable[selector]; | |
| 141 int functionId = function == null ? 0 : function.functionId; | |
| 142 result = result.insert(selector, functionId); | |
| 143 } else { | |
| 144 result = result.insert(selector, _implicitAccessorTable[selector]); | |
| 145 } | |
| 146 } | |
| 147 return result; | |
| 148 } | |
| 149 | |
| 150 void updateImplicitAccessors(FletchBackend backend) { | |
| 151 _implicitAccessorTable.clear(); | |
| 152 // If we don't have an element (stub class), we don't have anything to | |
| 153 // generate accessors for. | |
| 154 if (element == null) return; | |
| 155 // TODO(ajohnsen): Don't do this once dart2js can enqueue field getters in | |
| 156 // CodegenEnqueuer. | |
| 157 int fieldIndex = superclassFields; | |
| 158 element.implementation.forEachInstanceField((enclosing, field) { | |
| 159 var getter = new Selector.getter(field.memberName); | |
| 160 int getterSelector = backend.context.toFletchSelector(getter); | |
| 161 _implicitAccessorTable[getterSelector] = backend.makeGetter(fieldIndex); | |
| 162 | |
| 163 if (!field.isFinal) { | |
| 164 var setter = new Selector.setter(new Name(field.name, field.library)); | |
| 165 var setterSelector = backend.context.toFletchSelector(setter); | |
| 166 _implicitAccessorTable[setterSelector] = backend.makeSetter(fieldIndex); | |
| 167 } | |
| 168 | |
| 169 fieldIndex++; | |
| 170 }); | |
| 171 } | |
| 172 | |
| 173 void createIsFunctionEntry(FletchBackend backend, int arity) { | |
| 174 int fletchSelector = backend.context.toFletchIsSelector( | |
| 175 backend.compiler.coreClasses.functionClass); | |
| 176 addIsSelector(fletchSelector); | |
| 177 fletchSelector = backend.context.toFletchIsSelector( | |
| 178 backend.compiler.coreClasses.functionClass, arity); | |
| 179 addIsSelector(fletchSelector); | |
| 180 } | |
| 181 | |
| 182 FletchClass finalizeClass( | |
| 183 FletchContext context, | |
| 184 List<VmCommand> commands) { | |
| 185 if (isBuiltin) { | |
| 186 int nameId = context.getSymbolId(element.name); | |
| 187 commands.add(new PushBuiltinClass(nameId, fields)); | |
| 188 } else { | |
| 189 commands.add(new PushNewClass(fields)); | |
| 190 } | |
| 191 | |
| 192 commands.add(const Dup()); | |
| 193 commands.add(new PopToMap(MapId.classes, classId)); | |
| 194 | |
| 195 PersistentMap<int, int> methodTable = computeMethodTable(); | |
| 196 for (int selector in methodTable.keys.toList()..sort()) { | |
| 197 int functionId = methodTable[selector]; | |
| 198 commands.add(new PushNewInteger(selector)); | |
| 199 commands.add(new PushFromMap(MapId.methods, functionId)); | |
| 200 } | |
| 201 commands.add(new ChangeMethodTable(methodTable.length)); | |
| 202 | |
| 203 List<FieldElement> fieldsList = new List<FieldElement>(fields); | |
| 204 int index = 0; | |
| 205 forEachField(element, (field) { | |
| 206 fieldsList[index++] = field; | |
| 207 }); | |
| 208 | |
| 209 return new FletchClass( | |
| 210 classId, | |
| 211 // TODO(ajohnsen): Take name in FletchClassBuilder constructor. | |
| 212 element == null ? '<internal>' : element.name, | |
| 213 element, | |
| 214 superclass == null ? -1 : superclass.classId, | |
| 215 superclassFields, | |
| 216 methodTable, | |
| 217 fieldsList); | |
| 218 } | |
| 219 | |
| 220 String toString() => "FletchClassBuilder($element, $classId)"; | |
| 221 } | |
| 222 | |
| 223 class FletchPatchClassBuilder extends FletchClassBuilder { | |
| 224 final FletchClass klass; | |
| 225 final FletchClassBuilder superclass; | |
| 226 | |
| 227 final Map<int, int> _implicitAccessorTable = <int, int>{}; | |
| 228 final Map<int, FletchFunctionBase> _newMethods = <int, FletchFunctionBase>{}; | |
| 229 final Set<FletchFunctionBase> _removedMethods = new Set<FletchFunctionBase>(); | |
| 230 final Set<FieldElement> _removedFields = new Set<FieldElement>(); | |
| 231 final List<int> _removedAccessors = <int>[]; | |
| 232 bool _fieldsChanged = false; | |
| 233 | |
| 234 // TODO(ajohnsen): Reconsider bookkeeping of extra fields (this is really only | |
| 235 // extra super-class fields). | |
| 236 int extraFields = 0; | |
| 237 | |
| 238 // TODO(ajohnsen): Can the element change? | |
| 239 FletchPatchClassBuilder(this.klass, this.superclass); | |
| 240 | |
| 241 int get classId => klass.classId; | |
| 242 ClassElement get element => klass.element; | |
| 243 int get fields => klass.fields.length; | |
| 244 | |
| 245 void addToMethodTable(int selector, FletchFunctionBase functionBase) { | |
| 246 _newMethods[selector] = functionBase; | |
| 247 } | |
| 248 | |
| 249 void removeFromMethodTable(FletchFunctionBase function) { | |
| 250 assert(function != null); | |
| 251 _removedMethods.add(function); | |
| 252 } | |
| 253 | |
| 254 void removeField(FieldElement field) { | |
| 255 if (field.enclosingClass != element) extraFields--; | |
| 256 _fieldsChanged = true; | |
| 257 _removedFields.add(field); | |
| 258 } | |
| 259 | |
| 260 void addField(FieldElement field) { | |
| 261 if (field.enclosingClass != element) extraFields++; | |
| 262 _fieldsChanged = true; | |
| 263 } | |
| 264 | |
| 265 void addIsSelector(int selector) { | |
| 266 // TODO(ajohnsen): Implement. | |
| 267 } | |
| 268 | |
| 269 void createIsFunctionEntry(FletchBackend backend, int arity) { | |
| 270 // TODO(ajohnsen): Implement. | |
| 271 } | |
| 272 | |
| 273 void updateImplicitAccessors(FletchBackend backend) { | |
| 274 // If we don't have an element (stub class), we don't have anything to | |
| 275 // generate accessors for. | |
| 276 if (element == null) return; | |
| 277 // TODO(ajohnsen): Don't do this once dart2js can enqueue field getters in | |
| 278 // CodegenEnqueuer. | |
| 279 int fieldIndex = superclassFields + extraFields; | |
| 280 element.implementation.forEachInstanceField((enclosing, field) { | |
| 281 var getter = new Selector.getter(new Name(field.name, field.library)); | |
| 282 int getterSelector = backend.context.toFletchSelector(getter); | |
| 283 _implicitAccessorTable[getterSelector] = backend.makeGetter(fieldIndex); | |
| 284 | |
| 285 if (!field.isFinal) { | |
| 286 var setter = new Selector.setter(new Name(field.name, field.library)); | |
| 287 var setterSelector = backend.context.toFletchSelector(setter); | |
| 288 _implicitAccessorTable[setterSelector] = backend.makeSetter(fieldIndex); | |
| 289 } | |
| 290 | |
| 291 fieldIndex++; | |
| 292 }); | |
| 293 | |
| 294 for (FieldElement field in _removedFields) { | |
| 295 Selector getter = | |
| 296 new Selector.getter(new Name(field.name, field.library)); | |
| 297 int getterSelector = backend.context.toFletchSelector(getter); | |
| 298 _removedAccessors.add(getterSelector); | |
| 299 | |
| 300 if (!field.isFinal) { | |
| 301 Selector setter = | |
| 302 new Selector.setter(new Name(field.name, field.library)); | |
| 303 int setterSelector = backend.context.toFletchSelector(setter); | |
| 304 _removedAccessors.add(setterSelector); | |
| 305 } | |
| 306 } | |
| 307 } | |
| 308 | |
| 309 PersistentMap<int, int> computeMethodTable() { | |
| 310 PersistentMap<int, int> methodTable = klass.methodTable; | |
| 311 | |
| 312 for (int selector in _removedAccessors) { | |
| 313 methodTable = methodTable.delete(selector); | |
| 314 } | |
| 315 | |
| 316 for (FletchFunctionBase function in _removedMethods) { | |
| 317 methodTable.forEachKeyValue((int selector, int functionId) { | |
| 318 if (functionId == function.functionId) { | |
| 319 methodTable = methodTable.delete(selector); | |
| 320 } | |
| 321 }); | |
| 322 } | |
| 323 | |
| 324 // TODO(ajohnsen): Generate this from add/remove field operations. | |
| 325 _implicitAccessorTable.forEach((int selector, int functionId) { | |
| 326 methodTable = methodTable.insert(selector, functionId); | |
| 327 }); | |
| 328 | |
| 329 _newMethods.forEach((int selector, FletchFunctionBase function) { | |
| 330 methodTable = methodTable.insert(selector, function.functionId); | |
| 331 }); | |
| 332 | |
| 333 return methodTable; | |
| 334 } | |
| 335 | |
| 336 FletchClass finalizeClass( | |
| 337 FletchContext context, | |
| 338 List<VmCommand> commands) { | |
| 339 // TODO(ajohnsen): We need to figure out when to do this. It should be after | |
| 340 // we have updated class fields, but before we hit 'computeSystem'. | |
| 341 updateImplicitAccessors(context.backend); | |
| 342 | |
| 343 commands.add(new PushFromMap(MapId.classes, classId)); | |
| 344 | |
| 345 PersistentMap<int, int> methodTable = computeMethodTable(); | |
| 346 for (int selector in methodTable.keys.toList()..sort()) { | |
| 347 int functionId = methodTable[selector]; | |
| 348 commands.add(new PushNewInteger(selector)); | |
| 349 commands.add(new PushFromMap(MapId.methods, functionId)); | |
| 350 } | |
| 351 commands.add(new ChangeMethodTable(methodTable.length)); | |
| 352 | |
| 353 List<FieldElement> fieldsList = <FieldElement>[]; | |
| 354 forEachField(element, (field) { fieldsList.add(field); }); | |
| 355 | |
| 356 return new FletchClass( | |
| 357 classId, | |
| 358 // TODO(ajohnsen): Take name in FletchClassBuilder constructor. | |
| 359 element == null ? '<internal>' : element.name, | |
| 360 element, | |
| 361 superclass == null ? -1 : superclass.classId, | |
| 362 superclassFields + extraFields, | |
| 363 methodTable, | |
| 364 fieldsList); | |
| 365 } | |
| 366 | |
| 367 bool computeSchemaChange(List<VmCommand> commands) { | |
| 368 if (!_fieldsChanged) return false; | |
| 369 | |
| 370 // TODO(ajohnsen): Don't recompute this list. | |
| 371 List<FieldElement> afterFields = <FieldElement>[]; | |
| 372 forEachField(element, (field) { afterFields.add(field); }); | |
| 373 | |
| 374 // TODO(ajohnsen): Handle sub/super classes. | |
| 375 int numberOfClasses = 1; | |
| 376 commands.add(new PushFromMap(MapId.classes, classId)); | |
| 377 | |
| 378 // Then we push a transformation mapping that tells the runtime system how | |
| 379 // to build the values for the first part of all instances of the classes. | |
| 380 // Pre-existing fields that fall after the mapped part will be copied with | |
| 381 // no changes. | |
| 382 const VALUE_FROM_ELSEWHERE = 0; | |
| 383 const VALUE_FROM_OLD_INSTANCE = 1; | |
| 384 for (int i = 0; i < afterFields.length; i++) { | |
| 385 FieldElement field = afterFields[i]; | |
| 386 int beforeIndex = klass.fields.indexOf(field); | |
| 387 if (beforeIndex >= 0) { | |
| 388 commands.add(const PushNewInteger(VALUE_FROM_OLD_INSTANCE)); | |
| 389 commands.add(new PushNewInteger(beforeIndex)); | |
| 390 } else { | |
| 391 commands.add(const PushNewInteger(VALUE_FROM_ELSEWHERE)); | |
| 392 commands.add(const PushNull()); | |
| 393 } | |
| 394 } | |
| 395 commands.add(new PushNewArray(afterFields.length * 2)); | |
| 396 | |
| 397 // Finally, ask the runtime to change the schemas! | |
| 398 int fieldCountDelta = afterFields.length - klass.fields.length; | |
| 399 commands.add(new ChangeSchemas(numberOfClasses, fieldCountDelta)); | |
| 400 | |
| 401 return true; | |
| 402 } | |
| 403 } | |
| OLD | NEW |