From cd0ae20c38aafbb1f92b186fd155659b89257944 Mon Sep 17 00:00:00 2001 From: Artem Kobzar Date: Tue, 20 Dec 2022 18:15:02 +0000 Subject: [PATCH] [K/JS] Capture stackTrace before the init function call ^Fixed KT-55315 --- .../kotlin/ir/backend/js/JsLoweringPhases.kt | 17 +++++----- .../js/lower/CaptureStackTraceInThrowables.kt | 8 +++-- .../kotlin/js/test/BoxJsTestGenerated.java | 6 ++++ .../js/test/fir/FirJsBoxTestGenerated.java | 6 ++++ .../js/test/ir/IrBoxJsTestGenerated.java | 6 ++++ .../misc/stackTraceAccessInsideInitBlock.kt | 32 +++++++++++++++++++ 6 files changed, 65 insertions(+), 10 deletions(-) create mode 100644 js/js.translator/testData/box/expression/misc/stackTraceAccessInsideInitBlock.kt diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt index ad2f8f53f89..8e2502a9ee6 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt @@ -279,6 +279,12 @@ private val removeInlineDeclarationsWithReifiedTypeParametersLoweringPhase = mak prerequisite = setOf(functionInliningPhase) ) +private val captureStackTraceInThrowablesPhase = makeBodyLoweringPhase( + ::CaptureStackTraceInThrowables, + name = "CaptureStackTraceInThrowables", + description = "Capture stack trace in Throwable constructors" +) + private val throwableSuccessorsLoweringPhase = makeBodyLoweringPhase( { context -> context.run { @@ -289,7 +295,8 @@ private val throwableSuccessorsLoweringPhase = makeBodyLoweringPhase( } }, name = "ThrowableLowering", - description = "Link kotlin.Throwable and JavaScript Error together to provide proper interop between language and platform exceptions" + description = "Link kotlin.Throwable and JavaScript Error together to provide proper interop between language and platform exceptions", + prerequisite = setOf(captureStackTraceInThrowablesPhase) ) private val tailrecLoweringPhase = makeBodyLoweringPhase( @@ -783,12 +790,6 @@ private val objectUsageLoweringPhase = makeBodyLoweringPhase( description = "Transform IrGetObjectValue into instance generator call" ) -private val captureStackTraceInThrowablesPhase = makeBodyLoweringPhase( - ::CaptureStackTraceInThrowables, - name = "CaptureStackTraceInThrowables", - description = "Capture stack trace in Throwable constructors" -) - private val escapedIdentifiersLowering = makeBodyLoweringPhase( ::EscapedIdentifiersLowering, name = "EscapedIdentifiersLowering", @@ -901,6 +902,7 @@ val loweringList = listOf( defaultArgumentPatchOverridesPhase, defaultParameterInjectorPhase, defaultParameterCleanerPhase, + captureStackTraceInThrowablesPhase, throwableSuccessorsLoweringPhase, es6AddInternalParametersToConstructorPhase, es6ConstructorLowering, @@ -921,7 +923,6 @@ val loweringList = listOf( objectDeclarationLoweringPhase, invokeStaticInitializersPhase, objectUsageLoweringPhase, - captureStackTraceInThrowablesPhase, callsLoweringPhase, escapedIdentifiersLowering, implicitlyExportedDeclarationsMarkingLowering, diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CaptureStackTraceInThrowables.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CaptureStackTraceInThrowables.kt index 9b85d02ddf1..ec1897d4fb4 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CaptureStackTraceInThrowables.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CaptureStackTraceInThrowables.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.ir.declarations.IrConstructor import org.jetbrains.kotlin.ir.declarations.IrDeclaration import org.jetbrains.kotlin.ir.expressions.IrBlockBody import org.jetbrains.kotlin.ir.expressions.IrBody +import org.jetbrains.kotlin.ir.expressions.IrDelegatingConstructorCall import org.jetbrains.kotlin.ir.expressions.impl.IrRawFunctionReferenceImpl import org.jetbrains.kotlin.ir.util.isSubclassOf import org.jetbrains.kotlin.ir.util.parentAsClass @@ -30,12 +31,15 @@ class CaptureStackTraceInThrowables(val context: JsIrBackendContext) : BodyLower if (!klass.isSubclassOf(context.irBuiltIns.throwableClass.owner)) return - (irBody as IrBlockBody).statements += JsIrBuilder.buildCall(context.intrinsics.captureStack).also { call -> + val statements = (irBody as IrBlockBody).statements + val delegatingConstructorCallIndex = statements.indexOfLast { it is IrDelegatingConstructorCall } + + statements.add(delegatingConstructorCallIndex + 1, JsIrBuilder.buildCall(context.intrinsics.captureStack).also { call -> call.putValueArgument(0, JsIrBuilder.buildGetValue(klass.thisReceiver!!.symbol)) call.putValueArgument( 1, IrRawFunctionReferenceImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.anyType, container.symbol) ) - } + }) } } diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java index 34fe2a936e8..fc2b2b62ef2 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java @@ -3627,6 +3627,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { runTest("js/js.translator/testData/box/expression/misc/safeCallComputesExpressionOnlyOnce.kt"); } + @Test + @TestMetadata("stackTraceAccessInsideInitBlock.kt") + public void testStackTraceAccessInsideInitBlock() throws Exception { + runTest("js/js.translator/testData/box/expression/misc/stackTraceAccessInsideInitBlock.kt"); + } + @Test @TestMetadata("stackTraceCapturing.kt") public void testStackTraceCapturing() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsBoxTestGenerated.java index 6db0f6ac32a..a9e3891fbc5 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsBoxTestGenerated.java @@ -4249,6 +4249,12 @@ public class FirJsBoxTestGenerated extends AbstractFirJsBoxTest { runTest("js/js.translator/testData/box/expression/misc/safeCallComputesExpressionOnlyOnce.kt"); } + @Test + @TestMetadata("stackTraceAccessInsideInitBlock.kt") + public void testStackTraceAccessInsideInitBlock() throws Exception { + runTest("js/js.translator/testData/box/expression/misc/stackTraceAccessInsideInitBlock.kt"); + } + @Test @TestMetadata("stackTraceCapturing.kt") public void testStackTraceCapturing() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java index 1f50a3efea5..ba6d04094c7 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java @@ -4249,6 +4249,12 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest { runTest("js/js.translator/testData/box/expression/misc/safeCallComputesExpressionOnlyOnce.kt"); } + @Test + @TestMetadata("stackTraceAccessInsideInitBlock.kt") + public void testStackTraceAccessInsideInitBlock() throws Exception { + runTest("js/js.translator/testData/box/expression/misc/stackTraceAccessInsideInitBlock.kt"); + } + @Test @TestMetadata("stackTraceCapturing.kt") public void testStackTraceCapturing() throws Exception { diff --git a/js/js.translator/testData/box/expression/misc/stackTraceAccessInsideInitBlock.kt b/js/js.translator/testData/box/expression/misc/stackTraceAccessInsideInitBlock.kt new file mode 100644 index 00000000000..01ede0d065f --- /dev/null +++ b/js/js.translator/testData/box/expression/misc/stackTraceAccessInsideInitBlock.kt @@ -0,0 +1,32 @@ +// EXPECTED_REACHABLE_NODES: 1462 +// KJS_WITH_FULL_RUNTIME +// IGNORE_BACKEND: JS +// KT-55315 + +var stackTrace: String = "" + +open class MyOwnException1: Throwable("Test message 1") { + init { + stackTrace = asDynamic().stack + } +} + +class MyOwnException2: MyOwnException1() { + init { + stackTrace = asDynamic().stack + } +} + +fun box(): String { + try { + throw MyOwnException1() + } catch (e: Throwable) { + if (!stackTrace.contains("MyOwnException1: Test message 1\n")) return "fail" + } + try { + throw MyOwnException2() + } catch (e: Throwable) { + if (!stackTrace.contains("MyOwnException2: Test message 1\n")) return "fail" + } + return "OK" +} \ No newline at end of file