Skip to content

Commit

Permalink
Allow CopyableThrowable to modify exception message
Browse files Browse the repository at this point in the history
  • Loading branch information
qwwdfsad committed Jun 7, 2021
1 parent f8a2123 commit 113e034
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 1 deletion.
5 changes: 5 additions & 0 deletions kotlinx-coroutines-core/common/src/Debug.common.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,15 @@ public interface CopyableThrowable<T> where T : Throwable, T : CopyableThrowable

/**
* Creates a copy of the current instance.
*
* For better debuggability, it is recommended to use original exception as [cause][Throwable.cause] of the resulting one.
* Stacktrace of copied exception will be overwritten by stacktrace recovery machinery by [Throwable.setStackTrace] call.
* An exception can opt-out of copying by returning `null` from this function.
* Suppressed exceptions of the original exception should not be copied in order to avoid circular exceptions.
*
* This function is allowed to create a copy with modified [message][Throwable.message], but it should be noted
* that the copy can be later recovered as well and message modification code should handle this situation correctly
* (e.g. by also storing the original message and checking over it) to produce a human-readable result.
*/
public fun createCopy(): T?
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ private fun <E : Throwable> recoverFromStackFrame(exception: E, continuation: Co
private fun <E : Throwable> tryCopyAndVerify(exception: E): E? {
val newException = tryCopyException(exception) ?: return null
// Verify that the new exception has the same message as the original one (bail out if not, see #1631)
if (newException.message != exception.message) return null
// CopyableThrowable has control over its message and thus can modify iit in the way it want
if (exception !is CopyableThrowable<*> && newException.message != exception.message) return null
return newException
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,35 @@ class StackTraceRecoveryCustomExceptionsTest : TestBase() {
throw WrongMessageException("OK")
}
val ex = runCatching {
@Suppress("ControlFlowWithEmptyBody")
for (unit in result) {
// Iterator has a special code path
}
}.exceptionOrNull() ?: error("Expected to fail")
assertTrue(ex is WrongMessageException)
assertEquals("Token OK", ex.message)
}

class CopyableWithCustomMessage(
message: String?,
cause: Throwable? = null
) : RuntimeException(message, cause),
CopyableThrowable<CopyableWithCustomMessage> {

override fun createCopy(): CopyableWithCustomMessage {
return CopyableWithCustomMessage("Recovered: [$message]", cause)
}
}

@Test
fun testCustomCopyableMessage() = runTest {
val result = runCatching {
coroutineScope<Unit> {
throw CopyableWithCustomMessage("OK")
}
}
val ex = result.exceptionOrNull() ?: error("Expected to fail")
assertTrue(ex is CopyableWithCustomMessage)
assertEquals("Recovered: [OK]", ex.message)
}
}

0 comments on commit 113e034

Please sign in to comment.