| 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 |