mirror of
https://github.com/mamoe/mirai.git
synced 2025-03-29 09:10:11 +08:00
Automatically produce detailed logs when failed to decode JceStructs, if 'mirai.jce.deserializer.debug' is set to true
, helps #1899
This commit is contained in:
parent
366ea34fde
commit
ceb192937f
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2019-2021 Mamoe Technologies and contributors.
|
* Copyright 2019-2022 Mamoe Technologies and contributors.
|
||||||
*
|
*
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||||
@ -17,6 +17,7 @@ import kotlinx.io.core.*
|
|||||||
import kotlinx.serialization.*
|
import kotlinx.serialization.*
|
||||||
import kotlinx.serialization.modules.EmptySerializersModule
|
import kotlinx.serialization.modules.EmptySerializersModule
|
||||||
import kotlinx.serialization.modules.SerializersModule
|
import kotlinx.serialization.modules.SerializersModule
|
||||||
|
import net.mamoe.mirai.internal.utils.io.serialization.tars.internal.DebugLogger
|
||||||
import net.mamoe.mirai.internal.utils.io.serialization.tars.internal.TarsDecoder
|
import net.mamoe.mirai.internal.utils.io.serialization.tars.internal.TarsDecoder
|
||||||
import net.mamoe.mirai.internal.utils.io.serialization.tars.internal.TarsInput
|
import net.mamoe.mirai.internal.utils.io.serialization.tars.internal.TarsInput
|
||||||
import net.mamoe.mirai.internal.utils.io.serialization.tars.internal.TarsOld
|
import net.mamoe.mirai.internal.utils.io.serialization.tars.internal.TarsOld
|
||||||
@ -38,8 +39,9 @@ internal class Tars(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> load(deserializer: DeserializationStrategy<T>, input: Input): T {
|
fun <T> load(deserializer: DeserializationStrategy<T>, input: Input, debugLogger: DebugLogger? = null): T {
|
||||||
return TarsDecoder(TarsInput(input, charset), serializersModule).decodeSerializableValue(deserializer)
|
val l = debugLogger ?: DebugLogger(null)
|
||||||
|
return TarsDecoder(TarsInput(input, charset, l), serializersModule, l).decodeSerializableValue(deserializer)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun <T> encodeToByteArray(serializer: SerializationStrategy<T>, value: T): ByteArray {
|
override fun <T> encodeToByteArray(serializer: SerializationStrategy<T>, value: T): ByteArray {
|
||||||
@ -48,7 +50,7 @@ internal class Tars(
|
|||||||
|
|
||||||
override fun <T> decodeFromByteArray(deserializer: DeserializationStrategy<T>, bytes: ByteArray): T {
|
override fun <T> decodeFromByteArray(deserializer: DeserializationStrategy<T>, bytes: ByteArray): T {
|
||||||
bytes.read {
|
bytes.read {
|
||||||
return load(deserializer, this)
|
return load(deserializer, this, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2019-2021 Mamoe Technologies and contributors.
|
* Copyright 2019-2022 Mamoe Technologies and contributors.
|
||||||
*
|
*
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||||
*
|
*
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
* https://github.com/mamoe/mirai/blob/dev/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@file:Suppress("PrivatePropertyName")
|
@file:Suppress("PrivatePropertyName")
|
||||||
@ -20,18 +20,37 @@ import kotlinx.serialization.internal.TaggedDecoder
|
|||||||
import kotlinx.serialization.modules.SerializersModule
|
import kotlinx.serialization.modules.SerializersModule
|
||||||
import net.mamoe.mirai.internal.utils.io.serialization.tars.Tars
|
import net.mamoe.mirai.internal.utils.io.serialization.tars.Tars
|
||||||
import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId
|
import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId
|
||||||
|
import net.mamoe.mirai.utils.MiraiLogger
|
||||||
|
import java.io.PrintStream
|
||||||
|
|
||||||
|
internal class DebugLogger(
|
||||||
|
val out: PrintStream?
|
||||||
|
) {
|
||||||
|
var structureHierarchy: Int = 0
|
||||||
|
|
||||||
|
fun println(message: Any?) {
|
||||||
|
out?.println(" ".repeat(structureHierarchy) + message)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun println() {
|
||||||
|
out?.println()
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun println(lazyMessage: () -> String) {
|
||||||
|
out?.println(" ".repeat(structureHierarchy) + lazyMessage())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@OptIn(InternalSerializationApi::class, ExperimentalSerializationApi::class)
|
@OptIn(InternalSerializationApi::class, ExperimentalSerializationApi::class)
|
||||||
internal class TarsDecoder(
|
internal class TarsDecoder(
|
||||||
val input: TarsInput, override val serializersModule: SerializersModule,
|
val input: TarsInput, override val serializersModule: SerializersModule, val debugLogger: DebugLogger
|
||||||
) : TaggedDecoder<TarsTag>() {
|
) : TaggedDecoder<TarsTag>() {
|
||||||
override fun SerialDescriptor.getTag(index: Int): TarsTag {
|
override fun SerialDescriptor.getTag(index: Int): TarsTag {
|
||||||
val annotations = this.getElementAnnotations(index)
|
val annotations = this.getElementAnnotations(index)
|
||||||
|
|
||||||
val id = annotations.filterIsInstance<TarsId>().single().id
|
val id = annotations.filterIsInstance<TarsId>().single().id
|
||||||
// ?: error("cannot find @TarsId or @ProtoNumber for ${this.getElementName(index)} in ${this.serialName}")
|
// ?: error("cannot find @TarsId or @ProtoNumber for ${this.getElementName(index)} in ${this.serialName}")
|
||||||
//println("getTag: ${this.getElementName(index)}=$id")
|
//debugLogger.println("getTag: ${this.getElementName(index)}=$id")
|
||||||
|
|
||||||
return TarsTagCommon(id)
|
return TarsTagCommon(id)
|
||||||
}
|
}
|
||||||
@ -79,7 +98,7 @@ internal class TarsDecoder(
|
|||||||
override fun decodeCollectionSize(descriptor: SerialDescriptor): Int {
|
override fun decodeCollectionSize(descriptor: SerialDescriptor): Int {
|
||||||
// 不要读下一个 head
|
// 不要读下一个 head
|
||||||
return input.currentHead.let { input.readTarsIntValue(it) }.also {
|
return input.currentHead.let { input.readTarsIntValue(it) }.also {
|
||||||
println { "SimpleByteArrayReader.decodeCollectionSize: $it" }
|
debugLogger.println { "SimpleByteArrayReader.decodeCollectionSize: $it" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -114,7 +133,7 @@ internal class TarsDecoder(
|
|||||||
override fun decodeString(): String = input.useHead { input.readTarsStringValue(it) }
|
override fun decodeString(): String = input.useHead { input.readTarsStringValue(it) }
|
||||||
|
|
||||||
override fun decodeCollectionSize(descriptor: SerialDescriptor): Int {
|
override fun decodeCollectionSize(descriptor: SerialDescriptor): Int {
|
||||||
//println("decodeCollectionSize: ${descriptor.serialName}")
|
//debugLogger.println("decodeCollectionSize: ${descriptor.serialName}")
|
||||||
// 不读下一个 head
|
// 不读下一个 head
|
||||||
return input.useHead { input.readTarsIntValue(it) }
|
return input.useHead { input.readTarsIntValue(it) }
|
||||||
}
|
}
|
||||||
@ -131,19 +150,19 @@ internal class TarsDecoder(
|
|||||||
override fun decodeElementIndex(descriptor: SerialDescriptor): Int = error("stub")
|
override fun decodeElementIndex(descriptor: SerialDescriptor): Int = error("stub")
|
||||||
|
|
||||||
override fun endStructure(descriptor: SerialDescriptor) {
|
override fun endStructure(descriptor: SerialDescriptor) {
|
||||||
println { "MapReader.endStructure: ${input.currentHeadOrNull}" }
|
debugLogger.println { "MapReader.endStructure: ${input.currentHeadOrNull}" }
|
||||||
this@TarsDecoder.endStructure(descriptor)
|
this@TarsDecoder.endStructure(descriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
|
override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
|
||||||
println { "MapReader.beginStructure: ${input.currentHead}" }
|
debugLogger.println { "MapReader.beginStructure: ${input.currentHead}" }
|
||||||
this@TarsDecoder.pushTag(
|
this@TarsDecoder.pushTag(
|
||||||
when (input.currentHead.tag) {
|
when (input.currentHead.tag) {
|
||||||
0 -> TarsTagMapEntryKey()
|
0 -> TarsTagMapEntryKey()
|
||||||
1 -> TarsTagMapEntryValue()
|
1 -> TarsTagMapEntryValue()
|
||||||
else -> error("illegal map entry head: ${input.currentHead.tag}")
|
else -> error("illegal map entry head: ${input.currentHead.tag}")
|
||||||
}.also {
|
}.also {
|
||||||
println("MapReader.pushTag $it")
|
debugLogger.println("MapReader.pushTag $it")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return this@TarsDecoder.beginStructure(descriptor)
|
return this@TarsDecoder.beginStructure(descriptor)
|
||||||
@ -161,16 +180,16 @@ internal class TarsDecoder(
|
|||||||
override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = decodeInt()
|
override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = decodeInt()
|
||||||
override fun decodeString(): String = input.useHead { head ->
|
override fun decodeString(): String = input.useHead { head ->
|
||||||
input.readTarsStringValue(head).also {
|
input.readTarsStringValue(head).also {
|
||||||
println { "MapReader.decodeString: $it" }
|
debugLogger.println { "MapReader.decodeString: $it" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun decodeCollectionSize(descriptor: SerialDescriptor): Int {
|
override fun decodeCollectionSize(descriptor: SerialDescriptor): Int {
|
||||||
println { "decodeCollectionSize in MapReader: ${descriptor.serialName}" }
|
debugLogger.println { "decodeCollectionSize in MapReader: ${descriptor.serialName}" }
|
||||||
// 不读下一个 head
|
// 不读下一个 head
|
||||||
return input.useHead { head ->
|
return input.useHead { head ->
|
||||||
input.readTarsIntValue(head).also {
|
input.readTarsIntValue(head).also {
|
||||||
println { "decodeCollectionSize = $it" }
|
debugLogger.println { "decodeCollectionSize = $it" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -178,10 +197,10 @@ internal class TarsDecoder(
|
|||||||
|
|
||||||
|
|
||||||
override fun endStructure(descriptor: SerialDescriptor) {
|
override fun endStructure(descriptor: SerialDescriptor) {
|
||||||
structureHierarchy--
|
debugLogger.structureHierarchy--
|
||||||
println { "endStructure: ${descriptor.serialName}, $currentTagOrNull, ${input.currentHeadOrNull}" }
|
debugLogger.println { "endStructure: ${descriptor.serialName}, $currentTagOrNull, ${input.currentHeadOrNull}" }
|
||||||
if (currentTagOrNull?.isSimpleByteArray == true) {
|
if (currentTagOrNull?.isSimpleByteArray == true) {
|
||||||
println { "endStructure: prepareNextHead() called" }
|
debugLogger.println { "endStructure: prepareNextHead() called" }
|
||||||
currentTag.isSimpleByteArray = false
|
currentTag.isSimpleByteArray = false
|
||||||
input.prepareNextHead() // read to next head
|
input.prepareNextHead() // read to next head
|
||||||
}
|
}
|
||||||
@ -193,10 +212,10 @@ internal class TarsDecoder(
|
|||||||
val currentHead = input.currentHeadOrNull ?: return
|
val currentHead = input.currentHeadOrNull ?: return
|
||||||
if (currentHead.type == Tars.STRUCT_END) {
|
if (currentHead.type == Tars.STRUCT_END) {
|
||||||
input.prepareNextHead()
|
input.prepareNextHead()
|
||||||
//println("current end")
|
//debugLogger.println("current end")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
//println("current $currentHead")
|
//debugLogger.println("current $currentHead")
|
||||||
input.skipField(currentHead.type)
|
input.skipField(currentHead.type)
|
||||||
input.prepareNextHead()
|
input.prepareNextHead()
|
||||||
}
|
}
|
||||||
@ -208,48 +227,32 @@ internal class TarsDecoder(
|
|||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@Suppress("MemberVisibilityCanBePrivate")
|
val logger = MiraiLogger.Factory.create(TarsDecoder::class, "TarsDecoder")
|
||||||
var debuggingMode: Boolean = false
|
|
||||||
|
|
||||||
var structureHierarchy: Int = 0
|
|
||||||
|
|
||||||
inline fun println(value: () -> String) {
|
|
||||||
if (debuggingMode) {
|
|
||||||
kotlin.io.println(" ".repeat(structureHierarchy) + value())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
inline fun println(value: Any? = "") {
|
|
||||||
if (debuggingMode) {
|
|
||||||
kotlin.io.println(" ".repeat(structureHierarchy) + value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
|
override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
|
||||||
println()
|
debugLogger.println()
|
||||||
println { "beginStructure: ${descriptor.serialName}, ${descriptor.kind}" }
|
debugLogger.println { "beginStructure: ${descriptor.serialName}, ${descriptor.kind}" }
|
||||||
structureHierarchy++
|
debugLogger.structureHierarchy++
|
||||||
return when (descriptor.kind) {
|
return when (descriptor.kind) {
|
||||||
is PrimitiveKind -> this@TarsDecoder
|
is PrimitiveKind -> this@TarsDecoder
|
||||||
|
|
||||||
StructureKind.MAP -> {
|
StructureKind.MAP -> {
|
||||||
val tag = popTag()
|
val tag = popTag()
|
||||||
// println("!! MAP, tag=$tag")
|
// debugLogger.println("!! MAP, tag=$tag")
|
||||||
return input.skipToHeadAndUseIfPossibleOrFail(tag.id) {
|
return input.skipToHeadAndUseIfPossibleOrFail(tag.id) {
|
||||||
it.checkType(Tars.MAP, "beginStructure", tag, descriptor)
|
it.checkType(Tars.MAP, "beginStructure", tag, descriptor)
|
||||||
MapReader
|
MapReader
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StructureKind.LIST -> {
|
StructureKind.LIST -> {
|
||||||
//println("!! ByteArray")
|
//debugLogger.println("!! ByteArray")
|
||||||
//println("decoderTag: $currentTagOrNull")
|
//debugLogger.println("decoderTag: $currentTagOrNull")
|
||||||
//println("TarsHead: " + Tars.currentHeadOrNull)
|
//debugLogger.println("TarsHead: " + Tars.currentHeadOrNull)
|
||||||
return input.skipToHeadAndUseIfPossibleOrFail(currentTag.id) {
|
return input.skipToHeadAndUseIfPossibleOrFail(currentTag.id) {
|
||||||
// don't check type. it's polymorphic
|
// don't check type. it's polymorphic
|
||||||
|
|
||||||
//println("listHead: $it")
|
//debugLogger.println("listHead: $it")
|
||||||
when (it.type) {
|
when (it.type) {
|
||||||
Tars.SIMPLE_LIST -> {
|
Tars.SIMPLE_LIST -> {
|
||||||
currentTag.isSimpleByteArray = true
|
currentTag.isSimpleByteArray = true
|
||||||
@ -264,9 +267,9 @@ internal class TarsDecoder(
|
|||||||
StructureKind.CLASS -> {
|
StructureKind.CLASS -> {
|
||||||
currentTagOrNull ?: return this@TarsDecoder // outermost
|
currentTagOrNull ?: return this@TarsDecoder // outermost
|
||||||
|
|
||||||
//println("!! CLASS")
|
//debugLogger.println("!! CLASS")
|
||||||
//println("decoderTag: $currentTag")
|
//debugLogger.println("decoderTag: $currentTag")
|
||||||
//println("TarsHead: " + Tars.currentHeadOrNull)
|
//debugLogger.println("TarsHead: " + Tars.currentHeadOrNull)
|
||||||
val tag = popTag()
|
val tag = popTag()
|
||||||
return input.skipToHeadAndUseIfPossibleOrFail(tag.id) { TarsHead ->
|
return input.skipToHeadAndUseIfPossibleOrFail(tag.id) { TarsHead ->
|
||||||
TarsHead.checkType(Tars.STRUCT_BEGIN, "beginStructure", tag, descriptor)
|
TarsHead.checkType(Tars.STRUCT_BEGIN, "beginStructure", tag, descriptor)
|
||||||
@ -288,21 +291,21 @@ internal class TarsDecoder(
|
|||||||
override fun decodeSequentially(): Boolean = false
|
override fun decodeSequentially(): Boolean = false
|
||||||
override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
|
override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
|
||||||
var tarsHead = input.currentHeadOrNull ?: kotlin.run {
|
var tarsHead = input.currentHeadOrNull ?: kotlin.run {
|
||||||
println("decodeElementIndex: currentHead == null")
|
debugLogger.println("decodeElementIndex: currentHead == null")
|
||||||
return CompositeDecoder.DECODE_DONE
|
return CompositeDecoder.DECODE_DONE
|
||||||
}
|
}
|
||||||
|
|
||||||
println { "decodeElementIndex: ${input.currentHead}" }
|
debugLogger.println { "decodeElementIndex: ${input.currentHead}" }
|
||||||
while (!input.input.endOfInput) {
|
while (!input.input.endOfInput) {
|
||||||
if (tarsHead.type == Tars.STRUCT_END) {
|
if (tarsHead.type == Tars.STRUCT_END) {
|
||||||
println { "decodeElementIndex: ${input.currentHead}" }
|
debugLogger.println { "decodeElementIndex: ${input.currentHead}" }
|
||||||
return CompositeDecoder.DECODE_DONE
|
return CompositeDecoder.DECODE_DONE
|
||||||
}
|
}
|
||||||
|
|
||||||
repeat(descriptor.elementsCount) {
|
repeat(descriptor.elementsCount) {
|
||||||
val tag = descriptor.getTarsTagId(it)
|
val tag = descriptor.getTarsTagId(it)
|
||||||
if (tag == tarsHead.tag) {
|
if (tag == tarsHead.tag) {
|
||||||
println {
|
debugLogger.println {
|
||||||
"name=" + descriptor.getElementName(
|
"name=" + descriptor.getElementName(
|
||||||
it
|
it
|
||||||
)
|
)
|
||||||
@ -313,11 +316,11 @@ internal class TarsDecoder(
|
|||||||
|
|
||||||
input.skipField(tarsHead.type)
|
input.skipField(tarsHead.type)
|
||||||
if (!input.prepareNextHead()) {
|
if (!input.prepareNextHead()) {
|
||||||
println { "decodeElementIndex EOF" }
|
debugLogger.println { "decodeElementIndex EOF" }
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
tarsHead = input.currentHead
|
tarsHead = input.currentHead
|
||||||
println { "next! $tarsHead" }
|
debugLogger.println { "next! $tarsHead" }
|
||||||
}
|
}
|
||||||
|
|
||||||
return CompositeDecoder.DECODE_DONE // optional support
|
return CompositeDecoder.DECODE_DONE // optional support
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2019-2021 Mamoe Technologies and contributors.
|
* Copyright 2019-2022 Mamoe Technologies and contributors.
|
||||||
*
|
*
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||||
@ -12,14 +12,13 @@ package net.mamoe.mirai.internal.utils.io.serialization.tars.internal
|
|||||||
import kotlinx.io.charsets.Charset
|
import kotlinx.io.charsets.Charset
|
||||||
import kotlinx.io.core.*
|
import kotlinx.io.core.*
|
||||||
import net.mamoe.mirai.internal.utils.io.serialization.tars.Tars
|
import net.mamoe.mirai.internal.utils.io.serialization.tars.Tars
|
||||||
import net.mamoe.mirai.internal.utils.io.serialization.tars.internal.TarsDecoder.Companion.println
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tars Input. 需要手动管理 head.
|
* Tars Input. 需要手动管理 head.
|
||||||
*/
|
*/
|
||||||
internal class TarsInput(
|
internal class TarsInput(
|
||||||
val input: Input, private val charset: Charset,
|
val input: Input, private val charset: Charset, private val debugLogger: DebugLogger,
|
||||||
) {
|
) {
|
||||||
private var _head: TarsHead? = null
|
private var _head: TarsHead? = null
|
||||||
private var _nextHead: TarsHead? = null
|
private var _nextHead: TarsHead? = null
|
||||||
@ -114,7 +113,7 @@ internal class TarsInput(
|
|||||||
crossinline message: () -> String = { "tag not found: $tag" },
|
crossinline message: () -> String = { "tag not found: $tag" },
|
||||||
crossinline block: (TarsHead) -> R,
|
crossinline block: (TarsHead) -> R,
|
||||||
): R {
|
): R {
|
||||||
return checkNotNull<R>(skipToHeadAndUseIfPossibleOrNull(tag, block), message)
|
return checkNotNull(skipToHeadAndUseIfPossibleOrNull(tag, block), message)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("MemberVisibilityCanBePrivate")
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
@ -170,30 +169,30 @@ internal class TarsInput(
|
|||||||
Tars.STRING1 -> this.input.discardExact(this.input.readUByte().toInt())
|
Tars.STRING1 -> this.input.discardExact(this.input.readUByte().toInt())
|
||||||
Tars.STRING4 -> this.input.discardExact(this.input.readInt())
|
Tars.STRING4 -> this.input.discardExact(this.input.readInt())
|
||||||
Tars.MAP -> { // map
|
Tars.MAP -> { // map
|
||||||
TarsDecoder.structureHierarchy++
|
debugLogger.structureHierarchy++
|
||||||
repeat(readInt32(0).also {
|
repeat(readInt32(0).also {
|
||||||
println("SIZE = $it")
|
println("SIZE = $it")
|
||||||
} * 2) {
|
} * 2) {
|
||||||
skipField(nextHead().type)
|
skipField(nextHead().type)
|
||||||
}
|
}
|
||||||
TarsDecoder.structureHierarchy--
|
debugLogger.structureHierarchy--
|
||||||
}
|
}
|
||||||
Tars.LIST -> { // list
|
Tars.LIST -> { // list
|
||||||
TarsDecoder.structureHierarchy++
|
debugLogger.structureHierarchy++
|
||||||
repeat(readInt32(0).also {
|
repeat(readInt32(0).also {
|
||||||
println("SIZE = $it")
|
println("SIZE = $it")
|
||||||
}) {
|
}) {
|
||||||
skipField(nextHead().type)
|
skipField(nextHead().type)
|
||||||
}
|
}
|
||||||
TarsDecoder.structureHierarchy--
|
debugLogger.structureHierarchy--
|
||||||
}
|
}
|
||||||
Tars.STRUCT_BEGIN -> {
|
Tars.STRUCT_BEGIN -> {
|
||||||
TarsDecoder.structureHierarchy++
|
debugLogger.structureHierarchy++
|
||||||
var head: TarsHead
|
var head: TarsHead
|
||||||
do {
|
do {
|
||||||
head = nextHead()
|
head = nextHead()
|
||||||
if (head.type == Tars.STRUCT_END) {
|
if (head.type == Tars.STRUCT_END) {
|
||||||
TarsDecoder.structureHierarchy--
|
debugLogger.structureHierarchy--
|
||||||
skipField(head.type)
|
skipField(head.type)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -203,7 +202,7 @@ internal class TarsInput(
|
|||||||
Tars.STRUCT_END, Tars.ZERO_TYPE -> {
|
Tars.STRUCT_END, Tars.ZERO_TYPE -> {
|
||||||
}
|
}
|
||||||
Tars.SIMPLE_LIST -> {
|
Tars.SIMPLE_LIST -> {
|
||||||
TarsDecoder.structureHierarchy++
|
debugLogger.structureHierarchy++
|
||||||
var head = nextHead()
|
var head = nextHead()
|
||||||
check(head.type == Tars.BYTE) { "bad simple list element type: " + head.type + ", $head" }
|
check(head.type == Tars.BYTE) { "bad simple list element type: " + head.type + ", $head" }
|
||||||
check(head.tag == 0) { "simple list element tag must be 0, but was ${head.tag}" }
|
check(head.tag == 0) { "simple list element tag must be 0, but was ${head.tag}" }
|
||||||
@ -211,7 +210,7 @@ internal class TarsInput(
|
|||||||
head = nextHead()
|
head = nextHead()
|
||||||
check(head.tag == 0) { "tag for size for simple list must be 0, but was ${head.tag}" }
|
check(head.tag == 0) { "tag for size for simple list must be 0, but was ${head.tag}" }
|
||||||
this.input.discardExact(readTarsIntValue(head))
|
this.input.discardExact(readTarsIntValue(head))
|
||||||
TarsDecoder.structureHierarchy--
|
debugLogger.structureHierarchy--
|
||||||
}
|
}
|
||||||
else -> error("invalid type: $type")
|
else -> error("invalid type: $type")
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2019-2021 Mamoe Technologies and contributors.
|
* Copyright 2019-2022 Mamoe Technologies and contributors.
|
||||||
*
|
*
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||||
*
|
*
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
* https://github.com/mamoe/mirai/blob/dev/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@file:JvmName("SerializationUtils")
|
@file:JvmName("SerializationUtils")
|
||||||
@ -14,9 +14,11 @@
|
|||||||
package net.mamoe.mirai.internal.utils.io.serialization
|
package net.mamoe.mirai.internal.utils.io.serialization
|
||||||
|
|
||||||
import kotlinx.io.core.*
|
import kotlinx.io.core.*
|
||||||
|
import kotlinx.io.streams.asInput
|
||||||
import kotlinx.serialization.DeserializationStrategy
|
import kotlinx.serialization.DeserializationStrategy
|
||||||
import kotlinx.serialization.SerializationStrategy
|
import kotlinx.serialization.SerializationStrategy
|
||||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
|
import net.mamoe.mirai.internal.message.contextualBugReportException
|
||||||
import net.mamoe.mirai.internal.network.protocol.data.jce.RequestDataVersion2
|
import net.mamoe.mirai.internal.network.protocol.data.jce.RequestDataVersion2
|
||||||
import net.mamoe.mirai.internal.network.protocol.data.jce.RequestDataVersion3
|
import net.mamoe.mirai.internal.network.protocol.data.jce.RequestDataVersion3
|
||||||
import net.mamoe.mirai.internal.network.protocol.data.jce.RequestPacket
|
import net.mamoe.mirai.internal.network.protocol.data.jce.RequestPacket
|
||||||
@ -24,9 +26,12 @@ import net.mamoe.mirai.internal.network.protocol.data.proto.OidbSso
|
|||||||
import net.mamoe.mirai.internal.utils.io.JceStruct
|
import net.mamoe.mirai.internal.utils.io.JceStruct
|
||||||
import net.mamoe.mirai.internal.utils.io.ProtoBuf
|
import net.mamoe.mirai.internal.utils.io.ProtoBuf
|
||||||
import net.mamoe.mirai.internal.utils.io.serialization.tars.Tars
|
import net.mamoe.mirai.internal.utils.io.serialization.tars.Tars
|
||||||
|
import net.mamoe.mirai.internal.utils.io.serialization.tars.internal.DebugLogger
|
||||||
|
import net.mamoe.mirai.internal.utils.io.serialization.tars.internal.TarsDecoder
|
||||||
import net.mamoe.mirai.internal.utils.printStructure
|
import net.mamoe.mirai.internal.utils.printStructure
|
||||||
import net.mamoe.mirai.utils.read
|
import net.mamoe.mirai.utils.*
|
||||||
import net.mamoe.mirai.utils.readPacketExact
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.io.PrintStream
|
||||||
import kotlin.contracts.InvocationKind
|
import kotlin.contracts.InvocationKind
|
||||||
import kotlin.contracts.contract
|
import kotlin.contracts.contract
|
||||||
|
|
||||||
@ -39,7 +44,7 @@ internal fun <T : JceStruct> ByteArray.loadWithUniPacket(
|
|||||||
|
|
||||||
internal fun <T : JceStruct> ByteArray.loadAs(
|
internal fun <T : JceStruct> ByteArray.loadAs(
|
||||||
deserializer: DeserializationStrategy<T>,
|
deserializer: DeserializationStrategy<T>,
|
||||||
): T = this.read { Tars.UTF_8.load(deserializer, this) }
|
): T = this.read { readJceStruct(deserializer) }
|
||||||
|
|
||||||
internal fun <T : JceStruct> BytePacketBuilder.writeJceStruct(
|
internal fun <T : JceStruct> BytePacketBuilder.writeJceStruct(
|
||||||
serializer: SerializationStrategy<T>,
|
serializer: SerializationStrategy<T>,
|
||||||
@ -52,11 +57,58 @@ internal fun <T : JceStruct> ByteReadPacket.readJceStruct(
|
|||||||
serializer: DeserializationStrategy<T>,
|
serializer: DeserializationStrategy<T>,
|
||||||
length: Int = this.remaining.toInt(),
|
length: Int = this.remaining.toInt(),
|
||||||
): T {
|
): T {
|
||||||
this.readPacketExact(length).use {
|
if (JCE_DESERIALIZER_DEBUG) {
|
||||||
return Tars.UTF_8.load(serializer, it)
|
// since 2.11
|
||||||
|
this.useBytes(n = length) { data, arrayLength ->
|
||||||
|
try {
|
||||||
|
return data.inputStream(offset = 0, length = arrayLength).asInput().use { input ->
|
||||||
|
Tars.UTF_8.load(serializer, input)
|
||||||
|
}
|
||||||
|
} catch (originalException: Exception) {
|
||||||
|
val log = ByteArrayOutputStream()
|
||||||
|
try {
|
||||||
|
val value = PrintStream(log).use { stream ->
|
||||||
|
stream.println("\nData: ")
|
||||||
|
stream.println(data.toUHexString(length = arrayLength))
|
||||||
|
stream.println("Trace:")
|
||||||
|
|
||||||
|
data.inputStream(offset = 0, length = arrayLength).asInput().use { input ->
|
||||||
|
Tars.UTF_8.load(serializer, input, debugLogger = DebugLogger(stream))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value.also {
|
||||||
|
TarsDecoder.logger.warning(
|
||||||
|
contextualBugReportException(
|
||||||
|
"解析 " + serializer.descriptor.serialName,
|
||||||
|
"启用 debug 模式后解析正常: $value \n\n${log.toByteArray().decodeToString()}",
|
||||||
|
originalException
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (secondFailure: Exception) {
|
||||||
|
throw contextualBugReportException(
|
||||||
|
"解析 " + serializer.descriptor.serialName,
|
||||||
|
log.toByteArray().decodeToString(),
|
||||||
|
ExceptionCollector.compressExceptions(originalException, secondFailure)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// since 2.0
|
||||||
|
return this.readPacketExact(length).use {
|
||||||
|
Tars.UTF_8.load(serializer, it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 2.11
|
||||||
|
*/
|
||||||
|
internal var JCE_DESERIALIZER_DEBUG by lateinitMutableProperty {
|
||||||
|
systemProp("mirai.jce.deserializer.debug", false)
|
||||||
|
}
|
||||||
|
|
||||||
internal fun <T : JceStruct> BytePacketBuilder.writeJceRequestPacket(
|
internal fun <T : JceStruct> BytePacketBuilder.writeJceRequestPacket(
|
||||||
version: Int = 3,
|
version: Int = 3,
|
||||||
servantName: String,
|
servantName: String,
|
||||||
|
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019-2022 Mamoe Technologies and contributors.
|
||||||
|
*
|
||||||
|
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||||
|
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||||
|
*
|
||||||
|
* https://github.com/mamoe/mirai/blob/dev/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.mamoe.mirai.internal.utils.io.serialization.tars.internal
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import net.mamoe.mirai.internal.test.AbstractTest
|
||||||
|
import net.mamoe.mirai.internal.utils.io.JceStruct
|
||||||
|
import net.mamoe.mirai.internal.utils.io.serialization.JCE_DESERIALIZER_DEBUG
|
||||||
|
import net.mamoe.mirai.internal.utils.io.serialization.readJceStruct
|
||||||
|
import net.mamoe.mirai.internal.utils.io.serialization.tars.Tars
|
||||||
|
import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId
|
||||||
|
import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
|
||||||
|
import net.mamoe.mirai.utils.toReadPacket
|
||||||
|
import net.mamoe.mirai.utils.toUHexString
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.io.PrintStream
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertFails
|
||||||
|
|
||||||
|
class DebugLoggerTest : AbstractTest() {
|
||||||
|
init {
|
||||||
|
JCE_DESERIALIZER_DEBUG = true
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Struct(
|
||||||
|
@TarsId(1) val str: String,
|
||||||
|
@TarsId(2) val int: Int,
|
||||||
|
) : JceStruct
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `can log`() {
|
||||||
|
val out = ByteArrayOutputStream()
|
||||||
|
val logger = DebugLogger(PrintStream(out))
|
||||||
|
val original = Struct("string", 1)
|
||||||
|
val bytes = original.toByteArray(Struct.serializer())
|
||||||
|
val value = bytes.toReadPacket().use { Tars.UTF_8.load(Struct.serializer(), it, logger) }
|
||||||
|
assertEquals(original, value)
|
||||||
|
assertEquals(
|
||||||
|
"""
|
||||||
|
beginStructure: net.mamoe.mirai.internal.utils.io.serialization.tars.internal.DebugLoggerTest.Struct, CLASS
|
||||||
|
decodeElementIndex: TarsHead(tag=1, type=6(String1))
|
||||||
|
name=str
|
||||||
|
decodeElementIndex: TarsHead(tag=2, type=0(Byte))
|
||||||
|
name=int
|
||||||
|
decodeElementIndex: currentHead == null
|
||||||
|
endStructure: net.mamoe.mirai.internal.utils.io.serialization.tars.internal.DebugLoggerTest.Struct, null, null
|
||||||
|
""".trimIndent(), out.toByteArray().decodeToString().trim()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `can auto log`() {
|
||||||
|
val original = Struct("string", 1)
|
||||||
|
val bytes = original.toByteArray(Struct.serializer())
|
||||||
|
println(bytes.toUHexString()) // 16 06 73 74 72 69 6E 67 20 01
|
||||||
|
bytes[bytes.lastIndex - 1] = 0x30.toByte() // change tag
|
||||||
|
val exception = assertFails { bytes.toReadPacket().use { it.readJceStruct(Struct.serializer()) } }
|
||||||
|
assertEquals(
|
||||||
|
"""
|
||||||
|
在 解析 net.mamoe.mirai.internal.utils.io.serialization.tars.internal.DebugLoggerTest.Struct 时遇到了意料之中的问题. 请完整复制此日志提交给 mirai: https://github.com/mamoe/mirai/issues/new/choose 调试信息:
|
||||||
|
Data:
|
||||||
|
16 06 73 74 72 69 6E 67 30 01
|
||||||
|
Trace:
|
||||||
|
|
||||||
|
beginStructure: net.mamoe.mirai.internal.utils.io.serialization.tars.internal.DebugLoggerTest.Struct, CLASS
|
||||||
|
decodeElementIndex: TarsHead(tag=1, type=6(String1))
|
||||||
|
name=str
|
||||||
|
decodeElementIndex: TarsHead(tag=3, type=0(Byte))
|
||||||
|
""".trimIndent(), exception.message!!.trim()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user