diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmCompiledModuleFragment.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmCompiledModuleFragment.kt index b25a0d52e39..3b12b3f7607 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmCompiledModuleFragment.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmCompiledModuleFragment.kt @@ -192,21 +192,29 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) { } } - val typeDeclarations = mutableListOf() - typeDeclarations.addAll(vTableGcTypes.elements) - typeDeclarations.addAll(gcTypes.elements) - typeDeclarations.addAll(classITableGcType.elements.distinct()) - typeDeclarations.sortBy(::wasmTypeDeclarationOrderKey) + val recGroupTypes = mutableListOf() + recGroupTypes.addAll(vTableGcTypes.elements) + recGroupTypes.addAll(this.gcTypes.elements) + recGroupTypes.addAll(classITableGcType.elements.distinct()) + recGroupTypes.sortBy(::wasmTypeDeclarationOrderKey) val globals = mutableListOf() globals.addAll(globalFields.elements) globals.addAll(globalVTables.elements) globals.addAll(globalClassITables.elements.distinct()) + val allFunctionTypes = canonicalFunctionTypes.values.toList() + tagFuncType + masterInitFunctionType + + // Partition out function types that can't be recursive, + // we don't need to put them into a rec group + // so that they can be matched with function types from other Wasm modules. + val (potentiallyRecursiveFunctionTypes, nonRecursiveFunctionTypes) = + allFunctionTypes.partition { it.referencesTypeDeclarations() } + recGroupTypes.addAll(potentiallyRecursiveFunctionTypes) + val module = WasmModule( - functionTypes = canonicalFunctionTypes.values.toList() + tagFuncType + masterInitFunctionType, - gcTypes = typeDeclarations, - gcTypesInRecursiveGroup = true, + functionTypes = nonRecursiveFunctionTypes, + recGroupTypes = recGroupTypes, importsInOrder = importedFunctions, importedFunctions = importedFunctions, definedFunctions = functions.elements.filterIsInstance() + masterInitFunction, diff --git a/compiler/testData/codegen/boxWasmJsInterop/callingWasmDirectly.kt b/compiler/testData/codegen/boxWasmJsInterop/callingWasmDirectly.kt new file mode 100644 index 00000000000..1b2ea27dc66 --- /dev/null +++ b/compiler/testData/codegen/boxWasmJsInterop/callingWasmDirectly.kt @@ -0,0 +1,30 @@ +// TARGET_BACKEND: WASM + +/* + Here we pass export of another Wasm module to our import directly without JS layer. + This enables strict type check without JS conversons. + For instance, recursion groups of function types must fully match. + +(module + (func (export "addTwo") (param i32 i32) (result i32) + local.get 0 + local.get 1 + i32.add)) + */ +@JsFun(""" +(() => { + let bytes = [0, 97, 115, 109, 1, 0, 0, 0, 1, 7, 1, 96, 2, 127, 127, 1, 127, 3, + 2, 1, 0, 7, 10, 1, 6, 97, 100, 100, 84, 119, 111, 0, 0, 10, 9, 1, + 7, 0, 32, 0, 32, 1, 106, 11, 0, 10, 4, 110, 97, 109, 101, 2, 3, 1, 0, 0]; + let buffer = (new Int8Array(bytes)).buffer; + let module = new WebAssembly.Module(buffer); + let instance = new WebAssembly.Instance(module); + return instance.exports.addTwo; +})() +""") +external fun addTwo(a: Int, b: Int): Int + +fun box(): String { + if (addTwo(100, 200) != 300) return "Fail1" + return "OK" +} \ No newline at end of file diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenWasmJsInteropWasmTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenWasmJsInteropWasmTestGenerated.java index 55c368cfbe5..7bc38049002 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenWasmJsInteropWasmTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/testOld/wasm/semantics/IrCodegenWasmJsInteropWasmTestGenerated.java @@ -30,6 +30,11 @@ public class IrCodegenWasmJsInteropWasmTestGenerated extends AbstractIrCodegenWa KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/boxWasmJsInterop"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.WASM, true); } + @TestMetadata("callingWasmDirectly.kt") + public void testCallingWasmDirectly() throws Exception { + runTest("compiler/testData/codegen/boxWasmJsInterop/callingWasmDirectly.kt"); + } + @TestMetadata("defaultValues.kt") public void testDefaultValues() throws Exception { runTest("compiler/testData/codegen/boxWasmJsInterop/defaultValues.kt"); diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Declarations.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Declarations.kt index 1296a2fccb9..f270d87608b 100644 --- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Declarations.kt +++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Declarations.kt @@ -10,9 +10,7 @@ import org.jetbrains.kotlin.wasm.ir.source.location.SourceLocation class WasmModule( val functionTypes: List = emptyList(), - val gcTypes: List = emptyList(), - val gcTypesInRecursiveGroup: Boolean, - + val recGroupTypes: List = emptyList(), val importsInOrder: List = emptyList(), val importedFunctions: List = emptyList(), val importedMemories: List = emptyList(), diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Types.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Types.kt index 37d8f364d4c..bdeb999a94b 100644 --- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Types.kt +++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Types.kt @@ -77,3 +77,15 @@ fun WasmType.getHeapType(): WasmHeapType = is WasmExternRef -> WasmHeapType.Simple.Extern else -> error("Unknown heap type for type $this") } + +fun WasmFunctionType.referencesTypeDeclarations(): Boolean = + parameterTypes.any { it.referencesTypeDeclaration() } or resultTypes.any { it.referencesTypeDeclaration() } + +fun WasmType.referencesTypeDeclaration(): Boolean { + val heapType = when (this) { + is WasmRefNullType -> getHeapType() + is WasmRefType -> getHeapType() + else -> return false + } + return heapType is WasmHeapType.Type +} diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Utils.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Utils.kt index a0bb9ddf095..0e8c7ade5a3 100644 --- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Utils.kt +++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Utils.kt @@ -16,7 +16,7 @@ fun WasmModule.calculateIds() { } functionTypes.calculateIds() - gcTypes.calculateIds(startIndex = functionTypes.size) + recGroupTypes.calculateIds(startIndex = functionTypes.size) importedFunctions.calculateIds() importedMemories.calculateIds() importedTables.calculateIds() diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmBinaryToIR.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmBinaryToIR.kt index 7e8eab0c24c..a6ce00dcdee 100644 --- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmBinaryToIR.kt +++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmBinaryToIR.kt @@ -329,8 +329,7 @@ class WasmBinaryToIR(val b: MyByteReader) { return WasmModule( functionTypes = functionTypes, - gcTypes = gcTypes, - gcTypesInRecursiveGroup = false, + recGroupTypes = gcTypes, importsInOrder = importsInOrder, importedFunctions = importedFunctions, importedMemories = importedMemories, diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToBinary.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToBinary.kt index 345c7ac173b..803d01b7e02 100644 --- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToBinary.kt +++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToBinary.kt @@ -35,17 +35,18 @@ class WasmIrToBinary( with(module) { // type section appendSection(1u) { - if (module.gcTypesInRecursiveGroup) { - appendVectorSize(1) - b.writeByte(0x4f) - } - appendVectorSize(functionTypes.size + gcTypes.size) + val numRecGroups = if (recGroupTypes.isEmpty()) 0 else 1 + appendVectorSize(functionTypes.size + numRecGroups) functionTypes.forEach { appendFunctionTypeDeclaration(it) } - gcTypes.forEach { - when (it) { - is WasmStructDeclaration -> appendStructTypeDeclaration(it) - is WasmArrayDeclaration -> appendArrayTypeDeclaration(it) - is WasmFunctionType -> error("Function type in GC types") + if (!recGroupTypes.isEmpty()) { + b.writeByte(0x4f) + appendVectorSize(recGroupTypes.size) + recGroupTypes.forEach { + when (it) { + is WasmStructDeclaration -> appendStructTypeDeclaration(it) + is WasmArrayDeclaration -> appendArrayTypeDeclaration(it) + is WasmFunctionType -> appendFunctionTypeDeclaration(it) + } } } } @@ -172,8 +173,8 @@ class WasmIrToBinary( // https://github.com/WebAssembly/extended-name-section/blob/main/document/core/appendix/custom.rst appendSection(4u) { - appendVectorSize(module.gcTypes.size) - module.gcTypes.forEach { + appendVectorSize(module.recGroupTypes.size) + module.recGroupTypes.forEach { appendModuleFieldReference(it) b.writeString(it.name) } @@ -190,7 +191,7 @@ class WasmIrToBinary( // Experimental fields name section // https://github.com/WebAssembly/gc/issues/193 appendSection(10u) { - val structDeclarations = module.gcTypes.filterIsInstance() + val structDeclarations = module.recGroupTypes.filterIsInstance() appendVectorSize(structDeclarations.size) structDeclarations.forEach { appendModuleFieldReference(it) diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToText.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToText.kt index b83cbfcb563..3b886483786 100644 --- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToText.kt +++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/convertors/WasmIrToText.kt @@ -228,13 +228,14 @@ class WasmIrToText : SExpressionBuilder() { with(module) { newLineList("module") { functionTypes.forEach { appendFunctionTypeDeclaration(it) } - gcTypes.forEach { + recGroupTypes.forEach { when (it) { is WasmStructDeclaration -> appendStructTypeDeclaration(it) is WasmArrayDeclaration -> appendArrayTypeDeclaration(it) - else -> error("Unexpected GC type: $it") + is WasmFunctionType -> + appendFunctionTypeDeclaration(it) } } importsInOrder.forEach {