diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ThrowableLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ThrowableLowering.kt index cab6f26fe5b..60671a76ec4 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ThrowableLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ThrowableLowering.kt @@ -24,12 +24,14 @@ class ThrowableLowering( val context: JsIrBackendContext, val extendThrowableFunction: IrSimpleFunctionSymbol ) : BodyLoweringPass { - private val nothingNType get() = context.irBuiltIns.nothingNType + private val nothingNType = context.irBuiltIns.nothingNType private val throwableConstructors = context.throwableConstructors private val newThrowableFunction = context.newThrowableSymbol + private val jsUndefined = context.intrinsics.jsUndefined.symbol fun nullValue(): IrExpression = IrConstImpl.constNull(UNDEFINED_OFFSET, UNDEFINED_OFFSET, nothingNType) + fun undefinedValue(): IrExpression = IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, nothingNType, jsUndefined, 0, 0) data class ThrowableArguments( val message: IrExpression, @@ -44,7 +46,7 @@ class ThrowableLowering( private fun IrFunctionAccessExpression.extractThrowableArguments(): ThrowableArguments = when (valueArgumentsCount) { - 0 -> ThrowableArguments(nullValue(), nullValue()) + 0 -> ThrowableArguments(nullValue(), undefinedValue()) 2 -> ThrowableArguments( message = getValueArgument(0)!!, cause = getValueArgument(1)!! @@ -53,10 +55,10 @@ class ThrowableLowering( val arg = getValueArgument(0)!! val parameter = symbol.owner.valueParameters[0] when { - parameter.type.isNullableString() -> ThrowableArguments(message = arg, cause = nullValue()) + parameter.type.isNullableString() -> ThrowableArguments(message = arg, cause = undefinedValue()) else -> { assert(parameter.type.makeNotNull().isThrowable()) - ThrowableArguments(message = nullValue(), cause = arg) + ThrowableArguments(message = undefinedValue(), cause = arg) } } } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/es6/semantics/IrBoxJsES6TestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/es6/semantics/IrBoxJsES6TestGenerated.java index 40ae1fc109f..e280c046d0e 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/es6/semantics/IrBoxJsES6TestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/es6/semantics/IrBoxJsES6TestGenerated.java @@ -7675,6 +7675,11 @@ public class IrBoxJsES6TestGenerated extends AbstractIrBoxJsES6Test { public void testStringPlus() throws Exception { runTest("js/js.translator/testData/box/standardClasses/stringPlus.kt"); } + + @TestMetadata("throwableCtor.kt") + public void testThrowableCtor() throws Exception { + runTest("js/js.translator/testData/box/standardClasses/throwableCtor.kt"); + } } @TestMetadata("js/js.translator/testData/box/superCall") diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java index e80dfd5f528..3622a1e324f 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java @@ -7675,6 +7675,11 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest { public void testStringPlus() throws Exception { runTest("js/js.translator/testData/box/standardClasses/stringPlus.kt"); } + + @TestMetadata("throwableCtor.kt") + public void testThrowableCtor() throws Exception { + runTest("js/js.translator/testData/box/standardClasses/throwableCtor.kt"); + } } @TestMetadata("js/js.translator/testData/box/superCall") diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java index 555c09afe59..2d949c7e7fe 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java @@ -7705,6 +7705,11 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { public void testStringPlus() throws Exception { runTest("js/js.translator/testData/box/standardClasses/stringPlus.kt"); } + + @TestMetadata("throwableCtor.kt") + public void testThrowableCtor() throws Exception { + runTest("js/js.translator/testData/box/standardClasses/throwableCtor.kt"); + } } @TestMetadata("js/js.translator/testData/box/superCall") diff --git a/js/js.translator/testData/box/standardClasses/throwableCtor.kt b/js/js.translator/testData/box/standardClasses/throwableCtor.kt new file mode 100644 index 00000000000..0fe4f0444bc --- /dev/null +++ b/js/js.translator/testData/box/standardClasses/throwableCtor.kt @@ -0,0 +1,10 @@ +// EXPECTED_REACHABLE_NODES: 1237 + +// KT-39964 + + +fun box(): String { + val e = Throwable(null, IllegalStateException("fail")) + if (e.message != null) return "FAIL" + return "OK" +} \ No newline at end of file diff --git a/libraries/stdlib/js-ir/runtime/coreRuntime.kt b/libraries/stdlib/js-ir/runtime/coreRuntime.kt index a65bef3d1f9..1e517e81e78 100644 --- a/libraries/stdlib/js-ir/runtime/coreRuntime.kt +++ b/libraries/stdlib/js-ir/runtime/coreRuntime.kt @@ -89,7 +89,7 @@ internal fun captureStack(instance: Throwable, constructorFunction: Any) { internal fun newThrowable(message: String?, cause: Throwable?): Throwable { val throwable = js("new Error()") - throwable.message = message ?: cause?.toString() ?: undefined + throwable.message = if (isUndefined(message)) cause?.toString() else message throwable.cause = cause throwable.name = "Throwable" return throwable.unsafeCast() @@ -102,7 +102,17 @@ internal fun extendThrowable(this_: dynamic, message: String?, cause: Throwable? internal fun setPropertiesToThrowableInstance(this_: dynamic, message: String?, cause: Throwable?) { if (!hasOwnPrototypeProperty(this_, "message")) { - this_.message = message ?: cause?.toString() ?: undefined + @Suppress("IfThenToElvis") + this_.message = if (message == null) { + @Suppress("SENSELESS_COMPARISON") + if (message !== null) { + // undefined + cause?.toString() ?: undefined + } else { + // real null + message + } + } else message } if (!hasOwnPrototypeProperty(this_, "cause")) { this_.cause = cause @@ -122,5 +132,8 @@ internal fun errorCode(description: String): Nothing { throw IllegalStateException(description) } +@Suppress("SENSELESS_COMPARISON") +internal fun isUndefined(value: dynamic): Boolean = value === undefined + internal fun boxIntrinsic(@Suppress("UNUSED_PARAMETER") x: T): R = error("Should be lowered") internal fun unboxIntrinsic(@Suppress("UNUSED_PARAMETER") x: T): R = error("Should be lowered")