diff --git a/pkg/dart2wasm/lib/functions.dart b/pkg/dart2wasm/lib/functions.dart index 61f53578d20..916394b3173 100644 --- a/pkg/dart2wasm/lib/functions.dart +++ b/pkg/dart2wasm/lib/functions.dart @@ -66,9 +66,14 @@ class FunctionCollector { String module = importName.substring(0, dot); String name = importName.substring(dot + 1); if (member is Procedure) { + // Define the function type in a singular recursion group to enable it + // to be unified with function types defined in FFI modules or using + // `WebAssembly.Function`. + m.splitRecursionGroup(); w.FunctionType ftype = _makeFunctionType( translator, member.reference, member.function.returnType, null, isImportOrExport: true); + m.splitRecursionGroup(); _functions[member.reference] = m.importFunction(module, name, ftype, "$importName (import)"); } @@ -242,34 +247,29 @@ w.FunctionType _makeFunctionType(Translator translator, Reference target, function.positionalParameters.map((p) => p.type); } - List typeParameters = List.filled(typeParamCount, - translator.classInfo[translator.typeClass]!.nonNullableType); + // Translate types differently for imports and exports. + w.ValueType translateType(DartType type) => isImportOrExport + ? translator.translateExternalType(type) + : translator.translateType(type); - // The only reference types allowed as parameters and returns on imported or - // exported functions for JS interop are `externref` and `funcref`. - w.ValueType adjustExternalType(w.ValueType type) { - if (isImportOrExport && type is w.RefType) { - if (type.heapType.isSubtypeOf(w.HeapType.func)) { - return w.RefType.func(nullable: true); - } - return w.RefType.extern(nullable: true); - } - return type; - } + List typeParameters = List.filled( + typeParamCount, + translateType( + InterfaceType(translator.typeClass, Nullability.nonNullable))); List inputs = []; if (receiverType != null) { - inputs.add(adjustExternalType(receiverType)); + assert(!isImportOrExport); + inputs.add(receiverType); } - inputs.addAll(typeParameters.map(adjustExternalType)); - inputs.addAll( - params.map((t) => adjustExternalType(translator.translateType(t)))); + inputs.addAll(typeParameters); + inputs.addAll(params.map(translateType)); List outputs = returnType is VoidType ? member.function?.asyncMarker == AsyncMarker.Async - ? [adjustExternalType(translator.topInfo.nullableType)] + ? [translateType(translator.coreTypes.objectNullableRawType)] : const [] - : [adjustExternalType(translator.translateType(returnType))]; + : [translateType(returnType)]; return translator.m.addFunctionType(inputs, outputs); } diff --git a/pkg/dart2wasm/lib/translator.dart b/pkg/dart2wasm/lib/translator.dart index 301defbdc4a..9f9d9c15fba 100644 --- a/pkg/dart2wasm/lib/translator.dart +++ b/pkg/dart2wasm/lib/translator.dart @@ -196,9 +196,12 @@ class Translator with KernelNodes { voidMarker = w.RefType.def(w.StructType("void"), nullable: true); mainFunction = _findMainMethod(libraries.first); + // Collect imports and exports as the very first thing so the function types + // for the imports can be places in singleton recursion groups. + functions.collectImportsAndExports(); + closureLayouter.collect([mainFunction.function]); classInfoCollector.collect(); - functions.collectImportsAndExports(); initFunction = m.addFunction(m.addFunctionType(const [], const []), "#init"); @@ -495,6 +498,30 @@ class Translator with KernelNodes { () => m.addArrayType("Array<$name>", elementType: w.FieldType(type))); } + /// Translate a Dart type as it should appear on parameters and returns of + /// imported and exported functions. The only reference types allowed here + /// for JS interop are `externref` and `funcref`. + /// + /// This function can be called before the class info is built. + w.ValueType translateExternalType(DartType type) { + if (type is InterfaceType) { + Class cls = type.classNode; + if (cls == wasmFuncRefClass || cls == wasmFunctionClass) { + return w.RefType.func(nullable: true); + } + if (!type.isPotentiallyNullable) { + w.StorageType? builtin = builtinTypes[cls]; + if (builtin != null && builtin.isPrimitive) { + return builtin as w.ValueType; + } + if (isFfiCompound(cls)) { + return w.NumType.i32; + } + } + } + return w.RefType.extern(nullable: true); + } + w.DefinedGlobal makeFunctionRef(w.BaseFunction f) { return functionRefCache.putIfAbsent(f, () { w.DefinedGlobal global = m.addGlobal( diff --git a/pkg/wasm_builder/lib/src/module.dart b/pkg/wasm_builder/lib/src/module.dart index d38f51d01bb..806f96f003c 100644 --- a/pkg/wasm_builder/lib/src/module.dart +++ b/pkg/wasm_builder/lib/src/module.dart @@ -121,7 +121,12 @@ class Module with SerializerMixin { /// Insert a recursion group split in the list of type definitions. Types can /// only reference other types in the same or earlier recursion groups. void splitRecursionGroup() { - recursionGroupSplits.add(defTypes.length); + int typeCount = defTypes.length; + if (typeCount > 0 && + (recursionGroupSplits.isEmpty || + recursionGroupSplits.last != typeCount)) { + recursionGroupSplits.add(typeCount); + } } /// Add a new function to the module with the given function type.