Skip to content

Commit 2b8698f

Browse files
committed
Call initCause during reflective exception copy in runCatching block to avoid DispatchException when initCause throws
Fixes #933
1 parent c34aeea commit 2b8698f

File tree

2 files changed

+29
-1
lines changed

2 files changed

+29
-1
lines changed

core/kotlinx-coroutines-core/src/internal/ExceptionsConstuctor.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ internal fun <E : Throwable> tryCopyException(exception: E): E? {
3535
ctor = { e -> runCatching { constructor.newInstance(e) as E }.getOrNull() }
3636
break
3737
} else if (parameters.isEmpty()) {
38-
ctor = { e -> runCatching { constructor.newInstance() as E }.getOrNull()?.also { it.initCause(e) } }
38+
ctor = { e -> runCatching { (constructor.newInstance() as E).also { it.initCause(e) } }.getOrNull() }
3939
break
4040
}
4141
}

core/kotlinx-coroutines-core/test/exceptions/StackTraceRecoveryTest.kt

+28
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,34 @@ class StackTraceRecoveryTest : TestBase() {
188188
deferred.join()
189189
}
190190

191+
public class TrickyException() : Throwable() {
192+
// To be sure ctor is never invoked
193+
@Suppress("UNUSED", "UNUSED_PARAMETER")
194+
private constructor(message: String, cause: Throwable): this() {
195+
error("Should never be called")
196+
}
197+
198+
override fun initCause(cause: Throwable?): Throwable {
199+
error("Can't call initCause")
200+
}
201+
}
202+
203+
@Test
204+
fun testThrowingInitCause() = runTest {
205+
val deferred = async(NonCancellable) {
206+
expect(2)
207+
throw TrickyException()
208+
}
209+
210+
try {
211+
expect(1)
212+
deferred.await()
213+
} catch (e: TrickyException) {
214+
assertNull(e.cause)
215+
finish(3)
216+
}
217+
}
218+
191219
private suspend fun outerScopedMethod(deferred: Deferred<Nothing>, vararg traces: String) = coroutineScope {
192220
innerMethod(deferred, *traces)
193221
assertTrue(true)

0 commit comments

Comments
 (0)