blob: 6cb80fd7ce7f9910e6a83eb1209d19647abff068 [file] [log] [blame]
// 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) {
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>> closureMap =
new Map<ClassEntity, List<Local>>();
for (Local closure in worldBuilder.localFunctions) {
closureMap
.putIfAbsent(closure.memberContext.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 = closureMap[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 = closureMap[cls];
if (closures != null) {
for (Local closure in closures) {
MemberEntity member = closure.memberContext;
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 [closureMap].
if (closureMap.containsKey(null)) {
for (Local closure in closureMap[null]) {
if (isMemberReferencedFromMirrorSystem(closure.memberContext)) {
_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));
}
}
}