| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of dart2js.js_emitter; | 5 part of dart2js.js_emitter; |
| 6 | 6 |
| 7 const USE_LAZY_EMITTER = const bool.fromEnvironment("dart2js.use.lazy.emitter"); | 7 const USE_LAZY_EMITTER = const bool.fromEnvironment("dart2js.use.lazy.emitter"); |
| 8 | 8 |
| 9 /** | 9 /** |
| 10 * Generates the code for all used classes in the program. Static fields (even | 10 * Generates the code for all used classes in the program. Static fields (even |
| 11 * in classes) are ignored, since they can be treated as non-class elements. | 11 * in classes) are ignored, since they can be treated as non-class elements. |
| 12 * | 12 * |
| 13 * The code for the containing (used) methods must exist in the `universe`. | 13 * The code for the containing (used) methods must exist in the `universe`. |
| 14 */ | 14 */ |
| 15 class CodeEmitterTask extends CompilerTask { | 15 class CodeEmitterTask extends CompilerTask { |
| 16 // TODO(floitsch): the code-emitter task should not need a namer. | 16 // TODO(floitsch): the code-emitter task should not need a namer. |
| 17 final Namer namer; | 17 final Namer namer; |
| 18 final TypeTestRegistry typeTestRegistry; | 18 final TypeTestRegistry typeTestRegistry; |
| 19 NativeEmitter nativeEmitter; | 19 NativeEmitter nativeEmitter; |
| 20 MetadataCollector metadataCollector; | 20 MetadataCollector metadataCollector; |
| 21 OldEmitter oldEmitter; | 21 OldEmitter oldEmitter; |
| 22 Emitter emitter; | 22 Emitter emitter; |
| 23 | 23 |
| 24 final Set<ClassElement> neededClasses = new Set<ClassElement>(); | |
| 25 Set<ClassElement> classesOnlyNeededForRti; | |
| 26 final Map<OutputUnit, List<ClassElement>> outputClassLists = | |
| 27 new Map<OutputUnit, List<ClassElement>>(); | |
| 28 final Map<OutputUnit, List<ConstantValue>> outputConstantLists = | |
| 29 new Map<OutputUnit, List<ConstantValue>>(); | |
| 30 final Map<OutputUnit, List<Element>> outputStaticLists = | |
| 31 new Map<OutputUnit, List<Element>>(); | |
| 32 final Map<OutputUnit, List<VariableElement>> outputStaticNonFinalFieldLists = | |
| 33 new Map<OutputUnit, List<VariableElement>>(); | |
| 34 final Map<OutputUnit, Set<LibraryElement>> outputLibraryLists = | |
| 35 new Map<OutputUnit, Set<LibraryElement>>(); | |
| 36 | |
| 37 /// True, if the output contains a constant list. | |
| 38 /// | |
| 39 /// This flag is updated in [computeNeededConstants]. | |
| 40 bool outputContainsConstantList = false; | |
| 41 | |
| 42 final List<ClassElement> nativeClassesAndSubclasses = <ClassElement>[]; | |
| 43 | |
| 44 /// Records if a type variable is read dynamically for type tests. | 24 /// Records if a type variable is read dynamically for type tests. |
| 45 final Set<TypeVariableElement> readTypeVariables = | 25 final Set<TypeVariableElement> readTypeVariables = |
| 46 new Set<TypeVariableElement>(); | 26 new Set<TypeVariableElement>(); |
| 47 | 27 |
| 48 List<TypedefElement> typedefsNeededForReflection; | 28 JavaScriptBackend get backend => compiler.backend; |
| 49 | 29 |
| 50 JavaScriptBackend get backend => compiler.backend; | 30 @deprecated |
| 31 // This field should be removed. It's currently only needed for dump-info and |
| 32 // tests. |
| 33 // The field is set after the program has been emitted. |
| 34 /// Contains a list of all classes that are emitted. |
| 35 Set<ClassElement> neededClasses; |
| 51 | 36 |
| 52 CodeEmitterTask(Compiler compiler, Namer namer, bool generateSourceMap) | 37 CodeEmitterTask(Compiler compiler, Namer namer, bool generateSourceMap) |
| 53 : super(compiler), | 38 : super(compiler), |
| 54 this.namer = namer, | 39 this.namer = namer, |
| 55 this.typeTestRegistry = new TypeTestRegistry(compiler) { | 40 this.typeTestRegistry = new TypeTestRegistry(compiler) { |
| 56 nativeEmitter = new NativeEmitter(this); | 41 nativeEmitter = new NativeEmitter(this); |
| 57 oldEmitter = new OldEmitter(compiler, namer, generateSourceMap, this); | 42 oldEmitter = new OldEmitter(compiler, namer, generateSourceMap, this); |
| 58 emitter = USE_LAZY_EMITTER | 43 emitter = USE_LAZY_EMITTER |
| 59 ? new lazy_js_emitter.Emitter(compiler, namer, nativeEmitter) | 44 ? new lazy_js_emitter.Emitter(compiler, namer, nativeEmitter) |
| 60 : oldEmitter; | 45 : oldEmitter; |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 127 | 112 |
| 128 /// Returns the JS template for the given [builtin]. | 113 /// Returns the JS template for the given [builtin]. |
| 129 jsAst.Template builtinTemplateFor(JsBuiltin builtin) { | 114 jsAst.Template builtinTemplateFor(JsBuiltin builtin) { |
| 130 return emitter.templateForBuiltin(builtin); | 115 return emitter.templateForBuiltin(builtin); |
| 131 } | 116 } |
| 132 | 117 |
| 133 void registerReadTypeVariable(TypeVariableElement element) { | 118 void registerReadTypeVariable(TypeVariableElement element) { |
| 134 readTypeVariables.add(element); | 119 readTypeVariables.add(element); |
| 135 } | 120 } |
| 136 | 121 |
| 137 Set<ClassElement> computeInterceptorsReferencedFromConstants() { | 122 Set<ClassElement> _finalizeRti() { |
| 138 Set<ClassElement> classes = new Set<ClassElement>(); | |
| 139 JavaScriptConstantCompiler handler = backend.constants; | |
| 140 List<ConstantValue> constants = handler.getConstantsForEmission(); | |
| 141 for (ConstantValue constant in constants) { | |
| 142 if (constant is InterceptorConstantValue) { | |
| 143 InterceptorConstantValue interceptorConstant = constant; | |
| 144 classes.add(interceptorConstant.dispatchedType.element); | |
| 145 } | |
| 146 } | |
| 147 return classes; | |
| 148 } | |
| 149 | |
| 150 /** | |
| 151 * Return a function that returns true if its argument is a class | |
| 152 * that needs to be emitted. | |
| 153 */ | |
| 154 Function computeClassFilter() { | |
| 155 if (backend.isTreeShakingDisabled) return (ClassElement cls) => true; | |
| 156 | |
| 157 Set<ClassElement> unneededClasses = new Set<ClassElement>(); | |
| 158 // The [Bool] class is not marked as abstract, but has a factory | |
| 159 // constructor that always throws. We never need to emit it. | |
| 160 unneededClasses.add(compiler.boolClass); | |
| 161 | |
| 162 // Go over specialized interceptors and then constants to know which | |
| 163 // interceptors are needed. | |
| 164 Set<ClassElement> needed = new Set<ClassElement>(); | |
| 165 backend.specializedGetInterceptors.forEach( | |
| 166 (_, Iterable<ClassElement> elements) { | |
| 167 needed.addAll(elements); | |
| 168 } | |
| 169 ); | |
| 170 | |
| 171 // Add interceptors referenced by constants. | |
| 172 needed.addAll(computeInterceptorsReferencedFromConstants()); | |
| 173 | |
| 174 // Add unneeded interceptors to the [unneededClasses] set. | |
| 175 for (ClassElement interceptor in backend.interceptedClasses) { | |
| 176 if (!needed.contains(interceptor) | |
| 177 && interceptor != compiler.objectClass) { | |
| 178 unneededClasses.add(interceptor); | |
| 179 } | |
| 180 } | |
| 181 | |
| 182 // These classes are just helpers for the backend's type system. | |
| 183 unneededClasses.add(backend.jsMutableArrayClass); | |
| 184 unneededClasses.add(backend.jsFixedArrayClass); | |
| 185 unneededClasses.add(backend.jsExtendableArrayClass); | |
| 186 unneededClasses.add(backend.jsUInt32Class); | |
| 187 unneededClasses.add(backend.jsUInt31Class); | |
| 188 unneededClasses.add(backend.jsPositiveIntClass); | |
| 189 | |
| 190 return (ClassElement cls) => !unneededClasses.contains(cls); | |
| 191 } | |
| 192 | |
| 193 /** | |
| 194 * Compute all the constants that must be emitted. | |
| 195 */ | |
| 196 void computeNeededConstants() { | |
| 197 // Make sure we retain all metadata of all elements. This could add new | |
| 198 // constants to the handler. | |
| 199 if (backend.mustRetainMetadata) { | |
| 200 // TODO(floitsch): verify that we don't run through the same elements | |
| 201 // multiple times. | |
| 202 for (Element element in backend.generatedCode.keys) { | |
| 203 if (backend.isAccessibleByReflection(element)) { | |
| 204 bool shouldRetainMetadata = backend.retainMetadataOf(element); | |
| 205 if (shouldRetainMetadata && | |
| 206 (element.isFunction || element.isConstructor || | |
| 207 element.isSetter)) { | |
| 208 FunctionElement function = element; | |
| 209 function.functionSignature.forEachParameter( | |
| 210 backend.retainMetadataOf); | |
| 211 } | |
| 212 } | |
| 213 } | |
| 214 for (ClassElement cls in neededClasses) { | |
| 215 final onlyForRti = classesOnlyNeededForRti.contains(cls); | |
| 216 if (!onlyForRti) { | |
| 217 backend.retainMetadataOf(cls); | |
| 218 oldEmitter.classEmitter.visitFields(cls, false, | |
| 219 (Element member, | |
| 220 jsAst.Name name, | |
| 221 jsAst.Name accessorName, | |
| 222 bool needsGetter, | |
| 223 bool needsSetter, | |
| 224 bool needsCheckedSetter) { | |
| 225 bool needsAccessor = needsGetter || needsSetter; | |
| 226 if (needsAccessor && backend.isAccessibleByReflection(member)) { | |
| 227 backend.retainMetadataOf(member); | |
| 228 } | |
| 229 }); | |
| 230 } | |
| 231 } | |
| 232 typedefsNeededForReflection.forEach(backend.retainMetadataOf); | |
| 233 } | |
| 234 | |
| 235 JavaScriptConstantCompiler handler = backend.constants; | |
| 236 List<ConstantValue> constants = handler.getConstantsForEmission( | |
| 237 compiler.hasIncrementalSupport ? null : emitter.compareConstants); | |
| 238 for (ConstantValue constant in constants) { | |
| 239 if (emitter.isConstantInlinedOrAlreadyEmitted(constant)) continue; | |
| 240 | |
| 241 if (constant.isList) outputContainsConstantList = true; | |
| 242 | |
| 243 OutputUnit constantUnit = | |
| 244 compiler.deferredLoadTask.outputUnitForConstant(constant); | |
| 245 if (constantUnit == null) { | |
| 246 // The back-end introduces some constants, like "InterceptorConstant" or | |
| 247 // some list constants. They are emitted in the main output-unit. | |
| 248 // TODO(sigurdm): We should track those constants. | |
| 249 constantUnit = compiler.deferredLoadTask.mainOutputUnit; | |
| 250 } | |
| 251 outputConstantLists.putIfAbsent( | |
| 252 constantUnit, () => new List<ConstantValue>()).add(constant); | |
| 253 } | |
| 254 } | |
| 255 | |
| 256 /// Compute all the classes and typedefs that must be emitted. | |
| 257 void computeNeededDeclarations(Set<ClassElement> rtiNeededClasses) { | |
| 258 // Compute needed typedefs. | |
| 259 typedefsNeededForReflection = Elements.sortedByPosition( | |
| 260 compiler.world.allTypedefs | |
| 261 .where(backend.isAccessibleByReflection) | |
| 262 .toList()); | |
| 263 | |
| 264 // Compute needed classes. | |
| 265 Set<ClassElement> instantiatedClasses = | |
| 266 compiler.codegenWorld.directlyInstantiatedClasses | |
| 267 .where(computeClassFilter()).toSet(); | |
| 268 | |
| 269 void addClassWithSuperclasses(ClassElement cls) { | |
| 270 neededClasses.add(cls); | |
| 271 for (ClassElement superclass = cls.superclass; | |
| 272 superclass != null; | |
| 273 superclass = superclass.superclass) { | |
| 274 neededClasses.add(superclass); | |
| 275 } | |
| 276 } | |
| 277 | |
| 278 void addClassesWithSuperclasses(Iterable<ClassElement> classes) { | |
| 279 for (ClassElement cls in classes) { | |
| 280 addClassWithSuperclasses(cls); | |
| 281 } | |
| 282 } | |
| 283 | |
| 284 // 1. We need to generate all classes that are instantiated. | |
| 285 addClassesWithSuperclasses(instantiatedClasses); | |
| 286 | |
| 287 // 2. Add all classes used as mixins. | |
| 288 Set<ClassElement> mixinClasses = neededClasses | |
| 289 .where((ClassElement element) => element.isMixinApplication) | |
| 290 .map(computeMixinClass) | |
| 291 .toSet(); | |
| 292 neededClasses.addAll(mixinClasses); | |
| 293 | |
| 294 // 3. Find all classes needed for rti. | |
| 295 // It is important that this is the penultimate step, at this point, | |
| 296 // neededClasses must only contain classes that have been resolved and | |
| 297 // codegen'd. The rtiNeededClasses may contain additional classes, but | |
| 298 // these are thought to not have been instantiated, so we neeed to be able | |
| 299 // to identify them later and make sure we only emit "empty shells" without | |
| 300 // fields, etc. | |
| 301 classesOnlyNeededForRti = rtiNeededClasses.difference(neededClasses); | |
| 302 | |
| 303 neededClasses.addAll(classesOnlyNeededForRti); | |
| 304 | |
| 305 // TODO(18175, floitsch): remove once issue 18175 is fixed. | |
| 306 if (neededClasses.contains(backend.jsIntClass)) { | |
| 307 neededClasses.add(compiler.intClass); | |
| 308 } | |
| 309 if (neededClasses.contains(backend.jsDoubleClass)) { | |
| 310 neededClasses.add(compiler.doubleClass); | |
| 311 } | |
| 312 if (neededClasses.contains(backend.jsNumberClass)) { | |
| 313 neededClasses.add(compiler.numClass); | |
| 314 } | |
| 315 if (neededClasses.contains(backend.jsStringClass)) { | |
| 316 neededClasses.add(compiler.stringClass); | |
| 317 } | |
| 318 if (neededClasses.contains(backend.jsBoolClass)) { | |
| 319 neededClasses.add(compiler.boolClass); | |
| 320 } | |
| 321 if (neededClasses.contains(backend.jsArrayClass)) { | |
| 322 neededClasses.add(compiler.listClass); | |
| 323 } | |
| 324 | |
| 325 // 4. Finally, sort the classes. | |
| 326 List<ClassElement> sortedClasses = Elements.sortedByPosition(neededClasses); | |
| 327 | |
| 328 for (ClassElement element in sortedClasses) { | |
| 329 if (Elements.isNativeOrExtendsNative(element) && | |
| 330 !classesOnlyNeededForRti.contains(element)) { | |
| 331 // For now, native classes and related classes cannot be deferred. | |
| 332 nativeClassesAndSubclasses.add(element); | |
| 333 assert(invariant(element, | |
| 334 !compiler.deferredLoadTask.isDeferred(element))); | |
| 335 outputClassLists.putIfAbsent(compiler.deferredLoadTask.mainOutputUnit, | |
| 336 () => new List<ClassElement>()).add(element); | |
| 337 } else { | |
| 338 outputClassLists.putIfAbsent( | |
| 339 compiler.deferredLoadTask.outputUnitForElement(element), | |
| 340 () => new List<ClassElement>()) | |
| 341 .add(element); | |
| 342 } | |
| 343 } | |
| 344 } | |
| 345 | |
| 346 void computeNeededStatics() { | |
| 347 bool isStaticFunction(Element element) => | |
| 348 !element.isInstanceMember && !element.isField; | |
| 349 | |
| 350 Iterable<Element> elements = | |
| 351 backend.generatedCode.keys.where(isStaticFunction); | |
| 352 | |
| 353 for (Element element in Elements.sortedByPosition(elements)) { | |
| 354 List<Element> list = outputStaticLists.putIfAbsent( | |
| 355 compiler.deferredLoadTask.outputUnitForElement(element), | |
| 356 () => new List<Element>()); | |
| 357 list.add(element); | |
| 358 } | |
| 359 } | |
| 360 | |
| 361 void computeNeededStaticNonFinalFields() { | |
| 362 JavaScriptConstantCompiler handler = backend.constants; | |
| 363 Iterable<VariableElement> staticNonFinalFields = handler | |
| 364 .getStaticNonFinalFieldsForEmission() | |
| 365 .where(compiler.codegenWorld.allReferencedStaticFields.contains); | |
| 366 for (Element element in Elements.sortedByPosition(staticNonFinalFields)) { | |
| 367 List<VariableElement> list = outputStaticNonFinalFieldLists.putIfAbsent( | |
| 368 compiler.deferredLoadTask.outputUnitForElement(element), | |
| 369 () => new List<VariableElement>()); | |
| 370 list.add(element); | |
| 371 } | |
| 372 } | |
| 373 | |
| 374 void computeNeededLibraries() { | |
| 375 void addSurroundingLibraryToSet(Element element) { | |
| 376 OutputUnit unit = compiler.deferredLoadTask.outputUnitForElement(element); | |
| 377 LibraryElement library = element.library; | |
| 378 outputLibraryLists.putIfAbsent(unit, () => new Set<LibraryElement>()) | |
| 379 .add(library); | |
| 380 } | |
| 381 | |
| 382 backend.generatedCode.keys.forEach(addSurroundingLibraryToSet); | |
| 383 neededClasses.forEach(addSurroundingLibraryToSet); | |
| 384 } | |
| 385 | |
| 386 void computeAllNeededEntities() { | |
| 387 // Compute the required type checks to know which classes need a | 123 // Compute the required type checks to know which classes need a |
| 388 // 'is$' method. | 124 // 'is$' method. |
| 389 typeTestRegistry.computeRequiredTypeChecks(); | 125 typeTestRegistry.computeRequiredTypeChecks(); |
| 390 // Compute the classes needed by RTI. | 126 // Compute the classes needed by RTI. |
| 391 Set<ClassElement> rtiClasses = typeTestRegistry.computeRtiNeededClasses(); | 127 return typeTestRegistry.computeRtiNeededClasses(); |
| 392 | |
| 393 computeNeededDeclarations(rtiClasses); | |
| 394 computeNeededConstants(); | |
| 395 computeNeededStatics(); | |
| 396 computeNeededStaticNonFinalFields(); | |
| 397 computeNeededLibraries(); | |
| 398 } | 128 } |
| 399 | 129 |
| 400 int assembleProgram() { | 130 int assembleProgram() { |
| 401 return measure(() { | 131 return measure(() { |
| 402 emitter.invalidateCaches(); | 132 emitter.invalidateCaches(); |
| 403 | 133 |
| 404 computeAllNeededEntities(); | 134 Set<ClassElement> rtiNeededClasses = _finalizeRti(); |
| 405 | 135 ProgramBuilder programBuilder = new ProgramBuilder( |
| 406 ProgramBuilder programBuilder = new ProgramBuilder(compiler, namer, this); | 136 compiler, namer, this, emitter, oldEmitter, rtiNeededClasses); |
| 407 return emitter.emitProgram(programBuilder); | 137 int size = emitter.emitProgram(programBuilder); |
| 138 // TODO(floitsch): we shouldn't need the `neededClasses` anymore. |
| 139 neededClasses = programBuilder.collector.neededClasses; |
| 140 return size; |
| 408 }); | 141 }); |
| 409 } | 142 } |
| 410 } | 143 } |
| 411 | 144 |
| 412 abstract class Emitter { | 145 abstract class Emitter { |
| 413 /// Uses the [programBuilder] to generate a model of the program, emits | 146 /// Uses the [programBuilder] to generate a model of the program, emits |
| 414 /// the program, and returns the size of the generated output. | 147 /// the program, and returns the size of the generated output. |
| 415 int emitProgram(ProgramBuilder programBuilder); | 148 int emitProgram(ProgramBuilder programBuilder); |
| 416 | 149 |
| 417 /// Returns the JS function that must be invoked to get the value of the | 150 /// Returns the JS function that must be invoked to get the value of the |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 452 bool isConstantInlinedOrAlreadyEmitted(ConstantValue constant); | 185 bool isConstantInlinedOrAlreadyEmitted(ConstantValue constant); |
| 453 | 186 |
| 454 /// Returns the JS code for accessing the given [constant]. | 187 /// Returns the JS code for accessing the given [constant]. |
| 455 jsAst.Expression constantReference(ConstantValue constant); | 188 jsAst.Expression constantReference(ConstantValue constant); |
| 456 | 189 |
| 457 /// Returns the JS template for the given [builtin]. | 190 /// Returns the JS template for the given [builtin]. |
| 458 jsAst.Template templateForBuiltin(JsBuiltin builtin); | 191 jsAst.Template templateForBuiltin(JsBuiltin builtin); |
| 459 | 192 |
| 460 void invalidateCaches(); | 193 void invalidateCaches(); |
| 461 } | 194 } |
| OLD | NEW |