mirror of
https://github.com/mamoe/mirai.git
synced 2025-02-05 17:37:03 +08:00
- Take line number into account of ExceptionCollector.collect
;
- Fix suppression; - Add manual cleanup of references to help gc
This commit is contained in:
parent
5ebd65e78d
commit
9ceb7c3fc2
@ -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() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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())
|
||||||
|
Loading…
Reference in New Issue
Block a user