[K/Wasm] Fix Promise API compatibility
Merge-request: KT-MR-22764
Merged-by: Artem Kobzar <Artem.Kobzar@jetbrains.com>
diff --git a/compiler/testData/codegen/box/size/add.kt b/compiler/testData/codegen/box/size/add.kt
index 63cf5d1..3b137f1 100644
--- a/compiler/testData/codegen/box/size/add.kt
+++ b/compiler/testData/codegen/box/size/add.kt
@@ -2,7 +2,7 @@
// RUN_THIRD_PARTY_OPTIMIZER
// WASM_DCE_EXPECTED_OUTPUT_SIZE: wasm 54_055
-// WASM_DCE_EXPECTED_OUTPUT_SIZE: mjs 5_865
+// WASM_DCE_EXPECTED_OUTPUT_SIZE: mjs 5_955
// WASM_OPT_EXPECTED_OUTPUT_SIZE: 76
// FILE: test.kt
diff --git a/compiler/testData/codegen/box/size/helloWorld.kt b/compiler/testData/codegen/box/size/helloWorld.kt
index 035c09a..57d7596 100644
--- a/compiler/testData/codegen/box/size/helloWorld.kt
+++ b/compiler/testData/codegen/box/size/helloWorld.kt
@@ -2,8 +2,8 @@
// RUN_THIRD_PARTY_OPTIMIZER
// WASM_DCE_EXPECTED_OUTPUT_SIZE: wasm 54_371
-// WASM_DCE_EXPECTED_OUTPUT_SIZE: mjs 5_831
-// WASM_OPT_EXPECTED_OUTPUT_SIZE: 6_270
+// WASM_DCE_EXPECTED_OUTPUT_SIZE: mjs 5_890
+// WASM_OPT_EXPECTED_OUTPUT_SIZE: 6_353
fun box(): String {
println("Hello, World!")
diff --git a/compiler/testData/codegen/box/size/helloWorldPromise.kt b/compiler/testData/codegen/box/size/helloWorldPromise.kt
index b049932..dcb9d16 100644
--- a/compiler/testData/codegen/box/size/helloWorldPromise.kt
+++ b/compiler/testData/codegen/box/size/helloWorldPromise.kt
@@ -3,7 +3,7 @@
// RUN_THIRD_PARTY_OPTIMIZER
// WASM_DCE_EXPECTED_OUTPUT_SIZE: wasm 54_474
// WASM_DCE_EXPECTED_OUTPUT_SIZE: mjs 6_067
-// WASM_OPT_EXPECTED_OUTPUT_SIZE: 6_465
+// WASM_OPT_EXPECTED_OUTPUT_SIZE: 6_548
// FILE: test.kt
diff --git a/compiler/testData/codegen/box/size/objectsOptimization.kt b/compiler/testData/codegen/box/size/objectsOptimization.kt
index 3c5650d..43d2703 100644
--- a/compiler/testData/codegen/box/size/objectsOptimization.kt
+++ b/compiler/testData/codegen/box/size/objectsOptimization.kt
@@ -2,8 +2,8 @@
// RUN_THIRD_PARTY_OPTIMIZER
// WASM_DCE_EXPECTED_OUTPUT_SIZE: wasm 58_966
-// WASM_DCE_EXPECTED_OUTPUT_SIZE: mjs 5_762
-// WASM_OPT_EXPECTED_OUTPUT_SIZE: 7_654
+// WASM_DCE_EXPECTED_OUTPUT_SIZE: mjs 5_821
+// WASM_OPT_EXPECTED_OUTPUT_SIZE: 7_737
object Simple
diff --git a/compiler/testData/codegen/box/size/ok.kt b/compiler/testData/codegen/box/size/ok.kt
index b2c2373..d87f872 100644
--- a/compiler/testData/codegen/box/size/ok.kt
+++ b/compiler/testData/codegen/box/size/ok.kt
@@ -2,7 +2,7 @@
// RUN_THIRD_PARTY_OPTIMIZER
// WASM_DCE_EXPECTED_OUTPUT_SIZE: wasm 54_104
-// WASM_DCE_EXPECTED_OUTPUT_SIZE: mjs 5_762
-// WASM_OPT_EXPECTED_OUTPUT_SIZE: 6_174
+// WASM_DCE_EXPECTED_OUTPUT_SIZE: mjs 5_821
+// WASM_OPT_EXPECTED_OUTPUT_SIZE: 6_257
fun box() = "OK"
\ No newline at end of file
diff --git a/compiler/testData/codegen/box/size/removeUnusedOverride.kt b/compiler/testData/codegen/box/size/removeUnusedOverride.kt
index 513fbb6..debacb5 100644
--- a/compiler/testData/codegen/box/size/removeUnusedOverride.kt
+++ b/compiler/testData/codegen/box/size/removeUnusedOverride.kt
@@ -3,8 +3,8 @@
// RUN_THIRD_PARTY_OPTIMIZER
// WASM_DCE_EXPECTED_OUTPUT_SIZE: wasm 54_786
-// WASM_DCE_EXPECTED_OUTPUT_SIZE: mjs 5_762
-// WASM_OPT_EXPECTED_OUTPUT_SIZE: 6_178
+// WASM_DCE_EXPECTED_OUTPUT_SIZE: mjs 5_821
+// WASM_OPT_EXPECTED_OUTPUT_SIZE: 6_261
interface I {
fun foo() = "OK"
diff --git a/libraries/stdlib/common-js-wasmjs/src/kotlin/js/JsError.kt b/libraries/stdlib/common-js-wasmjs/src/kotlin/js/JsError.kt
deleted file mode 100644
index 2c6c1a8..0000000
--- a/libraries/stdlib/common-js-wasmjs/src/kotlin/js/JsError.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-/*
- * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors.
- * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
- */
-
-package kotlin.js
-
-@ExperimentalWasmJsInterop
-@SinceKotlin("2.2")
-public expect class JsError : JsAny
diff --git a/libraries/stdlib/common-js-wasmjs/src/kotlin/js/JsPromiseError.kt b/libraries/stdlib/common-js-wasmjs/src/kotlin/js/JsPromiseError.kt
new file mode 100644
index 0000000..ce47312
--- /dev/null
+++ b/libraries/stdlib/common-js-wasmjs/src/kotlin/js/JsPromiseError.kt
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package kotlin.js
+
+@ExperimentalWasmJsInterop
+@SinceKotlin("2.2")
+@Suppress("EXPECT_ACTUAL_IR_INCOMPATIBILITY")
+public expect class JsPromiseError : JsAny
+
+@ExperimentalWasmJsInterop
+@SinceKotlin("2.2")
+public expect fun JsPromiseError.asJsException(): JsException
diff --git a/libraries/stdlib/common-js-wasmjs/src/kotlin/js/Promise.kt b/libraries/stdlib/common-js-wasmjs/src/kotlin/js/Promise.kt
index b31f166..8a406cc 100644
--- a/libraries/stdlib/common-js-wasmjs/src/kotlin/js/Promise.kt
+++ b/libraries/stdlib/common-js-wasmjs/src/kotlin/js/Promise.kt
@@ -12,20 +12,20 @@
*/
@ExperimentalWasmJsInterop
@SinceKotlin("2.2")
-public expect class Promise<out T : JsAny?>(executor: (resolve: (T) -> Unit, reject: (JsError) -> Unit) -> Unit): JsAny {
+public expect class Promise<out T : JsAny?>(executor: (resolve: (T) -> Unit, reject: (JsPromiseError) -> Unit) -> Unit): JsAny {
@LowPriorityInOverloadResolution
public fun <S : JsAny?> then(onFulfilled: ((T) -> S)?): Promise<S>
@LowPriorityInOverloadResolution
- public fun <S : JsAny?> then(onFulfilled: ((T) -> S)?, onRejected: ((JsError) -> S)?): Promise<S>
+ public fun <S : JsAny?> then(onFulfilled: ((T) -> S)?, onRejected: ((JsPromiseError) -> S)?): Promise<S>
- public fun <S : JsAny?> catch(onRejected: (JsError) -> S): Promise<S>
+ public fun <S : JsAny?> catch(onRejected: (JsPromiseError) -> S): Promise<S>
public fun finally(onFinally: () -> Unit): Promise<T>
public companion object {
public fun <S : JsAny?> all(promise: JsArray<out Promise<S>>): Promise<JsArray<out S>>
public fun <S : JsAny?> race(promise: JsArray<out Promise<S>>): Promise<S>
- public fun reject(e: JsError): Promise<Nothing>
+ public fun reject(e: JsPromiseError): Promise<Nothing>
public fun <S : JsAny?> resolve(e: S): Promise<S>
public fun <S : JsAny?> resolve(e: Promise<S>): Promise<S>
}
diff --git a/libraries/stdlib/js/src/kotlin/wasmJs/JsError.kt b/libraries/stdlib/js/src/kotlin/wasmJs/JsError.kt
deleted file mode 100644
index 2b76171..0000000
--- a/libraries/stdlib/js/src/kotlin/wasmJs/JsError.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors.
- * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
- */
-
-package kotlin.js
-
-@SinceKotlin("2.2")
-@ExperimentalWasmJsInterop
-public actual typealias JsError = Throwable
-
diff --git a/libraries/stdlib/js/src/kotlin/wasmJs/JsPromiseError.kt b/libraries/stdlib/js/src/kotlin/wasmJs/JsPromiseError.kt
new file mode 100644
index 0000000..aaaffdc
--- /dev/null
+++ b/libraries/stdlib/js/src/kotlin/wasmJs/JsPromiseError.kt
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package kotlin.js
+
+import kotlin.internal.InlineOnly
+
+@SinceKotlin("2.2")
+@ExperimentalWasmJsInterop
+public actual typealias JsPromiseError = Throwable
+
+@InlineOnly
+@ExperimentalWasmJsInterop
+@SinceKotlin("2.2")
+public actual inline fun JsPromiseError.asJsException(): Throwable = this
diff --git a/libraries/stdlib/wasm/js/src/kotlin/js/JsError.kt b/libraries/stdlib/wasm/js/src/kotlin/js/JsError.kt
deleted file mode 100644
index 6be0819..0000000
--- a/libraries/stdlib/wasm/js/src/kotlin/js/JsError.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors.
- * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
- */
-
-package kotlin.js
-
-import kotlin.wasm.internal.ExternalInterfaceType
-
-@SinceKotlin("2.2")
-@JsName("Error")
-@ExperimentalWasmJsInterop
-public actual external class JsError : JsAny {
- internal val message: String
- internal var name: String
- internal val stack: ExternalInterfaceType
- internal val cause: JsError?
- internal var kotlinException: JsReference<Throwable>?
-}
-
diff --git a/libraries/stdlib/wasm/js/src/kotlin/js/JsException.kt b/libraries/stdlib/wasm/js/src/kotlin/js/JsException.kt
index fedd561..67501ba 100644
--- a/libraries/stdlib/wasm/js/src/kotlin/js/JsException.kt
+++ b/libraries/stdlib/wasm/js/src/kotlin/js/JsException.kt
@@ -43,3 +43,13 @@
@ExperimentalWasmJsInterop
public actual val JsException.thrownValue: JsAny? get() = this.thrownValue
+
+@OptIn(ExperimentalWasmJsInterop::class)
+@JsName("Error")
+internal external class JsError : JsAny {
+ val message: String
+ var name: String
+ val stack: ExternalInterfaceType
+ val cause: JsError?
+ var kotlinException: JsReference<Throwable>?
+}
\ No newline at end of file
diff --git a/libraries/stdlib/wasm/js/src/kotlin/js/JsPromiseError.kt b/libraries/stdlib/wasm/js/src/kotlin/js/JsPromiseError.kt
new file mode 100644
index 0000000..a92e2d6
--- /dev/null
+++ b/libraries/stdlib/wasm/js/src/kotlin/js/JsPromiseError.kt
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+@file:Suppress("EXPECT_ACTUAL_INCOMPATIBLE_CLASS_KIND", "EXPECT_ACTUAL_INCOMPATIBLE_SUPERTYPES")
+package kotlin.js
+
+@SinceKotlin("2.2")
+@ExperimentalWasmJsInterop
+public actual typealias JsPromiseError = JsAny
+
+@ExperimentalWasmJsInterop
+@SinceKotlin("2.2")
+public actual fun JsPromiseError.asJsException(): JsException =
+ JsException(this)
diff --git a/libraries/stdlib/wasm/js/src/kotlin/js/Promise.kt b/libraries/stdlib/wasm/js/src/kotlin/js/Promise.kt
index 5c56e9c..4f483f4 100644
--- a/libraries/stdlib/wasm/js/src/kotlin/js/Promise.kt
+++ b/libraries/stdlib/wasm/js/src/kotlin/js/Promise.kt
@@ -2,7 +2,7 @@
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
-
+@file:Suppress("EXPECT_ACTUAL_INCOMPATIBLE_VISIBILITY")
package kotlin.js
import kotlin.internal.LowPriorityInOverloadResolution
@@ -12,27 +12,15 @@
*/
@ExperimentalWasmJsInterop
public actual external class Promise<out T : JsAny?>
-@SinceKotlin("2.2")
-@LowPriorityInOverloadResolution
-actual constructor(executor: (resolve: (T) -> Unit, reject: (JsError) -> Unit) -> Unit) : JsAny {
-
- public constructor(executor: (resolve: (T) -> Unit, reject: (JsAny) -> Unit) -> Unit)
+actual constructor(executor: (resolve: (T) -> Unit, reject: (JsAny) -> Unit) -> Unit) : JsAny {
@LowPriorityInOverloadResolution
public actual fun <S : JsAny?> then(onFulfilled: ((T) -> S)?): Promise<S>
- @SinceKotlin("2.2")
@LowPriorityInOverloadResolution
- public actual fun <S : JsAny?> then(onFulfilled: ((T) -> S)?, onRejected: ((JsError) -> S)?): Promise<S>
+ public actual fun <S : JsAny?> then(onFulfilled: ((T) -> S)?, onRejected: ((JsAny) -> S)?): Promise<S>
- @LowPriorityInOverloadResolution
- public fun <S : JsAny?> then(onFulfilled: ((T) -> S)?, onRejected: ((JsAny) -> S)?): Promise<S>
-
- @SinceKotlin("2.2")
- @LowPriorityInOverloadResolution
- public actual fun <S : JsAny?> catch(onRejected: (JsError) -> S): Promise<S>
-
- public fun <S : JsAny?> catch(onRejected: (JsAny) -> S): Promise<S>
+ public actual fun <S : JsAny?> catch(onRejected: (JsAny) -> S): Promise<S>
public actual fun finally(onFinally: () -> Unit): Promise<T>
@@ -40,10 +28,7 @@
public actual fun <S : JsAny?> all(promise: JsArray<out Promise<S>>): Promise<JsArray<out S>>
public actual fun <S : JsAny?> race(promise: JsArray<out Promise<S>>): Promise<S>
- @SinceKotlin("2.2")
- @LowPriorityInOverloadResolution
- public actual fun reject(e: JsError): Promise<Nothing>
- public fun reject(e: JsAny): Promise<Nothing>
+ public actual fun reject(e: JsAny): Promise<Nothing>
public actual fun <S : JsAny?> resolve(e: S): Promise<S>
public actual fun <S : JsAny?> resolve(e: Promise<S>): Promise<S>
diff --git a/libraries/tools/binary-compatibility-validator/klib-public-api/kotlin-stdlib.api b/libraries/tools/binary-compatibility-validator/klib-public-api/kotlin-stdlib.api
index 1390ae1..04d3d1d 100644
--- a/libraries/tools/binary-compatibility-validator/klib-public-api/kotlin-stdlib.api
+++ b/libraries/tools/binary-compatibility-validator/klib-public-api/kotlin-stdlib.api
@@ -15139,6 +15139,9 @@
final inline fun (kotlin/String).kotlin.text/uppercase(): kotlin/String // kotlin.text/uppercase|uppercase@kotlin.String(){}[0]
// Targets: [js]
+final inline fun (kotlin/Throwable).kotlin.js/asJsException(): kotlin/Throwable // kotlin.js/asJsException|asJsException@kotlin.Throwable(){}[0]
+
+// Targets: [js]
final inline fun /withType(kotlin/String, dynamic): dynamic // /withType|withType(kotlin.String;<dynamic>){}[0]
// Targets: [js]
@@ -15470,13 +15473,10 @@
// Targets: [wasmJs]
final class <#A: out kotlin.js/JsAny?> kotlin.js/Promise : kotlin.js/JsAny { // kotlin.js/Promise|null[0]
constructor <init>(kotlin/Function2<kotlin/Function1<#A, kotlin/Unit>, kotlin/Function1<kotlin.js/JsAny, kotlin/Unit>, kotlin/Unit>) // kotlin.js/Promise.<init>|<init>(kotlin.Function2<kotlin.Function1<1:0,kotlin.Unit>,kotlin.Function1<kotlin.js.JsAny,kotlin.Unit>,kotlin.Unit>){}[0]
- constructor <init>(kotlin/Function2<kotlin/Function1<#A, kotlin/Unit>, kotlin/Function1<kotlin.js/JsError, kotlin/Unit>, kotlin/Unit>) // kotlin.js/Promise.<init>|<init>(kotlin.Function2<kotlin.Function1<1:0,kotlin.Unit>,kotlin.Function1<kotlin.js.JsError,kotlin.Unit>,kotlin.Unit>){}[0]
final fun <#A1: kotlin.js/JsAny?> catch(kotlin/Function1<kotlin.js/JsAny, #A1>): kotlin.js/Promise<#A1> // kotlin.js/Promise.catch|catch(kotlin.Function1<kotlin.js.JsAny,0:0>){0§<kotlin.js.JsAny?>}[0]
- final fun <#A1: kotlin.js/JsAny?> catch(kotlin/Function1<kotlin.js/JsError, #A1>): kotlin.js/Promise<#A1> // kotlin.js/Promise.catch|catch(kotlin.Function1<kotlin.js.JsError,0:0>){0§<kotlin.js.JsAny?>}[0]
final fun <#A1: kotlin.js/JsAny?> then(kotlin/Function1<#A, #A1>?): kotlin.js/Promise<#A1> // kotlin.js/Promise.then|then(kotlin.Function1<1:0,0:0>?){0§<kotlin.js.JsAny?>}[0]
final fun <#A1: kotlin.js/JsAny?> then(kotlin/Function1<#A, #A1>?, kotlin/Function1<kotlin.js/JsAny, #A1>?): kotlin.js/Promise<#A1> // kotlin.js/Promise.then|then(kotlin.Function1<1:0,0:0>?;kotlin.Function1<kotlin.js.JsAny,0:0>?){0§<kotlin.js.JsAny?>}[0]
- final fun <#A1: kotlin.js/JsAny?> then(kotlin/Function1<#A, #A1>?, kotlin/Function1<kotlin.js/JsError, #A1>?): kotlin.js/Promise<#A1> // kotlin.js/Promise.then|then(kotlin.Function1<1:0,0:0>?;kotlin.Function1<kotlin.js.JsError,0:0>?){0§<kotlin.js.JsAny?>}[0]
final fun finally(kotlin/Function0<kotlin/Unit>): kotlin.js/Promise<#A> // kotlin.js/Promise.finally|finally(kotlin.Function0<kotlin.Unit>){}[0]
final object Companion { // kotlin.js/Promise.Companion|null[0]
@@ -15485,7 +15485,6 @@
final fun <#A2: kotlin.js/JsAny?> resolve(#A2): kotlin.js/Promise<#A2> // kotlin.js/Promise.Companion.resolve|resolve(0:0){0§<kotlin.js.JsAny?>}[0]
final fun <#A2: kotlin.js/JsAny?> resolve(kotlin.js/Promise<#A2>): kotlin.js/Promise<#A2> // kotlin.js/Promise.Companion.resolve|resolve(kotlin.js.Promise<0:0>){0§<kotlin.js.JsAny?>}[0]
final fun reject(kotlin.js/JsAny): kotlin.js/Promise<kotlin/Nothing> // kotlin.js/Promise.Companion.reject|reject(kotlin.js.JsAny){}[0]
- final fun reject(kotlin.js/JsError): kotlin.js/Promise<kotlin/Nothing> // kotlin.js/Promise.Companion.reject|reject(kotlin.js.JsError){}[0]
}
}
@@ -15496,11 +15495,6 @@
final class kotlin.js/JsBoolean : kotlin.js/JsAny // kotlin.js/JsBoolean|null[0]
// Targets: [wasmJs]
-final class kotlin.js/JsError : kotlin.js/JsAny { // kotlin.js/JsError|null[0]
- constructor <init>() // kotlin.js/JsError.<init>|<init>(){}[0]
-}
-
-// Targets: [wasmJs]
final class kotlin.js/JsException : kotlin/Throwable { // kotlin.js/JsException|null[0]
final val message // kotlin.js/JsException.message|{}message[0]
final fun <get-message>(): kotlin/String? // kotlin.js/JsException.message.<get-message>|<get-message>(){}[0]
@@ -15523,6 +15517,9 @@
final fun (kotlin.js/JsException).<get-thrownValue>(): kotlin.js/JsAny? // kotlin.js/thrownValue.<get-thrownValue>|<get-thrownValue>@kotlin.js.JsException(){}[0]
// Targets: [wasmJs]
+final fun (kotlin.js/JsAny).kotlin.js/asJsException(): kotlin.js/JsException // kotlin.js/asJsException|asJsException@kotlin.js.JsAny(){}[0]
+
+// Targets: [wasmJs]
final fun (kotlin.js/JsAny).kotlin.js/toThrowableOrNull(): kotlin/Throwable? // kotlin.js/toThrowableOrNull|toThrowableOrNull@kotlin.js.JsAny(){}[0]
// Targets: [wasmJs]