| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2014, 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 library dart2js.js_emitter.program_builder; | |
| 6 | |
| 7 import 'model.dart'; | |
| 8 import '../common.dart'; | |
| 9 import '../js/js.dart' as js; | |
| 10 | |
| 11 import '../js_backend/js_backend.dart' show | |
| 12 Namer, | |
| 13 JavaScriptBackend, | |
| 14 JavaScriptConstantCompiler; | |
| 15 | |
| 16 import '../closure.dart' show ClosureFieldElement; | |
| 17 | |
| 18 import 'js_emitter.dart' as emitterTask show | |
| 19 CodeEmitterTask, | |
| 20 Emitter, | |
| 21 InterceptorStubGenerator; | |
| 22 | |
| 23 import '../universe/universe.dart' show Universe; | |
| 24 import '../deferred_load.dart' show DeferredLoadTask, OutputUnit; | |
| 25 | |
| 26 part 'registry.dart'; | |
| 27 | |
| 28 class ProgramBuilder { | |
| 29 final Compiler _compiler; | |
| 30 final Namer namer; | |
| 31 final emitterTask.CodeEmitterTask _task; | |
| 32 | |
| 33 final Registry _registry; | |
| 34 | |
| 35 ProgramBuilder(Compiler compiler, | |
| 36 this.namer, | |
| 37 this._task) | |
| 38 : this._compiler = compiler, | |
| 39 this._registry = new Registry(compiler); | |
| 40 | |
| 41 JavaScriptBackend get backend => _compiler.backend; | |
| 42 Universe get universe => _compiler.codegenWorld; | |
| 43 | |
| 44 /// Mapping from [ClassElement] to constructed [Class]. We need this to | |
| 45 /// update the superclass in the [Class]. | |
| 46 final Map<ClassElement, Class> _classes = <ClassElement, Class>{}; | |
| 47 | |
| 48 /// Mapping from [OutputUnit] to constructed [Output]. We need this to | |
| 49 /// generate the deferredLoadingMap (to know which hunks to load). | |
| 50 final Map<OutputUnit, Output> _outputs = <OutputUnit, Output>{}; | |
| 51 | |
| 52 /// Mapping from [ConstantValue] to constructed [Constant]. We need this to | |
| 53 /// update field-initializers to point to the ConstantModel. | |
| 54 final Map<ConstantValue, Constant> _constants = <ConstantValue, Constant>{}; | |
| 55 | |
| 56 Program buildProgram() { | |
| 57 _task.outputClassLists.forEach(_registry.registerElements); | |
| 58 _task.outputStaticLists.forEach(_registry.registerElements); | |
| 59 _task.outputConstantLists.forEach(_registerConstants); | |
| 60 | |
| 61 // TODO(kasperl): There's code that implicitly needs access to the special | |
| 62 // $ holder so we have to register that. Can we track if we have to? | |
| 63 _registry.registerHolder(r'$'); | |
| 64 | |
| 65 MainOutput mainOutput = _buildMainOutput(_registry.mainFragment); | |
| 66 Iterable<Output> deferredOutputs = _registry.deferredFragments | |
| 67 .map((fragment) => _buildDeferredOutput(mainOutput, fragment)); | |
| 68 | |
| 69 List<Output> outputs = new List<Output>(_registry.fragmentCount); | |
| 70 outputs[0] = mainOutput; | |
| 71 outputs.setAll(1, deferredOutputs); | |
| 72 | |
| 73 Program result = | |
| 74 new Program(outputs, _task.outputContainsConstantList, _buildLoadMap()); | |
| 75 | |
| 76 // Resolve the superclass references after we've processed all the classes. | |
| 77 _classes.forEach((ClassElement element, Class c) { | |
| 78 if (element.superclass != null) { | |
| 79 c.setSuperclass(_classes[element.superclass]); | |
| 80 } | |
| 81 }); | |
| 82 | |
| 83 _markEagerClasses(); | |
| 84 | |
| 85 return result; | |
| 86 } | |
| 87 | |
| 88 void _markEagerClasses() { | |
| 89 _markEagerInterceptorClasses(); | |
| 90 } | |
| 91 | |
| 92 /// Builds a map from loadId to outputs-to-load. | |
| 93 Map<String, List<Output>> _buildLoadMap() { | |
| 94 List<OutputUnit> convertHunks(List<OutputUnit> hunks) { | |
| 95 return hunks.map((OutputUnit unit) => _outputs[unit]) | |
| 96 .toList(growable: false); | |
| 97 } | |
| 98 | |
| 99 Map<String, List<Output>> loadMap = <String, List<Output>>{}; | |
| 100 _compiler.deferredLoadTask.hunksToLoad | |
| 101 .forEach((String loadId, List<OutputUnit> outputUnits) { | |
| 102 loadMap[loadId] = outputUnits | |
| 103 .map((OutputUnit unit) => _outputs[unit]) | |
| 104 .toList(growable: false); | |
| 105 }); | |
| 106 return loadMap; | |
| 107 } | |
| 108 | |
| 109 MainOutput _buildMainOutput(Fragment fragment) { | |
| 110 // Construct the main output from the libraries and the registered holders. | |
| 111 MainOutput result = new MainOutput( | |
| 112 "", // The empty string is the name for the main output file. | |
| 113 namer.elementAccess(_compiler.mainFunction), | |
| 114 _buildLibraries(fragment), | |
| 115 _buildStaticNonFinalFields(fragment), | |
| 116 _buildStaticLazilyInitializedFields(fragment), | |
| 117 _buildConstants(fragment), | |
| 118 _registry.holders.toList(growable: false)); | |
| 119 _outputs[fragment.outputUnit] = result; | |
| 120 return result; | |
| 121 } | |
| 122 | |
| 123 /// Returns a name composed of the main output file name and [name]. | |
| 124 String _outputFileName(String name) { | |
| 125 assert(name != ""); | |
| 126 String outPath = _compiler.outputUri != null | |
| 127 ? _compiler.outputUri.path | |
| 128 : "out"; | |
| 129 String outName = outPath.substring(outPath.lastIndexOf('/') + 1); | |
| 130 return "${outName}_$name"; | |
| 131 } | |
| 132 | |
| 133 DeferredOutput _buildDeferredOutput(MainOutput mainOutput, | |
| 134 Fragment fragment) { | |
| 135 DeferredOutput result = new DeferredOutput( | |
| 136 _outputFileName(fragment.name), fragment.name, | |
| 137 mainOutput, | |
| 138 _buildLibraries(fragment), | |
| 139 _buildStaticNonFinalFields(fragment), | |
| 140 _buildStaticLazilyInitializedFields(fragment), | |
| 141 _buildConstants(fragment)); | |
| 142 _outputs[fragment.outputUnit] = result; | |
| 143 return result; | |
| 144 } | |
| 145 | |
| 146 List<Constant> _buildConstants(Fragment fragment) { | |
| 147 List<ConstantValue> constantValues = | |
| 148 _task.outputConstantLists[fragment.outputUnit]; | |
| 149 if (constantValues == null) return const <Constant>[]; | |
| 150 return constantValues.map((ConstantValue value) => _constants[value]) | |
| 151 .toList(growable: false); | |
| 152 } | |
| 153 | |
| 154 List<StaticField> _buildStaticNonFinalFields(Fragment fragment) { | |
| 155 // TODO(floitsch): handle static non-final fields correctly with deferred | |
| 156 // libraries. | |
| 157 if (fragment != _registry.mainFragment) return const <StaticField>[]; | |
| 158 Iterable<VariableElement> staticNonFinalFields = | |
| 159 backend.constants.getStaticNonFinalFieldsForEmission(); | |
| 160 return Elements.sortedByPosition(staticNonFinalFields) | |
| 161 .map(_buildStaticField) | |
| 162 .toList(growable: false); | |
| 163 } | |
| 164 | |
| 165 StaticField _buildStaticField(Element element) { | |
| 166 JavaScriptConstantCompiler handler = backend.constants; | |
| 167 ConstantValue initialValue = handler.getInitialValueFor(element).value; | |
| 168 js.Expression code = _task.emitter.constantReference(initialValue); | |
| 169 String name = namer.getNameOfGlobalField(element); | |
| 170 bool isFinal = false; | |
| 171 bool isLazy = false; | |
| 172 return new StaticField(name, _registry.registerHolder(r'$'), code, | |
| 173 isFinal, isLazy); | |
| 174 } | |
| 175 | |
| 176 List<StaticField> _buildStaticLazilyInitializedFields(Fragment fragment) { | |
| 177 // TODO(floitsch): lazy fields should just be in their respective | |
| 178 // libraries. | |
| 179 if (fragment != _registry.mainFragment) return const <StaticField>[]; | |
| 180 | |
| 181 JavaScriptConstantCompiler handler = backend.constants; | |
| 182 List<VariableElement> lazyFields = | |
| 183 handler.getLazilyInitializedFieldsForEmission(); | |
| 184 return Elements.sortedByPosition(lazyFields) | |
| 185 .map(_buildLazyField) | |
| 186 .where((field) => field != null) // Happens when the field was unused. | |
| 187 .toList(growable: false); | |
| 188 } | |
| 189 | |
| 190 StaticField _buildLazyField(Element element) { | |
| 191 JavaScriptConstantCompiler handler = backend.constants; | |
| 192 js.Expression code = backend.generatedCode[element]; | |
| 193 // The code is null if we ended up not needing the lazily | |
| 194 // initialized field after all because of constant folding | |
| 195 // before code generation. | |
| 196 if (code == null) return null; | |
| 197 | |
| 198 String name = namer.getNameOfGlobalField(element); | |
| 199 bool isFinal = element.isFinal; | |
| 200 bool isLazy = true; | |
| 201 return new StaticField(name, _registry.registerHolder(r'$'), code, | |
| 202 isFinal, isLazy); | |
| 203 } | |
| 204 | |
| 205 List<Library> _buildLibraries(Fragment fragment) { | |
| 206 List<Library> libraries = new List<Library>(fragment.length); | |
| 207 int count = 0; | |
| 208 fragment.forEach((LibraryElement library, List<Element> elements) { | |
| 209 libraries[count++] = _buildLibrary(library, elements); | |
| 210 }); | |
| 211 return libraries; | |
| 212 } | |
| 213 | |
| 214 // Note that a library-element may have multiple [Library]s, if it is split | |
| 215 // into multiple output units. | |
| 216 Library _buildLibrary(LibraryElement library, List<Element> elements) { | |
| 217 String uri = library.canonicalUri.toString(); | |
| 218 | |
| 219 List<StaticMethod> statics = elements | |
| 220 .where((e) => e is FunctionElement).map(_buildStaticMethod).toList(); | |
| 221 | |
| 222 statics.addAll(elements | |
| 223 .where((e) => e is FunctionElement) | |
| 224 .where((e) => universe.staticFunctionsNeedingGetter.contains(e)) | |
| 225 .map(_buildStaticMethodTearOff)); | |
| 226 | |
| 227 if (library == backend.interceptorsLibrary) { | |
| 228 statics.addAll(_generateGetInterceptorMethods()); | |
| 229 statics.addAll(_generateOneShotInterceptors()); | |
| 230 } | |
| 231 | |
| 232 List<Class> classes = elements | |
| 233 .where((e) => e is ClassElement) | |
| 234 .map(_buildClass) | |
| 235 .toList(growable: false); | |
| 236 | |
| 237 return new Library(uri, statics, classes); | |
| 238 } | |
| 239 | |
| 240 Class _buildClass(ClassElement element) { | |
| 241 bool isInstantiated = | |
| 242 _compiler.codegenWorld.directlyInstantiatedClasses.contains(element); | |
| 243 | |
| 244 List<Method> methods = []; | |
| 245 List<InstanceField> fields = []; | |
| 246 | |
| 247 void visitMember(ClassElement enclosing, Element member) { | |
| 248 assert(invariant(element, member.isDeclaration)); | |
| 249 assert(invariant(element, element == enclosing)); | |
| 250 | |
| 251 if (Elements.isNonAbstractInstanceMember(member)) { | |
| 252 js.Expression code = backend.generatedCode[member]; | |
| 253 // TODO(kasperl): Figure out under which conditions code is null. | |
| 254 if (code != null) methods.add(_buildMethod(member, code)); | |
| 255 } else if (member.isField && !member.isStatic) { | |
| 256 fields.add(_buildInstanceField(member, enclosing)); | |
| 257 } | |
| 258 } | |
| 259 | |
| 260 ClassElement implementation = element.implementation; | |
| 261 if (isInstantiated) { | |
| 262 implementation.forEachMember(visitMember, includeBackendMembers: true); | |
| 263 } | |
| 264 String name = namer.getNameOfClass(element); | |
| 265 String holderName = namer.globalObjectFor(element); | |
| 266 Holder holder = _registry.registerHolder(holderName); | |
| 267 Class result = new Class(name, holder, methods, fields); | |
| 268 _classes[element] = result; | |
| 269 return result; | |
| 270 } | |
| 271 | |
| 272 Method _buildMethod(FunctionElement element, js.Expression code) { | |
| 273 String name = namer.getNameOfInstanceMember(element); | |
| 274 return new Method(name, code); | |
| 275 } | |
| 276 | |
| 277 // The getInterceptor methods directly access the prototype of classes. | |
| 278 // We must evaluate these classes eagerly so that the prototype is | |
| 279 // accessible. | |
| 280 void _markEagerInterceptorClasses() { | |
| 281 Map<String, Set<ClassElement>> specializedGetInterceptors = | |
| 282 backend.specializedGetInterceptors; | |
| 283 for (Set<ClassElement> classes in specializedGetInterceptors.values) { | |
| 284 for (ClassElement element in classes) { | |
| 285 Class cls = _classes[element]; | |
| 286 if (cls != null) cls.isEager = true; | |
| 287 } | |
| 288 } | |
| 289 } | |
| 290 | |
| 291 Iterable<StaticMethod> _generateGetInterceptorMethods() { | |
| 292 emitterTask.InterceptorStubGenerator stubGenerator = | |
| 293 new emitterTask.InterceptorStubGenerator(_compiler, namer, backend); | |
| 294 | |
| 295 String holderName = namer.globalObjectFor(backend.interceptorsLibrary); | |
| 296 Holder holder = _registry.registerHolder(holderName); | |
| 297 | |
| 298 Map<String, Set<ClassElement>> specializedGetInterceptors = | |
| 299 backend.specializedGetInterceptors; | |
| 300 List<String> names = specializedGetInterceptors.keys.toList()..sort(); | |
| 301 return names.map((String name) { | |
| 302 Set<ClassElement> classes = specializedGetInterceptors[name]; | |
| 303 js.Expression code = stubGenerator.generateGetInterceptorMethod(classes); | |
| 304 return new StaticMethod(name, holder, code); | |
| 305 }); | |
| 306 } | |
| 307 | |
| 308 bool _fieldNeedsGetter(VariableElement field) { | |
| 309 assert(field.isField); | |
| 310 if (_fieldAccessNeverThrows(field)) return false; | |
| 311 return backend.shouldRetainGetter(field) | |
| 312 || _compiler.codegenWorld.hasInvokedGetter(field, _compiler.world); | |
| 313 } | |
| 314 | |
| 315 bool _fieldNeedsSetter(VariableElement field) { | |
| 316 assert(field.isField); | |
| 317 if (_fieldAccessNeverThrows(field)) return false; | |
| 318 return (!field.isFinal && !field.isConst) | |
| 319 && (backend.shouldRetainSetter(field) | |
| 320 || _compiler.codegenWorld.hasInvokedSetter(field, _compiler.world)); | |
| 321 } | |
| 322 | |
| 323 // We never access a field in a closure (a captured variable) without knowing | |
| 324 // that it is there. Therefore we don't need to use a getter (that will throw | |
| 325 // if the getter method is missing), but can always access the field directly. | |
| 326 bool _fieldAccessNeverThrows(VariableElement field) { | |
| 327 return field is ClosureFieldElement; | |
| 328 } | |
| 329 | |
| 330 InstanceField _buildInstanceField(VariableElement field, | |
| 331 ClassElement holder) { | |
| 332 assert(invariant(field, field.isDeclaration)); | |
| 333 String name = namer.fieldPropertyName(field); | |
| 334 | |
| 335 int getterFlags = 0; | |
| 336 if (_fieldNeedsGetter(field)) { | |
| 337 bool isIntercepted = backend.fieldHasInterceptedGetter(field); | |
| 338 if (isIntercepted) { | |
| 339 getterFlags += 2; | |
| 340 if (backend.isInterceptorClass(holder)) { | |
| 341 getterFlags += 1; | |
| 342 } | |
| 343 } else { | |
| 344 getterFlags = 1; | |
| 345 } | |
| 346 } | |
| 347 | |
| 348 int setterFlags = 0; | |
| 349 if (_fieldNeedsSetter(field)) { | |
| 350 bool isIntercepted = backend.fieldHasInterceptedSetter(field); | |
| 351 if (isIntercepted) { | |
| 352 setterFlags += 2; | |
| 353 if (backend.isInterceptorClass(holder)) { | |
| 354 setterFlags += 1; | |
| 355 } | |
| 356 } else { | |
| 357 setterFlags = 1; | |
| 358 } | |
| 359 } | |
| 360 | |
| 361 return new InstanceField(name, getterFlags, setterFlags); | |
| 362 } | |
| 363 | |
| 364 Iterable<StaticMethod> _generateOneShotInterceptors() { | |
| 365 emitterTask.InterceptorStubGenerator stubGenerator = | |
| 366 new emitterTask.InterceptorStubGenerator(_compiler, namer, backend); | |
| 367 | |
| 368 String holderName = namer.globalObjectFor(backend.interceptorsLibrary); | |
| 369 Holder holder = _registry.registerHolder(holderName); | |
| 370 | |
| 371 List<String> names = backend.oneShotInterceptors.keys.toList()..sort(); | |
| 372 return names.map((String name) { | |
| 373 js.Expression code = stubGenerator.generateOneShotInterceptor(name); | |
| 374 return new StaticMethod(name, holder, code); | |
| 375 }); | |
| 376 } | |
| 377 | |
| 378 StaticMethod _buildStaticMethod(FunctionElement element) { | |
| 379 String name = namer.getNameOfMember(element); | |
| 380 String holder = namer.globalObjectFor(element); | |
| 381 js.Expression code = backend.generatedCode[element]; | |
| 382 return new StaticMethod(name, _registry.registerHolder(holder), code); | |
| 383 } | |
| 384 | |
| 385 StaticMethod _buildStaticMethodTearOff(FunctionElement element) { | |
| 386 String name = namer.getStaticClosureName(element); | |
| 387 String holder = namer.globalObjectFor(element); | |
| 388 // TODO(kasperl): This clearly doesn't work yet. | |
| 389 js.Expression code = js.string("<<unimplemented>>"); | |
| 390 return new StaticMethod(name, _registry.registerHolder(holder), code); | |
| 391 } | |
| 392 | |
| 393 void _registerConstants(OutputUnit outputUnit, | |
| 394 List<ConstantValue> constantValues) { | |
| 395 if (constantValues == null) return; | |
| 396 for (ConstantValue constantValue in constantValues) { | |
| 397 assert(!_constants.containsKey(constantValue)); | |
| 398 String name = namer.constantName(constantValue); | |
| 399 String constantObject = namer.globalObjectForConstant(constantValue); | |
| 400 Holder holder = _registry.registerHolder(constantObject); | |
| 401 Constant constant = new Constant(name, holder, constantValue); | |
| 402 _constants[constantValue] = constant; | |
| 403 }; | |
| 404 } | |
| 405 } | |
| OLD | NEW |