From f0224c6c07240a877bbdab4800ecc90823a4ef4f Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Tue, 17 Mar 2020 07:25:35 +0300 Subject: [PATCH] Introduce Throwable.stackTraceToString #KT-37603 (cherry picked from commit 1cc88979314ae8bb48ad77625fdd5bce91dd74a8) --- runtime/src/main/kotlin/kotlin/Throwable.kt | 52 +++++++++++++++------ 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/runtime/src/main/kotlin/kotlin/Throwable.kt b/runtime/src/main/kotlin/kotlin/Throwable.kt index 816b61c57dc..93dbc3429ae 100644 --- a/runtime/src/main/kotlin/kotlin/Throwable.kt +++ b/runtime/src/main/kotlin/kotlin/Throwable.kt @@ -43,34 +43,45 @@ public open class Throwable(open val message: String?, open val cause: Throwable (0 until stackTrace.size).map { index -> stackTrace[index].toLong() } /** - * Prints the stack trace of this throwable to the standard output. + * Prints the [detailed description][Throwable.stackTraceToString] of this throwable to the standard output. */ - public fun printStackTrace(): Unit = dumpStackTrace { println(it) } + public fun printStackTrace(): Unit = dumpStackTrace("", "") { println(it) } internal fun dumpStackTrace(): String = buildString { - dumpStackTrace { appendln(it) } + dumpStackTrace("", "") { appendln(it) } } - private inline fun writeStackTraceElements(throwable: Throwable, writeln: (String) -> Unit) { - for (element in throwable.stackTraceStrings) { - writeln(" at $element") - } - } - - private inline fun dumpStackTrace(crossinline writeln: (String) -> Unit) { - writeln(this@Throwable.toString()) - - writeStackTraceElements(this, writeln) + private fun Throwable.dumpStackTrace(indent: String, qualifier: String, writeln: (String) -> Unit) { + this.dumpSelfTrace(indent, qualifier, writeln) var cause = this.cause while (cause != null) { // TODO: should skip common stack frames - writeln("Caused by: $cause") - writeStackTraceElements(cause, writeln) + cause.dumpSelfTrace(indent, "Caused by: ", writeln) cause = cause.cause } } + private fun Throwable.dumpSelfTrace(indent: String, qualifier: String, writeln: (String) -> Unit) { + writeln(indent + qualifier + this.toString()) + for (element in stackTraceStrings) { + writeln("$indent\tat $element") + } + val suppressed = suppressedExceptionsList + if (!suppressed.isNullOrEmpty()) { + val suppressedIndent = indent + '\t' + for (s in suppressed) { + s.dumpStackTrace(suppressedIndent, "Suppressed: ", writeln) + } + } + } + + + /** + * Returns the short description of this throwable consisting of + * the exception class name (fully qualified if possible) + * followed by the exception message if it is not null. + */ public override fun toString(): String { val kClass = this::class val s = kClass.qualifiedName ?: kClass.simpleName ?: "Throwable" @@ -86,6 +97,17 @@ private external fun getCurrentStackTrace(): NativePtrArray @SymbolName("Kotlin_getStackTraceStrings") private external fun getStackTraceStrings(stackTrace: NativePtrArray): Array +/** + * Returns the detailed description of this throwable with its stack trace. + * + * The detailed description includes: + * - the short description (see [Throwable.toString]) of this throwable; + * - the complete stack trace; + * - detailed descriptions of the exceptions that were [suppressed][suppressedExceptions] in order to deliver this exception; + * - the detailed description of each throwable in the [Throwable.cause] chain. + */ +@SinceKotlin("1.4") +public actual fun Throwable.stackTraceToString(): String = dumpStackTrace() /** * Adds the specified exception to the list of exceptions that were