diff --git a/pkg/compiler/lib/src/inferrer/abstract_value_domain.dart b/pkg/compiler/lib/src/inferrer/abstract_value_domain.dart index 77a6fa68a9d..171a91d4589 100644 --- a/pkg/compiler/lib/src/inferrer/abstract_value_domain.dart +++ b/pkg/compiler/lib/src/inferrer/abstract_value_domain.dart @@ -631,7 +631,7 @@ abstract class AbstractValueDomain { /// Returns a set of members that are ancestors of all possible targets for /// a call targeting [selector] on an entity with type represented by /// [receiver]. - Iterable findRootsOfTargets(AbstractValue receiver, + Iterable findRootsOfTargets(AbstractValue receiver, Selector selector, MemberHierarchyBuilder memberHierarchyBuilder); /// Deserializes an [AbstractValue] for this domain from [source]. diff --git a/pkg/compiler/lib/src/inferrer/computable.dart b/pkg/compiler/lib/src/inferrer/computable.dart index 75d3440e77e..fe60cf63a7d 100644 --- a/pkg/compiler/lib/src/inferrer/computable.dart +++ b/pkg/compiler/lib/src/inferrer/computable.dart @@ -590,7 +590,7 @@ class ComputableAbstractValueDomain with AbstractValueDomain { _wrappedDomain.isFixedLengthJsIndexable(_unwrap(value)); @override - Iterable findRootsOfTargets(AbstractValue receiver, + Iterable findRootsOfTargets(AbstractValue receiver, Selector selector, MemberHierarchyBuilder memberHierarchyBuilder) { return _wrappedDomain.findRootsOfTargets( receiver, selector, memberHierarchyBuilder); diff --git a/pkg/compiler/lib/src/inferrer/powersets/powersets.dart b/pkg/compiler/lib/src/inferrer/powersets/powersets.dart index 425ff9e9e9f..7c144e6a08b 100644 --- a/pkg/compiler/lib/src/inferrer/powersets/powersets.dart +++ b/pkg/compiler/lib/src/inferrer/powersets/powersets.dart @@ -809,8 +809,10 @@ class PowersetDomain with AbstractValueDomain { _abstractValueDomain.typeType, _powersetBitsDomain.typeType); @override - Iterable findRootsOfTargets(covariant PowersetValue receiver, - Selector selector, MemberHierarchyBuilder memberHierarchyBuilder) => + Iterable findRootsOfTargets( + covariant PowersetValue receiver, + Selector selector, + MemberHierarchyBuilder memberHierarchyBuilder) => _abstractValueDomain.findRootsOfTargets( receiver.abstractValue, selector, memberHierarchyBuilder); } diff --git a/pkg/compiler/lib/src/inferrer/trivial.dart b/pkg/compiler/lib/src/inferrer/trivial.dart index eef7b853b2c..1331647c98e 100644 --- a/pkg/compiler/lib/src/inferrer/trivial.dart +++ b/pkg/compiler/lib/src/inferrer/trivial.dart @@ -370,7 +370,7 @@ class TrivialAbstractValueDomain with AbstractValueDomain { } @override - Iterable findRootsOfTargets( + Iterable findRootsOfTargets( covariant TrivialAbstractValue receiver, Selector selector, MemberHierarchyBuilder memberHierarchyBuilder) => diff --git a/pkg/compiler/lib/src/inferrer/typemasks/masks.dart b/pkg/compiler/lib/src/inferrer/typemasks/masks.dart index 9d422c20548..a1565c50a3a 100644 --- a/pkg/compiler/lib/src/inferrer/typemasks/masks.dart +++ b/pkg/compiler/lib/src/inferrer/typemasks/masks.dart @@ -926,7 +926,7 @@ class CommonMasks with AbstractValueDomain { } @override - Iterable findRootsOfTargets(covariant TypeMask receiver, + Iterable findRootsOfTargets(covariant TypeMask receiver, Selector selector, MemberHierarchyBuilder memberHierarchyBuilder) { return const []; } diff --git a/pkg/compiler/lib/src/inferrer/wrapped.dart b/pkg/compiler/lib/src/inferrer/wrapped.dart index 7c8bb73239c..1bfc711c761 100644 --- a/pkg/compiler/lib/src/inferrer/wrapped.dart +++ b/pkg/compiler/lib/src/inferrer/wrapped.dart @@ -602,7 +602,7 @@ class WrappedAbstractValueDomain with AbstractValueDomain { WrappedAbstractValue(_abstractValueDomain.typeType); @override - Iterable findRootsOfTargets( + Iterable findRootsOfTargets( covariant WrappedAbstractValue receiver, Selector selector, MemberHierarchyBuilder memberHierarchyBuilder) => diff --git a/pkg/compiler/lib/src/inferrer_experimental/closure_tracer.dart b/pkg/compiler/lib/src/inferrer_experimental/closure_tracer.dart index bd5372d5044..0ad8c8641ee 100644 --- a/pkg/compiler/lib/src/inferrer_experimental/closure_tracer.dart +++ b/pkg/compiler/lib/src/inferrer_experimental/closure_tracer.dart @@ -119,16 +119,16 @@ class ClosureTracerVisitor extends TracerVisitor { if (selector.isCall) { if (info.arguments!.contains(user)) { if (info.hasClosureCallTargets || - info.concreteTargets.any((element) => !element.isFunction)) { + info.callees.any((element) => !element.isFunction)) { bailout('Passed to a closure'); } - if (info.concreteTargets.any(_checkIfFunctionApply)) { + if (info.callees.any(_checkIfFunctionApply)) { _tagAsFunctionApplyTarget("dynamic call"); } } else { if (user is MemberTypeInformation) { final currentUserMember = user.member; - if (info.concreteTargets.contains(currentUserMember)) { + if (info.callees.contains(currentUserMember)) { _registerCallForLaterAnalysis(info); } } diff --git a/pkg/compiler/lib/src/inferrer_experimental/engine.dart b/pkg/compiler/lib/src/inferrer_experimental/engine.dart index cb9e1a07cc8..221397be9a9 100644 --- a/pkg/compiler/lib/src/inferrer_experimental/engine.dart +++ b/pkg/compiler/lib/src/inferrer_experimental/engine.dart @@ -478,7 +478,7 @@ class InferrerEngine { if (info.hasClosureCallTargets) { print(''); } - for (MemberEntity target in info.concreteTargets) { + for (MemberEntity target in info.callees) { if (target is FunctionEntity) { print('${types.getInferredSignatureOfMethod(target)} ' 'for ${target}'); @@ -761,14 +761,19 @@ class InferrerEngine { /// Update the inputs to parameters in the graph. [remove] tells whether /// inputs must be added or removed. If [addToQueue] is `true`, parameters are /// added to the work queue. Returns `true` if the call requires [callee] to - /// be closurized. + /// be closurized. If [virtualCall] is `true` inputs are added and removed + /// from the virtual types for [callee]. bool updateParameterInputs(TypeInformation callSiteType, MemberEntity callee, ArgumentsTypes? arguments, Selector? selector, - {required bool remove, required bool addToQueue}) { + {required bool remove, + required bool addToQueue, + bool virtualCall = false}) { if (callee.name == Identifiers.noSuchMethod_) return false; if (callee is FieldEntity) { if (selector!.isSetter) { - ElementTypeInformation info = types.getInferredTypeOfMember(callee); + ElementTypeInformation info = virtualCall + ? types.getInferredTypeOfVirtualMember(callee) + : types.getInferredTypeOfMember(callee); if (remove) { info.removeInput(arguments!.positional[0]); } else { @@ -781,7 +786,9 @@ class InferrerEngine { } else if (selector != null && selector.isGetter) { // We are tearing a function off and thus create a closure. assert(callee.isFunction); - final memberInfo = types.getInferredTypeOfMember(callee); + final memberInfo = virtualCall + ? types.getInferredTypeOfVirtualMember(callee) + : types.getInferredTypeOfMember(callee); _markForClosurization(memberInfo, callSiteType, remove: remove, addToQueue: addToQueue); return true; @@ -800,7 +807,9 @@ class InferrerEngine { type = localArguments.positional[parameterIndex]; } if (type == null) type = getDefaultTypeOfParameter(parameter); - TypeInformation info = types.getInferredTypeOfParameter(parameter); + TypeInformation info = virtualCall + ? types.getInferredTypeOfVirtualParameter(parameter) + : types.getInferredTypeOfParameter(parameter); if (remove) { info.removeInput(type); } else { @@ -813,6 +822,29 @@ class InferrerEngine { return false; } + void _setupVirtualCall( + MemberTypeInformation virtualCallType, MemberEntity member) { + if (member is FieldEntity || member.isGetter) { + final realMember = types.getInferredTypeOfMember(member); + virtualCallType.addInput(realMember); + if (member.isAssignable) { + realMember.addInput(virtualCallType); + } + } else { + assert(member.isSetter || member.isFunction); + types.strategy.forEachParameter(member as FunctionEntity, + (Local parameter) { + final virtualParamInfo = + types.getInferredTypeOfVirtualParameter(parameter); + final realParamInfo = types.getInferredTypeOfParameter(parameter); + realParamInfo.addInput(virtualParamInfo); + }); + if (member.isFunction) { + virtualCallType.addInput(types.getInferredTypeOfMember(member)); + } + } + } + void _joinOverrideParameters(MemberEntity parent, MemberEntity override) { final method = parent as FunctionEntity; ParameterStructure parameterStructure = method.parameterStructure; @@ -821,7 +853,7 @@ class InferrerEngine { final List positional = []; final Map named = {}; types.strategy.forEachParameter(parent, (Local parameter) { - TypeInformation type = types.getInferredTypeOfParameter(parameter); + TypeInformation type = types.getInferredTypeOfVirtualParameter(parameter); if (parameterIndex < parameterStructure.requiredPositionalParameters) { positional.add(type); } else if (parameterStructure.namedParameters.isNotEmpty) { @@ -850,12 +882,20 @@ class InferrerEngine { // default value will be used within the body of the override. parentParamInfo ??= getDefaultTypeOfParameter(parameter); TypeInformation overrideParamInfo = - types.getInferredTypeOfParameter(parameter); + types.getInferredTypeOfVirtualParameter(parameter); overrideParamInfo.addInput(parentParamInfo); parameterIndex++; }); } + MemberTypeInformation _getAndSetupVirtualMember(MemberEntity member) { + final memberType = types.getCachedOrInferredTypeOfVirtualMember(member); + if (memberType.b && !member.isAbstract) { + _setupVirtualCall(memberType.a, member); + } + return memberType.a; + } + /// Adds edges between [parent] and [override] based on the type of member /// each is. /// @@ -870,15 +910,21 @@ class InferrerEngine { /// - method/method void _joinOverriddenMember(MemberEntity parent, MemberEntity override) { if (parent.name == Identifiers.noSuchMethod_) return; - final parentType = types.getInferredTypeOfMember(parent); - final overrideType = types.getInferredTypeOfMember(override); + final parentType = _getAndSetupVirtualMember(parent); + final overrideType = _getAndSetupVirtualMember(override); + + _joinOverriddenMemberTypes(parent, override, parentType, overrideType); + } + + void _joinOverriddenMemberTypes(MemberEntity parent, MemberEntity override, + MemberTypeInformation parentType, MemberTypeInformation overrideType) { if (parent is FieldEntity) { if (override.isGetter) { parentType.addInput(overrideType); } else if (override.isSetter) { types.strategy.forEachParameter(override as FunctionEntity, (Local parameter) { - final paramInfo = types.getInferredTypeOfParameter(parameter); + final paramInfo = types.getInferredTypeOfVirtualParameter(parameter); paramInfo.addInput(parentType); }); } else { @@ -937,12 +983,13 @@ class InferrerEngine { } void _registerOverridesCalled( - MemberEntity callee, + DynamicCallTarget target, DynamicCallSiteTypeInformation callSiteType, ir.Node? callSite, - Set visited, - {required bool isClosurized}) { - memberHierarchyBuilder.forEachOverride(callee, (override) { + Set visited) { + final member = target.member; + final isClosurized = callSiteType.closurizedTargets.contains(member); + void handleTarget(MemberEntity override) { if (override.isAbstract || !visited.add(override)) return; MemberTypeInformation info = types.getInferredTypeOfMember(override); info.addCall(callSiteType.caller, callSite); @@ -951,17 +998,20 @@ class InferrerEngine { _markForClosurization(info, callSiteType, remove: false, addToQueue: false); } - }); + } + + handleTarget(member); + if (target.isVirtual) { + memberHierarchyBuilder.forEachOverride(member, handleTarget); + } } void _updateOverrideClosurizations() { final Set visited = {}; for (final call in types.allocatedCalls) { if (call is! DynamicCallSiteTypeInformation) continue; - for (final target in call.callees) { - if (!visited.add(target)) continue; - _registerOverridesCalled(target, call, null, visited, - isClosurized: call.closurizedTargets.contains(target)); + for (final target in call.concreteTargets) { + _registerOverridesCalled(target, call, null, visited); } } } @@ -979,6 +1029,9 @@ class InferrerEngine { info.inputs.replace(existing, type); // Also forward all users. type.addUsersOf(existing); + TypeInformation virtualInfo = + types.getInferredTypeOfVirtualParameter(parameter); + virtualInfo.inputs.replace(existing, type); } else { assert(existing == null); } @@ -1003,16 +1056,28 @@ class InferrerEngine { return types.getInferredTypeOfParameter(element); } + MemberTypeInformation _inferredTypeOfMember(MemberEntity element, + {required bool isVirtual}) { + return isVirtual + ? types.getInferredTypeOfVirtualMember(element) + : types.getInferredTypeOfMember(element); + } + + MemberTypeInformation inferredTypeOfTarget(DynamicCallTarget target) { + return _inferredTypeOfMember(target.member, isVirtual: target.isVirtual); + } + /// Returns the type of [element]. - TypeInformation typeOfMember(MemberEntity element) { + TypeInformation typeOfMember(MemberEntity element, {bool isVirtual = false}) { if (element is FunctionEntity) return types.functionType; - return types.getInferredTypeOfMember(element); + return _inferredTypeOfMember(element, isVirtual: isVirtual); } /// Returns the return type of [element]. - TypeInformation returnTypeOfMember(MemberEntity element) { + TypeInformation returnTypeOfMember(MemberEntity element, + {bool isVirtual = false}) { if (element is! FunctionEntity) return types.dynamicType; - return types.getInferredTypeOfMember(element); + return _inferredTypeOfMember(element, isVirtual: isVirtual); } /// Records that [element] is of type [type]. @@ -1236,7 +1301,8 @@ class InferrerEngine { /// Returns the type of [element] when being called with [selector]. TypeInformation typeOfMemberWithSelector( - MemberEntity element, Selector? selector) { + MemberEntity element, Selector? selector, + {required bool isVirtual}) { if (element.name == Identifiers.noSuchMethod_ && selector!.name != element.name) { // An invocation can resolve to a [noSuchMethod], in which case @@ -1246,9 +1312,9 @@ class InferrerEngine { if (element.isFunction) { return types.functionType; } else if (element is FieldEntity) { - return typeOfMember(element); + return typeOfMember(element, isVirtual: isVirtual); } else if (element.isGetter) { - return returnTypeOfMember(element); + return returnTypeOfMember(element, isVirtual: isVirtual); } else { assert(false, failedAt(element, "Unexpected member $element")); return types.dynamicType; @@ -1257,7 +1323,7 @@ class InferrerEngine { assert(selector.isCall || selector.isSetter); return types.dynamicType; } else { - return returnTypeOfMember(element); + return returnTypeOfMember(element, isVirtual: isVirtual); } } diff --git a/pkg/compiler/lib/src/inferrer_experimental/list_tracer.dart b/pkg/compiler/lib/src/inferrer_experimental/list_tracer.dart index 6124abfb85f..efcb7d31f04 100644 --- a/pkg/compiler/lib/src/inferrer_experimental/list_tracer.dart +++ b/pkg/compiler/lib/src/inferrer_experimental/list_tracer.dart @@ -213,7 +213,7 @@ class ListTracerVisitor extends TracerVisitor { } } else if (selector.isCall && (info.hasClosureCallTargets || - info.concreteTargets.any((element) => !element.isFunction))) { + info.callees.any((element) => !element.isFunction))) { bailout('Passed to a closure'); return; } diff --git a/pkg/compiler/lib/src/inferrer_experimental/map_tracer.dart b/pkg/compiler/lib/src/inferrer_experimental/map_tracer.dart index fe3bf6bb324..85c8ecc4e62 100644 --- a/pkg/compiler/lib/src/inferrer_experimental/map_tracer.dart +++ b/pkg/compiler/lib/src/inferrer_experimental/map_tracer.dart @@ -126,7 +126,7 @@ class MapTracerVisitor extends TracerVisitor { } } else if (selector.isCall && (info.hasClosureCallTargets || - info.concreteTargets.any((element) => !element.isFunction))) { + info.callees.any((element) => !element.isFunction))) { bailout('Passed to a closure'); return; } diff --git a/pkg/compiler/lib/src/inferrer_experimental/node_tracer.dart b/pkg/compiler/lib/src/inferrer_experimental/node_tracer.dart index 15554576a7d..3767b2868c7 100644 --- a/pkg/compiler/lib/src/inferrer_experimental/node_tracer.dart +++ b/pkg/compiler/lib/src/inferrer_experimental/node_tracer.dart @@ -475,7 +475,7 @@ abstract class TracerVisitor implements TypeInformationVisitor { final user = currentUser; if (user is MemberTypeInformation) { - if (info.concreteTargets.contains(user.member)) { + if (info.callees.contains(user.member)) { addNewEscapeInformation(info); } } diff --git a/pkg/compiler/lib/src/inferrer_experimental/powersets/powersets.dart b/pkg/compiler/lib/src/inferrer_experimental/powersets/powersets.dart index 0f06384b2ae..dd73c52bc42 100644 --- a/pkg/compiler/lib/src/inferrer_experimental/powersets/powersets.dart +++ b/pkg/compiler/lib/src/inferrer_experimental/powersets/powersets.dart @@ -809,8 +809,10 @@ class PowersetDomain with AbstractValueDomain { _abstractValueDomain.typeType, _powersetBitsDomain.typeType); @override - Iterable findRootsOfTargets(covariant PowersetValue receiver, - Selector selector, MemberHierarchyBuilder memberHierarchyBuilder) => + Iterable findRootsOfTargets( + covariant PowersetValue receiver, + Selector selector, + MemberHierarchyBuilder memberHierarchyBuilder) => _abstractValueDomain.findRootsOfTargets( receiver.abstractValue, selector, memberHierarchyBuilder); } diff --git a/pkg/compiler/lib/src/inferrer_experimental/set_tracer.dart b/pkg/compiler/lib/src/inferrer_experimental/set_tracer.dart index 817a3467a3a..d2d40e07a8f 100644 --- a/pkg/compiler/lib/src/inferrer_experimental/set_tracer.dart +++ b/pkg/compiler/lib/src/inferrer_experimental/set_tracer.dart @@ -123,7 +123,7 @@ class SetTracerVisitor extends TracerVisitor { } } else if (selector.isCall && (info.hasClosureCallTargets || - info.concreteTargets.any((element) => !element.isFunction))) { + info.callees.any((element) => !element.isFunction))) { bailout('Passed to a closure'); return; } diff --git a/pkg/compiler/lib/src/inferrer_experimental/trivial.dart b/pkg/compiler/lib/src/inferrer_experimental/trivial.dart index de0c93dcfd6..cf975f15926 100644 --- a/pkg/compiler/lib/src/inferrer_experimental/trivial.dart +++ b/pkg/compiler/lib/src/inferrer_experimental/trivial.dart @@ -370,7 +370,7 @@ class TrivialAbstractValueDomain with AbstractValueDomain { } @override - Iterable findRootsOfTargets( + Iterable findRootsOfTargets( covariant TrivialAbstractValue receiver, Selector selector, MemberHierarchyBuilder memberHierarchyBuilder) => diff --git a/pkg/compiler/lib/src/inferrer_experimental/type_graph_nodes.dart b/pkg/compiler/lib/src/inferrer_experimental/type_graph_nodes.dart index d04d73e1a25..05acc1d5465 100644 --- a/pkg/compiler/lib/src/inferrer_experimental/type_graph_nodes.dart +++ b/pkg/compiler/lib/src/inferrer_experimental/type_graph_nodes.dart @@ -13,6 +13,7 @@ import '../constants/values.dart'; import '../elements/entities.dart'; import '../elements/types.dart'; import '../js_model/js_world.dart' show JClosedWorld; +import '../universe/member_hierarchy.dart'; import '../universe/selector.dart' show Selector; import '../util/util.dart' show Setlet; import '../inferrer/abstract_value_domain.dart'; @@ -1018,7 +1019,8 @@ class StaticCallSiteTypeInformation extends CallSiteTypeInformation { } TypeInformation _getCalledTypeInfoWithSelector(InferrerEngine inferrer) { - return inferrer.typeOfMemberWithSelector(calledElement, selector); + return inferrer.typeOfMemberWithSelector(calledElement, selector, + isVirtual: false); } @override @@ -1067,7 +1069,7 @@ class DynamicCallSiteTypeInformation bool? _hasClosureCallTargets; /// Cached concrete targets of this call. - Iterable? _concreteTargets; + Iterable? _concreteTargets; /// Recomputed when _concreteTargets changes. bool? _targetsIncludeComplexNoSuchMethod; @@ -1097,10 +1099,9 @@ class DynamicCallSiteTypeInformation late final Set closurizedTargets = {}; - void _handleCalledTarget(MemberEntity target, InferrerEngine inferrer, + void _handleCalledTarget(DynamicCallTarget target, InferrerEngine inferrer, {required bool addToQueue, required bool remove}) { - MemberTypeInformation targetType = - inferrer.types.getInferredTypeOfMember(target); + MemberTypeInformation targetType = inferrer.inferredTypeOfTarget(target); if (remove) { _removeCall(targetType); targetType.removeUser(this); @@ -1108,14 +1109,15 @@ class DynamicCallSiteTypeInformation _addCall(targetType); targetType.addUser(this); } + final member = target.member; final isClosurized = inferrer.updateParameterInputs( - this, target, arguments, selector, - addToQueue: addToQueue, remove: remove); + this, member, arguments, selector, + addToQueue: addToQueue, remove: remove, virtualCall: target.isVirtual); if (isClosurized) { if (remove) { - closurizedTargets.remove(target); + closurizedTargets.remove(member); } else { - closurizedTargets.add(target); + closurizedTargets.add(member); } } } @@ -1144,10 +1146,18 @@ class DynamicCallSiteTypeInformation /// All concrete targets of this invocation. If [hasClosureCallTargets] is /// `true` the invocation can additional target an unknown set of 'call' /// methods on closures. - Iterable get concreteTargets => _concreteTargets!; + Iterable get concreteTargets => _concreteTargets!; @override - Iterable get callees => concreteTargets; + Iterable get callees => + _callees ??= concreteTargets.map((e) => e.member).toSet(); + + Iterable? _callees; + + void updateTargets(Iterable targets) { + _concreteTargets = targets; + _callees = null; + } AbstractValue? computeTypedSelector(InferrerEngine inferrer) { AbstractValue receiverType = receiver.type; @@ -1161,8 +1171,7 @@ class DynamicCallSiteTypeInformation } bool targetsIncludeComplexNoSuchMethod(InferrerEngine inferrer) { - return _targetsIncludeComplexNoSuchMethod ??= - _concreteTargets!.any((MemberEntity e) { + return _targetsIncludeComplexNoSuchMethod ??= callees.any((MemberEntity e) { return e.isFunction && e.isInstanceMember && e.name == Identifiers.noSuchMethod_ && @@ -1298,14 +1307,14 @@ class DynamicCallSiteTypeInformation // Add calls to new targets to the graph. concreteTargets .where((target) => !oldTargets.contains(target)) - .forEach((MemberEntity target) { + .forEach((DynamicCallTarget target) { _handleCalledTarget(target, inferrer, addToQueue: true, remove: false); }); // Walk over the old targets, and remove calls that cannot happen anymore. oldTargets .where((target) => !concreteTargets.contains(target)) - .forEach((MemberEntity target) { + .forEach((DynamicCallTarget target) { _handleCalledTarget(target, inferrer, addToQueue: true, remove: true); }); } @@ -1317,7 +1326,8 @@ class DynamicCallSiteTypeInformation result = abstractValueDomain.dynamicType; } else { result = inferrer.types - .joinTypeMasks(concreteTargets.map((MemberEntity element) { + .joinTypeMasks(concreteTargets.map((DynamicCallTarget target) { + final element = target.member; if (typeMask != null && inferrer.returnsListElementType(localSelector, typeMask)) { return abstractValueDomain.getContainerElementType(receiver.type); @@ -1355,7 +1365,10 @@ class DynamicCallSiteTypeInformation final info = handleIntrisifiedSelector(localSelector, typeMask, inferrer); if (info != null) return info.type; - return inferrer.typeOfMemberWithSelector(element, selector).type; + return inferrer + .typeOfMemberWithSelector(element, selector, + isVirtual: target.isVirtual) + .type; } })); } @@ -1381,7 +1394,7 @@ class DynamicCallSiteTypeInformation final newConcreteTargets = _concreteTargets = inferrer.memberHierarchyBuilder.rootsForCall(mask, localSelector); _targetsIncludeComplexNoSuchMethod = null; - for (MemberEntity target in newConcreteTargets) { + for (final target in newConcreteTargets) { if (!oldTargets.contains(target)) { _handleCalledTarget(target, inferrer, addToQueue: true, remove: false); @@ -1393,7 +1406,7 @@ class DynamicCallSiteTypeInformation @override void removeAndClearReferences(InferrerEngine inferrer) { - for (MemberEntity element in concreteTargets) { + for (MemberEntity element in callees) { MemberTypeInformation callee = inferrer.types.getInferredTypeOfMember(element); callee.removeUser(this); @@ -1415,7 +1428,7 @@ class DynamicCallSiteTypeInformation @override bool hasStableType(InferrerEngine inferrer) { return receiver.isStable && - concreteTargets.every((MemberEntity element) => + callees.every((MemberEntity element) => inferrer.types.getInferredTypeOfMember(element).isStable) && (arguments == null || arguments!.every((info) => info.isStable)) && super.hasStableType(inferrer); diff --git a/pkg/compiler/lib/src/inferrer_experimental/type_system.dart b/pkg/compiler/lib/src/inferrer_experimental/type_system.dart index 68d1039352c..80575d92736 100644 --- a/pkg/compiler/lib/src/inferrer_experimental/type_system.dart +++ b/pkg/compiler/lib/src/inferrer_experimental/type_system.dart @@ -9,6 +9,7 @@ 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 @@ -63,6 +64,11 @@ class TypeSystem { final Map memberTypeInformations = Map(); + final Map virtualParameterTypeInformations = + Map(); + final Map virtualCallTypeInformations = + Map(); + /// [ListTypeInformation] for allocated lists. final Map allocatedLists = Map(); @@ -325,6 +331,16 @@ class TypeSystem { }); } + ParameterTypeInformation getInferredTypeOfVirtualParameter(Local parameter) { + return virtualParameterTypeInformations.putIfAbsent(parameter, () { + ParameterTypeInformation typeInformation = + strategy.createParameterTypeInformation( + _abstractValueDomain, parameter, this); + _orderedTypeInformations.add(typeInformation); + return typeInformation; + }); + } + void forEachParameterType( void f(Local parameter, ParameterTypeInformation typeInformation)) { parameterTypeInformations.forEach(f); @@ -334,6 +350,19 @@ class TypeSystem { return memberTypeInformations[member] ??= _getInferredTypeOfMember(member); } + Pair 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; + } + void forEachMemberType( void f(MemberEntity member, MemberTypeInformation typeInformation)) { memberTypeInformations.forEach(f); diff --git a/pkg/compiler/lib/src/inferrer_experimental/typemasks/flat_type_mask.dart b/pkg/compiler/lib/src/inferrer_experimental/typemasks/flat_type_mask.dart index 7804f55362a..60254bcbbd7 100644 --- a/pkg/compiler/lib/src/inferrer_experimental/typemasks/flat_type_mask.dart +++ b/pkg/compiler/lib/src/inferrer_experimental/typemasks/flat_type_mask.dart @@ -703,7 +703,7 @@ class FlatTypeMask extends TypeMask { } @override - Iterable findRootsOfTargets(Selector selector, + Iterable findRootsOfTargets(Selector selector, MemberHierarchyBuilder memberHierarchyBuilder, JClosedWorld closedWorld) { if (isEmptyOrFlagged) return const []; final baseCls = base!; @@ -718,15 +718,16 @@ class FlatTypeMask extends TypeMask { if (isExact) { // Return the member that declares the selector on the mask's class // (check only superclasses). - final match = - memberHierarchyBuilder.findMatchInSuperclass(baseCls, selector); + final match = memberHierarchyBuilder + .findSuperclassTarget(baseCls, selector, virtualResult: false); return match != null ? [match] : const []; } if (isSubclass) { // Try to find a superclass that contains a matching member. - final superclassMatch = - memberHierarchyBuilder.findMatchInSuperclass(baseCls, selector); + final superclassMatch = memberHierarchyBuilder + .findSuperclassTarget(baseCls, selector, virtualResult: true); + if (superclassMatch != null) return [superclassMatch]; } diff --git a/pkg/compiler/lib/src/inferrer_experimental/typemasks/forwarding_type_mask.dart b/pkg/compiler/lib/src/inferrer_experimental/typemasks/forwarding_type_mask.dart index 6809ce46f4d..a409e0fe6b9 100644 --- a/pkg/compiler/lib/src/inferrer_experimental/typemasks/forwarding_type_mask.dart +++ b/pkg/compiler/lib/src/inferrer_experimental/typemasks/forwarding_type_mask.dart @@ -136,7 +136,7 @@ abstract class ForwardingTypeMask extends TypeMask { } @override - Iterable findRootsOfTargets(Selector selector, + Iterable findRootsOfTargets(Selector selector, MemberHierarchyBuilder memberHierarchyBuilder, JClosedWorld closedWorld) { return forwardTo.findRootsOfTargets( selector, memberHierarchyBuilder, closedWorld); diff --git a/pkg/compiler/lib/src/inferrer_experimental/typemasks/masks.dart b/pkg/compiler/lib/src/inferrer_experimental/typemasks/masks.dart index 8b39e71d149..ac79c09dbcf 100644 --- a/pkg/compiler/lib/src/inferrer_experimental/typemasks/masks.dart +++ b/pkg/compiler/lib/src/inferrer_experimental/typemasks/masks.dart @@ -926,7 +926,7 @@ class CommonMasks with AbstractValueDomain { } @override - Iterable findRootsOfTargets(covariant TypeMask receiver, + Iterable findRootsOfTargets(covariant TypeMask receiver, Selector selector, MemberHierarchyBuilder memberHierarchyBuilder) { return receiver.findRootsOfTargets( selector, memberHierarchyBuilder, _closedWorld); diff --git a/pkg/compiler/lib/src/inferrer_experimental/typemasks/type_mask.dart b/pkg/compiler/lib/src/inferrer_experimental/typemasks/type_mask.dart index 4645097c2d8..71a5173a7a7 100644 --- a/pkg/compiler/lib/src/inferrer_experimental/typemasks/type_mask.dart +++ b/pkg/compiler/lib/src/inferrer_experimental/typemasks/type_mask.dart @@ -427,6 +427,6 @@ abstract class TypeMask implements AbstractValue { /// Returns a set of members that are ancestors of all possible targets for /// a call targeting [selector] on a receiver with the type represented by /// this mask. - Iterable findRootsOfTargets(Selector selector, + Iterable findRootsOfTargets(Selector selector, MemberHierarchyBuilder memberHierarchyBuilder, JClosedWorld closedWorld); } diff --git a/pkg/compiler/lib/src/inferrer_experimental/typemasks/union_type_mask.dart b/pkg/compiler/lib/src/inferrer_experimental/typemasks/union_type_mask.dart index c00a9b6cb17..52a11217bcc 100644 --- a/pkg/compiler/lib/src/inferrer_experimental/typemasks/union_type_mask.dart +++ b/pkg/compiler/lib/src/inferrer_experimental/typemasks/union_type_mask.dart @@ -450,11 +450,11 @@ class UnionTypeMask extends TypeMask { } @override - Iterable findRootsOfTargets(Selector selector, + Iterable findRootsOfTargets(Selector selector, MemberHierarchyBuilder memberHierarchyBuilder, JClosedWorld closedWorld) { // Find the ancestors for each disjoint mask separately and combine the // results. - final Set results = {}; + final Set results = {}; for (final submask in disjointMasks) { results.addAll(submask.findRootsOfTargets( selector, memberHierarchyBuilder, closedWorld)); diff --git a/pkg/compiler/lib/src/inferrer_experimental/wrapped.dart b/pkg/compiler/lib/src/inferrer_experimental/wrapped.dart index 9677db299be..4cfca4c4650 100644 --- a/pkg/compiler/lib/src/inferrer_experimental/wrapped.dart +++ b/pkg/compiler/lib/src/inferrer_experimental/wrapped.dart @@ -602,7 +602,7 @@ class WrappedAbstractValueDomain with AbstractValueDomain { WrappedAbstractValue(_abstractValueDomain.typeType); @override - Iterable findRootsOfTargets( + Iterable findRootsOfTargets( covariant WrappedAbstractValue receiver, Selector selector, MemberHierarchyBuilder memberHierarchyBuilder) => diff --git a/pkg/compiler/lib/src/universe/member_hierarchy.dart b/pkg/compiler/lib/src/universe/member_hierarchy.dart index b0ba6212422..71225f74b11 100644 --- a/pkg/compiler/lib/src/universe/member_hierarchy.dart +++ b/pkg/compiler/lib/src/universe/member_hierarchy.dart @@ -30,9 +30,34 @@ class _QueuedMember { _QueuedMember(this.member, this.cls, {required this.isDeclaration}); } +class DynamicCallTarget { + final MemberEntity member; + final bool isVirtual; + + factory DynamicCallTarget.virtual(MemberEntity member) => + DynamicCallTarget(member, isVirtual: true); + factory DynamicCallTarget.concrete(MemberEntity member) => + DynamicCallTarget(member, isVirtual: false); + DynamicCallTarget(this.member, {required this.isVirtual}); + + @override + int get hashCode => Object.hash(member, isVirtual); + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other is DynamicCallTarget && + member == other.member && + isVirtual == other.isVirtual); + } + + @override + String toString() => 'TargetResult($member, virtual: $isVirtual)'; +} + class MemberHierarchyBuilder { final JClosedWorld closedWorld; - final Map> _callCache = {}; + final Map> _callCache = {}; final Map> _selectorRoots = {}; final Map> _overrides = {}; @@ -56,7 +81,18 @@ class MemberHierarchyBuilder { _forEachOverride(entity, f, {}); } - MemberEntity? findMatchInSuperclass(ClassEntity cls, Selector selector) { + /// Finds the first non-strict superclass of [cls] that contains a member + /// matching [selector] and returns that member. + /// + /// Returns the first non-abstract match found while ascending the class + /// hierarchy. If no non-abstract matches are found then the first abstract + /// match is used. + /// + /// If [virtualResult] is true, the resulting [DynamicCallTarget] will be + /// virtual when the match is non-abstract and has overrides. + /// Otherwise the resulting [DynamicCallTarget] is concrete. + DynamicCallTarget? findSuperclassTarget(ClassEntity cls, Selector selector, + {required bool virtualResult}) { MemberEntity? firstAbstractMatch; ClassEntity? current = cls; final elementEnv = closedWorld.elementEnvironment; @@ -67,22 +103,25 @@ class MemberHierarchyBuilder { if (match.isAbstract) { firstAbstractMatch ??= match; } else { - return match; + return DynamicCallTarget(match, + isVirtual: virtualResult && _hasOverride(match)); } } current = elementEnv.getSuperClass(current); } - return firstAbstractMatch; + return firstAbstractMatch != null + ? DynamicCallTarget.virtual(firstAbstractMatch) + : null; } /// For each subclass/subtype try to find a member matching selector. /// /// If we find a match then it covers the entire subtree below that match so /// we do not need to check subclasses/subtypes below it. - Iterable findMatchingAncestors( + Iterable findMatchingAncestors( ClassEntity baseCls, Selector selector, {required bool isSubtype}) { - final Set results = {}; + final Set results = {}; final Queue toVisit = Queue(); final Set visited = {}; void addSubclasses(ClassEntity cls) { @@ -113,7 +152,8 @@ class MemberHierarchyBuilder { } while (toVisit.isNotEmpty) { final current = toVisit.removeFirst(); - final match = findMatchInSuperclass(current, selector); + final match = + findSuperclassTarget(current, selector, virtualResult: true); if (match != null) { results.add(match); } else { @@ -134,7 +174,8 @@ class MemberHierarchyBuilder { /// consider each mixin application to declare a "copy" of each member of that /// mixin. They share the same [MemberEntity] but each copy is considered a /// separate potential root. - Iterable rootsForSelector(ClassEntity cls, Selector selector) { + Iterable rootsForSelector( + ClassEntity cls, Selector selector) { final roots = _selectorRoots[_normalizeSelector(selector)]; if (roots == null) return const []; final classHierarchy = closedWorld.classHierarchy; @@ -142,6 +183,7 @@ class MemberHierarchyBuilder { .where((r) => selector.appliesStructural(r) && classHierarchy.isSubclassOf(r.enclosingClass!, cls)) + .map((r) => DynamicCallTarget(r, isVirtual: _hasOverride(r))) .toSet(); } @@ -153,7 +195,7 @@ class MemberHierarchyBuilder { /// `null` the receiver is considered dynamic and any member that matches /// [selector] is a possible target. Also adds relevant [noSuchMethod] and /// `null` targets if needed for the call. - Iterable rootsForCall( + Iterable rootsForCall( AbstractValue? receiverType, Selector selector) { final domain = closedWorld.abstractValueDomain; receiverType ??= domain.dynamicType; @@ -161,7 +203,7 @@ class MemberHierarchyBuilder { final cachedResult = _callCache[selectorMask]; if (cachedResult != null) return cachedResult; - Iterable targetsForReceiver = + Iterable targetsForReceiver = domain.findRootsOfTargets(receiverType, selector, this); // TODO(natebiggs): Can we calculate this as part of the above call to @@ -178,7 +220,10 @@ class MemberHierarchyBuilder { final nullMatch = closedWorld.elementEnvironment.lookupLocalClassMember( closedWorld.commonElements.jsNullClass, selector.memberName); if (nullMatch != null) { - targetsForReceiver = {...targetsForReceiver, nullMatch}; + targetsForReceiver = { + ...targetsForReceiver, + DynamicCallTarget.concrete(nullMatch) + }; } } @@ -205,6 +250,10 @@ class MemberHierarchyBuilder { return _skipMemberInternal(member) || !selector.appliesStructural(member); } + bool _hasOverride(MemberEntity member) { + return _overrides.containsKey(member); + } + static List _selectorsForMember(MemberEntity member) { final List result = []; if (member is FieldEntity) {