Prevent infinite recursion in case of circular suppressed chains

#KT-37603

(cherry picked from commit 3346ad37999edf69efd1c49382bb987da48e281c)
This commit is contained in:
Ilya Gorbunov
2020-03-19 05:45:39 +03:00
committed by Vasily Levchenko
parent f0224c6c07
commit b80b1d65ca
+12 -7
View File
@@ -45,24 +45,28 @@ public open class Throwable(open val message: String?, open val cause: Throwable
/** /**
* Prints the [detailed description][Throwable.stackTraceToString] 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) }, mutableSetOf())
internal fun dumpStackTrace(): String = buildString { internal fun dumpStackTrace(): String = buildString {
dumpStackTrace("", "") { appendln(it) } dumpStackTrace("", "", { appendln(it) }, mutableSetOf())
} }
private fun Throwable.dumpStackTrace(indent: String, qualifier: String, writeln: (String) -> Unit) { private fun Throwable.dumpStackTrace(indent: String, qualifier: String, writeln: (String) -> Unit, visited: MutableSet<Throwable>) {
this.dumpSelfTrace(indent, qualifier, writeln) this.dumpSelfTrace(indent, qualifier, writeln, visited) || return
var cause = this.cause var cause = this.cause
while (cause != null) { while (cause != null) {
// TODO: should skip common stack frames // TODO: should skip common stack frames
cause.dumpSelfTrace(indent, "Caused by: ", writeln) cause.dumpSelfTrace(indent, "Caused by: ", writeln, visited)
cause = cause.cause cause = cause.cause
} }
} }
private fun Throwable.dumpSelfTrace(indent: String, qualifier: String, writeln: (String) -> Unit) { private fun Throwable.dumpSelfTrace(indent: String, qualifier: String, writeln: (String) -> Unit, visited: MutableSet<Throwable>): Boolean {
if (!visited.add(this)) {
writeln(indent + qualifier + "[CIRCULAR REFERENCE, SEE ABOVE: $this]")
return false
}
writeln(indent + qualifier + this.toString()) writeln(indent + qualifier + this.toString())
for (element in stackTraceStrings) { for (element in stackTraceStrings) {
writeln("$indent\tat $element") writeln("$indent\tat $element")
@@ -71,9 +75,10 @@ public open class Throwable(open val message: String?, open val cause: Throwable
if (!suppressed.isNullOrEmpty()) { if (!suppressed.isNullOrEmpty()) {
val suppressedIndent = indent + '\t' val suppressedIndent = indent + '\t'
for (s in suppressed) { for (s in suppressed) {
s.dumpStackTrace(suppressedIndent, "Suppressed: ", writeln) s.dumpStackTrace(suppressedIndent, "Suppressed: ", writeln, visited)
} }
} }
return true
} }