blob: 9ddc9079fc686c4b5054ae3a0f56c4e373acb829 [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.
// @dart = 2.10
import 'dart:collection';
import '../common.dart';
import '../common/elements.dart';
import '../common/names.dart' show Identifiers;
import '../constants/values.dart';
import '../elements/entities.dart';
import '../elements/types.dart';
import '../js_backend/annotations.dart' show AnnotationsData;
import '../js_backend/interceptor_data.dart' show OneShotInterceptorData;
import '../js_backend/native_data.dart' show NativeBasicData;
import '../js_model/elements.dart';
import '../universe/class_hierarchy.dart';
import '../util/enumset.dart';
import '../util/util.dart';
import '../world.dart';
import 'call_structure.dart';
import 'codegen_world_builder_interfaces.dart' as interfaces;
import 'member_usage.dart';
import 'selector.dart' show Selector;
import 'use.dart'
show ConstantUse, DynamicUse, DynamicUseKind, StaticUse, StaticUseKind;
import 'world_builder.dart';
/// World builder specific to codegen.
///
/// This adds additional access to liveness of selectors and elements.
abstract class CodegenWorldBuilder
implements interfaces.CodegenWorldBuilderImplForEnqueuer {
/// Register [constant] as needed for emission.
void addCompileTimeConstantForEmission(ConstantValue constant);
/// Close the codegen world builder and return the immutable [CodegenWorld]
/// as the result.
CodegenWorld close();
}
// The immutable result of the [CodegenWorldBuilder].
abstract class CodegenWorld extends BuiltWorld {
/// Calls [f] for each generic call method on a live closure class.
void forEachGenericClosureCallMethod(void Function(FunctionEntity) f);
bool hasInvokedGetter(MemberEntity member);
/// Returns `true` if [member] is invoked as a setter.
bool hasInvokedSetter(MemberEntity member);
Map<Selector, SelectorConstraints> invocationsByName(String name);
Iterable<Selector> getterInvocationsByName(String name);
Iterable<Selector> setterInvocationsByName(String name);
void forEachInvokedName(
f(String name, Map<Selector, SelectorConstraints> selectors));
void forEachInvokedGetter(
f(String name, Map<Selector, SelectorConstraints> selectors));
void forEachInvokedSetter(
f(String name, Map<Selector, SelectorConstraints> selectors));
/// All directly instantiated classes, that is, classes with a generative
/// constructor that has been called directly and not only through a
/// super-call.
// TODO(johnniwinther): Improve semantic precision.
Iterable<ClassEntity> get directlyInstantiatedClasses;
Iterable<ClassEntity> get constructorReferences;
/// All directly or indirectly instantiated classes.
Iterable<ClassEntity> get instantiatedClasses;
bool methodsNeedsSuperGetter(FunctionEntity function);
/// The calls [f] for all static fields.
void forEachStaticField(void Function(FieldEntity) f);
/// Returns the types that are live as constant type literals.
Iterable<DartType /*!*/ > get constTypeLiterals;
/// Returns the types that are live as constant type arguments.
Iterable<DartType /*!*/ > get liveTypeArguments;
/// Returns a list of constants topologically sorted so that dependencies
/// appear before the dependent constant.
///
/// [preSortCompare] is a comparator function that gives the constants a
/// consistent order prior to the topological sort which gives the constants
/// an ordering that is less sensitive to perturbations in the source code.
Iterable<ConstantValue> getConstantsForEmission(
[Comparator<ConstantValue> preSortCompare]);
/// Returns `true` if [member] is called from a subclass via `super`.
bool isAliasedSuperMember(MemberEntity member);
OneShotInterceptorData get oneShotInterceptorData;
}
class CodegenWorldBuilderImpl extends WorldBuilder
implements CodegenWorldBuilder {
final JClosedWorld _closedWorld;
final OneShotInterceptorData _oneShotInterceptorData;
/// The set of all directly instantiated classes, that is, classes with a
/// generative constructor that has been called directly and not only through
/// a super-call.
///
/// Invariant: Elements are declaration elements.
// TODO(johnniwinther): [_directlyInstantiatedClasses] and
// [_instantiatedTypes] sets should be merged.
final Set<ClassEntity> _directlyInstantiatedClasses = {};
/// The set of all directly instantiated types, that is, the types of the
/// directly instantiated classes.
///
/// See [_directlyInstantiatedClasses].
final Set<InterfaceType> _instantiatedTypes = {};
/// Classes implemented by directly instantiated classes.
final Set<ClassEntity> _implementedClasses = {};
final Map<String, Map<Selector, SelectorConstraints>> _invokedNames = {};
final Map<String, Map<Selector, SelectorConstraints>> _invokedGetters = {};
final Map<String, Map<Selector, SelectorConstraints>> _invokedSetters = {};
final Map<ClassEntity, ClassUsage> _processedClasses = {};
Map<ClassEntity, ClassUsage> get classUsageForTesting => _processedClasses;
/// Map of registered usage of static and instance members.
final Map<MemberEntity, MemberUsage> _memberUsage = {};
/// Map containing instance members of live classes that have not yet been
/// fully invoked dynamically.
///
/// A method is fully invoked if all is optional parameter have been passed
/// in some invocation.
final Map<String, Set<MemberUsage>> _invokableInstanceMembersByName = {};
/// Map containing instance members of live classes that have not yet been
/// read from dynamically.
final Map<String, Set<MemberUsage>> _readableInstanceMembersByName = {};
/// Map containing instance members of live classes that have not yet been
/// written to dynamically.
final Map<String, Set<MemberUsage>> _writableInstanceMembersByName = {};
final Set<DartType> _isChecks = {};
final SelectorConstraintsStrategy _selectorConstraintsStrategy;
final Set<ConstantValue> _constantValues = {};
final Set<DartType> _constTypeLiterals = {};
final Set<DartType> _liveTypeArguments = {};
final Set<TypeVariableType> _namedTypeVariablesNewRti = {};
final Set<ClassEntity> _constructorReferences = {};
CodegenWorldBuilderImpl(this._closedWorld, this._selectorConstraintsStrategy,
this._oneShotInterceptorData);
ElementEnvironment get _elementEnvironment => _closedWorld.elementEnvironment;
NativeBasicData get _nativeBasicData => _closedWorld.nativeData;
Iterable<ClassEntity> get instantiatedClasses => _processedClasses.keys
.where((cls) => _processedClasses[cls].isInstantiated);
// TODO(johnniwinther): Improve semantic precision.
@override
Iterable<ClassEntity> get directlyInstantiatedClasses {
return _directlyInstantiatedClasses;
}
/// Register [type] as (directly) instantiated.
// TODO(johnniwinther): Fully enforce the separation between exact, through
// subclass and through subtype instantiated types/classes.
// TODO(johnniwinther): Support unknown type arguments for generic types.
@override
void registerTypeInstantiation(
InterfaceType type, ClassUsedCallback classUsed) {
ClassEntity cls = type.element;
bool isNative = _nativeBasicData.isNativeClass(cls);
_instantiatedTypes.add(type);
// We can't use the closed-world assumption with native abstract
// classes; a native abstract class may have non-abstract subclasses
// not declared to the program. Instances of these classes are
// indistinguishable from the abstract class.
if (!cls.isAbstract || isNative) {
_directlyInstantiatedClasses.add(cls);
_processInstantiatedClass(cls, classUsed);
}
// TODO(johnniwinther): Replace this by separate more specific mappings that
// include the type arguments.
if (_implementedClasses.add(cls)) {
classUsed(cls, _getClassUsage(cls).implement());
_elementEnvironment.forEachSupertype(cls, (InterfaceType supertype) {
if (_implementedClasses.add(supertype.element)) {
classUsed(
supertype.element, _getClassUsage(supertype.element).implement());
}
});
}
}
bool _hasMatchingSelector(Map<Selector, SelectorConstraints> selectors,
MemberEntity member, JClosedWorld world) {
if (selectors == null) return false;
for (Selector selector in selectors.keys) {
if (selector.appliesUnnamed(member)) {
SelectorConstraints masks = selectors[selector];
if (masks.canHit(member, selector.memberName, world)) {
return true;
}
}
}
return false;
}
Iterable<CallStructure> _getMatchingCallStructures(
Map<Selector, SelectorConstraints> selectors, MemberEntity member) {
if (selectors == null) return const [];
Set<CallStructure> callStructures;
for (Selector selector in selectors.keys) {
if (selector.appliesUnnamed(member)) {
SelectorConstraints masks = selectors[selector];
if (masks.canHit(member, selector.memberName, _closedWorld)) {
callStructures ??= {};
callStructures.add(selector.callStructure);
}
}
}
return callStructures ?? const [];
}
Iterable<CallStructure> _getInvocationCallStructures(MemberEntity member) {
return _getMatchingCallStructures(_invokedNames[member.name], member);
}
bool _hasInvokedGetter(MemberEntity member) {
return _hasMatchingSelector(
_invokedGetters[member.name], member, _closedWorld);
}
bool _hasInvokedSetter(MemberEntity member) {
return _hasMatchingSelector(
_invokedSetters[member.name], member, _closedWorld);
}
@override
void registerDynamicUse(
DynamicUse dynamicUse, MemberUsedCallback memberUsed) {
Selector selector = dynamicUse.selector;
String methodName = selector.name;
void _process(
Map<String, Set<MemberUsage>> memberMap,
EnumSet<MemberUse> action(MemberUsage usage),
bool shouldBeRemoved(MemberUsage usage)) {
_processSet(memberMap, methodName, (MemberUsage usage) {
if (selector.appliesUnnamed(usage.entity) &&
_selectorConstraintsStrategy.appliedUnnamed(
dynamicUse, usage.entity, _closedWorld)) {
memberUsed(usage.entity, action(usage));
return shouldBeRemoved(usage);
}
return false;
});
}
switch (dynamicUse.kind) {
case DynamicUseKind.INVOKE:
registerDynamicInvocation(
dynamicUse.selector, dynamicUse.typeArguments);
if (_registerNewSelector(dynamicUse, _invokedNames)) {
_process(
_invokableInstanceMembersByName,
(m) => m.invoke(Accesses.dynamicAccess, selector.callStructure),
// If not all optional parameters have been passed in invocations
// we must keep the member in [_invokableInstanceMembersByName].
(u) => !u.hasPendingDynamicInvoke);
}
break;
case DynamicUseKind.GET:
if (_registerNewSelector(dynamicUse, _invokedGetters)) {
_process(
_readableInstanceMembersByName,
(m) => m.read(Accesses.dynamicAccess),
(u) => !u.hasPendingDynamicRead);
}
break;
case DynamicUseKind.SET:
if (_registerNewSelector(dynamicUse, _invokedSetters)) {
_process(
_writableInstanceMembersByName,
(m) => m.write(Accesses.dynamicAccess),
(u) => !u.hasPendingDynamicWrite);
}
break;
}
}
bool _registerNewSelector(DynamicUse dynamicUse,
Map<String, Map<Selector, SelectorConstraints>> selectorMap) {
Selector selector = dynamicUse.selector;
String name = selector.name;
Object constraint = dynamicUse.receiverConstraint;
Map<Selector, SelectorConstraints> selectors =
selectorMap[name] ??= Maplet<Selector, SelectorConstraints>();
UniverseSelectorConstraints constraints = selectors[selector];
if (constraints == null) {
selectors[selector] = _selectorConstraintsStrategy
.createSelectorConstraints(selector, constraint);
return true;
}
return constraints.addReceiverConstraint(constraint);
}
@override
void registerIsCheck(covariant DartType type) {
_isChecks.add(type);
}
@override
void registerNamedTypeVariableNewRti(TypeVariableType type) {
_namedTypeVariablesNewRti.add(type);
}
@override
void registerStaticUse(StaticUse staticUse, MemberUsedCallback memberUsed) {
MemberEntity element = staticUse.element;
EnumSet<MemberUse> useSet = EnumSet();
MemberUsage usage = _getMemberUsage(element, useSet);
switch (staticUse.kind) {
case StaticUseKind.STATIC_TEAR_OFF:
useSet.addAll(usage.read(Accesses.staticAccess));
break;
case StaticUseKind.INSTANCE_FIELD_GET:
case StaticUseKind.INSTANCE_FIELD_SET:
case StaticUseKind.CALL_METHOD:
// TODO(johnniwinther): Avoid this. Currently [FIELD_GET] and
// [FIELD_SET] contains [BoxFieldElement]s which we cannot enqueue.
// Also [CLOSURE] contains [LocalFunctionElement] which we cannot
// enqueue.
break;
case StaticUseKind.SUPER_INVOKE:
registerStaticInvocation(staticUse);
useSet.addAll(
usage.invoke(Accesses.superAccess, staticUse.callStructure));
break;
case StaticUseKind.STATIC_INVOKE:
registerStaticInvocation(staticUse);
useSet.addAll(
usage.invoke(Accesses.staticAccess, staticUse.callStructure));
break;
case StaticUseKind.SUPER_FIELD_SET:
useSet.addAll(usage.write(Accesses.superAccess));
break;
case StaticUseKind.SUPER_SETTER_SET:
useSet.addAll(usage.write(Accesses.superAccess));
break;
case StaticUseKind.STATIC_SET:
useSet.addAll(usage.write(Accesses.staticAccess));
break;
case StaticUseKind.SUPER_TEAR_OFF:
useSet.addAll(usage.read(Accesses.superAccess));
break;
case StaticUseKind.SUPER_GET:
useSet.addAll(usage.read(Accesses.superAccess));
break;
case StaticUseKind.STATIC_GET:
useSet.addAll(usage.read(Accesses.staticAccess));
break;
case StaticUseKind.FIELD_INIT:
useSet.addAll(usage.init());
break;
case StaticUseKind.FIELD_CONSTANT_INIT:
useSet.addAll(usage.constantInit(staticUse.constant));
break;
case StaticUseKind.CONSTRUCTOR_INVOKE:
case StaticUseKind.CONST_CONSTRUCTOR_INVOKE:
// We don't track parameters in the codegen world builder, so we
// pass `null` instead of the concrete call structure.
useSet.addAll(
usage.invoke(Accesses.staticAccess, staticUse.callStructure));
break;
case StaticUseKind.DIRECT_INVOKE:
MemberEntity member = staticUse.element;
// We don't track parameters in the codegen world builder, so we
// pass `null` instead of the concrete call structure.
useSet.addAll(
usage.invoke(Accesses.staticAccess, staticUse.callStructure));
if (staticUse.typeArguments?.isNotEmpty ?? false) {
registerDynamicInvocation(
Selector.call(member.memberName, staticUse.callStructure),
staticUse.typeArguments);
}
break;
case StaticUseKind.INLINING:
registerStaticInvocation(staticUse);
break;
case StaticUseKind.CLOSURE:
case StaticUseKind.CLOSURE_CALL:
failedAt(CURRENT_ELEMENT_SPANNABLE,
"Static use ${staticUse.kind} is not supported during codegen.");
}
if (useSet.isNotEmpty) {
memberUsed(usage.entity, useSet);
}
}
@override
void processClassMembers(ClassEntity cls, MemberUsedCallback memberUsed,
{bool checkEnqueuerConsistency = false}) {
_elementEnvironment.forEachClassMember(cls,
(ClassEntity cls, MemberEntity member) {
_processInstantiatedClassMember(cls, member, memberUsed,
checkEnqueuerConsistency: checkEnqueuerConsistency);
});
}
void _processInstantiatedClassMember(
ClassEntity cls, MemberEntity member, MemberUsedCallback memberUsed,
{bool checkEnqueuerConsistency = false}) {
if (!member.isInstanceMember) return;
EnumSet<MemberUse> useSet = EnumSet();
MemberUsage usage = _getMemberUsage(member, useSet);
if (useSet.isNotEmpty) {
if (checkEnqueuerConsistency) {
throw SpannableAssertionFailure(member,
'Unenqueued usage of $member: \nbefore: <none>\nafter : $usage');
} else {
memberUsed(member, useSet);
}
}
}
MemberUsage _getMemberUsage(MemberEntity member, EnumSet<MemberUse> useSet,
{bool checkEnqueuerConsistency = false}) {
// TODO(johnniwinther): Change [TypeMask] to not apply to a superclass
// member unless the class has been instantiated. Similar to
// [StrongModeConstraint].
MemberUsage usage = _memberUsage[member];
if (usage == null) {
MemberAccess potentialAccess = _closedWorld.getMemberAccess(member);
if (member.isInstanceMember) {
String memberName = member.name;
ClassEntity cls = member.enclosingClass;
bool isNative = _nativeBasicData.isNativeClass(cls);
usage = MemberUsage(member, potentialAccess: potentialAccess);
if (member.isField && !isNative) {
useSet.addAll(usage.init());
}
if (member is JSignatureMethod) {
// We mark signature methods as "always used" to prevent them from
// being optimized away.
// TODO(johnniwinther): Make this a part of the regular enqueueing.
useSet.addAll(
usage.invoke(Accesses.dynamicAccess, CallStructure.NO_ARGS));
}
if (usage.hasPendingDynamicRead && _hasInvokedGetter(member)) {
useSet.addAll(usage.read(Accesses.dynamicAccess));
}
if (usage.hasPendingDynamicWrite && _hasInvokedSetter(member)) {
useSet.addAll(usage.write(Accesses.dynamicAccess));
}
if (usage.hasPendingDynamicInvoke) {
Iterable<CallStructure> callStructures =
_getInvocationCallStructures(member);
for (CallStructure callStructure in callStructures) {
useSet.addAll(usage.invoke(Accesses.dynamicAccess, callStructure));
if (!usage.hasPendingDynamicInvoke) {
break;
}
}
}
if (!checkEnqueuerConsistency) {
if (usage.hasPendingDynamicInvoke) {
_invokableInstanceMembersByName
.putIfAbsent(memberName, () => {})
.add(usage);
}
if (usage.hasPendingDynamicRead) {
_readableInstanceMembersByName
.putIfAbsent(memberName, () => {})
.add(usage);
}
if (usage.hasPendingDynamicWrite) {
_writableInstanceMembersByName
.putIfAbsent(memberName, () => {})
.add(usage);
}
}
} else {
usage = MemberUsage(member, potentialAccess: potentialAccess);
if (member.isField) {
useSet.addAll(usage.init());
}
}
if (!checkEnqueuerConsistency) {
_memberUsage[member] = usage;
}
} else {
if (checkEnqueuerConsistency) {
usage = usage.clone();
}
}
return usage;
}
void _processSet(Map<String, Set<MemberUsage>> map, String memberName,
bool f(MemberUsage e)) {
Set<MemberUsage> members = map[memberName];
if (members == null) return;
// [f] might add elements to [: map[memberName] :] during the loop below
// so we create a new list for [: map[memberName] :] and prepend the
// [remaining] members after the loop.
map[memberName] = {};
Set<MemberUsage> remaining = {};
for (MemberUsage member in members) {
if (!f(member)) remaining.add(member);
}
map[memberName].addAll(remaining);
}
/// Return the canonical [ClassUsage] for [cls].
ClassUsage _getClassUsage(ClassEntity cls) {
return _processedClasses.putIfAbsent(cls, () => ClassUsage(cls));
}
void _processInstantiatedClass(ClassEntity cls, ClassUsedCallback classUsed) {
// Registers [superclass] as instantiated. Returns `true` if it wasn't
// already instantiated and we therefore have to process its superclass as
// well.
bool processClass(ClassEntity superclass) {
ClassUsage usage = _getClassUsage(superclass);
if (!usage.isInstantiated) {
classUsed(usage.cls, usage.instantiate());
return true;
}
return false;
}
while (cls != null && processClass(cls)) {
cls = _elementEnvironment.getSuperClass(cls);
}
}
/// Set of all registered compiled constants.
final Set<ConstantValue> _compiledConstants = {};
Iterable<ConstantValue> get compiledConstantsForTesting => _compiledConstants;
@override
void addCompileTimeConstantForEmission(ConstantValue constant) {
_compiledConstants.add(constant);
}
/// Register the constant [use] with this world builder. Returns `true` if
/// the constant use was new to the world.
@override
bool registerConstantUse(ConstantUse use) {
addCompileTimeConstantForEmission(use.value);
return _constantValues.add(use.value);
}
@override
void registerConstTypeLiteral(DartType type) {
_constTypeLiterals.add(type);
}
@override
void registerTypeArgument(DartType type) {
_liveTypeArguments.add(type);
}
@override
void registerConstructorReference(InterfaceType type) {
_constructorReferences.add(type.element);
}
@override
CodegenWorld close() {
Map<MemberEntity, MemberUsage> liveMemberUsage = {};
_memberUsage.forEach((MemberEntity member, MemberUsage usage) {
if (usage.hasUse) {
liveMemberUsage[member] = usage;
}
});
return CodegenWorldImpl(_closedWorld, liveMemberUsage,
constTypeLiterals: _constTypeLiterals,
constructorReferences: _constructorReferences,
directlyInstantiatedClasses: directlyInstantiatedClasses,
typeVariableTypeLiterals: typeVariableTypeLiterals,
instantiatedClasses: instantiatedClasses,
isChecks: _isChecks,
namedTypeVariablesNewRti: _namedTypeVariablesNewRti,
instantiatedTypes: _instantiatedTypes,
liveTypeArguments: _liveTypeArguments,
compiledConstants: _compiledConstants,
invokedNames: _invokedNames,
invokedGetters: _invokedGetters,
invokedSetters: _invokedSetters,
staticTypeArgumentDependencies: staticTypeArgumentDependencies,
dynamicTypeArgumentDependencies: dynamicTypeArgumentDependencies,
oneShotInterceptorData: _oneShotInterceptorData);
}
}
class CodegenWorldImpl implements CodegenWorld {
final JClosedWorld _closedWorld;
final Map<MemberEntity, MemberUsage> _liveMemberUsage;
@override
final Iterable<DartType> constTypeLiterals;
@override
final Iterable<ClassEntity> constructorReferences;
@override
final Iterable<ClassEntity> directlyInstantiatedClasses;
@override
final Iterable<TypeVariableType> typeVariableTypeLiterals;
@override
final Iterable<ClassEntity> instantiatedClasses;
@override
final Iterable<DartType> isChecks;
@override
final Set<TypeVariableType> namedTypeVariablesNewRti;
@override
final Iterable<InterfaceType> instantiatedTypes;
@override
final Iterable<DartType> liveTypeArguments;
final Iterable<ConstantValue> _compiledConstants;
final Map<String, Map<Selector, SelectorConstraints>> _invokedNames;
final Map<String, Map<Selector, SelectorConstraints>> _invokedGetters;
final Map<String, Map<Selector, SelectorConstraints>> _invokedSetters;
final Map<Entity, Set<DartType>> _staticTypeArgumentDependencies;
final Map<Selector, Set<DartType>> _dynamicTypeArgumentDependencies;
@override
final OneShotInterceptorData oneShotInterceptorData;
CodegenWorldImpl(this._closedWorld, this._liveMemberUsage,
{this.constTypeLiterals,
this.constructorReferences,
this.directlyInstantiatedClasses,
this.typeVariableTypeLiterals,
this.instantiatedClasses,
this.isChecks,
this.namedTypeVariablesNewRti,
this.instantiatedTypes,
this.liveTypeArguments,
Iterable<ConstantValue> compiledConstants,
Map<String, Map<Selector, SelectorConstraints>> invokedNames,
Map<String, Map<Selector, SelectorConstraints>> invokedGetters,
Map<String, Map<Selector, SelectorConstraints>> invokedSetters,
Map<Entity, Set<DartType>> staticTypeArgumentDependencies,
Map<Selector, Set<DartType>> dynamicTypeArgumentDependencies,
this.oneShotInterceptorData})
: _compiledConstants = compiledConstants,
_invokedNames = invokedNames,
_invokedGetters = invokedGetters,
_invokedSetters = invokedSetters,
_staticTypeArgumentDependencies = staticTypeArgumentDependencies,
_dynamicTypeArgumentDependencies = dynamicTypeArgumentDependencies;
@override
AnnotationsData get annotationsData => _closedWorld.annotationsData;
@override
ClassHierarchy get classHierarchy => _closedWorld.classHierarchy;
@override
void forEachStaticField(void Function(FieldEntity) f) {
bool failure = false;
_liveMemberUsage.forEach((MemberEntity member, MemberUsage usage) {
if (member is FieldEntity && (member.isStatic || member.isTopLevel)) {
f(member);
}
});
if (failure) throw 'failure';
}
@override
void forEachGenericMethod(Function f) {
_liveMemberUsage.forEach((MemberEntity member, MemberUsage usage) {
if (member is FunctionEntity &&
_closedWorld.elementEnvironment
.getFunctionTypeVariables(member)
.isNotEmpty) {
f(member);
}
});
}
@override
void forEachGenericInstanceMethod(Function f) {
_liveMemberUsage.forEach((MemberEntity member, MemberUsage usage) {
if (member is FunctionEntity &&
member.isInstanceMember &&
_closedWorld.elementEnvironment
.getFunctionTypeVariables(member)
.isNotEmpty) {
f(member);
}
});
}
@override
void forEachGenericClosureCallMethod(Function f) {
_liveMemberUsage.forEach((MemberEntity member, MemberUsage usage) {
if (member.name == Identifiers.call &&
member.isInstanceMember &&
member.enclosingClass.isClosure &&
member is FunctionEntity &&
_closedWorld.elementEnvironment
.getFunctionTypeVariables(member)
.isNotEmpty) {
f(member);
}
});
}
List<FunctionEntity> _userNoSuchMethodsCache;
@override
Iterable<FunctionEntity> get userNoSuchMethods {
if (_userNoSuchMethodsCache == null) {
_userNoSuchMethodsCache = [];
_liveMemberUsage.forEach((MemberEntity member, MemberUsage memberUsage) {
if (member is FunctionEntity) {
if (member.isInstanceMember &&
member.name == Identifiers.noSuchMethod_ &&
!_closedWorld.commonElements
.isDefaultNoSuchMethodImplementation(member)) {
_userNoSuchMethodsCache.add(member);
}
}
});
}
return _userNoSuchMethodsCache;
}
@override
Iterable<Local> get genericLocalFunctions => const [];
Set<FunctionEntity> _closurizedMembersCache;
@override
Iterable<FunctionEntity> get closurizedMembers {
if (_closurizedMembersCache == null) {
_closurizedMembersCache = {};
_liveMemberUsage.forEach((MemberEntity member, MemberUsage usage) {
if ((member.isFunction || member is JGeneratorBody) &&
member.isInstanceMember &&
usage.hasRead) {
_closurizedMembersCache.add(member);
}
});
}
return _closurizedMembersCache;
}
Set<FunctionEntity> _closurizedStaticsCache;
@override
Iterable<FunctionEntity> get closurizedStatics {
if (_closurizedStaticsCache == null) {
_closurizedStaticsCache = {};
_liveMemberUsage.forEach((MemberEntity member, MemberUsage usage) {
if (member.isFunction &&
(member.isStatic || member.isTopLevel) &&
usage.hasRead) {
_closurizedStaticsCache.add(member);
}
});
}
return _closurizedStaticsCache;
}
Map<MemberEntity, DartType> _genericCallablePropertiesCache;
@override
Map<MemberEntity, DartType> get genericCallableProperties {
if (_genericCallablePropertiesCache == null) {
_genericCallablePropertiesCache = {};
_liveMemberUsage.forEach((MemberEntity member, MemberUsage usage) {
if (usage.hasRead) {
DartType type;
if (member.isField) {
type = _closedWorld.elementEnvironment.getFieldType(member);
} else if (member.isGetter) {
type = _closedWorld.elementEnvironment
.getFunctionType(member)
.returnType;
}
if (type == null) return;
if (_closedWorld.dartTypes.canAssignGenericFunctionTo(type)) {
_genericCallablePropertiesCache[member] = type;
} else {
type = type.withoutNullability;
if (type is InterfaceType) {
FunctionType callType = _closedWorld.dartTypes.getCallType(type);
if (callType != null &&
_closedWorld.dartTypes.canAssignGenericFunctionTo(callType)) {
_genericCallablePropertiesCache[member] = callType;
}
}
}
}
});
}
return _genericCallablePropertiesCache;
}
@override
void forEachStaticTypeArgument(
void f(Entity function, Set<DartType> typeArguments)) {
_staticTypeArgumentDependencies.forEach(f);
}
@override
void forEachDynamicTypeArgument(
void f(Selector selector, Set<DartType> typeArguments)) {
_dynamicTypeArgumentDependencies.forEach(f);
}
@override
void forEachInvokedName(
f(String name, Map<Selector, SelectorConstraints> selectors)) {
_invokedNames.forEach(f);
}
@override
void forEachInvokedGetter(
f(String name, Map<Selector, SelectorConstraints> selectors)) {
_invokedGetters.forEach(f);
}
@override
void forEachInvokedSetter(
f(String name, Map<Selector, SelectorConstraints> selectors)) {
_invokedSetters.forEach(f);
}
@override
bool hasInvokedGetter(MemberEntity member) {
MemberUsage memberUsage = _liveMemberUsage[member];
if (memberUsage == null) return false;
return memberUsage.reads.contains(Access.dynamicAccess);
}
@override
bool methodsNeedsSuperGetter(FunctionEntity function) {
MemberUsage memberUsage = _liveMemberUsage[function];
if (memberUsage == null) return false;
return memberUsage.reads.contains(Access.superAccess);
}
@override
bool hasInvokedSetter(MemberEntity member) {
MemberUsage memberUsage = _liveMemberUsage[member];
if (memberUsage == null) return false;
return memberUsage.writes.contains(Access.dynamicAccess);
}
Map<Selector, SelectorConstraints> _asUnmodifiable(
Map<Selector, SelectorConstraints> map) {
if (map == null) return null;
return UnmodifiableMapView(map);
}
@override
Map<Selector, SelectorConstraints> invocationsByName(String name) {
return _asUnmodifiable(_invokedNames[name]);
}
@override
Iterable<Selector> getterInvocationsByName(String name) {
return _invokedGetters[name]?.keys;
}
@override
Iterable<Selector> setterInvocationsByName(String name) {
return _invokedSetters[name]?.keys;
}
@override
Iterable<ConstantValue> getConstantsForEmission(
[Comparator<ConstantValue> preSortCompare]) {
// We must emit dependencies before their uses.
Set<ConstantValue> seenConstants = {};
List<ConstantValue> result = [];
void addConstant(ConstantValue constant) {
if (!seenConstants.contains(constant)) {
constant.getDependencies().forEach(addConstant);
assert(!seenConstants.contains(constant));
result.add(constant);
seenConstants.add(constant);
}
}
if (preSortCompare != null) {
List<ConstantValue> sorted = _compiledConstants.toList();
sorted.sort(preSortCompare);
sorted.forEach(addConstant);
} else {
_compiledConstants.forEach(addConstant);
}
return result;
}
@override
bool isAliasedSuperMember(MemberEntity member) {
MemberUsage usage = _liveMemberUsage[member];
if (usage == null) return false;
return usage.invokes.contains(Access.superAccess) ||
usage.writes.contains(Access.superAccess);
}
}