mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 12:57:42 +00:00
[dart2js] Update experimental inferrer with changes from comments in unfork.
Change-Id: I467f94d17a6d2fa002eb7f506360d6b4a62806a8 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/290441 Commit-Queue: Nate Biggs <natebiggs@google.com> Reviewed-by: Sigmund Cherem <sigmund@google.com>
This commit is contained in:
parent
a6a040658c
commit
0ac7c843ee
|
@ -620,10 +620,12 @@ class ComputableAbstractValueDomain with AbstractValueDomain {
|
|||
_wrappedDomain.isFixedLengthJsIndexable(_unwrap(value));
|
||||
|
||||
@override
|
||||
Iterable<DynamicCallTarget> findRootsOfTargets(AbstractValue receiver,
|
||||
Selector selector, MemberHierarchyBuilder memberHierarchyBuilder) {
|
||||
Iterable<DynamicCallTarget> findRootsOfTargets(
|
||||
covariant ComputableAbstractValue receiver,
|
||||
Selector selector,
|
||||
MemberHierarchyBuilder memberHierarchyBuilder) {
|
||||
return _wrappedDomain.findRootsOfTargets(
|
||||
receiver, selector, memberHierarchyBuilder);
|
||||
_unwrap(receiver), selector, memberHierarchyBuilder);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -1715,12 +1715,8 @@ class KernelTypeGraphBuilder extends ir.Visitor<TypeInformation?>
|
|||
TypeInformation _handleRecordFieldGet(
|
||||
ir.Expression node, ir.Expression receiver, String fieldName) {
|
||||
final receiverType = visit(receiver)!;
|
||||
final staticType = _getStaticType(node);
|
||||
final staticTypeMask = _closedWorld.abstractValueDomain
|
||||
.createFromStaticType(staticType, nullable: true)
|
||||
.abstractValue;
|
||||
(_memberData as KernelGlobalTypeInferenceElementData)
|
||||
.setReceiverTypeMask(node, staticTypeMask);
|
||||
.setReceiverTypeMask(node, receiverType.type);
|
||||
return _types.allocateRecordFieldGet(node, fieldName, receiverType);
|
||||
}
|
||||
|
||||
|
|
|
@ -117,6 +117,7 @@ class InferrerEngine {
|
|||
NoSuchMethodData get noSuchMethodData => closedWorld.noSuchMethodData;
|
||||
|
||||
final MemberHierarchyBuilder memberHierarchyBuilder;
|
||||
Set<MemberEntity>? _initializedVirtualMembers = {};
|
||||
|
||||
InferrerEngine(
|
||||
this._options,
|
||||
|
@ -140,16 +141,11 @@ class InferrerEngine {
|
|||
Selector selector, AbstractValue? mask, bool f(MemberEntity element)) {
|
||||
final targets = memberHierarchyBuilder.rootsForCall(mask, selector);
|
||||
for (final target in targets) {
|
||||
if (!target.member.isAbstract) {
|
||||
if (!f(target.member)) return;
|
||||
}
|
||||
if (target.isVirtual) {
|
||||
memberHierarchyBuilder.forEachOverride(target.member, (override) {
|
||||
return (override.isAbstract || f(override))
|
||||
memberHierarchyBuilder.forEachTargetMember(
|
||||
target,
|
||||
(member) => (member.isAbstract || f(member))
|
||||
? IterationStep.CONTINUE
|
||||
: IterationStep.STOP;
|
||||
});
|
||||
}
|
||||
: IterationStep.STOP);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -332,8 +328,8 @@ class InferrerEngine {
|
|||
}
|
||||
|
||||
void _runOverAllElements() {
|
||||
metrics.memberHierarchy
|
||||
.measure(() => memberHierarchyBuilder.init(_joinOverriddenMember));
|
||||
_initMemberHierarchy();
|
||||
|
||||
metrics.analyze.measure(_analyzeAllElements);
|
||||
final dump =
|
||||
debug.PRINT_GRAPH ? TypeGraphDump(_compilerOutput, this) : null;
|
||||
|
@ -342,9 +338,10 @@ class InferrerEngine {
|
|||
_buildWorkQueue();
|
||||
metrics.refine1.measure(_refine);
|
||||
|
||||
// Update overrides that need to be considered for closurization before
|
||||
// tracing.
|
||||
_updateOverrideClosurizations(markCalled: false);
|
||||
// Process the refined targets of calls labeling closureized members. We do
|
||||
// not need to mark targets as called in this pass because we don't use this
|
||||
// information in global inference.
|
||||
_processCalledTargets(shouldMarkCalled: false);
|
||||
|
||||
metrics.trace.measure(() {
|
||||
// Try to infer element types of lists and compute their escape information.
|
||||
|
@ -409,6 +406,7 @@ class InferrerEngine {
|
|||
trace(elements, ClosureTracerVisitor(elements, info, this));
|
||||
} else if (info is CallSiteTypeInformation) {
|
||||
final selector = info.selector;
|
||||
List<FunctionEntity> elements;
|
||||
if (info is StaticCallSiteTypeInformation &&
|
||||
selector != null &&
|
||||
selector.isCall) {
|
||||
|
@ -419,16 +417,27 @@ class InferrerEngine {
|
|||
calledElement.isGenerativeConstructor);
|
||||
final cls = calledElement.enclosingClass!;
|
||||
final callMethod = _lookupCallMethod(cls)!;
|
||||
Iterable<FunctionEntity> elements = [callMethod];
|
||||
trace(elements, ClosureTracerVisitor(elements, info, this));
|
||||
} else {
|
||||
elements = [callMethod];
|
||||
} else if (info is DynamicCallSiteTypeInformation) {
|
||||
// We only are interested in functions here, as other targets
|
||||
// of this closure call are not a root to trace but an intermediate
|
||||
// for some other function.
|
||||
Iterable<FunctionEntity> elements = List<FunctionEntity>.from(
|
||||
info.callees.where((e) => e.isFunction && !e.isAbstract));
|
||||
trace(elements, ClosureTracerVisitor(elements, info, this));
|
||||
elements = [];
|
||||
for (final target in info.concreteTargets) {
|
||||
IterationStep processTarget(MemberEntity entity) {
|
||||
if (entity.isFunction && !entity.isAbstract) {
|
||||
elements.add(entity as FunctionEntity);
|
||||
}
|
||||
return IterationStep.CONTINUE;
|
||||
}
|
||||
|
||||
memberHierarchyBuilder.forEachTargetMember(target, processTarget);
|
||||
}
|
||||
} else {
|
||||
elements = List<FunctionEntity>.from(
|
||||
info.callees.where((e) => e.isFunction));
|
||||
}
|
||||
trace(elements, ClosureTracerVisitor(elements, info, this));
|
||||
} else if (info is MemberTypeInformation) {
|
||||
final member = info.member as FunctionEntity;
|
||||
trace(
|
||||
|
@ -458,9 +467,9 @@ class InferrerEngine {
|
|||
_workQueue.addAll(seenTypes);
|
||||
metrics.refine2.measure(_refine);
|
||||
|
||||
// Update overrides that need to be considered for closurization after
|
||||
// the final round of refines.
|
||||
_updateOverrideClosurizations(markCalled: true);
|
||||
// Process the refined targets of calls labeling closureized members and
|
||||
// marking targeted members as called.
|
||||
_processCalledTargets(shouldMarkCalled: true);
|
||||
|
||||
if (debug.PRINT_SUMMARY) {
|
||||
types.allocatedLists.values.forEach((_info) {
|
||||
|
@ -528,9 +537,10 @@ class InferrerEngine {
|
|||
/// Call [analyze] for all live members.
|
||||
void _analyzeAllElements() {
|
||||
_progress.startPhase();
|
||||
final toProcess = closedWorld.processedMembers
|
||||
.followedBy(closedWorld.liveAbstractInstanceMembers)
|
||||
.toSet();
|
||||
final toProcess = {
|
||||
...closedWorld.processedMembers,
|
||||
...closedWorld.liveAbstractInstanceMembers
|
||||
};
|
||||
toProcess.forEach((MemberEntity member) {
|
||||
_progress.showProgress(
|
||||
'Added ', _addedInGraph, ' elements in inferencing graph.');
|
||||
|
@ -543,6 +553,14 @@ class InferrerEngine {
|
|||
metrics.allTypesCount.add(types.allTypes.length);
|
||||
}
|
||||
|
||||
void _initMemberHierarchy() {
|
||||
metrics.memberHierarchy
|
||||
.measure(() => memberHierarchyBuilder.init(_initializeOverrideEdges));
|
||||
// Once the hierarchy is set up we will not need to initialize new
|
||||
// virtual members and can clear the initialization cache.
|
||||
_initializedVirtualMembers = null;
|
||||
}
|
||||
|
||||
/// Returns the body node for [member].
|
||||
ir.Node? _computeMemberBody(MemberEntity member) {
|
||||
MemberDefinition definition =
|
||||
|
@ -842,6 +860,30 @@ class InferrerEngine {
|
|||
return false;
|
||||
}
|
||||
|
||||
/// Adds edges between the virtual type information for [parent] and
|
||||
/// [override] based on the type of each member. If the virtual type
|
||||
/// information for either does not exist yet, create it and initialize
|
||||
/// it by adding edges to the concrete type information for that member.
|
||||
/// Passed to [MemberHierarchyBuilder.init] to be called as it discovers
|
||||
/// overrides.
|
||||
///
|
||||
/// Possible override configurations (parent/override):
|
||||
/// - field/getter
|
||||
/// - field/setter
|
||||
/// - field/field
|
||||
/// - getter/getter
|
||||
/// - getter/field
|
||||
/// - setter/setter
|
||||
/// - setter/field
|
||||
/// - method/method
|
||||
void _initializeOverrideEdges(MemberEntity parent, MemberEntity override) {
|
||||
if (parent.name == Identifiers.noSuchMethod_) return;
|
||||
final parentType = _getAndSetupVirtualMember(parent);
|
||||
final overrideType = _getAndSetupVirtualMember(override);
|
||||
|
||||
_addOverrideTypeInputs(parent, override, parentType, overrideType);
|
||||
}
|
||||
|
||||
void _setupVirtualCall(
|
||||
MemberTypeInformation virtualCallType, MemberEntity member) {
|
||||
if (member is FieldEntity || member.isGetter) {
|
||||
|
@ -865,7 +907,7 @@ class InferrerEngine {
|
|||
}
|
||||
}
|
||||
|
||||
void _joinOverrideParameters(MemberEntity parent, MemberEntity override) {
|
||||
void _addOverrideParameterEdges(MemberEntity parent, MemberEntity override) {
|
||||
final method = parent as FunctionEntity;
|
||||
ParameterStructure parameterStructure = method.parameterStructure;
|
||||
int parameterIndex = 0;
|
||||
|
@ -909,34 +951,14 @@ class InferrerEngine {
|
|||
}
|
||||
|
||||
MemberTypeInformation _getAndSetupVirtualMember(MemberEntity member) {
|
||||
final memberType = types.getCachedOrInferredTypeOfVirtualMember(member);
|
||||
if (memberType.b && !member.isAbstract) {
|
||||
_setupVirtualCall(memberType.a, member);
|
||||
final memberType = types.getInferredTypeOfVirtualMember(member);
|
||||
if (_initializedVirtualMembers!.add(member)) {
|
||||
_setupVirtualCall(memberType, member);
|
||||
}
|
||||
return memberType.a;
|
||||
return memberType;
|
||||
}
|
||||
|
||||
/// Adds edges between [parent] and [override] based on the type of member
|
||||
/// each is.
|
||||
///
|
||||
/// Possible override configurations (parent/override):
|
||||
/// - field/getter
|
||||
/// - field/setter
|
||||
/// - field/field
|
||||
/// - getter/getter
|
||||
/// - getter/field
|
||||
/// - setter/setter
|
||||
/// - setter/field
|
||||
/// - method/method
|
||||
void _joinOverriddenMember(MemberEntity parent, MemberEntity override) {
|
||||
if (parent.name == Identifiers.noSuchMethod_) return;
|
||||
final parentType = _getAndSetupVirtualMember(parent);
|
||||
final overrideType = _getAndSetupVirtualMember(override);
|
||||
|
||||
_joinOverriddenMemberTypes(parent, override, parentType, overrideType);
|
||||
}
|
||||
|
||||
void _joinOverriddenMemberTypes(MemberEntity parent, MemberEntity override,
|
||||
void _addOverrideTypeInputs(MemberEntity parent, MemberEntity override,
|
||||
MemberTypeInformation parentType, MemberTypeInformation overrideType) {
|
||||
if (parent is FieldEntity) {
|
||||
if (override.isGetter) {
|
||||
|
@ -961,7 +983,7 @@ class InferrerEngine {
|
|||
parentType.addInput(overrideType);
|
||||
} else if (parent.isSetter) {
|
||||
if (override.isSetter) {
|
||||
_joinOverrideParameters(parent, override);
|
||||
_addOverrideParameterEdges(parent, override);
|
||||
} else {
|
||||
assert(override is FieldEntity);
|
||||
types.strategy.forEachParameter(parent as FunctionEntity,
|
||||
|
@ -973,7 +995,7 @@ class InferrerEngine {
|
|||
} else {
|
||||
assert(parent.isFunction && override.isFunction);
|
||||
parentType.addInput(overrideType);
|
||||
_joinOverrideParameters(parent, override);
|
||||
_addOverrideParameterEdges(parent, override);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1002,18 +1024,24 @@ class InferrerEngine {
|
|||
}
|
||||
}
|
||||
|
||||
void _registerOverridesCalled(
|
||||
DynamicCallTarget target,
|
||||
DynamicCallSiteTypeInformation callSiteType,
|
||||
ir.Node callSite,
|
||||
Set<MemberEntity> visited) {
|
||||
final member = target.member;
|
||||
IterationStep handleTarget(MemberEntity override) {
|
||||
if (!override.isAbstract) {
|
||||
MemberTypeInformation info = types.getInferredTypeOfMember(override);
|
||||
info.markCalled();
|
||||
/// Iterate through reachable members for the given target. Label relevant
|
||||
/// members as needing closurization if necessary. If [shouldMarkCalled] then
|
||||
/// also mark reachable members as called.
|
||||
void _processDynamicTarget(
|
||||
DynamicCallTarget target, DynamicCallSiteTypeInformation callSiteType,
|
||||
{required bool shouldMarkCalled}) {
|
||||
final needsClosurization =
|
||||
types.getInferredTypeOfVirtualMember(target.member).closurizedCount > 0;
|
||||
|
||||
if (types.getInferredTypeOfVirtualMember(member).closurizedCount > 0) {
|
||||
// There is nothing to do so no need to iterate over target members.
|
||||
if (!needsClosurization && !shouldMarkCalled) return;
|
||||
|
||||
IterationStep handleTarget(MemberEntity member) {
|
||||
if (!member.isAbstract) {
|
||||
MemberTypeInformation info = types.getInferredTypeOfMember(member);
|
||||
if (shouldMarkCalled) info.markCalled();
|
||||
|
||||
if (needsClosurization) {
|
||||
_markForClosurization(info, callSiteType,
|
||||
remove: false, addToQueue: false);
|
||||
}
|
||||
|
@ -1021,23 +1049,22 @@ class InferrerEngine {
|
|||
return IterationStep.CONTINUE;
|
||||
}
|
||||
|
||||
if (visited.add(member)) {
|
||||
handleTarget(member);
|
||||
}
|
||||
if (target.isVirtual) {
|
||||
memberHierarchyBuilder.forEachOverrideSkipVisited(
|
||||
member, handleTarget, visited);
|
||||
}
|
||||
memberHierarchyBuilder.forEachTargetMember(target, handleTarget);
|
||||
}
|
||||
|
||||
void _updateOverrideClosurizations({required bool markCalled}) {
|
||||
final Set<MemberEntity> visited = {};
|
||||
/// Update call information for targets of calls.
|
||||
///
|
||||
/// Marks targets of dynamic calls that need closuraization. If [shouldMarkCalled]
|
||||
/// is true any members targeted by a dynamic or static call is also marked
|
||||
/// as being called.
|
||||
void _processCalledTargets({required bool shouldMarkCalled}) {
|
||||
for (final call in types.allocatedCalls) {
|
||||
if (call is DynamicCallSiteTypeInformation) {
|
||||
for (final target in call.concreteTargets) {
|
||||
_registerOverridesCalled(target, call, call.callNode, visited);
|
||||
_processDynamicTarget(target, call,
|
||||
shouldMarkCalled: shouldMarkCalled);
|
||||
}
|
||||
} else if (markCalled && call is StaticCallSiteTypeInformation) {
|
||||
} else if (shouldMarkCalled && call is StaticCallSiteTypeInformation) {
|
||||
types.getInferredTypeOfMember(call.calledElement).markCalled();
|
||||
}
|
||||
}
|
||||
|
@ -1052,13 +1079,13 @@ class InferrerEngine {
|
|||
_defaultTypeOfParameter[parameter] = type;
|
||||
TypeInformation info = types.getInferredTypeOfParameter(parameter);
|
||||
if (existing != null && existing is PlaceholderTypeInformation) {
|
||||
// Replace references to [existing] to use [type] instead.
|
||||
info.inputs.replace(existing, type);
|
||||
// Also forward all users.
|
||||
type.addUsersOf(existing);
|
||||
TypeInformation virtualInfo =
|
||||
types.getInferredTypeOfVirtualParameter(parameter);
|
||||
// Replace references to [existing] to use [type] instead.
|
||||
info.inputs.replace(existing, type);
|
||||
virtualInfo.inputs.replace(existing, type);
|
||||
// Also forward all users.
|
||||
type.addUsersOf(existing);
|
||||
} else {
|
||||
assert(existing == null);
|
||||
}
|
||||
|
@ -1119,6 +1146,7 @@ class InferrerEngine {
|
|||
// Even if x.== doesn't return a bool, 'x == null' evaluates to 'false'.
|
||||
info.addInput(types.boolType);
|
||||
}
|
||||
|
||||
if (info.inputs.isEmpty) info.addInput(type);
|
||||
}
|
||||
|
||||
|
@ -1325,13 +1353,10 @@ class InferrerEngine {
|
|||
(callers[callSite.calledElement] ??= {}).add(callSite.caller);
|
||||
} else if (callSite is DynamicCallSiteTypeInformation) {
|
||||
for (final target in callSite.concreteTargets) {
|
||||
(callers[target.member] ??= {}).add(callSite.caller);
|
||||
if (target.isVirtual) {
|
||||
memberHierarchyBuilder.forEachOverride(target.member, (override) {
|
||||
(callers[override] ??= {}).add(callSite.caller);
|
||||
return IterationStep.CONTINUE;
|
||||
});
|
||||
}
|
||||
memberHierarchyBuilder.forEachTargetMember(target, (member) {
|
||||
(callers[target.member] ??= {}).add(callSite.caller);
|
||||
return IterationStep.CONTINUE;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ library compiler.src.inferrer.node_tracer;
|
|||
|
||||
import '../common/names.dart' show Identifiers;
|
||||
import '../elements/entities.dart';
|
||||
import '../universe/class_set.dart';
|
||||
import '../util/util.dart' show Setlet;
|
||||
import '../inferrer/abstract_value_domain.dart';
|
||||
import 'debug.dart' as debug;
|
||||
|
@ -487,8 +488,17 @@ abstract class TracerVisitor implements TypeInformationVisitor {
|
|||
|
||||
final user = currentUser;
|
||||
if (user is MemberTypeInformation) {
|
||||
if (info.callees.contains(user.member)) {
|
||||
addNewEscapeInformation(info);
|
||||
for (final target in info.concreteTargets) {
|
||||
IterationStep checkMember(MemberEntity member) {
|
||||
if (member == user.member) {
|
||||
addNewEscapeInformation(info);
|
||||
return IterationStep.STOP;
|
||||
}
|
||||
return IterationStep.CONTINUE;
|
||||
}
|
||||
|
||||
inferrer.memberHierarchyBuilder
|
||||
.forEachTargetMember(target, checkMember);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1116,7 +1116,7 @@ class DynamicCallSiteTypeInformation<T extends ir.Node>
|
|||
|
||||
DynamicCallSiteTypeInformation(
|
||||
super.abstractValueDomain,
|
||||
super.ontext,
|
||||
super.context,
|
||||
this._callType,
|
||||
super.callNode,
|
||||
super.enclosing,
|
||||
|
|
|
@ -9,7 +9,6 @@ import '../elements/entities.dart';
|
|||
import '../elements/types.dart';
|
||||
import '../inferrer/abstract_value_domain.dart';
|
||||
import '../js_model/js_world.dart';
|
||||
import '../util/util.dart';
|
||||
import 'type_graph_nodes.dart';
|
||||
|
||||
/// Strategy for creating type information from members and parameters and type
|
||||
|
@ -351,17 +350,9 @@ class TypeSystem {
|
|||
return memberTypeInformations[member] ??= _getInferredTypeOfMember(member);
|
||||
}
|
||||
|
||||
Pair<MemberTypeInformation, bool> getCachedOrInferredTypeOfVirtualMember(
|
||||
MemberEntity member) {
|
||||
final cached = virtualCallTypeInformations[member];
|
||||
if (cached != null) return Pair(cached, false);
|
||||
final newType = virtualCallTypeInformations[member] =
|
||||
strategy.createMemberTypeInformation(_abstractValueDomain, member);
|
||||
return Pair(newType, true);
|
||||
}
|
||||
|
||||
MemberTypeInformation getInferredTypeOfVirtualMember(MemberEntity member) {
|
||||
return getCachedOrInferredTypeOfVirtualMember(member).a;
|
||||
return virtualCallTypeInformations[member] ??=
|
||||
strategy.createMemberTypeInformation(_abstractValueDomain, member);
|
||||
}
|
||||
|
||||
void forEachMemberType(
|
||||
|
|
|
@ -706,18 +706,18 @@ class FlatTypeMask extends TypeMask {
|
|||
Iterable<DynamicCallTarget> findRootsOfTargets(Selector selector,
|
||||
MemberHierarchyBuilder memberHierarchyBuilder, JClosedWorld closedWorld) {
|
||||
if (isEmptyOrFlagged) return const [];
|
||||
final baseCls = base!;
|
||||
if (closedWorld.isDefaultSuperclass(baseCls)) {
|
||||
final baseClass = base!;
|
||||
if (closedWorld.isDefaultSuperclass(baseClass)) {
|
||||
// Filter roots using the mask's class since each default superclass has
|
||||
// distinct roots.
|
||||
final results =
|
||||
memberHierarchyBuilder.rootsForSelector(baseCls, selector);
|
||||
memberHierarchyBuilder.rootsForSelector(baseClass, selector);
|
||||
return results.isEmpty ? const [] : results;
|
||||
}
|
||||
|
||||
// Try to find a superclass that contains a matching member.
|
||||
final superclassMatch = memberHierarchyBuilder.findSuperclassTarget(
|
||||
baseCls, selector,
|
||||
baseClass, selector,
|
||||
isExact: isExact, isSubclass: isSubclass);
|
||||
|
||||
// If this mask is exact then we should have found a matching target on a
|
||||
|
@ -727,7 +727,7 @@ class FlatTypeMask extends TypeMask {
|
|||
|
||||
// Default to a list of superclasses/supertypes that encompasses all
|
||||
// subclasses/subtypes of this type cone.
|
||||
return memberHierarchyBuilder.findMatchingAncestors(baseCls, selector,
|
||||
return memberHierarchyBuilder.findMatchingAncestors(baseClass, selector,
|
||||
isSubtype: isSubtype);
|
||||
}
|
||||
|
||||
|
|
|
@ -439,7 +439,7 @@ class RecordTypeMask extends TypeMask {
|
|||
final recordClass = _classForRecord(closedWorld);
|
||||
return memberHierarchyBuilder.rootsForCall(
|
||||
recordClass != null
|
||||
? FlatTypeMask.nonNullSubclass(recordClass, closedWorld)
|
||||
? closedWorld.abstractValueDomain.createNonNullSubclass(recordClass)
|
||||
: null,
|
||||
selector);
|
||||
}
|
||||
|
|
|
@ -116,14 +116,37 @@ class MemberHierarchyBuilder {
|
|||
|
||||
/// Applies [f] to each override of [entity].
|
||||
///
|
||||
/// If [f] returns `true` for a given input then its children are also
|
||||
/// visited.
|
||||
/// If [f] returns [IterationStep.CONTINUE] for a given input then that
|
||||
/// member's overrides are also visited. If [f] returns for a member
|
||||
/// [IterationStep.SKIP_SUBCLASSES] then the overrides of that member are
|
||||
/// skipped. If [f] returns [IterationStep.STOP] then iteration is immediately
|
||||
/// stopped and [f] is not called on any more members.
|
||||
void forEachOverride(
|
||||
MemberEntity entity, IterationStep Function(MemberEntity override) f) {
|
||||
forEachOverrideSkipVisited(entity, f, {entity});
|
||||
_forEachOverrideSkipVisited(entity, f, {entity});
|
||||
}
|
||||
|
||||
void forEachOverrideSkipVisited(
|
||||
/// Applies [f] to each target represented by [target] including overrides
|
||||
/// if the call is virtual.
|
||||
///
|
||||
/// If [f] returns [IterationStep.CONTINUE] for a given input then that
|
||||
/// member's overrides are also visited. If [f] returns for a member
|
||||
/// [IterationStep.SKIP_SUBCLASSES] then the overrides of that member are
|
||||
/// skipped. If [f] returns [IterationStep.STOP] then iteration is immediately
|
||||
/// stopped and [f] is not called on any more members.
|
||||
void forEachTargetMember(DynamicCallTarget target,
|
||||
IterationStep Function(MemberEntity override) f) {
|
||||
final initialResult = f(target.member);
|
||||
if (initialResult == IterationStep.STOP ||
|
||||
initialResult == IterationStep.SKIP_SUBCLASSES) {
|
||||
return;
|
||||
}
|
||||
if (target.isVirtual) {
|
||||
forEachOverride(target.member, f);
|
||||
}
|
||||
}
|
||||
|
||||
void _forEachOverrideSkipVisited(
|
||||
MemberEntity entity,
|
||||
IterationStep Function(MemberEntity override) f,
|
||||
Set<MemberEntity> visited) {
|
||||
|
@ -134,7 +157,7 @@ class MemberHierarchyBuilder {
|
|||
final result = f(override);
|
||||
if (result == IterationStep.SKIP_SUBCLASSES) continue;
|
||||
if (result == IterationStep.STOP) return;
|
||||
forEachOverrideSkipVisited(override, f, visited);
|
||||
_forEachOverrideSkipVisited(override, f, visited);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue