| // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| part of dart2js.js_emitter.program_builder; |
| |
| /// Generates the code for all used classes in the program. Static fields (even |
| /// in classes) are ignored, since they can be treated as non-class elements. |
| /// |
| /// The code for the containing (used) methods must exist in the `universe`. |
| class Collector { |
| final JCommonElements _commonElements; |
| final JElementEnvironment _elementEnvironment; |
| final OutputUnitData _outputUnitData; |
| final CodegenWorld _codegenWorld; |
| final Emitter _emitter; |
| final NativeData _nativeData; |
| final InterceptorData _interceptorData; |
| final OneShotInterceptorData _oneShotInterceptorData; |
| final JClosedWorld _closedWorld; |
| final Set<ClassEntity> _rtiNeededClasses; |
| final Map<MemberEntity, js.Expression> _generatedCode; |
| final Sorter _sorter; |
| |
| final Set<ClassEntity> neededClasses = {}; |
| final Set<ClassEntity> neededClassTypes = {}; |
| final Set<ClassEntity> classesOnlyNeededForConstructor = {}; |
| final Map<OutputUnit, List<ClassEntity>> outputClassLists = {}; |
| final Map<OutputUnit, List<ClassEntity>> outputClassTypeLists = {}; |
| final Map<OutputUnit, List<ConstantValue>> outputConstantLists = {}; |
| final Map<OutputUnit, List<MemberEntity>> outputStaticLists = {}; |
| final Map<OutputUnit, List<FieldEntity>> outputStaticNonFinalFieldLists = {}; |
| final Map<OutputUnit, List<FieldEntity>> outputLazyStaticFieldLists = {}; |
| final Map<OutputUnit, Set<LibraryEntity>> outputLibraryLists = {}; |
| |
| /// True, if the output contains a constant list. |
| /// |
| /// This flag is updated in [computeNeededConstants]. |
| bool outputContainsConstantList = false; |
| |
| final List<ClassEntity> nativeClassesAndSubclasses = []; |
| |
| Collector( |
| this._commonElements, |
| this._elementEnvironment, |
| this._outputUnitData, |
| this._codegenWorld, |
| this._emitter, |
| this._nativeData, |
| this._interceptorData, |
| this._oneShotInterceptorData, |
| this._closedWorld, |
| this._rtiNeededClasses, |
| this._generatedCode, |
| this._sorter); |
| |
| Set<ClassEntity> computeInterceptorsReferencedFromConstants() { |
| Set<ClassEntity> classes = {}; |
| Iterable<ConstantValue> constants = _codegenWorld.getConstantsForEmission(); |
| for (ConstantValue constant in constants) { |
| if (constant is InterceptorConstantValue) { |
| InterceptorConstantValue interceptorConstant = constant; |
| classes.add(interceptorConstant.cls); |
| } |
| } |
| return classes; |
| } |
| |
| /// Return a function that returns true if its argument is a class |
| /// that needs to be emitted. |
| Function computeClassFilter(Iterable<ClassEntity> backendTypeHelpers) { |
| Set<ClassEntity> unneededClasses = {}; |
| // The [Bool] class is not marked as abstract, but has a factory |
| // constructor that always throws. We never need to emit it. |
| unneededClasses.add(_commonElements.boolClass); |
| |
| // Go over specialized interceptors and then constants to know which |
| // interceptors are needed. |
| Set<ClassEntity> needed = {}; |
| for (SpecializedGetInterceptor interceptor |
| in _oneShotInterceptorData.specializedGetInterceptors) { |
| needed.addAll(interceptor.classes); |
| } |
| |
| // Add interceptors referenced by constants. |
| needed.addAll(computeInterceptorsReferencedFromConstants()); |
| |
| // Add unneeded interceptors to the [unneededClasses] set. |
| for (ClassEntity interceptor in _interceptorData.interceptedClasses) { |
| if (!needed.contains(interceptor) && |
| interceptor != _commonElements.objectClass) { |
| unneededClasses.add(interceptor); |
| } |
| } |
| |
| // These classes are just helpers for the backend's type system. |
| unneededClasses.addAll(backendTypeHelpers); |
| |
| return (ClassEntity cls) => !unneededClasses.contains(cls); |
| } |
| |
| // Return the classes that are just helpers for the backend's type system. |
| static Iterable<ClassEntity> getBackendTypeHelpers( |
| JCommonElements commonElements) { |
| return [ |
| commonElements.jsMutableArrayClass, |
| commonElements.jsFixedArrayClass, |
| commonElements.jsExtendableArrayClass, |
| // TODO(johnniwinther): Mark this as a backend type helper: |
| //commonElements.jsUnmodifiableArrayClass, |
| commonElements.jsUInt32Class, |
| commonElements.jsUInt31Class, |
| commonElements.jsPositiveIntClass |
| ]; |
| } |
| |
| /// Compute all the constants that must be emitted. |
| void computeNeededConstants() { |
| Iterable<ConstantValue> constants = |
| _codegenWorld.getConstantsForEmission(_emitter.compareConstants); |
| for (ConstantValue constant in constants) { |
| if (_emitter.isConstantInlinedOrAlreadyEmitted(constant)) continue; |
| |
| if (constant.isList) outputContainsConstantList = true; |
| |
| OutputUnit constantUnit = _outputUnitData.outputUnitForConstant(constant); |
| if (constantUnit == null) { |
| // The back-end introduces some constants, like "InterceptorConstant" or |
| // some list constants. They are emitted in the main output-unit. |
| // TODO(sigurdm): We should track those constants. |
| constantUnit = _outputUnitData.mainOutputUnit; |
| } |
| outputConstantLists.putIfAbsent(constantUnit, () => []).add(constant); |
| } |
| } |
| |
| /// Compute all the classes and typedefs that must be emitted. |
| void computeNeededDeclarations() { |
| Set<ClassEntity> backendTypeHelpers = |
| getBackendTypeHelpers(_commonElements).toSet(); |
| |
| // Compute needed classes. |
| Set<ClassEntity> instantiatedClasses = |
| // TODO(johnniwinther): This should be accessed from a codegen closed |
| // world. |
| _codegenWorld.directlyInstantiatedClasses |
| .where(computeClassFilter(backendTypeHelpers)) |
| .toSet(); |
| |
| void addClassesWithSuperclasses(Iterable<ClassEntity> classes) { |
| for (ClassEntity cls in classes) { |
| neededClasses.add(cls); |
| _elementEnvironment.forEachSuperClass( |
| cls, (superClass) => neededClasses.add(superClass)); |
| } |
| } |
| |
| // 1. We need to generate all classes that are instantiated. |
| addClassesWithSuperclasses(instantiatedClasses); |
| |
| // 2. Add all classes used as mixins. |
| Set<ClassEntity> mixinClasses = neededClasses |
| .where(_elementEnvironment.isMixinApplication) |
| .map(_elementEnvironment.getEffectiveMixinClass) |
| .toSet(); |
| neededClasses.addAll(mixinClasses); |
| |
| // 3. Add classes only needed for their constructors. |
| for (var cls in _codegenWorld.constructorReferences) { |
| if (neededClasses.add(cls)) { |
| classesOnlyNeededForConstructor.add(cls); |
| } |
| } |
| |
| // 4. Find all class types needed for rti. |
| for (ClassEntity cls in _rtiNeededClasses) { |
| if (backendTypeHelpers.contains(cls)) continue; |
| neededClassTypes.add(cls); |
| } |
| |
| // 5. Sort classes and add them to their respective OutputUnits. |
| for (ClassEntity cls in _sorter.sortClasses(neededClasses)) { |
| if (_nativeData.isNativeOrExtendsNative(cls) && |
| !classesOnlyNeededForConstructor.contains(cls)) { |
| // For now, native classes and related classes cannot be deferred. |
| nativeClassesAndSubclasses.add(cls); |
| assert(!_outputUnitData.isDeferredClass(cls), failedAt(cls)); |
| outputClassLists |
| .putIfAbsent(_outputUnitData.mainOutputUnit, () => []) |
| .add(cls); |
| } else { |
| outputClassLists |
| .putIfAbsent(_outputUnitData.outputUnitForClass(cls), () => []) |
| .add(cls); |
| } |
| } |
| |
| // 6. Sort classes needed for type checking and then add them to their |
| // respective OutputUnits. |
| for (ClassEntity cls in _sorter.sortClasses(neededClassTypes)) { |
| outputClassTypeLists |
| .putIfAbsent(_outputUnitData.outputUnitForClassType(cls), () => []) |
| .add(cls); |
| } |
| } |
| |
| void computeNeededStatics() { |
| bool isStaticFunction(MemberEntity element) => |
| !element.isInstanceMember && !element.isField; |
| |
| Iterable<MemberEntity> elements = |
| _generatedCode.keys.where(isStaticFunction); |
| |
| for (MemberEntity member in _sorter.sortMembers(elements)) { |
| List<MemberEntity> list = outputStaticLists.putIfAbsent( |
| _outputUnitData.outputUnitForMember(member), () => []); |
| list.add(member); |
| } |
| } |
| |
| void computeNeededStaticNonFinalFields() { |
| addToOutputUnit(FieldEntity element) { |
| List<FieldEntity> list = outputStaticNonFinalFieldLists.putIfAbsent( |
| _outputUnitData.outputUnitForMember(element), () => []); |
| list.add(element); |
| } |
| |
| List<FieldEntity> eagerFields = []; |
| _codegenWorld.forEachStaticField((FieldEntity field) { |
| if (_closedWorld.fieldAnalysis.getFieldData(field).isEager) { |
| eagerFields.add(field); |
| } |
| }); |
| |
| eagerFields.sort((FieldEntity a, FieldEntity b) { |
| FieldAnalysisData aFieldData = _closedWorld.fieldAnalysis.getFieldData(a); |
| FieldAnalysisData bFieldData = _closedWorld.fieldAnalysis.getFieldData(b); |
| int aIndex = aFieldData.eagerCreationIndex; |
| int bIndex = bFieldData.eagerCreationIndex; |
| if (aIndex != null && bIndex != null) { |
| return aIndex.compareTo(bIndex); |
| } else if (aIndex != null) { |
| // Sort [b] before [a]. |
| return 1; |
| } else if (bIndex != null) { |
| // Sort [a] before [b]. |
| return -1; |
| } else { |
| return _sorter.compareMembersByLocation(a, b); |
| } |
| }); |
| eagerFields.forEach(addToOutputUnit); |
| } |
| |
| void computeNeededLazyStaticFields() { |
| List<FieldEntity> lazyFields = []; |
| _codegenWorld.forEachStaticField((FieldEntity field) { |
| if (_closedWorld.fieldAnalysis.getFieldData(field).isLazy) { |
| lazyFields.add(field); |
| } |
| }); |
| |
| for (FieldEntity field in _sorter.sortMembers(lazyFields)) { |
| OutputUnit unit = _outputUnitData.outputUnitForMember(field); |
| (outputLazyStaticFieldLists[unit] ??= []).add(field); |
| } |
| } |
| |
| void computeNeededLibraries() { |
| _generatedCode.keys.forEach((MemberEntity element) { |
| OutputUnit unit = _outputUnitData.outputUnitForMember(element); |
| LibraryEntity library = element.library; |
| outputLibraryLists.putIfAbsent(unit, () => {}).add(library); |
| }); |
| neededClasses.forEach((ClassEntity element) { |
| OutputUnit unit = _outputUnitData.outputUnitForClass(element); |
| LibraryEntity library = element.library; |
| outputLibraryLists.putIfAbsent(unit, () => {}).add(library); |
| }); |
| neededClassTypes.forEach((ClassEntity element) { |
| OutputUnit unit = _outputUnitData.outputUnitForClassType(element); |
| LibraryEntity library = element.library; |
| outputLibraryLists.putIfAbsent(unit, () => {}).add(library); |
| }); |
| } |
| |
| void collect() { |
| computeNeededDeclarations(); |
| computeNeededConstants(); |
| computeNeededStatics(); |
| computeNeededStaticNonFinalFields(); |
| computeNeededLazyStaticFields(); |
| computeNeededLibraries(); |
| } |
| } |