| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 part of dart2js.js_emitter; | |
| 6 | |
| 7 const USE_NEW_EMITTER = const bool.fromEnvironment("dart2js.use.new.emitter"); | |
| 8 | |
| 9 /** | |
| 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. | |
| 12 * | |
| 13 * The code for the containing (used) methods must exist in the [:universe:]. | |
| 14 */ | |
| 15 class CodeEmitterTask extends CompilerTask { | |
| 16 // TODO(floitsch): the code-emitter task should not need a namer. | |
| 17 final Namer namer; | |
| 18 final TypeTestEmitter typeTestEmitter = new TypeTestEmitter(); | |
| 19 NativeEmitter nativeEmitter; | |
| 20 OldEmitter oldEmitter; | |
| 21 Emitter emitter; | |
| 22 | |
| 23 final Set<ClassElement> neededClasses = new Set<ClassElement>(); | |
| 24 final Map<OutputUnit, List<ClassElement>> outputClassLists = | |
| 25 new Map<OutputUnit, List<ClassElement>>(); | |
| 26 final Map<OutputUnit, List<ConstantValue>> outputConstantLists = | |
| 27 new Map<OutputUnit, List<ConstantValue>>(); | |
| 28 final Map<OutputUnit, List<Element>> outputStaticLists = | |
| 29 new Map<OutputUnit, List<Element>>(); | |
| 30 final Map<OutputUnit, Set<LibraryElement>> outputLibraryLists = | |
| 31 new Map<OutputUnit, Set<LibraryElement>>(); | |
| 32 | |
| 33 /// True, if the output contains a constant list. | |
| 34 /// | |
| 35 /// This flag is updated in [computeNeededConstants]. | |
| 36 bool outputContainsConstantList = false; | |
| 37 | |
| 38 final List<ClassElement> nativeClasses = <ClassElement>[]; | |
| 39 | |
| 40 /// Records if a type variable is read dynamically for type tests. | |
| 41 final Set<TypeVariableElement> readTypeVariables = | |
| 42 new Set<TypeVariableElement>(); | |
| 43 | |
| 44 List<TypedefElement> typedefsNeededForReflection; | |
| 45 | |
| 46 JavaScriptBackend get backend => compiler.backend; | |
| 47 | |
| 48 CodeEmitterTask(Compiler compiler, Namer namer, bool generateSourceMap) | |
| 49 : super(compiler), | |
| 50 this.namer = namer { | |
| 51 oldEmitter = new OldEmitter(compiler, namer, generateSourceMap, this); | |
| 52 emitter = USE_NEW_EMITTER | |
| 53 ? new new_js_emitter.Emitter(compiler, namer) | |
| 54 : oldEmitter; | |
| 55 nativeEmitter = new NativeEmitter(this); | |
| 56 typeTestEmitter.emitter = this.oldEmitter; | |
| 57 // TODO(18886): Remove this call (and the show in the import) once the | |
| 58 // memory-leak in the VM is fixed. | |
| 59 templateManager.clear(); | |
| 60 } | |
| 61 | |
| 62 | |
| 63 jsAst.Expression generateEmbeddedGlobalAccess(String global) { | |
| 64 return emitter.generateEmbeddedGlobalAccess(global); | |
| 65 } | |
| 66 | |
| 67 jsAst.Expression constantReference(ConstantValue value) { | |
| 68 return emitter.constantReference(value); | |
| 69 } | |
| 70 | |
| 71 void registerReadTypeVariable(TypeVariableElement element) { | |
| 72 readTypeVariables.add(element); | |
| 73 } | |
| 74 | |
| 75 Set<ClassElement> interceptorsReferencedFromConstants() { | |
| 76 Set<ClassElement> classes = new Set<ClassElement>(); | |
| 77 JavaScriptConstantCompiler handler = backend.constants; | |
| 78 List<ConstantValue> constants = handler.getConstantsForEmission(); | |
| 79 for (ConstantValue constant in constants) { | |
| 80 if (constant is InterceptorConstantValue) { | |
| 81 InterceptorConstantValue interceptorConstant = constant; | |
| 82 classes.add(interceptorConstant.dispatchedType.element); | |
| 83 } | |
| 84 } | |
| 85 return classes; | |
| 86 } | |
| 87 | |
| 88 /** | |
| 89 * Return a function that returns true if its argument is a class | |
| 90 * that needs to be emitted. | |
| 91 */ | |
| 92 Function computeClassFilter() { | |
| 93 if (backend.isTreeShakingDisabled) return (ClassElement cls) => true; | |
| 94 | |
| 95 Set<ClassElement> unneededClasses = new Set<ClassElement>(); | |
| 96 // The [Bool] class is not marked as abstract, but has a factory | |
| 97 // constructor that always throws. We never need to emit it. | |
| 98 unneededClasses.add(compiler.boolClass); | |
| 99 | |
| 100 // Go over specialized interceptors and then constants to know which | |
| 101 // interceptors are needed. | |
| 102 Set<ClassElement> needed = new Set<ClassElement>(); | |
| 103 backend.specializedGetInterceptors.forEach( | |
| 104 (_, Iterable<ClassElement> elements) { | |
| 105 needed.addAll(elements); | |
| 106 } | |
| 107 ); | |
| 108 | |
| 109 // Add interceptors referenced by constants. | |
| 110 needed.addAll(interceptorsReferencedFromConstants()); | |
| 111 | |
| 112 // Add unneeded interceptors to the [unneededClasses] set. | |
| 113 for (ClassElement interceptor in backend.interceptedClasses) { | |
| 114 if (!needed.contains(interceptor) | |
| 115 && interceptor != compiler.objectClass) { | |
| 116 unneededClasses.add(interceptor); | |
| 117 } | |
| 118 } | |
| 119 | |
| 120 // These classes are just helpers for the backend's type system. | |
| 121 unneededClasses.add(backend.jsMutableArrayClass); | |
| 122 unneededClasses.add(backend.jsFixedArrayClass); | |
| 123 unneededClasses.add(backend.jsExtendableArrayClass); | |
| 124 unneededClasses.add(backend.jsUInt32Class); | |
| 125 unneededClasses.add(backend.jsUInt31Class); | |
| 126 unneededClasses.add(backend.jsPositiveIntClass); | |
| 127 | |
| 128 return (ClassElement cls) => !unneededClasses.contains(cls); | |
| 129 } | |
| 130 | |
| 131 /** | |
| 132 * Compute all the constants that must be emitted. | |
| 133 */ | |
| 134 void computeNeededConstants() { | |
| 135 // Make sure we retain all metadata of all elements. This could add new | |
| 136 // constants to the handler. | |
| 137 if (backend.mustRetainMetadata) { | |
| 138 // TODO(floitsch): verify that we don't run through the same elements | |
| 139 // multiple times. | |
| 140 for (Element element in backend.generatedCode.keys) { | |
| 141 if (backend.isAccessibleByReflection(element)) { | |
| 142 bool shouldRetainMetadata = backend.retainMetadataOf(element); | |
| 143 if (shouldRetainMetadata && element.isFunction) { | |
| 144 FunctionElement function = element; | |
| 145 function.functionSignature.forEachParameter( | |
| 146 backend.retainMetadataOf); | |
| 147 } | |
| 148 } | |
| 149 } | |
| 150 for (ClassElement cls in neededClasses) { | |
| 151 final onlyForRti = typeTestEmitter.rtiNeededClasses.contains(cls); | |
| 152 if (!onlyForRti) { | |
| 153 backend.retainMetadataOf(cls); | |
| 154 oldEmitter.classEmitter.visitFields(cls, false, | |
| 155 (Element member, | |
| 156 String name, | |
| 157 String accessorName, | |
| 158 bool needsGetter, | |
| 159 bool needsSetter, | |
| 160 bool needsCheckedSetter) { | |
| 161 bool needsAccessor = needsGetter || needsSetter; | |
| 162 if (needsAccessor && backend.isAccessibleByReflection(member)) { | |
| 163 backend.retainMetadataOf(member); | |
| 164 } | |
| 165 }); | |
| 166 } | |
| 167 } | |
| 168 typedefsNeededForReflection.forEach(backend.retainMetadataOf); | |
| 169 } | |
| 170 | |
| 171 JavaScriptConstantCompiler handler = backend.constants; | |
| 172 List<ConstantValue> constants = handler.getConstantsForEmission( | |
| 173 compiler.hasIncrementalSupport ? null : emitter.compareConstants); | |
| 174 for (ConstantValue constant in constants) { | |
| 175 if (emitter.isConstantInlinedOrAlreadyEmitted(constant)) continue; | |
| 176 | |
| 177 if (constant.isList) outputContainsConstantList = true; | |
| 178 | |
| 179 OutputUnit constantUnit = | |
| 180 compiler.deferredLoadTask.outputUnitForConstant(constant); | |
| 181 if (constantUnit == null) { | |
| 182 // The back-end introduces some constants, like "InterceptorConstant" or | |
| 183 // some list constants. They are emitted in the main output-unit. | |
| 184 // TODO(sigurdm): We should track those constants. | |
| 185 constantUnit = compiler.deferredLoadTask.mainOutputUnit; | |
| 186 } | |
| 187 outputConstantLists.putIfAbsent( | |
| 188 constantUnit, () => new List<ConstantValue>()).add(constant); | |
| 189 } | |
| 190 } | |
| 191 | |
| 192 /// Compute all the classes and typedefs that must be emitted. | |
| 193 void computeNeededDeclarations() { | |
| 194 // Compute needed typedefs. | |
| 195 typedefsNeededForReflection = Elements.sortedByPosition( | |
| 196 compiler.world.allTypedefs | |
| 197 .where(backend.isAccessibleByReflection) | |
| 198 .toList()); | |
| 199 | |
| 200 // Compute needed classes. | |
| 201 Set<ClassElement> instantiatedClasses = | |
| 202 compiler.codegenWorld.directlyInstantiatedClasses | |
| 203 .where(computeClassFilter()).toSet(); | |
| 204 | |
| 205 void addClassWithSuperclasses(ClassElement cls) { | |
| 206 neededClasses.add(cls); | |
| 207 for (ClassElement superclass = cls.superclass; | |
| 208 superclass != null; | |
| 209 superclass = superclass.superclass) { | |
| 210 neededClasses.add(superclass); | |
| 211 } | |
| 212 } | |
| 213 | |
| 214 void addClassesWithSuperclasses(Iterable<ClassElement> classes) { | |
| 215 for (ClassElement cls in classes) { | |
| 216 addClassWithSuperclasses(cls); | |
| 217 } | |
| 218 } | |
| 219 | |
| 220 // 1. We need to generate all classes that are instantiated. | |
| 221 addClassesWithSuperclasses(instantiatedClasses); | |
| 222 | |
| 223 // 2. Add all classes used as mixins. | |
| 224 Set<ClassElement> mixinClasses = neededClasses | |
| 225 .where((ClassElement element) => element.isMixinApplication) | |
| 226 .map(computeMixinClass) | |
| 227 .toSet(); | |
| 228 neededClasses.addAll(mixinClasses); | |
| 229 | |
| 230 // 3. If we need noSuchMethod support, we run through all needed | |
| 231 // classes to figure out if we need the support on any native | |
| 232 // class. If so, we let the native emitter deal with it. | |
| 233 if (compiler.enabledNoSuchMethod) { | |
| 234 String noSuchMethodName = Compiler.NO_SUCH_METHOD; | |
| 235 Selector noSuchMethodSelector = compiler.noSuchMethodSelector; | |
| 236 for (ClassElement element in neededClasses) { | |
| 237 if (!element.isNative) continue; | |
| 238 Element member = element.lookupLocalMember(noSuchMethodName); | |
| 239 if (member == null) continue; | |
| 240 if (noSuchMethodSelector.applies(member, compiler.world)) { | |
| 241 nativeEmitter.handleNoSuchMethod = true; | |
| 242 break; | |
| 243 } | |
| 244 } | |
| 245 } | |
| 246 | |
| 247 // 4. Find all classes needed for rti. | |
| 248 // It is important that this is the penultimate step, at this point, | |
| 249 // neededClasses must only contain classes that have been resolved and | |
| 250 // codegen'd. The rtiNeededClasses may contain additional classes, but | |
| 251 // these are thought to not have been instantiated, so we neeed to be able | |
| 252 // to identify them later and make sure we only emit "empty shells" without | |
| 253 // fields, etc. | |
| 254 typeTestEmitter.computeRtiNeededClasses(); | |
| 255 typeTestEmitter.rtiNeededClasses.removeAll(neededClasses); | |
| 256 // rtiNeededClasses now contains only the "empty shells". | |
| 257 neededClasses.addAll(typeTestEmitter.rtiNeededClasses); | |
| 258 | |
| 259 // TODO(18175, floitsch): remove once issue 18175 is fixed. | |
| 260 if (neededClasses.contains(backend.jsIntClass)) { | |
| 261 neededClasses.add(compiler.intClass); | |
| 262 } | |
| 263 if (neededClasses.contains(backend.jsDoubleClass)) { | |
| 264 neededClasses.add(compiler.doubleClass); | |
| 265 } | |
| 266 if (neededClasses.contains(backend.jsNumberClass)) { | |
| 267 neededClasses.add(compiler.numClass); | |
| 268 } | |
| 269 if (neededClasses.contains(backend.jsStringClass)) { | |
| 270 neededClasses.add(compiler.stringClass); | |
| 271 } | |
| 272 if (neededClasses.contains(backend.jsBoolClass)) { | |
| 273 neededClasses.add(compiler.boolClass); | |
| 274 } | |
| 275 if (neededClasses.contains(backend.jsArrayClass)) { | |
| 276 neededClasses.add(compiler.listClass); | |
| 277 } | |
| 278 | |
| 279 // 5. Finally, sort the classes. | |
| 280 List<ClassElement> sortedClasses = Elements.sortedByPosition(neededClasses); | |
| 281 | |
| 282 for (ClassElement element in sortedClasses) { | |
| 283 if (Elements.isNativeOrExtendsNative(element) && | |
| 284 !typeTestEmitter.rtiNeededClasses.contains(element)) { | |
| 285 // For now, native classes and related classes cannot be deferred. | |
| 286 nativeClasses.add(element); | |
| 287 if (!element.isNative) { | |
| 288 assert(invariant(element, | |
| 289 !compiler.deferredLoadTask.isDeferred(element))); | |
| 290 outputClassLists.putIfAbsent(compiler.deferredLoadTask.mainOutputUnit, | |
| 291 () => new List<ClassElement>()).add(element); | |
| 292 } | |
| 293 } else { | |
| 294 outputClassLists.putIfAbsent( | |
| 295 compiler.deferredLoadTask.outputUnitForElement(element), | |
| 296 () => new List<ClassElement>()) | |
| 297 .add(element); | |
| 298 } | |
| 299 } | |
| 300 } | |
| 301 | |
| 302 void computeNeededStatics() { | |
| 303 bool isStaticFunction(Element element) => | |
| 304 !element.isInstanceMember && !element.isField; | |
| 305 | |
| 306 Iterable<Element> elements = | |
| 307 backend.generatedCode.keys.where(isStaticFunction); | |
| 308 | |
| 309 for (Element element in Elements.sortedByPosition(elements)) { | |
| 310 outputStaticLists.putIfAbsent( | |
| 311 compiler.deferredLoadTask.outputUnitForElement(element), | |
| 312 () => new List<Element>()) | |
| 313 .add(element); | |
| 314 } | |
| 315 } | |
| 316 | |
| 317 void computeNeededLibraries() { | |
| 318 void addSurroundingLibraryToSet(Element element) { | |
| 319 OutputUnit unit = compiler.deferredLoadTask.outputUnitForElement(element); | |
| 320 LibraryElement library = element.library; | |
| 321 outputLibraryLists.putIfAbsent(unit, () => new Set<LibraryElement>()) | |
| 322 .add(library); | |
| 323 } | |
| 324 | |
| 325 backend.generatedCode.keys.forEach(addSurroundingLibraryToSet); | |
| 326 neededClasses.forEach(addSurroundingLibraryToSet); | |
| 327 } | |
| 328 | |
| 329 void assembleProgram() { | |
| 330 measure(() { | |
| 331 emitter.invalidateCaches(); | |
| 332 | |
| 333 // Compute the required type checks to know which classes need a | |
| 334 // 'is$' method. | |
| 335 typeTestEmitter.computeRequiredTypeChecks(); | |
| 336 | |
| 337 computeNeededDeclarations(); | |
| 338 computeNeededConstants(); | |
| 339 computeNeededStatics(); | |
| 340 computeNeededLibraries(); | |
| 341 | |
| 342 | |
| 343 Program program; | |
| 344 if (USE_NEW_EMITTER) { | |
| 345 program = new ProgramBuilder(compiler, namer, this).buildProgram(); | |
| 346 } | |
| 347 emitter.emitProgram(program); | |
| 348 }); | |
| 349 } | |
| 350 } | |
| 351 | |
| 352 abstract class Emitter { | |
| 353 void emitProgram(Program program); | |
| 354 | |
| 355 jsAst.Expression generateEmbeddedGlobalAccess(String global); | |
| 356 jsAst.Expression constantReference(ConstantValue value); | |
| 357 | |
| 358 int compareConstants(ConstantValue a, ConstantValue b); | |
| 359 bool isConstantInlinedOrAlreadyEmitted(ConstantValue constant); | |
| 360 | |
| 361 void invalidateCaches(); | |
| 362 } | |
| OLD | NEW |