diff --git a/js/js.translator/testData/box/expression/try/overrideThrowableProperties.kt b/js/js.translator/testData/box/expression/try/overrideThrowableProperties.kt index 6f985a30695..b5abab03733 100644 --- a/js/js.translator/testData/box/expression/try/overrideThrowableProperties.kt +++ b/js/js.translator/testData/box/expression/try/overrideThrowableProperties.kt @@ -2,20 +2,57 @@ open class Ex0(msg: String, cs: Throwable): Throwable(msg, cs) -class Ex1: Ex0("A", Error("fail2")) { +open class Ex1: Ex0("A", Error("fail2")) { override val cause = Error("B") } -class Ex2: Ex0("fail3", Error("C")) { +open class Ex2: Ex0("fail3", Error("C")) { override val message = "D" } -fun box(): String { - val ex1: Throwable = Ex1() - val ex2: Throwable = Ex2() +open class Ex3: Ex0("fail", Error("fail")) { + override val message get() = "O" - val r = ex1.message + ex1.cause?.message + ex2.cause?.message + ex2.message - if (r != "ABCD") return "Fail: $r" + override val cause get() = Error("K") +} + +open class Ex4: Ex3() + +class Ex5: Ex4() { + override val message: String + get() = "!" +} + +@JsExport +open class A : Throwable("AM", Error("AC")) + +@JsExport +open class B : A() { + override val message = "BM" + + override val cause = Error("BC") +} + +@JsExport +class C : B() + +fun Throwable.check(expectedMessage: String, expectedCauseMessage: String?) { + assertEquals(expectedMessage, message) + assertEquals(expectedCauseMessage, cause?.message) +} + +fun box(): String { + + Ex1().check("A", "B") + Ex2().check("D", "C") + + Ex3().check("O", "K") + Ex4().check("O", "K") + Ex5().check("!", "K") + + A().check("AM", "AC") + B().check("BM", "BC") + C().check("BM", "BC") 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 e2fea15ad69..83521273b7e 100644 --- a/libraries/stdlib/js-ir/runtime/coreRuntime.kt +++ b/libraries/stdlib/js-ir/runtime/coreRuntime.kt @@ -103,7 +103,8 @@ internal fun extendThrowable(this_: dynamic, message: String?, cause: Throwable? } internal fun setPropertiesToThrowableInstance(this_: dynamic, message: String?, cause: Throwable?) { - if (!hasOwnPrototypeProperty(this_, "message")) { + val errorInfo = calculateErrorInfo(JsObject.getPrototypeOf(this_)) + if ((errorInfo and 0x1) == 0) { @Suppress("IfThenToElvis") this_.message = if (message == null) { @Suppress("SENSELESS_COMPARISON") @@ -116,7 +117,7 @@ internal fun setPropertiesToThrowableInstance(this_: dynamic, message: String?, } } else message } - if (!hasOwnPrototypeProperty(this_, "cause")) { + if ((errorInfo and 0x2) == 0) { this_.cause = cause } this_.name = JsObject.getPrototypeOf(this_).constructor.name diff --git a/libraries/stdlib/js-ir/runtime/typeCheckUtils.kt b/libraries/stdlib/js-ir/runtime/typeCheckUtils.kt index dd78d6982cd..970d96d8000 100644 --- a/libraries/stdlib/js-ir/runtime/typeCheckUtils.kt +++ b/libraries/stdlib/js-ir/runtime/typeCheckUtils.kt @@ -90,6 +90,8 @@ internal external interface Metadata { val iid: Int? var `$kClass$`: dynamic + + var errorInfo: Int? // Bits set for overridden properties: "message" => 0x1, "cause" => 0x2 } internal external interface Ctor { @@ -99,9 +101,32 @@ internal external interface Ctor { val prototype: dynamic } -@Suppress("UNUSED_PARAMETER") -private fun getPrototypeOf(obj: dynamic) = - js("Object.getPrototypeOf(obj)") +private fun hasProp(proto: dynamic, propName: String): Boolean = proto.hasOwnProperty(propName) + +internal fun calculateErrorInfo(proto: dynamic): Int { + val metadata: Metadata? = proto.constructor?.`$metadata$` + + metadata?.errorInfo?.let { return it } // cached + + var result = 0 + if (hasProp(proto, "message")) result = result or 0x1 + if (hasProp(proto, "cause")) result = result or 0x2 + + if (result != 0x3) { // + val parentProto = getPrototypeOf(proto) + if (parentProto != js("Error").prototype) { + result = result or calculateErrorInfo(parentProto) + } + } + + if (metadata != null) { + metadata.errorInfo = result + } + + return result +} + +private fun getPrototypeOf(obj: dynamic) = JsObject.getPrototypeOf(obj) private fun searchForMetadata(obj: dynamic): Metadata? { if (obj == null) {