From bf98ab78589004c4c5b2555a3299942837bea2a4 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 18 Dec 2021 23:41:58 +0000 Subject: [PATCH] Move `_contentToString` outside mirai-core main sourceSets, and rename it to `structureToString` --- .../commonMain/kotlin/contact/AbstractUser.kt | 4 +- .../commonMain/kotlin/message/imagesImpl.kt | 4 +- .../kotlin/message/incomingSourceImpl.kt | 8 +- .../notice/UnconsumedNoticesAlerter.kt | 8 +- .../group/GroupNotificationProcessor.kt | 4 +- .../group/GroupOrMemberListNoticeProcessor.kt | 8 +- .../notice/priv/FriendNoticeProcessor.kt | 4 +- .../notice/priv/OtherClientNoticeProcessor.kt | 6 +- .../network/protocol/packet/chat/MultiMsg.kt | 8 +- .../network/protocol/packet/login/StatSvc.kt | 4 +- .../network/protocol/packet/login/WtLogin.kt | 14 +- .../kotlin/utils/contentToString.kt | 222 +++--------------- .../kotlin/utils/io/serialization/utils.kt | 4 +- .../kotlin/message/data/ForwardRefineTest.kt | 4 +- .../message/data/MessageSerializationTest.kt | 4 +- .../kotlin/utils/StructureToStringLegacy.kt | 212 +++++++++++++++++ 16 files changed, 289 insertions(+), 229 deletions(-) create mode 100644 mirai-core/src/jvmTest/kotlin/utils/StructureToStringLegacy.kt diff --git a/mirai-core/src/commonMain/kotlin/contact/AbstractUser.kt b/mirai-core/src/commonMain/kotlin/contact/AbstractUser.kt index a0017452e..9b15fc7d6 100644 --- a/mirai-core/src/commonMain/kotlin/contact/AbstractUser.kt +++ b/mirai-core/src/commonMain/kotlin/contact/AbstractUser.kt @@ -31,7 +31,7 @@ import net.mamoe.mirai.internal.network.protocol.packet.chat.image.LongConn import net.mamoe.mirai.internal.network.protocol.packet.sendAndExpect import net.mamoe.mirai.internal.utils.AtomicIntSeq import net.mamoe.mirai.internal.utils.C2CPkgMsgParsingCache -import net.mamoe.mirai.internal.utils._miraiContentToString +import net.mamoe.mirai.internal.utils.structureToString import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.message.data.* import net.mamoe.mirai.utils.* @@ -112,7 +112,7 @@ internal sealed class AbstractUser( } else { throw contextualBugReportException( "Failed to compute friend image image from resourceId: ${resp.resourceId}", - resp._miraiContentToString(), + resp.structureToString(), additional = "并附加此时正在上传的文件" ) } diff --git a/mirai-core/src/commonMain/kotlin/message/imagesImpl.kt b/mirai-core/src/commonMain/kotlin/message/imagesImpl.kt index 55f161ac2..bfa2efadc 100644 --- a/mirai-core/src/commonMain/kotlin/message/imagesImpl.kt +++ b/mirai-core/src/commonMain/kotlin/message/imagesImpl.kt @@ -21,9 +21,9 @@ import net.mamoe.mirai.contact.ContactOrBot import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.User import net.mamoe.mirai.internal.network.protocol.data.proto.* -import net.mamoe.mirai.internal.utils._miraiContentToString import net.mamoe.mirai.internal.utils.io.serialization.loadAs import net.mamoe.mirai.internal.utils.io.serialization.toByteArray +import net.mamoe.mirai.internal.utils.structureToString import net.mamoe.mirai.message.data.* import net.mamoe.mirai.message.data.Image.Key.IMAGE_ID_REGEX import net.mamoe.mirai.utils.* @@ -109,7 +109,7 @@ OnlineFriendImage() { Image.logger.warning( contextualBugReportException( "Failed to compute friend imageId: resId=${delegate.resId}", - delegate._miraiContentToString(), + delegate.structureToString(), additional = "并描述此时 Bot 是否正在从好友或群接受消息, 尽量附加该图片原文件" ) ) diff --git a/mirai-core/src/commonMain/kotlin/message/incomingSourceImpl.kt b/mirai-core/src/commonMain/kotlin/message/incomingSourceImpl.kt index 0a8e8f5c4..ffca228e6 100644 --- a/mirai-core/src/commonMain/kotlin/message/incomingSourceImpl.kt +++ b/mirai-core/src/commonMain/kotlin/message/incomingSourceImpl.kt @@ -25,8 +25,8 @@ import net.mamoe.mirai.internal.getGroupByUinOrCodeOrFail import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm import net.mamoe.mirai.internal.network.protocol.data.proto.SourceMsg -import net.mamoe.mirai.internal.utils._miraiContentToString import net.mamoe.mirai.internal.utils.io.serialization.toByteArray +import net.mamoe.mirai.internal.utils.structureToString import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageSourceKind import net.mamoe.mirai.message.data.OnlineMessageSource @@ -177,10 +177,10 @@ internal class OnlineMessageSourceFromGroupImpl( override val subject: GroupImpl by lazy { val groupCode = msg.first().msgHead.groupInfo?.groupCode - ?: error("cannot find groupCode for OnlineMessageSourceFromGroupImpl. msg=${msg._miraiContentToString()}") + ?: error("cannot find groupCode for OnlineMessageSourceFromGroupImpl. msg=${msg.structureToString()}") val group = bot.getGroup(groupCode)?.checkIsGroupImpl() - ?: error("cannot find group for OnlineMessageSourceFromGroupImpl. msg=${msg._miraiContentToString()}") + ?: error("cannot find group for OnlineMessageSourceFromGroupImpl. msg=${msg.structureToString()}") group } @@ -192,7 +192,7 @@ internal class OnlineMessageSourceFromGroupImpl( if (member != null) return@lazy member val anonymousInfo = msg.first().msgBody.richText.elems.firstOrNull { it.anonGroupMsg != null } - ?: error("cannot find member for OnlineMessageSourceFromGroupImpl. msg=${msg._miraiContentToString()}") + ?: error("cannot find member for OnlineMessageSourceFromGroupImpl. msg=${msg.structureToString()}") anonymousInfo.run { group.newAnonymous(anonGroupMsg!!.anonNick.decodeToString(), anonGroupMsg.anonId.encodeBase64()) diff --git a/mirai-core/src/commonMain/kotlin/network/notice/UnconsumedNoticesAlerter.kt b/mirai-core/src/commonMain/kotlin/network/notice/UnconsumedNoticesAlerter.kt index aca2be60c..a5466163f 100644 --- a/mirai-core/src/commonMain/kotlin/network/notice/UnconsumedNoticesAlerter.kt +++ b/mirai-core/src/commonMain/kotlin/network/notice/UnconsumedNoticesAlerter.kt @@ -20,7 +20,7 @@ import net.mamoe.mirai.internal.network.protocol.data.proto.MsgOnlinePush import net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans import net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg import net.mamoe.mirai.internal.network.protocol.packet.chat.NewContact -import net.mamoe.mirai.internal.utils._miraiContentToString +import net.mamoe.mirai.internal.utils.structureToString import net.mamoe.mirai.utils.* internal class UnconsumedNoticesAlerter( @@ -81,7 +81,7 @@ internal class UnconsumedNoticesAlerter( logger.debug( contextualBugReportException( "解析 OnlinePush.PbPushTransMsg, msgType=${data.msgType}", - data._miraiContentToString(), + data.structureToString(), null, "并描述此时机器人是否被踢出, 或是否有成员列表变更等动作.", ) @@ -127,7 +127,7 @@ internal class UnconsumedNoticesAlerter( data.msg?.context { throw contextualBugReportException( "解析 NewContact.SystemMsgNewGroup, subType=$subType, groupMsgType=$groupMsgType", - forDebug = this._miraiContentToString(), + forDebug = this.structureToString(), additional = "并尽量描述此时机器人是否正被邀请加入群, 或者是有有新群员加入此群", ) } @@ -139,7 +139,7 @@ internal class UnconsumedNoticesAlerter( if (logger.isEnabled && logger.isDebugEnabled) { throw contextualBugReportException( "decode SvcRequestPushStatus (PC Client status change)", - data._miraiContentToString(), + data.structureToString(), additional = "unknown status=${data.status}", ) } diff --git a/mirai-core/src/commonMain/kotlin/network/notice/group/GroupNotificationProcessor.kt b/mirai-core/src/commonMain/kotlin/network/notice/group/GroupNotificationProcessor.kt index 5a47c27b5..5f9d60783 100644 --- a/mirai-core/src/commonMain/kotlin/network/notice/group/GroupNotificationProcessor.kt +++ b/mirai-core/src/commonMain/kotlin/network/notice/group/GroupNotificationProcessor.kt @@ -28,9 +28,9 @@ import net.mamoe.mirai.internal.network.protocol.data.jce.MsgType0x210 import net.mamoe.mirai.internal.network.protocol.data.proto.Submsgtype0x122 import net.mamoe.mirai.internal.network.protocol.data.proto.Submsgtype0x27 import net.mamoe.mirai.internal.network.protocol.data.proto.TroopTips0x857 -import net.mamoe.mirai.internal.utils._miraiContentToString import net.mamoe.mirai.internal.utils.io.serialization.loadAs import net.mamoe.mirai.internal.utils.parseToMessageDataList +import net.mamoe.mirai.internal.utils.structureToString import net.mamoe.mirai.utils.* internal class GroupNotificationProcessor( @@ -362,7 +362,7 @@ internal class GroupNotificationProcessor( else -> { markNotConsumed() logger.debug { - "Unknown Transformers528 0x14 template\ntemplId=${grayTip?.templId}\nPermList=${grayTip?.msgTemplParam?._miraiContentToString()}" + "Unknown Transformers528 0x14 template\ntemplId=${grayTip?.templId}\nPermList=${grayTip?.msgTemplParam?.structureToString()}" } } } diff --git a/mirai-core/src/commonMain/kotlin/network/notice/group/GroupOrMemberListNoticeProcessor.kt b/mirai-core/src/commonMain/kotlin/network/notice/group/GroupOrMemberListNoticeProcessor.kt index 203845cd7..759f2d0d7 100644 --- a/mirai-core/src/commonMain/kotlin/network/notice/group/GroupOrMemberListNoticeProcessor.kt +++ b/mirai-core/src/commonMain/kotlin/network/notice/group/GroupOrMemberListNoticeProcessor.kt @@ -33,9 +33,9 @@ import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm import net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans import net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg import net.mamoe.mirai.internal.network.protocol.data.proto.Submsgtype0x44 -import net.mamoe.mirai.internal.utils._miraiContentToString import net.mamoe.mirai.internal.utils.io.serialization.loadAs import net.mamoe.mirai.internal.utils.parseToMessageDataList +import net.mamoe.mirai.internal.utils.structureToString import net.mamoe.mirai.internal.utils.toMemberInfo import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.context @@ -212,7 +212,7 @@ internal class GroupOrMemberListNoticeProcessor( } else { throw contextualBugReportException( "解析 NewContact.SystemMsgNewGroup, subType=5, groupMsgType=$groupMsgType", - data._miraiContentToString(), + data.structureToString(), null, "并描述此时机器人是否被邀请加入群等其他", ) @@ -244,7 +244,7 @@ internal class GroupOrMemberListNoticeProcessor( } else -> throw contextualBugReportException( "parse SystemMsgNewGroup, subType=1", - this._miraiContentToString(), + this.structureToString(), additional = "并尽量描述此时机器人是否正被邀请加入群, 或者是有有新群员加入此群" ) } @@ -287,7 +287,7 @@ internal class GroupOrMemberListNoticeProcessor( else -> { throw contextualBugReportException( "解析 NewContact.SystemMsgNewGroup, subType=5, groupMsgType=$groupMsgType", - this._miraiContentToString(), + this.structureToString(), null, "并描述此时机器人是否被踢出群等", ) diff --git a/mirai-core/src/commonMain/kotlin/network/notice/priv/FriendNoticeProcessor.kt b/mirai-core/src/commonMain/kotlin/network/notice/priv/FriendNoticeProcessor.kt index e764c91fa..3521ed24e 100644 --- a/mirai-core/src/commonMain/kotlin/network/notice/priv/FriendNoticeProcessor.kt +++ b/mirai-core/src/commonMain/kotlin/network/notice/priv/FriendNoticeProcessor.kt @@ -36,9 +36,9 @@ import net.mamoe.mirai.internal.network.protocol.data.proto.Submsgtype0xb3.SubMs import net.mamoe.mirai.internal.network.protocol.packet.chat.NewContact import net.mamoe.mirai.internal.network.protocol.packet.list.FriendList.GetFriendGroupList import net.mamoe.mirai.internal.network.protocol.packet.sendAndExpect -import net.mamoe.mirai.internal.utils._miraiContentToString import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.internal.utils.io.serialization.loadAs +import net.mamoe.mirai.internal.utils.structureToString import net.mamoe.mirai.utils.* /** @@ -216,7 +216,7 @@ internal class FriendNoticeProcessor( } } if (body.msgProfileInfos.isEmpty() || containsUnknown) { - logger.debug { "Transformers528 0x27L: ProfileChanged new data: ${body._miraiContentToString()}" } + logger.debug { "Transformers528 0x27L: ProfileChanged new data: ${body.structureToString()}" } } } diff --git a/mirai-core/src/commonMain/kotlin/network/notice/priv/OtherClientNoticeProcessor.kt b/mirai-core/src/commonMain/kotlin/network/notice/priv/OtherClientNoticeProcessor.kt index b470ce1e9..855fdbe48 100644 --- a/mirai-core/src/commonMain/kotlin/network/notice/priv/OtherClientNoticeProcessor.kt +++ b/mirai-core/src/commonMain/kotlin/network/notice/priv/OtherClientNoticeProcessor.kt @@ -31,8 +31,8 @@ import net.mamoe.mirai.internal.network.handler.logger import net.mamoe.mirai.internal.network.protocol.data.jce.RequestPushStatus import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm import net.mamoe.mirai.internal.network.protocol.data.proto.SubMsgType0x7 -import net.mamoe.mirai.internal.utils._miraiContentToString import net.mamoe.mirai.internal.utils.io.serialization.loadAs +import net.mamoe.mirai.internal.utils.structureToString import net.mamoe.mirai.message.data.PlainText import net.mamoe.mirai.message.data.buildMessageChain import net.mamoe.mirai.utils.context @@ -70,9 +70,9 @@ internal class OtherClientNoticeProcessor : MixedNoticeProcessor() { bot.network.logger.warning( contextualBugReportException( "SvcRequestPushStatus (OtherClient online)", - "packet: \n" + data._miraiContentToString() + + "packet: \n" + data.structureToString() + "\n\nquery: \n" + - Mirai.getOnlineOtherClientsList(bot)._miraiContentToString(), + Mirai.getOnlineOtherClientsList(bot).structureToString(), additional = "Failed to find corresponding instanceInfo.", ), ) diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/MultiMsg.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/MultiMsg.kt index 7843b7eb5..44ff388dd 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/MultiMsg.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/MultiMsg.kt @@ -26,10 +26,10 @@ import net.mamoe.mirai.internal.network.protocol.data.proto.MsgTransmit import net.mamoe.mirai.internal.network.protocol.data.proto.MultiMsg import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacketFactory import net.mamoe.mirai.internal.network.protocol.packet.buildOutgoingUniPacket -import net.mamoe.mirai.internal.utils._miraiContentToString import net.mamoe.mirai.internal.utils.io.serialization.readProtoBuf import net.mamoe.mirai.internal.utils.io.serialization.toByteArray import net.mamoe.mirai.internal.utils.io.serialization.writeProtoBuf +import net.mamoe.mirai.internal.utils.structureToString import net.mamoe.mirai.message.data.ForwardMessage import net.mamoe.mirai.message.data.MessageSource import net.mamoe.mirai.message.data.toMessageChain @@ -120,7 +120,7 @@ internal class MultiMsg { ) : Response() { override fun toString(): String { if (PacketCodec.PacketLogger.isEnabled) { - return _miraiContentToString() + return structureToString() } return "MultiMsg.ApplyUp.Response.RequireUpload" } @@ -168,7 +168,7 @@ internal class MultiMsg { //1 -> Response.OK(resId = response.msgResid) else -> { error(kotlin.run { - println(response._miraiContentToString()) + println(response.structureToString()) }.let { "Protocol error: MultiMsg.ApplyUp failed with result ${response.result}" }) } } @@ -221,7 +221,7 @@ internal class MultiMsg { //1 -> Response.OK(resId = response.msgResid) else -> throw contextualBugReportException( "MultiMsg.ApplyDown", - response._miraiContentToString(), + response.structureToString(), additional = "Decode failure result=${response.result}" ) } diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/StatSvc.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/StatSvc.kt index 955aae08e..74d7d8543 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/StatSvc.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/StatSvc.kt @@ -41,8 +41,8 @@ import net.mamoe.mirai.internal.network.protocol.data.proto.StatSvcGetOnline import net.mamoe.mirai.internal.network.protocol.data.proto.StatSvcSimpleGet import net.mamoe.mirai.internal.network.protocol.packet.* import net.mamoe.mirai.internal.utils.NetworkType -import net.mamoe.mirai.internal.utils._miraiContentToString import net.mamoe.mirai.internal.utils.io.serialization.* +import net.mamoe.mirai.internal.utils.structureToString import net.mamoe.mirai.internal.utils.toIpV4Long import net.mamoe.mirai.utils.* @@ -369,7 +369,7 @@ internal class StatSvc { else -> throw contextualBugReportException( "decode SvcReqMSFLoginNotify (OtherClient status change)", - notify._miraiContentToString(), + notify.structureToString(), additional = "unknown notify.status=${notify.status}" ) } diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/WtLogin.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/WtLogin.kt index c7c4aa861..04d5d30df 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/WtLogin.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/WtLogin.kt @@ -22,9 +22,9 @@ import net.mamoe.mirai.internal.network.protocol.packet.* import net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin.WtLoginExt import net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin.analysisTlv0x531 import net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin.orEmpty -import net.mamoe.mirai.internal.utils._miraiContentToString import net.mamoe.mirai.internal.utils.crypto.TEA -import net.mamoe.mirai.internal.utils.soutv +import net.mamoe.mirai.internal.utils.printStructurally +import net.mamoe.mirai.internal.utils.structureToString import net.mamoe.mirai.utils.* internal class WtLogin { @@ -163,7 +163,7 @@ internal class WtLogin { val tlvMap: TlvMap = this._readTLVMap() if (SHOW_TLV_MAP_ON_LOGIN_SUCCESS) { - tlvMap.smartToString().soutv("tlvMap outer") + tlvMap.smartToString().printStructurally("tlvMap outer") } // tlvMap.printTLVMap() @@ -185,7 +185,7 @@ internal class WtLogin { // 1, 15 -> onErrorMessage(tlvMap) ?: error("Cannot find error message") else -> { onErrorMessage(type.toInt(), tlvMap, bot) - ?: error("Cannot find error message, unknown login result type: $type, TLVMap = ${tlvMap._miraiContentToString()}") + ?: error("Cannot find error message, unknown login result type: $type, TLVMap = ${tlvMap.structureToString()}") } } } @@ -242,7 +242,7 @@ internal class WtLogin { // } else error("UNKNOWN CAPTCHA QUESTION: ${question.toUHexString()}, tlvMap=" + tlvMap.contentToString()) } - error("UNKNOWN CAPTCHA, tlvMap=" + tlvMap._miraiContentToString()) + error("UNKNOWN CAPTCHA, tlvMap=" + tlvMap.structureToString()) } fun onLoginSuccess(subCommand: Int, tlvMap: TlvMap, bot: QQAndroidBot): LoginPacketResponse.Success { @@ -266,7 +266,7 @@ internal class WtLogin { val tlvMap119 = this._readTLVMap() if (SHOW_TLV_MAP_ON_LOGIN_SUCCESS) { - tlvMap119.smartToString().soutv("TlvMap119") + tlvMap119.smartToString().printStructurally("TlvMap119") } tlvMap119[0x106]?.let { client.analyzeTlv106(it) } @@ -366,7 +366,7 @@ internal class WtLogin { } ?: emptyMap() if (SHOW_TLV_MAP_ON_LOGIN_SUCCESS) { - changeTokenTimeMap._miraiContentToString().soutv("tokenChangeTime") + changeTokenTimeMap.structureToString().printStructurally("tokenChangeTime") } val outPSKeyMap: PSKeyMap? diff --git a/mirai-core/src/commonMain/kotlin/utils/contentToString.kt b/mirai-core/src/commonMain/kotlin/utils/contentToString.kt index 7c4832c4a..eb720d2e1 100644 --- a/mirai-core/src/commonMain/kotlin/utils/contentToString.kt +++ b/mirai-core/src/commonMain/kotlin/utils/contentToString.kt @@ -11,205 +11,53 @@ package net.mamoe.mirai.internal.utils -import kotlinx.serialization.Transient -import net.mamoe.mirai.IMirai +import net.mamoe.mirai.utils.DeprecatedSinceMirai import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.debug -import net.mamoe.mirai.utils.toUHexString -import java.lang.reflect.Modifier -import kotlin.reflect.KClass -import kotlin.reflect.KProperty -import kotlin.reflect.KProperty1 -import kotlin.reflect.full.hasAnnotation -import kotlin.reflect.jvm.javaField +import net.mamoe.mirai.utils.loadService -private val indent: String = " ".repeat(4) +internal fun Any?.structureToString(): String = StructureToStringTransformer.instance.transform(this) -/** - * 将所有元素加入转换为多行的字符串表示. - */ -private fun Sequence.joinToStringPrefixed(prefix: String, transform: (T) -> CharSequence): String { - return this.joinToString(prefix = "$prefix$indent", separator = "\n$prefix$indent", transform = transform) +@Suppress("FunctionName") +@Deprecated( + "", + ReplaceWith("this.structureToString()", "net.mamoe.mirai.internal.utils.structureToString"), + level = DeprecationLevel.ERROR +) // kept for local developers for some time +@DeprecatedSinceMirai(errorSince = "2.10") +internal fun Any?._miraiContentToString(): String = this.structureToString() + +private val SoutvLogger: MiraiLogger by lazy { + MiraiLogger.Factory.create( + StructureToStringTransformer::class, + "printStructurally" + ) } -private val SoutvLogger: MiraiLogger by lazy { MiraiLogger.Factory.create(IMirai::class, "soutv") } -internal fun Any?.soutv(name: String = "unnamed") { - @Suppress("DEPRECATION") - SoutvLogger.debug { "$name = ${this._miraiContentToString()}" } +@Deprecated( + "", + ReplaceWith("this.printStructurally(name)", "net.mamoe.mirai.internal.utils.printStructurally"), + level = DeprecationLevel.ERROR +) +@DeprecatedSinceMirai(errorSince = "2.10") +internal fun Any?.soutv(name: String = "unnamed") = this.printStructurally(name) + +internal fun Any?.printStructurally(name: String = "unnamed") { + return SoutvLogger.debug { "$name = ${this.structureToString()}" } } -/** - * 将内容格式化为较可读的字符串输出. - * - * 各数字类型及其无符号类型: 十六进制表示 + 十进制表示. e.g. `0x1000(4096)` - * [ByteArray] 和 [UByteArray]: 十六进制表示, 通过 [ByteArray.toUHexString] - * [Iterable], [Iterator], [Sequence]: 调用各自的 joinToString. - * [Map]: 多行输出. 每行显示一个值. 递归调用 [_miraiContentToString]. 嵌套结构将会以缩进表示 - * `data class`: 调用其 [toString] - * 其他类型: 反射获取它和它的所有来自 Mirai 的 super 类型的所有自有属性并递归调用 [_miraiContentToString]. 嵌套结构将会以缩进表示 - */ -@Suppress("FunctionName") // 这样就不容易被 IDE 提示 -internal fun Any?._miraiContentToString(prefix: String = ""): String = when (this) { - is Unit -> "Unit" - is UInt -> "0x" + this.toUHexString("") + "($this)" - is UByte -> "0x" + this.toUHexString() + "($this)" - is UShort -> "0x" + this.toUHexString("") + "($this)" - is ULong -> "0x" + this.toUHexString("") + "($this)" - is Int -> "0x" + this.toUHexString("") + "($this)" - is Byte -> "0x" + this.toUHexString() + "($this)" - is Short -> "0x" + this.toUHexString("") + "($this)" - is Long -> "0x" + this.toUHexString("") + "($this)" +internal fun interface StructureToStringTransformer { + fun transform(any: Any?): String - is Boolean -> if (this) "true" else "false" + companion object { + private class ObjectToStringStructureToStringTransformer : StructureToStringTransformer { + override fun transform(any: Any?): String = any.toString() + } - is ByteArray -> { - if (this.size == 0) "" - else this.toUHexString() - } - is UByteArray -> { - if (this.size == 0) "" - else this.toUHexString() - } - is ShortArray -> { - if (this.size == 0) "" - else this.iterator()._miraiContentToString() - } - is IntArray -> { - if (this.size == 0) "" - else this.iterator()._miraiContentToString() - } - is LongArray -> { - if (this.size == 0) "" - else this.iterator()._miraiContentToString() - } - is FloatArray -> { - if (this.size == 0) "" - else this.iterator()._miraiContentToString() - } - is DoubleArray -> { - if (this.size == 0) "" - else this.iterator()._miraiContentToString() - } - is UShortArray -> { - if (this.size == 0) "" - else this.iterator()._miraiContentToString() - } - is UIntArray -> { - if (this.size == 0) "" - else this.iterator()._miraiContentToString() - } - is ULongArray -> { - if (this.size == 0) "" - else this.iterator()._miraiContentToString() - } - is Array<*> -> { - if (this.size == 0) "" - else this.iterator()._miraiContentToString() - } - is BooleanArray -> { - if (this.size == 0) "" - else this.iterator()._miraiContentToString() - } - - is Iterable<*> -> this.joinToString(prefix = "[", postfix = "]") { it._miraiContentToString(prefix) } - is Iterator<*> -> this.asSequence().joinToString(prefix = "[", postfix = "]") { it._miraiContentToString(prefix) } - is Sequence<*> -> this.joinToString(prefix = "[", postfix = "]") { it._miraiContentToString(prefix) } - is Map<*, *> -> this.entries.joinToString( - prefix = "{", - postfix = "}" - ) { it.key._miraiContentToString(prefix) + "=" + it.value._miraiContentToString(prefix) } - else -> { - if (this == null) "null" - else if (this::class.isData) this.toString() - else { - if (this::class.qualifiedName?.startsWith("net.mamoe.mirai.") == true) { - this.contentToStringReflectively(prefix + indent) - } else this.toString() - /* - (this::class.simpleName ?: "") + "#" + this::class.hashCode() + "{\n" + - this::class.members.asSequence().filterIsInstance>().filter { !it.isSuspend && it.visibility == KVisibility.PUBLIC } - .joinToStringPrefixed( - prefix = indent - ) { it.name + "=" + kotlin.runCatching { it.call(it).contentToString(indent) }.getOrElse { "" } } - */ + val instance by lazy { + loadService(StructureToStringTransformer::class) { ObjectToStringStructureToStringTransformer() } } } } -internal fun KProperty1<*, *>.getValueAgainstPermission(receiver: Any): Any? { - return this.javaField?.apply { isAccessible = true }?.get(receiver) -} - -private fun Any.canBeIgnored(): Boolean { - return when (this) { - is String -> this.isEmpty() - is ByteArray -> this.isEmpty() - is Array<*> -> this.isEmpty() - is Number -> this == 0 - is Int -> this == 0 - is Float -> this == 0 - is Double -> this == 0 - is Byte -> this == 0 - is Short -> this == 0 - is Long -> this == 0 - else -> false - } -} - -private fun Any.contentToStringReflectively( - prefix: String, - filter: ((name: String, value: Any?) -> Boolean)? = null, -): String { - val newPrefix = "$prefix " - return (this::class.simpleName ?: "") + "#" + this::class.hashCode() + " {\n" + - this.allMembersFromSuperClassesMatching { it.qualifiedName?.startsWith("net.mamoe.mirai") == true } - .distinctBy { it.name } - .filterNot { it.name.contains("$") || it.name == "Companion" || it.isConst || it.name == "serialVersionUID" } - .mapNotNull { - val value = it.getValueAgainstPermission(this) ?: return@mapNotNull null - if (filter != null) { - if (!filter(it.name, value)) - return@mapNotNull it.name to value - else { - return@mapNotNull null - } - } - it.name to value - } - .joinToStringPrefixed( - prefix = newPrefix - ) { (name: String, value: Any?) -> - if (value.canBeIgnored()) "" - else { - "$name=" + kotlin.runCatching { - if (value == this) "" - else value._miraiContentToString(newPrefix) - }.getOrElse { "" } - } - }.lines().filterNot { it.isBlank() }.joinToString("\n") + "\n$prefix}" -} - -private fun KClass.thisClassAndSuperclassSequence(): Sequence> { - return sequenceOf(this) + - this.supertypes.asSequence() - .mapNotNull { type -> - type.classifier?.takeIf { it is KClass<*> }?.takeIf { it != Any::class } as? KClass - }.flatMap { it.thisClassAndSuperclassSequence() } -} - -@Suppress("UNCHECKED_CAST") -private fun Any.allMembersFromSuperClassesMatching(classFilter: (KClass) -> Boolean): Sequence> { - return this::class.thisClassAndSuperclassSequence() - .filter { classFilter(it) } - .map { it.members } - .flatMap { it.asSequence() } - .filterIsInstance>() - .filterNot { it.hasAnnotation() } - .filterNot { it.isTransient() } - .mapNotNull { it as KProperty1 } -} - -internal fun KProperty<*>.isTransient(): Boolean = - javaField?.modifiers?.and(Modifier.TRANSIENT) != 0 - diff --git a/mirai-core/src/commonMain/kotlin/utils/io/serialization/utils.kt b/mirai-core/src/commonMain/kotlin/utils/io/serialization/utils.kt index dfc9f094a..10a25cafc 100644 --- a/mirai-core/src/commonMain/kotlin/utils/io/serialization/utils.kt +++ b/mirai-core/src/commonMain/kotlin/utils/io/serialization/utils.kt @@ -24,7 +24,7 @@ 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.ProtoBuf import net.mamoe.mirai.internal.utils.io.serialization.tars.Tars -import net.mamoe.mirai.internal.utils.soutv +import net.mamoe.mirai.internal.utils.printStructurally import net.mamoe.mirai.utils.read import net.mamoe.mirai.utils.readPacketExact import kotlin.contracts.InvocationKind @@ -171,7 +171,7 @@ internal fun ByteArray.loadAs(deserializer: DeserializationStrate internal fun ByteArray.loadOidb(deserializer: DeserializationStrategy, log: Boolean = false): T { val oidb = loadAs(OidbSso.OIDBSSOPkg.serializer()) if (log) { - oidb.soutv("OIDB") + oidb.printStructurally("OIDB") } return oidb.bodybuffer.loadAs(deserializer) } diff --git a/mirai-core/src/jvmTest/kotlin/message/data/ForwardRefineTest.kt b/mirai-core/src/jvmTest/kotlin/message/data/ForwardRefineTest.kt index a1679b0e4..5d0485986 100644 --- a/mirai-core/src/jvmTest/kotlin/message/data/ForwardRefineTest.kt +++ b/mirai-core/src/jvmTest/kotlin/message/data/ForwardRefineTest.kt @@ -16,7 +16,7 @@ import net.mamoe.mirai.internal.message.DeepMessageRefiner.refineDeep import net.mamoe.mirai.internal.message.ForwardMessageInternal import net.mamoe.mirai.internal.message.SimpleRefineContext import net.mamoe.mirai.internal.test.runBlockingUnit -import net.mamoe.mirai.internal.utils._miraiContentToString +import net.mamoe.mirai.internal.utils.structureToString import net.mamoe.mirai.message.data.* import org.junit.jupiter.api.Test import kotlin.test.assertEquals @@ -49,7 +49,7 @@ internal class ForwardRefineTest : AbstractTestWithMiraiImpl() { }) println(refine.size) println(refine.first()::class) - println(refine._miraiContentToString()) + println(refine.structureToString()) assertTrue { refine.first() is MessageOrigin } assertTrue { refine.drop(1).first() is ForwardMessage } assertEquals( diff --git a/mirai-core/src/jvmTest/kotlin/message/data/MessageSerializationTest.kt b/mirai-core/src/jvmTest/kotlin/message/data/MessageSerializationTest.kt index abcf30fe6..d164bfd93 100644 --- a/mirai-core/src/jvmTest/kotlin/message/data/MessageSerializationTest.kt +++ b/mirai-core/src/jvmTest/kotlin/message/data/MessageSerializationTest.kt @@ -20,7 +20,7 @@ import net.mamoe.mirai.internal.message.MarketFaceImpl import net.mamoe.mirai.internal.message.OnlineAudioImpl import net.mamoe.mirai.internal.message.UnsupportedMessageImpl import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody -import net.mamoe.mirai.internal.utils._miraiContentToString +import net.mamoe.mirai.internal.utils.structureToString import net.mamoe.mirai.message.MessageSerializers import net.mamoe.mirai.message.data.* import net.mamoe.mirai.utils.cast @@ -201,7 +201,7 @@ internal class MessageSerializationTest { ignoreUnknownKeys = true } val source = j.decodeFromString(MessageSource.Serializer, a) - println(source._miraiContentToString()) + println(source.structureToString()) assertEquals( expected = Mirai.buildMessageSource(692928873, MessageSourceKind.GROUP) { id(44) diff --git a/mirai-core/src/jvmTest/kotlin/utils/StructureToStringLegacy.kt b/mirai-core/src/jvmTest/kotlin/utils/StructureToStringLegacy.kt new file mode 100644 index 000000000..175e76545 --- /dev/null +++ b/mirai-core/src/jvmTest/kotlin/utils/StructureToStringLegacy.kt @@ -0,0 +1,212 @@ +/* + * Copyright 2019-2021 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 + +import kotlinx.serialization.Transient +import net.mamoe.mirai.IMirai +import net.mamoe.mirai.utils.MiraiLogger +import net.mamoe.mirai.utils.debug +import net.mamoe.mirai.utils.toUHexString +import java.lang.reflect.Modifier +import kotlin.reflect.KClass +import kotlin.reflect.KProperty +import kotlin.reflect.KProperty1 +import kotlin.reflect.full.hasAnnotation +import kotlin.reflect.jvm.javaField + +// Kept for souvenir. Not used. +internal class StructureToStringLegacy : StructureToStringTransformer { + override fun transform(any: Any?): String = any._miraiContentToString() + + private val indent: String = " ".repeat(4) + + /** + * 将所有元素加入转换为多行的字符串表示. + */ + private fun Sequence.joinToStringPrefixed(prefix: String, transform: (T) -> CharSequence): String { + return this.joinToString(prefix = "$prefix$indent", separator = "\n$prefix$indent", transform = transform) + } + + /** + * 将内容格式化为较可读的字符串输出. + * + * 各数字类型及其无符号类型: 十六进制表示 + 十进制表示. e.g. `0x1000(4096)` + * [ByteArray] 和 [UByteArray]: 十六进制表示, 通过 [ByteArray.toUHexString] + * [Iterable], [Iterator], [Sequence]: 调用各自的 joinToString. + * [Map]: 多行输出. 每行显示一个值. 递归调用 [_miraiContentToString]. 嵌套结构将会以缩进表示 + * `data class`: 调用其 [toString] + * 其他类型: 反射获取它和它的所有来自 Mirai 的 super 类型的所有自有属性并递归调用 [_miraiContentToString]. 嵌套结构将会以缩进表示 + */ + @Suppress("FunctionName") // 这样就不容易被 IDE 提示 + internal fun Any?._miraiContentToString(prefix: String = ""): String = when (this) { + is Unit -> "Unit" + is UInt -> "0x" + this.toUHexString("") + "($this)" + is UByte -> "0x" + this.toUHexString() + "($this)" + is UShort -> "0x" + this.toUHexString("") + "($this)" + is ULong -> "0x" + this.toUHexString("") + "($this)" + is Int -> "0x" + this.toUHexString("") + "($this)" + is Byte -> "0x" + this.toUHexString() + "($this)" + is Short -> "0x" + this.toUHexString("") + "($this)" + is Long -> "0x" + this.toUHexString("") + "($this)" + + is Boolean -> if (this) "true" else "false" + + is ByteArray -> { + if (this.size == 0) "" + else this.toUHexString() + } + is UByteArray -> { + if (this.size == 0) "" + else this.toUHexString() + } + is ShortArray -> { + if (this.size == 0) "" + else this.iterator()._miraiContentToString() + } + is IntArray -> { + if (this.size == 0) "" + else this.iterator()._miraiContentToString() + } + is LongArray -> { + if (this.size == 0) "" + else this.iterator()._miraiContentToString() + } + is FloatArray -> { + if (this.size == 0) "" + else this.iterator()._miraiContentToString() + } + is DoubleArray -> { + if (this.size == 0) "" + else this.iterator()._miraiContentToString() + } + is UShortArray -> { + if (this.size == 0) "" + else this.iterator()._miraiContentToString() + } + is UIntArray -> { + if (this.size == 0) "" + else this.iterator()._miraiContentToString() + } + is ULongArray -> { + if (this.size == 0) "" + else this.iterator()._miraiContentToString() + } + is Array<*> -> { + if (this.size == 0) "" + else this.iterator()._miraiContentToString() + } + is BooleanArray -> { + if (this.size == 0) "" + else this.iterator()._miraiContentToString() + } + + is Iterable<*> -> this.joinToString(prefix = "[", postfix = "]") { it._miraiContentToString(prefix) } + is Iterator<*> -> this.asSequence() + .joinToString(prefix = "[", postfix = "]") { it._miraiContentToString(prefix) } + is Sequence<*> -> this.joinToString(prefix = "[", postfix = "]") { it._miraiContentToString(prefix) } + is Map<*, *> -> this.entries.joinToString( + prefix = "{", + postfix = "}" + ) { it.key._miraiContentToString(prefix) + "=" + it.value._miraiContentToString(prefix) } + else -> { + if (this == null) "null" + else if (this::class.isData) this.toString() + else { + if (this::class.qualifiedName?.startsWith("net.mamoe.mirai.") == true) { + this.contentToStringReflectively(prefix + indent) + } else this.toString() + /* + (this::class.simpleName ?: "") + "#" + this::class.hashCode() + "{\n" + + this::class.members.asSequence().filterIsInstance>().filter { !it.isSuspend && it.visibility == KVisibility.PUBLIC } + .joinToStringPrefixed( + prefix = indent + ) { it.name + "=" + kotlin.runCatching { it.call(it).contentToString(indent) }.getOrElse { "" } } + */ + } + } + } + + internal fun KProperty1<*, *>.getValueAgainstPermission(receiver: Any): Any? { + return this.javaField?.apply { isAccessible = true }?.get(receiver) + } + + private fun Any.canBeIgnored(): Boolean { + return when (this) { + is String -> this.isEmpty() + is ByteArray -> this.isEmpty() + is Array<*> -> this.isEmpty() + is Int -> this == 0 + is Float -> this == 0f + is Double -> this == 0.0 + is Byte -> this == 0.toByte() + is Short -> this == 0.toShort() + is Long -> this == 0.toLong() + else -> false + } + } + + private fun Any.contentToStringReflectively( + prefix: String, + filter: ((name: String, value: Any?) -> Boolean)? = null, + ): String { + val newPrefix = "$prefix " + return (this::class.simpleName ?: "") + "#" + this::class.hashCode() + " {\n" + + this.allMembersFromSuperClassesMatching { it.qualifiedName?.startsWith("net.mamoe.mirai") == true } + .distinctBy { it.name } + .filterNot { it.name.contains("$") || it.name == "Companion" || it.isConst || it.name == "serialVersionUID" } + .mapNotNull { + val value = it.getValueAgainstPermission(this) ?: return@mapNotNull null + if (filter != null) { + if (!filter(it.name, value)) + return@mapNotNull it.name to value + else { + return@mapNotNull null + } + } + it.name to value + } + .joinToStringPrefixed( + prefix = newPrefix + ) { (name: String, value: Any?) -> + if (value.canBeIgnored()) "" + else { + "$name=" + kotlin.runCatching { + if (value == this) "" + else value._miraiContentToString(newPrefix) + }.getOrElse { "" } + } + }.lines().filterNot { it.isBlank() }.joinToString("\n") + "\n$prefix}" + } + + private fun KClass.thisClassAndSuperclassSequence(): Sequence> { + return sequenceOf(this) + + this.supertypes.asSequence() + .mapNotNull { type -> + type.classifier?.takeIf { it is KClass<*> }?.takeIf { it != Any::class } as? KClass + }.flatMap { it.thisClassAndSuperclassSequence() } + } + + @Suppress("UNCHECKED_CAST") + private fun Any.allMembersFromSuperClassesMatching(classFilter: (KClass) -> Boolean): Sequence> { + return this::class.thisClassAndSuperclassSequence() + .filter { classFilter(it) } + .map { it.members } + .flatMap { it.asSequence() } + .filterIsInstance>() + .filterNot { it.hasAnnotation() } + .filterNot { it.isTransient() } + .map { it as KProperty1 } + } + + internal fun KProperty<*>.isTransient(): Boolean = + javaField?.modifiers?.and(Modifier.TRANSIENT) != 0 + + +} \ No newline at end of file