| // Copyright (c) 2017, 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. |
| |
| import '../common.dart'; |
| import '../common_elements.dart'; |
| import '../compiler.dart'; |
| import '../constants/values.dart'; |
| import '../elements/elements.dart' show AbstractFieldElement, Element; |
| import '../elements/entities.dart'; |
| import '../elements/names.dart'; |
| import '../elements/types.dart'; |
| import '../options.dart'; |
| import '../world.dart'; |
| import '../universe/world_builder.dart'; |
| import '../util/emptyset.dart'; |
| |
| abstract class MirrorsData { |
| /// True if a call to preserveMetadataMarker has been seen. This means that |
| /// metadata must be retained for dart:mirrors to work correctly. |
| // resolution-empty-queue |
| bool get mustRetainMetadata; |
| |
| /// True if any metadata has been retained. This is slightly different from |
| /// [mustRetainMetadata] and tells us if any metadata was retained. For |
| /// example, if [mustRetainMetadata] is true but there is no metadata in the |
| /// program, this variable will stil be false. |
| // emitter |
| bool get hasRetainedMetadata; |
| |
| /// True if a call to preserveLibraryNames has been seen. |
| // emitter |
| bool get mustRetainLibraryNames; |
| |
| /// True if a call to preserveNames has been seen. |
| // resolution-empty-queue |
| bool get mustPreserveNames; |
| |
| /// True if a call to disableTreeShaking has been seen. |
| bool get isTreeShakingDisabled; |
| |
| /// True if a call to preserveUris has been seen and the preserve-uris flag |
| /// is set. |
| bool get mustPreserveUris; |
| |
| /// Set of symbols that the user has requested for reflection. |
| Iterable<String> get symbolsUsed; |
| |
| /// The members that the user has requested for reflection through the |
| /// 'targets' property of a `MirrorsUsed` annotation. |
| Iterable<MemberEntity> get membersInMirrorsUsedTargets; |
| |
| /// The classes that the user has requested for reflection through the |
| /// 'targets' property of a `MirrorsUsed` annotation. |
| Iterable<ClassEntity> get classesInMirrorsUsedTargets; |
| |
| /// The libraries that the user has requested for reflection through the |
| /// 'targets' property of a `MirrorsUsed` annotation. |
| Iterable<LibraryEntity> get librariesInMirrorsUsedTargets; |
| |
| /// Should the getter for [element] that would normally not be generated due |
| /// to tree-shaking be retained for reflection? |
| bool shouldRetainGetter(FieldEntity element); |
| |
| /// Should the setter for [element] that would normally not be generated due |
| /// to tree-shaking be retained for reflection? |
| bool shouldRetainSetter(FieldEntity element); |
| |
| /// Should [name] be retained for reflection? |
| bool shouldRetainName(String name); |
| |
| /// Returns `true` if the class [element] is covered by a `MirrorsUsed` |
| /// annotation. |
| /// |
| /// Note that it might still be ok to tree shake the element away if no |
| /// reflection is used in the program (and thus [isTreeShakingDisabled] is |
| /// still false). Therefore _do not_ use this predicate to decide inclusion |
| /// in the tree, use [requiredByMirrorSystem] instead. |
| bool isClassReferencedFromMirrorSystem(ClassEntity element); |
| |
| /// Returns `true` if the member [element] is covered by a `MirrorsUsed` |
| /// annotation. |
| /// |
| /// Note that it might still be ok to tree shake the element away if no |
| /// reflection is used in the program (and thus [isTreeShakingDisabled] is |
| /// still false). Therefore _do not_ use this predicate to decide inclusion |
| /// in the tree, use [requiredByMirrorSystem] instead. |
| bool isMemberReferencedFromMirrorSystem(MemberEntity element); |
| |
| /// Returns `true` if the library [element] is covered by a `MirrorsUsed` |
| /// annotation. |
| bool isLibraryReferencedFromMirrorSystem(LibraryEntity element); |
| |
| /// Returns `true` if the typedef [element] needs reflection information at |
| /// runtime. |
| /// |
| /// This property is used to tag emitted elements with a marker which is |
| /// checked by the runtime system to throw an exception if an element is |
| /// accessed (invoked, get, set) that is not accessible for the reflective |
| /// system. |
| bool isTypedefAccessibleByReflection(TypedefEntity element); |
| |
| /// Returns `true` if the class [element] needs reflection information at |
| /// runtime. |
| /// |
| /// This property is used to tag emitted elements with a marker which is |
| /// checked by the runtime system to throw an exception if an element is |
| /// accessed (invoked, get, set) that is not accessible for the reflective |
| /// system. |
| bool isClassAccessibleByReflection(ClassEntity element); |
| |
| /// Returns `true` if the member [element] needs reflection information at |
| /// runtime. |
| /// |
| /// This property is used to tag emitted elements with a marker which is |
| /// checked by the runtime system to throw an exception if an element is |
| /// accessed (invoked, get, set) that is not accessible for the reflective |
| /// system. |
| bool isMemberAccessibleByReflection(MemberEntity element); |
| |
| bool retainMetadataOfLibrary(LibraryEntity element, |
| {bool addForEmission: true}); |
| bool retainMetadataOfTypedef(TypedefEntity element); |
| bool retainMetadataOfClass(ClassEntity element); |
| bool retainMetadataOfMember(MemberEntity element); |
| |
| /// Returns true if this element has to be enqueued due to |
| /// mirror usage. Might be a subset of [referencedFromMirrorSystem] if |
| /// normal tree shaking is still active ([isTreeShakingDisabled] is false). |
| bool isLibraryRequiredByMirrorSystem(LibraryEntity element); |
| bool isClassRequiredByMirrorSystem(ClassEntity element); |
| bool isMemberRequiredByMirrorSystem(MemberEntity element); |
| bool isClassResolved(ClassEntity element); |
| } |
| |
| abstract class MirrorsDataBuilder { |
| void registerUsedMember(MemberEntity member); |
| |
| /// Called by [MirrorUsageAnalyzerTask] after it has merged all @MirrorsUsed |
| /// annotations. The arguments corresponds to the unions of the corresponding |
| /// fields of the annotations. |
| void registerMirrorUsage( |
| Set<String> symbols, Set<Element> targets, Set<Element> metaTargets); |
| |
| /// Called when `const Symbol(name)` is seen. |
| void registerConstSymbol(String name); |
| |
| void maybeMarkClosureAsNeededForReflection( |
| ClassEntity closureClass, FunctionEntity callMethod, Local localFunction); |
| |
| void computeMembersNeededForReflection( |
| ResolutionWorldBuilder worldBuilder, ClosedWorld closedWorld); |
| } |
| |
| abstract class MirrorsDataImpl implements MirrorsData, MirrorsDataBuilder { |
| /// True if a call to preserveMetadataMarker has been seen. This means that |
| /// metadata must be retained for dart:mirrors to work correctly. |
| bool mustRetainMetadata = false; |
| |
| /// True if any metadata has been retained. This is slightly different from |
| /// [mustRetainMetadata] and tells us if any metadata was retained. For |
| /// example, if [mustRetainMetadata] is true but there is no metadata in the |
| /// program, this variable will stil be false. |
| bool hasRetainedMetadata = false; |
| |
| /// True if a call to preserveLibraryNames has been seen. |
| bool mustRetainLibraryNames = false; |
| |
| /// True if a call to preserveNames has been seen. |
| bool mustPreserveNames = false; |
| |
| /// True if a call to disableTreeShaking has been seen. |
| bool isTreeShakingDisabled = false; |
| |
| /// True if there isn't sufficient @MirrorsUsed data. |
| bool hasInsufficientMirrorsUsed = false; |
| |
| /// True if a call to preserveUris has been seen and the preserve-uris flag |
| /// is set. |
| bool mustPreserveUris = false; |
| |
| /// Set of symbols that the user has requested for reflection. |
| final Set<String> symbolsUsed = new Set<String>(); |
| |
| /// Set of elements that the user has requested for reflection. |
| final Set<MemberEntity> membersInMirrorsUsedTargets = new Set<MemberEntity>(); |
| final Set<ClassEntity> classesInMirrorsUsedTargets = new Set<ClassEntity>(); |
| final Set<LibraryEntity> librariesInMirrorsUsedTargets = |
| new Set<LibraryEntity>(); |
| final Set<TypedefEntity> _typedefsInMirrorsUsedTargets = |
| new Set<TypedefEntity>(); |
| |
| /// List of annotations provided by user that indicate that the annotated |
| /// element must be retained. |
| final Set<ClassEntity> metaTargetsUsed = new Set<ClassEntity>(); |
| |
| // TODO(johnniwinther): Avoid the need for this. |
| final Compiler _compiler; |
| |
| final CompilerOptions _options; |
| |
| final ElementEnvironment _elementEnvironment; |
| final CommonElements _commonElements; |
| |
| MirrorsDataImpl(this._compiler, this._options, this._elementEnvironment, |
| this._commonElements); |
| |
| void registerUsedMember(MemberEntity member) { |
| if (member == _commonElements.disableTreeShakingMarker) { |
| isTreeShakingDisabled = true; |
| } else if (member == _commonElements.preserveNamesMarker) { |
| mustPreserveNames = true; |
| } else if (member == _commonElements.preserveMetadataMarker) { |
| mustRetainMetadata = true; |
| } else if (member == _commonElements.preserveUrisMarker) { |
| if (_options.preserveUris) mustPreserveUris = true; |
| } else if (member == _commonElements.preserveLibraryNamesMarker) { |
| mustRetainLibraryNames = true; |
| } |
| } |
| |
| bool shouldRetainGetter(FieldEntity element) { |
| return isTreeShakingDisabled && isMemberAccessibleByReflection(element); |
| } |
| |
| bool shouldRetainSetter(FieldEntity element) { |
| return isTreeShakingDisabled && isMemberAccessibleByReflection(element); |
| } |
| |
| /// Should [name] be retained for reflection? |
| bool shouldRetainName(String name) { |
| if (hasInsufficientMirrorsUsed) return mustPreserveNames; |
| if (name == '') return false; |
| return symbolsUsed.contains(name); |
| } |
| |
| @override |
| bool retainMetadataOfMember(MemberEntity element) { |
| if (mustRetainMetadata) { |
| hasRetainedMetadata = true; |
| if (isMemberReferencedFromMirrorSystem(element)) { |
| _addConstantsForEmission( |
| getMemberMetadata(element, includeParameterMetadata: true)); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @override |
| bool retainMetadataOfClass(ClassEntity element) { |
| if (mustRetainMetadata) { |
| hasRetainedMetadata = true; |
| if (isClassReferencedFromMirrorSystem(element)) { |
| _addConstantsForEmission(getClassMetadata(element)); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @override |
| bool retainMetadataOfTypedef(TypedefEntity element) { |
| if (mustRetainMetadata) { |
| hasRetainedMetadata = true; |
| if (_isTypedefReferencedFromMirrorSystem(element)) { |
| _addConstantsForEmission(getTypedefMetadata(element)); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @override |
| bool retainMetadataOfLibrary(LibraryEntity element, |
| {bool addForEmission: true}) { |
| if (mustRetainMetadata) { |
| hasRetainedMetadata = true; |
| if (isLibraryReferencedFromMirrorSystem(element)) { |
| Iterable<ConstantValue> constants = getLibraryMetadata(element); |
| if (addForEmission) { |
| _addConstantsForEmission(constants); |
| } |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| Iterable<ConstantValue> getLibraryMetadata(LibraryEntity element) { |
| return _elementEnvironment.getLibraryMetadata(element); |
| } |
| |
| Iterable<ConstantValue> getClassMetadata(ClassEntity element) { |
| return _elementEnvironment.getClassMetadata(element); |
| } |
| |
| Iterable<ConstantValue> getMemberMetadata(MemberEntity element, |
| {bool includeParameterMetadata}) { |
| return _elementEnvironment.getMemberMetadata(element, |
| includeParameterMetadata: includeParameterMetadata); |
| } |
| |
| Iterable<ConstantValue> getTypedefMetadata(TypedefEntity element) { |
| return _elementEnvironment.getTypedefMetadata(element); |
| } |
| |
| void _addConstantsForEmission(Iterable<ConstantValue> constants) { |
| for (ConstantValue constant in constants) { |
| CodegenWorldBuilder worldBuilder = _compiler.codegenWorldBuilder; |
| worldBuilder.addCompileTimeConstantForEmission(constant); |
| } |
| } |
| |
| /// Sets of elements that are needed by reflection. Computed using |
| /// [computeMembersNeededForReflection] on first use. |
| Set<ClassEntity> _classesNeededForReflection; |
| Set<TypedefEntity> _typedefsNeededForReflection; |
| Set<MemberEntity> _membersNeededForReflection; |
| Set<Local> _closuresNeededForReflection; |
| |
| /// Called by [MirrorUsageAnalyzerTask] after it has merged all @MirrorsUsed |
| /// annotations. The arguments corresponds to the unions of the corresponding |
| /// fields of the annotations. |
| // TODO(redemption): Change type of [metaTargets] to `Set<ClassEntity>`. |
| void registerMirrorUsage( |
| Set<String> symbols, Set<Element> targets, Set<Element> metaTargets) { |
| if (symbols == null && targets == null && metaTargets == null) { |
| // The user didn't specify anything, or there are imports of |
| // 'dart:mirrors' without @MirrorsUsed. |
| hasInsufficientMirrorsUsed = true; |
| return; |
| } |
| if (symbols != null) symbolsUsed.addAll(symbols); |
| if (targets != null) { |
| for (Element target in targets) { |
| if (target.isAbstractField) { |
| AbstractFieldElement field = target; |
| if (field.getter != null) { |
| membersInMirrorsUsedTargets.add(field.getter); |
| } |
| if (field.setter != null) { |
| membersInMirrorsUsedTargets.add(field.setter); |
| } |
| } else if (target.isClass) { |
| classesInMirrorsUsedTargets.add(target as ClassEntity); |
| } else if (target.isTypedef) { |
| _typedefsInMirrorsUsedTargets.add(target as TypedefEntity); |
| } else if (target.isLibrary) { |
| librariesInMirrorsUsedTargets.add(target as LibraryEntity); |
| } else if (target != null) { |
| membersInMirrorsUsedTargets.add(target as MemberEntity); |
| } |
| } |
| } |
| if (metaTargets != null) { |
| for (dynamic element in metaTargets) { |
| if (element is ClassEntity) { |
| metaTargetsUsed.add(element); |
| } |
| } |
| } |
| } |
| |
| @override |
| bool isClassAccessibleByReflection(ClassEntity element) { |
| return _classesNeededForReflection.contains(_getDartClass(element)); |
| } |
| |
| @override |
| bool isTypedefAccessibleByReflection(TypedefEntity element) { |
| return _typedefsNeededForReflection.contains(element); |
| } |
| |
| ClassEntity _getDartClass(ClassEntity cls) { |
| if (cls == _commonElements.jsIntClass) { |
| return _commonElements.intClass; |
| } else if (cls == _commonElements.jsBoolClass) { |
| return _commonElements.boolClass; |
| } else if (cls == _commonElements.jsNumberClass) { |
| return _commonElements.numClass; |
| } else if (cls == _commonElements.jsDoubleClass) { |
| return _commonElements.doubleClass; |
| } else if (cls == _commonElements.jsStringClass) { |
| return _commonElements.stringClass; |
| } else if (cls == _commonElements.jsArrayClass) { |
| return _commonElements.listClass; |
| } else if (cls == _commonElements.jsNullClass) { |
| return _commonElements.nullClass; |
| } else { |
| return cls; |
| } |
| } |
| |
| bool isMemberAccessibleByReflection(MemberEntity element) { |
| return _membersNeededForReflection.contains(element); |
| } |
| |
| /// Returns true if this element has to be enqueued due to |
| /// mirror usage. Might be a subset of [referencedFromMirrorSystem] if |
| /// normal tree shaking is still active ([isTreeShakingDisabled] is false). |
| bool isLibraryRequiredByMirrorSystem(LibraryEntity element) { |
| return hasInsufficientMirrorsUsed && isTreeShakingDisabled || |
| _libraryMatchesMirrorsMetaTarget(element) || |
| librariesInMirrorsUsedTargets.contains(element); |
| } |
| |
| bool isClassRequiredByMirrorSystem(ClassEntity element) { |
| return hasInsufficientMirrorsUsed && isTreeShakingDisabled || |
| _classMatchesMirrorsMetaTarget(element) || |
| classesInMirrorsUsedTargets.contains(element); |
| } |
| |
| bool isMemberRequiredByMirrorSystem(MemberEntity element) { |
| return hasInsufficientMirrorsUsed && isTreeShakingDisabled || |
| _memberMatchesMirrorsMetaTarget(element) || |
| membersInMirrorsUsedTargets.contains(element); |
| } |
| |
| @override |
| bool isLibraryReferencedFromMirrorSystem(LibraryEntity element) { |
| return _libraryReferencedFromMirrorSystem(element); |
| } |
| |
| @override |
| bool isMemberReferencedFromMirrorSystem(MemberEntity element) { |
| if (_memberReferencedFromMirrorSystem(element)) return true; |
| if (element.enclosingClass != null) { |
| return isClassReferencedFromMirrorSystem(element.enclosingClass); |
| } else { |
| return isLibraryReferencedFromMirrorSystem(element.library); |
| } |
| } |
| |
| @override |
| bool isClassReferencedFromMirrorSystem(ClassEntity element) { |
| return _classReferencedFromMirrorSystem(element) || |
| isLibraryReferencedFromMirrorSystem(element.library); |
| } |
| |
| bool _isTypedefReferencedFromMirrorSystem(TypedefEntity element) { |
| return _typedefReferencedFromMirrorSystem(element) || |
| isLibraryReferencedFromMirrorSystem(element.library); |
| } |
| |
| bool _memberReferencedFromMirrorSystem(MemberEntity element) { |
| return hasInsufficientMirrorsUsed || |
| _memberMatchesMirrorsMetaTarget(element) || |
| membersInMirrorsUsedTargets.contains(element); |
| } |
| |
| bool _classReferencedFromMirrorSystem(ClassEntity element) { |
| return hasInsufficientMirrorsUsed || |
| _classMatchesMirrorsMetaTarget(element) || |
| classesInMirrorsUsedTargets.contains(element); |
| } |
| |
| bool _typedefReferencedFromMirrorSystem(TypedefEntity element) { |
| return hasInsufficientMirrorsUsed || |
| _typedefMatchesMirrorsMetaTarget(element) || |
| _typedefsInMirrorsUsedTargets.contains(element); |
| } |
| |
| bool _libraryReferencedFromMirrorSystem(LibraryEntity element) { |
| return hasInsufficientMirrorsUsed || |
| _libraryMatchesMirrorsMetaTarget(element) || |
| librariesInMirrorsUsedTargets.contains(element); |
| } |
| |
| bool _libraryMatchesMirrorsMetaTarget(LibraryEntity element) { |
| if (metaTargetsUsed.isEmpty) return false; |
| return _matchesMirrorsMetaTarget(getLibraryMetadata(element)); |
| } |
| |
| bool _classMatchesMirrorsMetaTarget(ClassEntity element) { |
| if (metaTargetsUsed.isEmpty) return false; |
| return _matchesMirrorsMetaTarget(getClassMetadata(element)); |
| } |
| |
| bool _memberMatchesMirrorsMetaTarget(MemberEntity element) { |
| if (metaTargetsUsed.isEmpty) return false; |
| return _matchesMirrorsMetaTarget( |
| getMemberMetadata(element, includeParameterMetadata: false)); |
| } |
| |
| bool _typedefMatchesMirrorsMetaTarget(TypedefEntity element) { |
| if (metaTargetsUsed.isEmpty) return false; |
| return _matchesMirrorsMetaTarget(getTypedefMetadata(element)); |
| } |
| |
| /** |
| * Returns `true` if the element is needed because it has an annotation |
| * of a type that is used as a meta target for reflection. |
| */ |
| bool _matchesMirrorsMetaTarget(Iterable<ConstantValue> constants) { |
| if (metaTargetsUsed.isEmpty) return false; |
| for (ConstantValue constant in constants) { |
| DartType type = constant.getType(_commonElements); |
| if (type is InterfaceType && metaTargetsUsed.contains(type.element)) |
| return true; |
| } |
| return false; |
| } |
| |
| void createImmutableSets() { |
| _classesNeededForReflection = const ImmutableEmptySet<ClassEntity>(); |
| _typedefsNeededForReflection = const ImmutableEmptySet<TypedefEntity>(); |
| _membersNeededForReflection = const ImmutableEmptySet<MemberEntity>(); |
| _closuresNeededForReflection = const ImmutableEmptySet<Local>(); |
| } |
| |
| bool isLibraryInternal(LibraryEntity library) { |
| return library.canonicalUri.scheme == 'dart' && |
| library.canonicalUri.path.startsWith('_'); |
| } |
| |
| /// Whether [cls] is 'injected'. |
| /// |
| /// An injected class is declared in a patch library with no corresponding |
| /// class in the origin library. |
| // TODO(redemption): Detect injected classes from .dill. |
| bool isClassInjected(ClassEntity cls) => false; |
| |
| bool isClassResolved(ClassEntity cls) => true; |
| |
| void forEachConstructor( |
| ClassEntity cls, void f(ConstructorEntity constructor)) { |
| _elementEnvironment.forEachConstructor(cls, f); |
| } |
| |
| void forEachClassMember( |
| ClassEntity cls, void f(MemberEntity member, Name memberName)) { |
| _elementEnvironment.forEachClassMember(cls, |
| (ClassEntity declarer, MemberEntity member) { |
| if (member.isSetter) { |
| f(member, member.memberName.setter); |
| } else { |
| f(member, member.memberName.getter); |
| } |
| }); |
| } |
| |
| /** |
| * Visits all classes and computes whether its members are needed for |
| * reflection. |
| * |
| * We have to precompute this set as we cannot easily answer the need for |
| * reflection locally when looking at the member: We lack the information by |
| * which classes a member is inherited. Called after resolution is complete. |
| * |
| * We filter out private libraries here, as their elements should not |
| * be visible by reflection unless some other interfaces makes them |
| * accessible. |
| */ |
| void computeMembersNeededForReflection( |
| ResolutionWorldBuilder worldBuilder, ClosedWorld closedWorld) { |
| if (_membersNeededForReflection != null) return; |
| if (!closedWorld.backendUsage.isMirrorsUsed || |
| _compiler.options.useKernel) { |
| createImmutableSets(); |
| return; |
| } |
| _classesNeededForReflection = new Set<ClassEntity>(); |
| _typedefsNeededForReflection = new Set<TypedefEntity>(); |
| _membersNeededForReflection = new Set<MemberEntity>(); |
| _closuresNeededForReflection = new Set<Local>(); |
| |
| // Compute a mapping from class to the closures it contains, so we |
| // can include the correct ones when including the class. |
| Map<ClassEntity, List<Local>> classToClosuresMap = |
| new Map<ClassEntity, List<Local>>(); |
| Map<Local, MemberEntity> closureToMemberMap = |
| new Map<Local, MemberEntity>(); |
| worldBuilder.forEachLocalFunction((MemberEntity member, Local closure) { |
| closureToMemberMap[closure] = member; |
| classToClosuresMap |
| .putIfAbsent(member.enclosingClass, () => []) |
| .add(closure); |
| }); |
| bool foundClosure = false; |
| for (ClassEntity cls in worldBuilder.directlyInstantiatedClasses) { |
| // Do not process internal classes. |
| if (isLibraryInternal(cls.library) || isClassInjected(cls)) continue; |
| if (isClassReferencedFromMirrorSystem(cls)) { |
| Set<Name> memberNames = new Set<Name>(); |
| // 1) the class (should be resolved) |
| assert(isClassResolved(cls), failedAt(cls)); |
| _classesNeededForReflection.add(cls); |
| // 2) its constructors (if resolved) |
| forEachConstructor(cls, (ConstructorEntity constructor) { |
| if (worldBuilder.isMemberUsed(constructor)) { |
| _membersNeededForReflection.add(constructor); |
| } |
| }); |
| // 3) all members, including fields via getter/setters (if resolved) |
| forEachClassMember(cls, (MemberEntity member, Name memberName) { |
| if (worldBuilder.isMemberUsed(member)) { |
| memberNames.add(memberName); |
| _membersNeededForReflection.add(member); |
| } |
| }); |
| // 4) all overriding members of subclasses/subtypes (should be resolved) |
| if (closedWorld.hasAnyStrictSubtype(cls)) { |
| closedWorld.forEachStrictSubtypeOf(cls, (ClassEntity subcls) { |
| forEachClassMember(subcls, (MemberEntity member, Name memberName) { |
| if (memberNames.contains(memberName)) { |
| // TODO(20993): find out why this assertion fails. |
| // assert(worldBuilder.isMemberUsed(member.element), |
| // failedAt(member.element)); |
| if (worldBuilder.isMemberUsed(member)) { |
| _membersNeededForReflection.add(member); |
| } |
| } |
| }); |
| }); |
| } |
| // 5) all its closures |
| List<Local> closures = classToClosuresMap[cls]; |
| if (closures != null) { |
| _closuresNeededForReflection.addAll(closures); |
| foundClosure = true; |
| } |
| } else { |
| // check members themselves |
| forEachConstructor(cls, (ConstructorEntity element) { |
| if (!worldBuilder.isMemberUsed(element)) return; |
| if (_memberReferencedFromMirrorSystem(element)) { |
| _membersNeededForReflection.add(element); |
| } |
| }); |
| forEachClassMember(cls, (MemberEntity member, _) { |
| if (!worldBuilder.isMemberUsed(member)) return; |
| if (_memberReferencedFromMirrorSystem(member)) { |
| _membersNeededForReflection.add(member); |
| } |
| }); |
| // Also add in closures. Those might be reflectable is their enclosing |
| // member is. |
| List<Local> closures = classToClosuresMap[cls]; |
| if (closures != null) { |
| for (Local closure in closures) { |
| MemberEntity member = closureToMemberMap[closure]; |
| if (_memberReferencedFromMirrorSystem(member)) { |
| _closuresNeededForReflection.add(closure); |
| foundClosure = true; |
| } |
| } |
| } |
| } |
| } |
| // We also need top-level non-class elements like static functions and |
| // global fields. We use the resolution queue to decide which elements are |
| // part of the live world. |
| for (LibraryEntity lib in _elementEnvironment.libraries) { |
| if (isLibraryInternal(lib)) continue; |
| _elementEnvironment.forEachLibraryMember(lib, (MemberEntity member) { |
| if (worldBuilder.isMemberUsed(member) && |
| isMemberReferencedFromMirrorSystem(member)) { |
| _membersNeededForReflection.add(member); |
| } |
| }); |
| } |
| // And closures inside top-level elements that do not have a surrounding |
| // class. These will be in the [:null:] bucket of the [classToClosureMap]. |
| if (classToClosuresMap.containsKey(null)) { |
| for (Local closure in classToClosuresMap[null]) { |
| if (isMemberReferencedFromMirrorSystem(closureToMemberMap[closure])) { |
| _closuresNeededForReflection.add(closure); |
| foundClosure = true; |
| } |
| } |
| } |
| // As we do not think about closures as classes, yet, we have to make sure |
| // their superclasses are available for reflection manually. |
| if (foundClosure) { |
| ClassEntity cls = _commonElements.closureClass; |
| _classesNeededForReflection.add(cls); |
| } |
| Set<FunctionEntity> closurizedMembers = worldBuilder.closurizedMembers; |
| if (closurizedMembers.any(_membersNeededForReflection.contains)) { |
| ClassEntity cls = _commonElements.boundClosureClass; |
| _classesNeededForReflection.add(cls); |
| } |
| // Add typedefs. |
| for (TypedefEntity element in closedWorld.allTypedefs) { |
| if (_isTypedefReferencedFromMirrorSystem(element)) { |
| _typedefsNeededForReflection.add(element); |
| } |
| } |
| // Register all symbols of reflectable elements |
| for (ClassEntity element in _classesNeededForReflection) { |
| symbolsUsed.add(element.name); |
| } |
| for (TypedefEntity element in _typedefsNeededForReflection) { |
| symbolsUsed.add(element.name); |
| } |
| for (MemberEntity element in _membersNeededForReflection) { |
| symbolsUsed.add(element.name); |
| } |
| for (Local element in _closuresNeededForReflection) { |
| symbolsUsed.add(element.name); |
| } |
| } |
| |
| // TODO(20791): compute closure classes after resolution and move this code to |
| // [computeMembersNeededForReflection]. |
| void maybeMarkClosureAsNeededForReflection(ClassEntity closureClass, |
| FunctionEntity callMethod, Local localFunction) { |
| if (!_closuresNeededForReflection.contains(localFunction)) return; |
| _membersNeededForReflection.add(callMethod); |
| _classesNeededForReflection.add(closureClass); |
| } |
| |
| /// Called when `const Symbol(name)` is seen. |
| void registerConstSymbol(String name) { |
| symbolsUsed.add(name); |
| if (name.endsWith('=')) { |
| symbolsUsed.add(name.substring(0, name.length - 1)); |
| } |
| } |
| } |