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 |