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