@@ -39,7 +39,7 @@ class SelectorInfo {
3939 final int callCount;
4040
4141 /// Least upper bound of [ParameterInfo] s of all targets.
42- final ParameterInfo paramInfo;
42+ late final ParameterInfo paramInfo;
4343
4444 /// Is this an implicit or explicit setter?
4545 final bool isSetter;
@@ -48,10 +48,14 @@ class SelectorInfo {
4848 ///
4949 /// We create multiple entry points when any implementation of this selector
5050 /// performs type checks on the passed arguments.
51- bool useMultipleEntryPoints = false ;
51+ late bool useMultipleEntryPoints;
5252
53- bool isDynamicSubmoduleOverridable = false ;
54- bool isDynamicSubmoduleCallable = false ;
53+ late bool isDynamicSubmoduleOverridable;
54+ late bool isDynamicSubmoduleCallable;
55+
56+ /// Whether the computation of [paramInfo] should enforce usage of sentinels
57+ /// for optional parameters.
58+ late bool _useSentinelForOptionalParameters;
5559
5660 /// Wasm function type for the selector.
5761 ///
@@ -65,6 +69,7 @@ class SelectorInfo {
6569 SelectorTargets ? _unchecked;
6670 SelectorTargets ? _normal;
6771
72+ /// The set of references we use to calculate the [paramInfo] .
6873 final List <Reference > _references = [];
6974
7075 SelectorTargets targets ({required bool unchecked}) {
@@ -80,8 +85,7 @@ class SelectorInfo {
8085 this .dispatchTable,
8186 this .id,
8287 this .name,
83- this .callCount,
84- this .paramInfo, {
88+ this .callCount, {
8589 required this .isSetter,
8690 });
8791
@@ -94,6 +98,7 @@ class SelectorInfo {
9498 useMultipleEntryPoints,
9599 isDynamicSubmoduleOverridable,
96100 isDynamicSubmoduleCallable,
101+ _useSentinelForOptionalParameters,
97102 ]);
98103 sink.writeNullable (_checked, (targets) => targets.serialize (sink));
99104 sink.writeNullable (_unchecked, (targets) => targets.serialize (sink));
@@ -111,6 +116,7 @@ class SelectorInfo {
111116 useMultipleEntryPoints,
112117 isDynamicSubmoduleOverridable,
113118 isDynamicSubmoduleCallable,
119+ useSentinelForOptionalParameters,
114120 ] = source.readBoolList ();
115121 final checked =
116122 source.readNullable (() => SelectorTargets .deserialize (source));
@@ -120,22 +126,19 @@ class SelectorInfo {
120126 source.readNullable (() => SelectorTargets .deserialize (source));
121127 final references = source.readList (source.readReference);
122128
123- final paramInfo = references.skip (1 ).fold (
124- ParameterInfo .fromMember (references.first),
125- (p, e) => p..merge (ParameterInfo .fromMember (e)));
126- return SelectorInfo ._(dispatchTable, id, name, callCount, paramInfo,
129+ final paramInfo = _parameterInfoFromReferences (
130+ references, useSentinelForOptionalParameters);
131+ return SelectorInfo ._(dispatchTable, id, name, callCount,
127132 isSetter: isSetter)
128133 ..useMultipleEntryPoints = useMultipleEntryPoints
129134 ..isDynamicSubmoduleCallable = isDynamicSubmoduleCallable
130135 ..isDynamicSubmoduleOverridable = isDynamicSubmoduleOverridable
136+ .._useSentinelForOptionalParameters = useSentinelForOptionalParameters
131137 .._checked = checked
132138 .._unchecked = unchecked
133- .._normal = normal;
134- }
135-
136- void _addImplementationReference (Reference reference) {
137- _references.add (reference);
138- paramInfo.merge (ParameterInfo .fromMember (reference));
139+ .._normal = normal
140+ .._references.addAll (references)
141+ ..paramInfo = paramInfo;
139142 }
140143
141144 String entryPointName (bool unchecked) {
@@ -473,9 +476,6 @@ class DispatchTable {
473476 metadata.methodOrSetterCalledDynamically ||
474477 member.name.text == "call" );
475478
476- final isDynamicSubmoduleOverridable =
477- member.isDynamicSubmoduleOverridable (translator.coreTypes);
478-
479479 // The compiler will generate calls to `noSuchMethod` in the dynamic
480480 // invocation forwarders. So we ensure that the call count is positive.
481481 final isNoSuchMethod = member == translator.objectNoSuchMethod;
@@ -484,20 +484,9 @@ class DispatchTable {
484484 final selector = _selectorInfo.putIfAbsent (
485485 selectorId,
486486 () => SelectorInfo ._(this , selectorId, member.name.text, callCount,
487- ParameterInfo .fromMember (target),
488487 isSetter: isSetter));
489488 assert (selector.isSetter == isSetter);
490- final useMultipleEntryPoints = ! member.isAbstract &&
491- ! member.isExternal &&
492- ! target.isGetter &&
493- ! target.isTearOffReference &&
494- translator.needToCheckTypesFor (member);
495- selector.useMultipleEntryPoints | = useMultipleEntryPoints;
496- selector.isDynamicSubmoduleOverridable | = isDynamicSubmoduleOverridable;
497- selector.isDynamicSubmoduleCallable | =
498- member.isDynamicSubmoduleCallable (translator.coreTypes);
499-
500- selector._addImplementationReference (target);
489+ selector._references.add (target);
501490
502491 if (calledDynamically) {
503492 if (isGetter) {
@@ -639,6 +628,30 @@ class DispatchTable {
639628 }
640629 }
641630
631+ _selectorInfo.forEach ((_, selector) {
632+ bool isDynamicSubmoduleCallable = false ;
633+ bool isDynamicSubmoduleOverridable = false ;
634+ for (final target in selector._references) {
635+ final member = target.asMember;
636+ isDynamicSubmoduleOverridable | =
637+ member.isDynamicSubmoduleOverridable (translator.coreTypes);
638+ isDynamicSubmoduleCallable | =
639+ member.isDynamicSubmoduleCallable (translator.coreTypes);
640+ }
641+ selector.isDynamicSubmoduleOverridable = isDynamicSubmoduleOverridable;
642+ selector.isDynamicSubmoduleCallable = isDynamicSubmoduleCallable;
643+
644+ if (! selectorTargets.containsKey (selector)) {
645+ // There are no concrete implementations for the given [selector].
646+ selector._normal = SelectorTargets ([], []);
647+ selector.useMultipleEntryPoints = false ;
648+ selector._useSentinelForOptionalParameters = true ;
649+ selector.paramInfo = _parameterInfoFromReferences (
650+ selector._references, selector._useSentinelForOptionalParameters);
651+ } else {
652+ // Will be initialized in the `selectorTargets.forEach()` below.
653+ }
654+ });
642655 selectorTargets
643656 .forEach ((SelectorInfo selector, Map <int , Reference > targets) {
644657 final List <({Range range, Reference target})> ranges = targets.entries
@@ -664,6 +677,47 @@ class DispatchTable {
664677 }
665678 ranges.length = writeIndex + 1 ;
666679
680+ bool useMultipleEntryPoints = false ;
681+ final implementationReferences = < Reference > [];
682+ for (final targetRange in ranges) {
683+ final target = targetRange.target;
684+ final member = target.asMember;
685+ assert (! member.isAbstract);
686+
687+ // Compute [useMultipleEntryPoints]
688+ if (! member.isExternal &&
689+ ! target.isGetter &&
690+ ! target.isTearOffReference &&
691+ translator.needToCheckTypesFor (member)) {
692+ useMultipleEntryPoints = true ;
693+ }
694+ implementationReferences.add (target);
695+ }
696+ selector.useMultipleEntryPoints = useMultipleEntryPoints;
697+
698+ if (! selector.isDynamicSubmoduleOverridable &&
699+ implementationReferences.isNotEmpty) {
700+ // We have global knowledge of all targets of the selector. We can use
701+ // this global knowledge to compute the [ParameterInfo].
702+ selector._references.clear ();
703+ selector._references.addAll (implementationReferences);
704+ selector._useSentinelForOptionalParameters = false ;
705+ } else {
706+ // Case 1) We may have no targets for the selector, but there may
707+ // still be calls to it (see e.g. https://dartbug.com/60733).
708+ //
709+ // Case 2) We may have 3rd party implementations of the selector
710+ // in a dynamic module with unknown default values for optionals.
711+ //
712+ // => We make caller pass sentinel if the optional parameter is not
713+ // provided.
714+ selector._useSentinelForOptionalParameters = true ;
715+ }
716+ selector.paramInfo = _parameterInfoFromReferences (
717+ selector._references, selector._useSentinelForOptionalParameters);
718+
719+ // Split up [ranges] into those that are statically dispatched to and
720+ // those are used via dispatch table.
667721 final staticDispatchRanges = selector.isDynamicSubmoduleOverridable
668722 ? const < ({Range range, Reference target})> []
669723 : (translator.options.polymorphicSpecialization || ranges.length == 1 )
@@ -752,20 +806,6 @@ class DispatchTable {
752806 }
753807 }
754808
755- _selectorInfo.forEach ((_, selector) {
756- if (! selectorTargets.containsKey (selector)) {
757- // There are no concrete implementations for the given [selector].
758- // But there may be an abstract interface target which is targed by a
759- // call. In this case the call should be unreachable.
760- if (selector.useMultipleEntryPoints) {
761- selector._checked = SelectorTargets (const [], const []);
762- selector._unchecked = SelectorTargets (const [], const []);
763- } else {
764- selector._normal = SelectorTargets (const [], const []);
765- }
766- }
767- });
768-
769809 _initializeWasmTable ();
770810 }
771811
@@ -872,14 +912,35 @@ bool _isUsedViaDispatchTableCall(SelectorInfo selector) {
872912
873913 final targets = selector.targets (unchecked: false );
874914
915+ // If there's 0 or 1 target than the call sites will either emit an
916+ // unreachable or a direct call, so no call site goes via dispatch table, so
917+ // we don't need a row in the table.
875918 if (targets.targetRanges.length <= 1 ) return false ;
919+
920+ // If all targets are dispatched to via static polymorphic dispatchers,
921+ // then no callsite will emit a dispatch table call, so we don't need a row in
922+ // the table.
876923 if (targets.staticDispatchRanges.length == targets.targetRanges.length) {
877924 return false ;
878925 }
879926
880927 return true ;
881928}
882929
930+ ParameterInfo _parameterInfoFromReferences (
931+ List <Reference > references, bool useDefaultValueSentinel) {
932+ // We know all target implementations (closed world) if all of them use
933+ // the same default value for optionals, we can make the caller pass it.
934+ final first = references.first;
935+ final paramInfo = ParameterInfo .fromMember (
936+ first, useDefaultValueSentinel || first.asMember.isAbstract);
937+ for (final target in references.skip (1 )) {
938+ paramInfo.merge (ParameterInfo .fromMember (
939+ target, useDefaultValueSentinel || target.asMember.isAbstract));
940+ }
941+ return paramInfo;
942+ }
943+
883944/// Build a row-displacement table based on fitting the [rows] .
884945///
885946/// The returned list is the resulting row displacement table with `null`
0 commit comments