- Take line number into account of ExceptionCollector.collect;

- Fix suppression;
- Add manual cleanup of references to help gc
This commit is contained in:
Him188 2021-06-07 17:46:38 +08:00
parent 5ebd65e78d
commit 9ceb7c3fc2
2 changed files with 27 additions and 11 deletions

View File

@ -27,17 +27,27 @@ public class ExceptionCollector {
@Volatile @Volatile
private var last: Throwable? = null private var last: Throwable? = null
private val hashCodes = mutableSetOf<Int>() private val hashCodes = mutableSetOf<Long>()
@Synchronized @Synchronized
public fun collect(e: Throwable?) { public fun collect(e: Throwable?) {
if (e == null) return if (e == null) return
val hashCode = e.stackTrace.contentHashCode() if (!hashCodes.add(hash(e))) return // filter out duplications
if (!hashCodes.add(hashCode)) return // filter out duplications
// we can also check suppressed exceptions of [e] but actual influence would be slight. // we can also check suppressed exceptions of [e] but actual influence would be slight.
this.last?.let { e.addSuppressed(it) }
this.last = e this.last = e
} }
private fun hash(e: Throwable): Long {
return e.stackTrace.fold(0L) { acc, stackTraceElement ->
acc * 31 + hash(stackTraceElement).toLongUnsigned()
}
}
private fun hash(element: StackTraceElement): Int {
return element.lineNumber.hashCode() xor element.className.hashCode() xor element.methodName.hashCode()
}
public fun collectGet(e: Throwable?): Throwable { public fun collectGet(e: Throwable?): Throwable {
this.collect(e) this.collect(e)
return getLast()!! return getLast()!!
@ -64,18 +74,21 @@ public class ExceptionCollector {
@DslMarker @DslMarker
private annotation class TerminalOperation private annotation class TerminalOperation
@TestOnly @TestOnly // very slow
public fun asSequence(): Sequence<Throwable> { public fun asSequence(): Sequence<Throwable> {
fun Throwable.itr(): Iterator<Throwable> { fun Throwable.itr(): Iterator<Throwable> {
return (sequenceOf(this) + this.suppressed.asSequence().flatMap { it.itr().asSequence() }).iterator() return (sequenceOf(this) + this.suppressed.asSequence().flatMap { it.itr().asSequence() }).iterator()
} }
return Sequence { val last = getLast() ?: return emptySequence()
val last = getLast() ?: return@Sequence emptyList<Throwable>().iterator() return Sequence { last.itr() }
last.itr()
}
} }
@Synchronized
public fun dispose() { // help gc
this.last = null
this.hashCodes.clear()
}
} }
/** /**
@ -83,7 +96,9 @@ public class ExceptionCollector {
*/ */
public inline fun <R> withExceptionCollector(action: ExceptionCollector.() -> R): R { public inline fun <R> withExceptionCollector(action: ExceptionCollector.() -> R): R {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return ExceptionCollector().withExceptionCollector(action) return ExceptionCollector().run {
withExceptionCollector(action).also { dispose() }
}
} }
/** /**

View File

@ -11,6 +11,7 @@ package net.mamoe.mirai.utils
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertIs
import kotlin.test.assertSame import kotlin.test.assertSame
import kotlin.test.assertTrue import kotlin.test.assertTrue
@ -34,7 +35,7 @@ internal class ExceptionCollectorTest {
collector.collect(IllegalArgumentException()) collector.collect(IllegalArgumentException())
collector.collect(IllegalStateException()) collector.collect(IllegalStateException())
assertTrue { collector.getLast() is IllegalStateException } assertIs<IllegalStateException>(collector.getLast())
assertTrue { collector.getLast()!!.suppressed.single() is IllegalArgumentException } assertTrue { collector.getLast()!!.suppressed.single() is IllegalArgumentException }
assertEquals(2, collector.asSequence().count()) assertEquals(2, collector.asSequence().count())
} }
@ -47,7 +48,7 @@ internal class ExceptionCollectorTest {
collector.collect(IllegalArgumentException()) collector.collect(IllegalArgumentException())
collector.collect(IllegalStateException()) collector.collect(IllegalStateException())
assertTrue { collector.getLast() is IllegalStateException } assertIs<IllegalStateException>(collector.getLast())
assertTrue { collector.getLast()!!.suppressed.single() is IllegalArgumentException } assertTrue { collector.getLast()!!.suppressed.single() is IllegalArgumentException }
assertTrue { collector.getLast()!!.suppressed.single()!!.suppressed.single() is StackOverflowError } assertTrue { collector.getLast()!!.suppressed.single()!!.suppressed.single() is StackOverflowError }
assertEquals(3, collector.asSequence().count()) assertEquals(3, collector.asSequence().count())