From 384c700a85bd0e892f48372e51017c3f772b1edf Mon Sep 17 00:00:00 2001 From: Svyatoslav Kuzmich Date: Tue, 25 Jul 2023 10:47:53 +0200 Subject: [PATCH] [Wasm] Fix access to WebIDL dictionary members Evaluate external interface companion objects as an empty JS object instead of null due to null checks on interop boundary. ^KT-59082 Fixed --- ...DeclarationsToTopLevelFunctionsLowering.kt | 21 +++++++++++++++++++ .../codegen/boxWasmJsInterop/kt59082.kt | 15 +++++++++++++ ...WasmCodegenWasmJsInteropTestGenerated.java | 6 ++++++ ...WasmCodegenWasmJsInteropTestGenerated.java | 6 ++++++ 4 files changed, 48 insertions(+) create mode 100644 compiler/testData/codegen/boxWasmJsInterop/kt59082.kt diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/ComplexExternalDeclarationsToTopLevelFunctionsLowering.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/ComplexExternalDeclarationsToTopLevelFunctionsLowering.kt index 8c3781909a2..b88ee01d09a 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/ComplexExternalDeclarationsToTopLevelFunctionsLowering.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/ComplexExternalDeclarationsToTopLevelFunctionsLowering.kt @@ -156,6 +156,27 @@ class ComplexExternalDeclarationsToTopLevelFunctionsLowering(val context: WasmBa private fun StringBuilder.appendExternalClassReference(klass: IrClass) { val parent = klass.parent if (parent is IrClass) { + + // This is hack to support Kotlin/JS like implementation of IDL string enums bindings + // with error suppression: + // + // @JsName("null") + // @Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE") + // public external interface CanvasFillRule { + // companion object + // } + // public inline val CanvasFillRule.Companion.NONZERO: CanvasFillRule get() = "nonzero".asDynamic().unsafeCast() + // + // Kotlin/JS translates access to CanvasFillRule.Companion as `null` due to @JsName("null"), + // but Kotlin/Wasm fails to do this due to stricter null checks on interop boundary. + // + // Instead, as a temporary solution, we evaluate such companion object to an empty JS object. + // TODO: Optimize (KT-60661) + if (parent.isInterface) { + append("({})") + return + } + appendExternalClassReference(parent) if (klass.isCompanion) { // Reference to external companion object is reference to its parent class diff --git a/compiler/testData/codegen/boxWasmJsInterop/kt59082.kt b/compiler/testData/codegen/boxWasmJsInterop/kt59082.kt new file mode 100644 index 00000000000..57e06286ade --- /dev/null +++ b/compiler/testData/codegen/boxWasmJsInterop/kt59082.kt @@ -0,0 +1,15 @@ +// TARGET_BACKEND: WASM + +@JsName("null") +@Suppress("NESTED_CLASS_IN_EXTERNAL_INTERFACE") +public external interface CanvasFillRule : JsAny { + companion object +} + +public inline val CanvasFillRule.Companion.EVENODD: CanvasFillRule get() = + "evenodd".toJsString().unsafeCast() + +fun box(): String { + if (CanvasFillRule.EVENODD.toString() != "evenodd") return "Fail 1" + return "OK" +} diff --git a/wasm/wasm.tests/tests-gen/org/jetbrains/kotlin/wasm/test/FirWasmCodegenWasmJsInteropTestGenerated.java b/wasm/wasm.tests/tests-gen/org/jetbrains/kotlin/wasm/test/FirWasmCodegenWasmJsInteropTestGenerated.java index 191d375f50d..b1dc05a9f55 100644 --- a/wasm/wasm.tests/tests-gen/org/jetbrains/kotlin/wasm/test/FirWasmCodegenWasmJsInteropTestGenerated.java +++ b/wasm/wasm.tests/tests-gen/org/jetbrains/kotlin/wasm/test/FirWasmCodegenWasmJsInteropTestGenerated.java @@ -115,6 +115,12 @@ public class FirWasmCodegenWasmJsInteropTestGenerated extends AbstractFirWasmCod runTest("compiler/testData/codegen/boxWasmJsInterop/kotlinToJsAdapters.kt"); } + @Test + @TestMetadata("kt59082.kt") + public void testKt59082() throws Exception { + runTest("compiler/testData/codegen/boxWasmJsInterop/kt59082.kt"); + } + @Test @TestMetadata("lambdaAdapterNameClash.kt") public void testLambdaAdapterNameClash() throws Exception { diff --git a/wasm/wasm.tests/tests-gen/org/jetbrains/kotlin/wasm/test/K1WasmCodegenWasmJsInteropTestGenerated.java b/wasm/wasm.tests/tests-gen/org/jetbrains/kotlin/wasm/test/K1WasmCodegenWasmJsInteropTestGenerated.java index 7a9de889d73..66db239d690 100644 --- a/wasm/wasm.tests/tests-gen/org/jetbrains/kotlin/wasm/test/K1WasmCodegenWasmJsInteropTestGenerated.java +++ b/wasm/wasm.tests/tests-gen/org/jetbrains/kotlin/wasm/test/K1WasmCodegenWasmJsInteropTestGenerated.java @@ -115,6 +115,12 @@ public class K1WasmCodegenWasmJsInteropTestGenerated extends AbstractK1WasmCodeg runTest("compiler/testData/codegen/boxWasmJsInterop/kotlinToJsAdapters.kt"); } + @Test + @TestMetadata("kt59082.kt") + public void testKt59082() throws Exception { + runTest("compiler/testData/codegen/boxWasmJsInterop/kt59082.kt"); + } + @Test @TestMetadata("lambdaAdapterNameClash.kt") public void testLambdaAdapterNameClash() throws Exception {