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.constructor_codegen; | |
6 | |
7 import 'package:compiler/src/elements/elements.dart'; | |
8 import 'package:compiler/src/resolution/tree_elements.dart' show | |
9 TreeElements; | |
10 import 'package:compiler/src/tree/tree.dart'; | |
11 import 'package:compiler/src/universe/call_structure.dart' show | |
12 CallStructure; | |
13 import 'package:compiler/src/dart_types.dart'; | |
14 | |
15 import 'fletch_context.dart'; | |
16 | |
17 import 'fletch_function_builder.dart' show | |
18 FletchFunctionBuilder; | |
19 | |
20 import 'fletch_class_builder.dart' show | |
21 FletchClassBuilder; | |
22 | |
23 import 'closure_environment.dart'; | |
24 | |
25 import 'lazy_field_initializer_codegen.dart'; | |
26 | |
27 import 'codegen_visitor.dart'; | |
28 | |
29 import 'fletch_registry.dart' show | |
30 FletchRegistry; | |
31 | |
32 class ConstructorCodegen extends CodegenVisitor with FletchRegistryMixin { | |
33 final FletchRegistry registry; | |
34 | |
35 final FletchClassBuilder classBuilder; | |
36 | |
37 final Map<FieldElement, LocalValue> fieldScope = <FieldElement, LocalValue>{}; | |
38 | |
39 final List<ConstructorElement> constructors = <ConstructorElement>[]; | |
40 | |
41 ClosureEnvironment initializerClosureEnvironment; | |
42 | |
43 ConstructorCodegen(FletchFunctionBuilder functionBuilder, | |
44 FletchContext context, | |
45 TreeElements elements, | |
46 this.registry, | |
47 ClosureEnvironment closureEnvironment, | |
48 ConstructorElement constructor, | |
49 this.classBuilder) | |
50 : super(functionBuilder, context, elements, | |
51 closureEnvironment, constructor); | |
52 | |
53 ConstructorElement get constructor => element; | |
54 | |
55 BytecodeAssembler get assembler => functionBuilder.assembler; | |
56 | |
57 ClosureEnvironment get closureEnvironment { | |
58 if (initializerClosureEnvironment != null) { | |
59 return initializerClosureEnvironment; | |
60 } | |
61 return super.closureEnvironment; | |
62 } | |
63 | |
64 void compile() { | |
65 // Push all initial field values (including super-classes). | |
66 pushInitialFieldValues(classBuilder); | |
67 // The stack is now: | |
68 // Value for field-0 | |
69 // ... | |
70 // Value for field-n | |
71 // | |
72 FunctionSignature signature = constructor.functionSignature; | |
73 int parameterCount = signature.parameterCount; | |
74 | |
75 // Visit constructor and evaluate initializers and super calls. The | |
76 // arguments to the constructor are located before the return address. | |
77 inlineInitializers(constructor, -parameterCount - 1); | |
78 | |
79 handleAllocationAndBodyCall(); | |
80 } | |
81 | |
82 LazyFieldInitializerCodegen lazyFieldInitializerCodegenFor( | |
83 FletchFunctionBuilder function, | |
84 FieldElement field) { | |
85 TreeElements elements = field.resolvedAst.elements; | |
86 return new LazyFieldInitializerCodegen( | |
87 function, | |
88 context, | |
89 elements, | |
90 registry, | |
91 context.backend.createClosureEnvironment(field, elements), | |
92 field); | |
93 } | |
94 | |
95 void handleAllocationAndBodyCall() { | |
96 // TODO(ajohnsen): Let allocate take an offset to the field stack, so we | |
97 // don't have to copy all the fields? | |
98 // Copy all the fields to the end of the stack. | |
99 int fields = classBuilder.fields; | |
100 for (int i = 0; i < fields; i++) { | |
101 assembler.loadSlot(i); | |
102 } | |
103 | |
104 // The stack is now: | |
105 // Value for field-0 | |
106 // ... | |
107 // Value for field-n | |
108 // [super arguments] | |
109 // Value for field-0 | |
110 // ... | |
111 // Value for field-n | |
112 | |
113 // Create the actual instance. | |
114 int classConstant = functionBuilder.allocateConstantFromClass( | |
115 classBuilder.classId); | |
116 // TODO(ajohnsen): Set immutable for all-final classes. | |
117 assembler.allocate(classConstant, fields, immutable: element.isConst); | |
118 | |
119 // The stack is now: | |
120 // Value for field-0 | |
121 // ... | |
122 // Value for field-n | |
123 // [super arguments] | |
124 // instance | |
125 | |
126 // Call constructor bodies in reverse order. | |
127 for (int i = constructors.length - 1; i >= 0; i--) { | |
128 callConstructorBody(constructors[i]); | |
129 } | |
130 | |
131 // Return the instance. | |
132 assembler | |
133 ..ret() | |
134 ..methodEnd(); | |
135 } | |
136 | |
137 /** | |
138 * Visit [constructor] and inline initializers and super calls, recursively. | |
139 */ | |
140 void inlineInitializers( | |
141 ConstructorElement constructor, | |
142 int firstParameterSlot) { | |
143 if (checkCompileError(constructor) || | |
144 checkCompileError(constructor.enclosingClass)) { | |
145 return; | |
146 } | |
147 | |
148 if (constructors.indexOf(constructor) >= 0) { | |
149 internalError(constructor.node, | |
150 "Multiple visits to the same constructor"); | |
151 } | |
152 | |
153 if (constructor.isSynthesized) { | |
154 // If the constructor is implicit, invoke the defining constructor. | |
155 if (constructor.functionSignature.parameterCount == 0) { | |
156 ConstructorElement defining = constructor.definingConstructor; | |
157 int initSlot = assembler.stackSize; | |
158 loadArguments(defining, new NodeList.empty(), CallStructure.NO_ARGS); | |
159 inlineInitializers(defining, initSlot); | |
160 return; | |
161 } | |
162 | |
163 // Otherwise the constructor is synthesized in the context of mixin | |
164 // applications, use the defining constructor. | |
165 do { | |
166 constructor = constructor.definingConstructor; | |
167 } while (constructor.isSynthesized); | |
168 } | |
169 | |
170 constructors.add(constructor); | |
171 FunctionSignature signature = constructor.functionSignature; | |
172 int parameterIndex = 0; | |
173 | |
174 initializerElements = constructor.resolvedAst.elements; | |
175 initializerClosureEnvironment = context.backend.createClosureEnvironment( | |
176 constructor, initializerElements); | |
177 | |
178 // Visit parameters and add them to scope. Note the scope is the scope of | |
179 // locals, in VisitingCodegen. | |
180 signature.orderedForEachParameter((ParameterElement parameter) { | |
181 LocalValue value = firstParameterSlot < 0 | |
182 ? createLocalValueForParameter( | |
183 parameter, | |
184 parameterIndex, | |
185 isCapturedValueBoxed: false) | |
186 : createLocalValueFor( | |
187 parameter, | |
188 slot: firstParameterSlot + parameterIndex, | |
189 isCapturedValueBoxed: false); | |
190 scope[parameter] = value; | |
191 if (parameter.isInitializingFormal) { | |
192 // If it's a initializing formal, store the value into initial | |
193 // field value. | |
194 InitializingFormalElement formal = parameter; | |
195 value.load(assembler); | |
196 fieldScope[formal.fieldElement].store(assembler); | |
197 assembler.pop(); | |
198 } | |
199 parameterIndex++; | |
200 }); | |
201 | |
202 visitInitializers(constructor.node, null); | |
203 } | |
204 | |
205 void doFieldInitializerSet(Send node, FieldElement field) { | |
206 fieldScope[field].store(assembler); | |
207 applyVisitState(); | |
208 } | |
209 | |
210 // This is called for each initializer list assignment. | |
211 void visitFieldInitializer( | |
212 SendSet node, | |
213 FieldElement field, | |
214 Node initializer, | |
215 _) { | |
216 // We only want the value of the actual initializer, not the usual | |
217 // 'body'. | |
218 visitForValue(initializer); | |
219 doFieldInitializerSet(node, field); | |
220 } | |
221 | |
222 void visitSuperConstructorInvoke( | |
223 Send node, | |
224 ConstructorElement superConstructor, | |
225 InterfaceType type, | |
226 NodeList arguments, | |
227 CallStructure callStructure, | |
228 _) { | |
229 // Load all parameters to the constructor, onto the stack. | |
230 loadArguments(superConstructor, arguments, callStructure); | |
231 int initSlot = assembler.stackSize - | |
232 superConstructor.functionSignature.parameterCount; | |
233 var previousElements = initializerElements; | |
234 var previousClosureEnvironment = initializerClosureEnvironment; | |
235 inlineInitializers(superConstructor, initSlot); | |
236 initializerElements = previousElements; | |
237 initializerClosureEnvironment = previousClosureEnvironment; | |
238 } | |
239 | |
240 void visitThisConstructorInvoke( | |
241 Send node, | |
242 ConstructorElement thisConstructor, | |
243 NodeList arguments, | |
244 CallStructure callStructure, | |
245 _) { | |
246 // TODO(ajohnsen): Is this correct behavior? | |
247 thisConstructor = thisConstructor.implementation; | |
248 // Load all parameters to the constructor, onto the stack. | |
249 loadArguments(thisConstructor, arguments, callStructure); | |
250 int initSlot = assembler.stackSize - | |
251 thisConstructor.functionSignature.parameterCount; | |
252 inlineInitializers(thisConstructor, initSlot); | |
253 } | |
254 | |
255 void visitImplicitSuperConstructorInvoke( | |
256 FunctionExpression node, | |
257 ConstructorElement superConstructor, | |
258 InterfaceType type, | |
259 _) { | |
260 int initSlot = assembler.stackSize; | |
261 // Always load arguments, as the super-constructor may have optional | |
262 // parameters. | |
263 loadArguments( | |
264 superConstructor, new NodeList.empty(), CallStructure.NO_ARGS); | |
265 inlineInitializers(superConstructor, initSlot); | |
266 } | |
267 | |
268 /** | |
269 * Load the [arguments] for caling [constructor]. | |
270 * | |
271 * Return the number of arguments pushed onto the stack. | |
272 */ | |
273 int loadArguments( | |
274 ConstructorElement constructor, | |
275 NodeList arguments, | |
276 CallStructure callStructure) { | |
277 FunctionSignature signature = constructor.functionSignature; | |
278 if (!signature.hasOptionalParameters || | |
279 !signature.optionalParametersAreNamed || | |
280 callStructure.namedArgumentCount == 0) { | |
281 return loadPositionalArguments(arguments, signature, constructor.name); | |
282 } | |
283 | |
284 int argumentCount = callStructure.argumentCount; | |
285 int namedArgumentCount = callStructure.namedArgumentCount; | |
286 | |
287 Iterator<Node> it = arguments.iterator; | |
288 int unnamedArguments = argumentCount - namedArgumentCount; | |
289 for (int i = 0; i < unnamedArguments; i++) { | |
290 it.moveNext(); | |
291 visitForValue(it.current); | |
292 } | |
293 | |
294 bool directMatch = namedArgumentCount == signature.optionalParameterCount; | |
295 Map<String, int> namedArguments = <String, int>{}; | |
296 for (int i = 0; i < namedArgumentCount; i++) { | |
297 String name = callStructure.namedArguments[i]; | |
298 namedArguments[name] = assembler.stackSize; | |
299 it.moveNext(); | |
300 visitForValue(it.current); | |
301 if (signature.orderedOptionalParameters[i].name != name) { | |
302 directMatch = false; | |
303 } | |
304 } | |
305 if (directMatch) return argumentCount; | |
306 | |
307 // There was no direct match. Push all unnamed arguments and all named | |
308 // arguments that have already been evaluated, in signature order. | |
309 for (int i = 0; i < unnamedArguments; i++) { | |
310 assembler.loadLocal(argumentCount - 1); | |
311 } | |
312 | |
313 for (ParameterElement parameter in signature.orderedOptionalParameters) { | |
314 int slot = namedArguments[parameter.name]; | |
315 if (slot != null) { | |
316 assembler.loadSlot(slot); | |
317 } else { | |
318 doParameterInitializer(parameter); | |
319 } | |
320 } | |
321 | |
322 // Some parameters may have defaulted to default value, making the | |
323 // parameter count larger than the argument count. | |
324 return argumentCount + signature.parameterCount; | |
325 } | |
326 | |
327 void callConstructorBody(ConstructorElement constructor) { | |
328 FunctionExpression node = constructor.node; | |
329 if (node == null || node.body.asEmptyStatement() != null) return; | |
330 | |
331 int functionId = requireFunction(constructor.declaration).functionId; | |
332 int constructorId = | |
333 functionBuilder.allocateConstantFromFunction(functionId); | |
334 | |
335 FunctionSignature signature = constructor.functionSignature; | |
336 | |
337 // Prepare for constructor body invoke. | |
338 assembler.dup(); | |
339 signature.orderedForEachParameter((FormalElement parameter) { | |
340 // Boxed parameters are passed as boxed objects, not as the values | |
341 // contained within like we do for ordinary invokes | |
342 scope[parameter].loadRaw(assembler); | |
343 }); | |
344 | |
345 assembler | |
346 ..invokeStatic(constructorId, 1 + signature.parameterCount) | |
347 ..pop(); | |
348 } | |
349 | |
350 void pushInitialFieldValues(FletchClassBuilder classBuilder) { | |
351 if (classBuilder.hasSuperClass) { | |
352 pushInitialFieldValues(classBuilder.superclass); | |
353 } | |
354 int fieldIndex = classBuilder.superclassFields; | |
355 ClassElement classElement = classBuilder.element.implementation; | |
356 classElement.forEachInstanceField((_, FieldElement field) { | |
357 fieldScope[field] = new UnboxedLocalValue(fieldIndex++, field); | |
358 Expression initializer = field.initializer; | |
359 if (initializer == null) { | |
360 assembler.loadLiteralNull(); | |
361 } else { | |
362 // Create a LazyFieldInitializerCodegen for compiling the initializer. | |
363 // Note that we reuse the functionBuilder, to inline it into the | |
364 // constructor. | |
365 LazyFieldInitializerCodegen codegen = | |
366 lazyFieldInitializerCodegenFor(functionBuilder, field); | |
367 | |
368 // We only want the value of the actual initializer, not the usual | |
369 // 'body'. | |
370 codegen.visitForValue(initializer); | |
371 } | |
372 }); | |
373 assert(fieldIndex <= classBuilder.fields); | |
374 } | |
375 } | |
OLD | NEW |