[dart2wasm] Add wrapper functions to settle type differences between main module and dynamic module dispatch tables.
Due to new overrides being introduced, the expected type of a function in the dispatch table can change between the main module and dynamic module. For functions imported from the main module into the dynamic module and included in the dynamic module's dispatch table, this can lead to type errors.
This change introduces a wrapper that casts any differing types to the type expected by the dyanmic module and includes the wrapper in the dispatch table instead.
Only creates the wrapper when the function types differ. The wrapper will only contains casts for the parameters (or return type) that differ, the others will be passed through without a cast.
Change-Id: I7265693adcd5d8f831b36ed1c8960fbf363b908c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/422345
Reviewed-by: Ömer Ağacan <omersa@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
diff --git a/pkg/dart2wasm/lib/dispatch_table.dart b/pkg/dart2wasm/lib/dispatch_table.dart
index 479375c..1d88684 100644
--- a/pkg/dart2wasm/lib/dispatch_table.dart
+++ b/pkg/dart2wasm/lib/dispatch_table.dart
@@ -765,6 +765,7 @@
}
void output() {
+ final Map<w.BaseFunction, w.BaseFunction> wrappedDynamicModuleImports = {};
for (int i = 0; i < _table.length; i++) {
Reference? target = _table[i];
if (target != null) {
@@ -780,6 +781,14 @@
if (fun != null) {
final targetModule = fun.enclosingModule;
if (targetModule == _definedWasmTable.enclosingModule) {
+ if (isDynamicModuleTable &&
+ targetModule == translator.dynamicModule &&
+ fun is w.ImportedFunction) {
+ // Functions imported into dynamic modules may need to be wrapped
+ // to match the updated dispatch table signature.
+ fun = wrappedDynamicModuleImports[fun] ??=
+ _wrapDynamicModuleFunction(target, fun);
+ }
_definedWasmTable.setElement(i, fun);
} else {
// This will generate the imported table if it doesn't already
@@ -791,6 +800,61 @@
}
}
}
+
+ w.BaseFunction _wrapDynamicModuleFunction(
+ Reference target, w.BaseFunction importedFunction) {
+ final mainSelector =
+ translator.dynamicMainModuleDispatchTable!.selectorForTarget(target);
+ final mainSignature = translator.signatureForMainModule(target);
+ final localSelector = translator.dispatchTable.selectorForTarget(target);
+ final localSignature = localSelector.signature;
+
+ // If the type is the same in both the main module and the dynamic module,
+ // use the imported function itself.
+ if (mainSignature.isStructurallyEqualTo(localSignature)) {
+ return importedFunction;
+ }
+
+ // Otherwise we need to create a wrapper to handle the differing types.
+ // The local signature should include all the parameters necessary to call
+ // the target in main since the local signature must include the target
+ // member itself and any other members in the main module's selector range.
+ final wrapper = translator.dynamicModule.functions
+ .define(localSignature, '${target.asMember} wrapper');
+
+ final ib = wrapper.body;
+
+ assert(mainSignature.inputs.length <= localSignature.inputs.length);
+
+ final mainModulePreParamCount =
+ (mainSelector.paramInfo.takesContextOrReceiver ? 1 : 0) +
+ mainSelector.paramInfo.typeParamCount;
+ final mainModuleBeforeNamedCount =
+ mainModulePreParamCount + mainSelector.paramInfo.positional.length;
+ int mainIndex = 0;
+ for (; mainIndex < mainModuleBeforeNamedCount; mainIndex++) {
+ final local = ib.locals[mainIndex];
+ ib.local_get(local);
+ translator.convertType(ib, local.type, mainSignature.inputs[mainIndex]);
+ }
+
+ final localPreParamCount =
+ (localSelector.paramInfo.takesContextOrReceiver ? 1 : 0) +
+ localSelector.paramInfo.typeParamCount;
+
+ for (final name in mainSelector.paramInfo.names) {
+ final namedIndex = localSelector.paramInfo.nameIndex[name]!;
+ final local = ib.locals[localPreParamCount + namedIndex];
+ ib.local_get(local);
+ translator.convertType(ib, local.type, mainSignature.inputs[mainIndex++]);
+ }
+ ib.call(importedFunction);
+ translator.convertType(
+ ib, mainSignature.outputs.single, localSignature.outputs.single);
+ ib.end();
+
+ return wrapper;
+ }
}
bool _isUsedViaDispatchTableCall(SelectorInfo selector) {