From 3a2663104b3b18eff88c83f4ab8a9b7335830209 Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Mon, 20 Dec 2021 19:07:50 +0000
Subject: [PATCH] Rewrite and generalize ConstructorCallCodegen for
 multipurpose usage.

---
 .../network/protocol/packet/login/WtLogin.kt  |   8 +-
 .../kotlin/utils/contentToString.kt           |   4 +-
 .../kotlin/utils/io/serialization/utils.kt    |   4 +-
 .../test/RecordingNoticeProcessorTest.kt      |   2 +-
 .../codegen/RemoveDefaultValuesVisitor.kt}    | 114 ++------
 .../codegen/ValueDescAnalyzer.kt}             |  54 ++--
 .../codegen/descriptors/ClassValueDesc.kt     |  46 +++
 .../descriptors/CollectionLikeValueDesc.kt    |  67 +++++
 .../descriptors/CollectionValueDesc.kt        |  42 +++
 .../codegen/descriptors/MapValueDesc.kt       |  56 ++++
 .../descriptors/ObjectArrayValueDesc.kt       |  43 +++
 .../codegen/descriptors/PlainValueDesc.kt     |  33 +++
 .../descriptors/PrimitiveArrayValueDesc.kt    |  49 ++++
 .../codegen/descriptors/ValueDesc.kt          |  43 +++
 .../codegen/test/IndenterTest.kt              |  23 ++
 ...mizeByteArrayAsHexStringTransformerTest.kt |  50 ++++
 .../test/visitors/ValueDescAnalyzerTest.kt    | 261 ++++++++++++++++++
 .../visitors/ValueDescToStringRendererTest.kt | 198 +++++++++++++
 .../codegen/visitor/ValueDescTransformer.kt   | 100 +++++++
 .../codegen/visitor/ValueDescVisitor.kt       |  44 +++
 .../codegen/visitor/ValueDescVisitorUnit.kt   |  17 ++
 .../AnalyzeDefaultValuesMappingVisitor.kt     |  68 +++++
 .../codegen/visitors/ClassFormatter.kt        |  34 +++
 .../codegen/visitors/Indenter.kt              |  29 ++
 ...OptimizeByteArrayAsHexStringTransformer.kt |  88 ++++++
 .../visitors/ValueDescToStringRenderer.kt     | 138 +++++++++
 .../desensitizer}/Desensitizer.kt             | 124 +++++----
 .../notice/RecordingNoticeHandler.kt          |   8 +-
 .../kotlin/testFramework/package.kt           |  10 +
 .../kotlin/utils/codegen/ValueCodegen.kt      | 136 ---------
 .../kotlin/utils/codegen/ValueDesc.kt         | 181 ------------
 .../test/ConstructorCallCodegenTest.kt        | 112 --------
 .../jvmTest/kotlin/bootstrap/RunRecorder.kt   |   4 +-
 ... => StructureToStringTransformerLegacy.kt} |   6 +-
 .../utils/StructureToStringTransformerNew.kt  |  29 ++
 .../StructureToStringTransformerNewTest.kt    |  34 +++
 ...nternal.utils.StructureToStringTransformer |  10 +
 37 files changed, 1641 insertions(+), 628 deletions(-)
 rename mirai-core/src/commonTest/kotlin/{utils/codegen/ValueDescVisitor.kt => testFramework/codegen/RemoveDefaultValuesVisitor.kt} (52%)
 rename mirai-core/src/commonTest/kotlin/{utils/codegen/ConstructorCallCodegenFacade.kt => testFramework/codegen/ValueDescAnalyzer.kt} (64%)
 create mode 100644 mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/ClassValueDesc.kt
 create mode 100644 mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/CollectionLikeValueDesc.kt
 create mode 100644 mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/CollectionValueDesc.kt
 create mode 100644 mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/MapValueDesc.kt
 create mode 100644 mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/ObjectArrayValueDesc.kt
 create mode 100644 mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/PlainValueDesc.kt
 create mode 100644 mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/PrimitiveArrayValueDesc.kt
 create mode 100644 mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/ValueDesc.kt
 create mode 100644 mirai-core/src/commonTest/kotlin/testFramework/codegen/test/IndenterTest.kt
 create mode 100644 mirai-core/src/commonTest/kotlin/testFramework/codegen/test/OptimizeByteArrayAsHexStringTransformerTest.kt
 create mode 100644 mirai-core/src/commonTest/kotlin/testFramework/codegen/test/visitors/ValueDescAnalyzerTest.kt
 create mode 100644 mirai-core/src/commonTest/kotlin/testFramework/codegen/test/visitors/ValueDescToStringRendererTest.kt
 create mode 100644 mirai-core/src/commonTest/kotlin/testFramework/codegen/visitor/ValueDescTransformer.kt
 create mode 100644 mirai-core/src/commonTest/kotlin/testFramework/codegen/visitor/ValueDescVisitor.kt
 create mode 100644 mirai-core/src/commonTest/kotlin/testFramework/codegen/visitor/ValueDescVisitorUnit.kt
 create mode 100644 mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/AnalyzeDefaultValuesMappingVisitor.kt
 create mode 100644 mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/ClassFormatter.kt
 create mode 100644 mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/Indenter.kt
 create mode 100644 mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/OptimizeByteArrayAsHexStringTransformer.kt
 create mode 100644 mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/ValueDescToStringRenderer.kt
 rename mirai-core/src/commonTest/kotlin/{notice => testFramework/desensitizer}/Desensitizer.kt (62%)
 rename mirai-core/src/commonTest/kotlin/{ => testFramework}/notice/RecordingNoticeHandler.kt (82%)
 create mode 100644 mirai-core/src/commonTest/kotlin/testFramework/package.kt
 delete mode 100644 mirai-core/src/commonTest/kotlin/utils/codegen/ValueCodegen.kt
 delete mode 100644 mirai-core/src/commonTest/kotlin/utils/codegen/ValueDesc.kt
 delete mode 100644 mirai-core/src/commonTest/kotlin/utils/codegen/test/ConstructorCallCodegenTest.kt
 rename mirai-core/src/jvmTest/kotlin/utils/{StructureToStringLegacy.kt => StructureToStringTransformerLegacy.kt} (97%)
 create mode 100644 mirai-core/src/jvmTest/kotlin/utils/StructureToStringTransformerNew.kt
 create mode 100644 mirai-core/src/jvmTest/kotlin/utils/test/StructureToStringTransformerNewTest.kt
 create mode 100644 mirai-core/src/jvmTest/resources/META-INF/services/net.mamoe.mirai.internal.utils.StructureToStringTransformer

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 04d5d30df..1a3c6d8d5 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
@@ -23,7 +23,7 @@ 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.crypto.TEA
-import net.mamoe.mirai.internal.utils.printStructurally
+import net.mamoe.mirai.internal.utils.printStructure
 import net.mamoe.mirai.internal.utils.structureToString
 import net.mamoe.mirai.utils.*
 
@@ -163,7 +163,7 @@ internal class WtLogin {
             val tlvMap: TlvMap = this._readTLVMap()
 
             if (SHOW_TLV_MAP_ON_LOGIN_SUCCESS) {
-                tlvMap.smartToString().printStructurally("tlvMap outer")
+                tlvMap.smartToString().printStructure("tlvMap outer")
             }
 
             // tlvMap.printTLVMap()
@@ -266,7 +266,7 @@ internal class WtLogin {
                     val tlvMap119 = this._readTLVMap()
 
                     if (SHOW_TLV_MAP_ON_LOGIN_SUCCESS) {
-                        tlvMap119.smartToString().printStructurally("TlvMap119")
+                        tlvMap119.smartToString().printStructure("TlvMap119")
                     }
 
                     tlvMap119[0x106]?.let { client.analyzeTlv106(it) }
@@ -366,7 +366,7 @@ internal class WtLogin {
                     } ?: emptyMap()
 
                     if (SHOW_TLV_MAP_ON_LOGIN_SUCCESS) {
-                        changeTokenTimeMap.structureToString().printStructurally("tokenChangeTime")
+                        changeTokenTimeMap.structureToString().printStructure("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 eb720d2e1..a70a13dbb 100644
--- a/mirai-core/src/commonMain/kotlin/utils/contentToString.kt
+++ b/mirai-core/src/commonMain/kotlin/utils/contentToString.kt
@@ -41,9 +41,9 @@ private val SoutvLogger: MiraiLogger by lazy {
     level = DeprecationLevel.ERROR
 )
 @DeprecatedSinceMirai(errorSince = "2.10")
-internal fun Any?.soutv(name: String = "unnamed") = this.printStructurally(name)
+internal fun Any?.soutv(name: String = "unnamed") = this.printStructure(name)
 
-internal fun Any?.printStructurally(name: String = "unnamed") {
+internal fun Any?.printStructure(name: String = "unnamed") {
     return SoutvLogger.debug { "$name = ${this.structureToString()}" }
 }
 
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 10a25cafc..d25ff113d 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.printStructurally
+import net.mamoe.mirai.internal.utils.printStructure
 import net.mamoe.mirai.utils.read
 import net.mamoe.mirai.utils.readPacketExact
 import kotlin.contracts.InvocationKind
@@ -171,7 +171,7 @@ internal fun <T : ProtoBuf> ByteArray.loadAs(deserializer: DeserializationStrate
 internal fun <T : ProtoBuf> ByteArray.loadOidb(deserializer: DeserializationStrategy<T>, log: Boolean = false): T {
     val oidb = loadAs(OidbSso.OIDBSSOPkg.serializer())
     if (log) {
-        oidb.printStructurally("OIDB")
+        oidb.printStructure("OIDB")
     }
     return oidb.bodybuffer.loadAs(deserializer)
 }
diff --git a/mirai-core/src/commonTest/kotlin/notice/test/RecordingNoticeProcessorTest.kt b/mirai-core/src/commonTest/kotlin/notice/test/RecordingNoticeProcessorTest.kt
index 1fe94c5d3..d8ad24a96 100644
--- a/mirai-core/src/commonTest/kotlin/notice/test/RecordingNoticeProcessorTest.kt
+++ b/mirai-core/src/commonTest/kotlin/notice/test/RecordingNoticeProcessorTest.kt
@@ -12,7 +12,7 @@ package net.mamoe.mirai.internal.notice.test
 import kotlinx.serialization.Serializable
 import kotlinx.serialization.decodeFromString
 import kotlinx.serialization.protobuf.ProtoNumber
-import net.mamoe.mirai.internal.notice.Desensitizer
+import net.mamoe.mirai.internal.testFramework.desensitizer.Desensitizer
 import net.mamoe.mirai.internal.test.AbstractTest
 import net.mamoe.mirai.internal.utils.io.ProtocolStruct
 import net.mamoe.yamlkt.Yaml
diff --git a/mirai-core/src/commonTest/kotlin/utils/codegen/ValueDescVisitor.kt b/mirai-core/src/commonTest/kotlin/testFramework/codegen/RemoveDefaultValuesVisitor.kt
similarity index 52%
rename from mirai-core/src/commonTest/kotlin/utils/codegen/ValueDescVisitor.kt
rename to mirai-core/src/commonTest/kotlin/testFramework/codegen/RemoveDefaultValuesVisitor.kt
index 7c9a00832..60f2e93b3 100644
--- a/mirai-core/src/commonTest/kotlin/utils/codegen/ValueDescVisitor.kt
+++ b/mirai-core/src/commonTest/kotlin/testFramework/codegen/RemoveDefaultValuesVisitor.kt
@@ -7,107 +7,33 @@
  * https://github.com/mamoe/mirai/blob/dev/LICENSE
  */
 
-package net.mamoe.mirai.internal.utils.codegen
+package net.mamoe.mirai.internal.testFramework.codegen
 
-import net.mamoe.mirai.utils.cast
-import kotlin.reflect.KClass
+import net.mamoe.mirai.internal.testFramework.codegen.descriptors.ClassValueDesc
+import net.mamoe.mirai.internal.testFramework.codegen.descriptors.ValueDesc
+import net.mamoe.mirai.internal.testFramework.codegen.descriptors.accept
+import net.mamoe.mirai.internal.testFramework.codegen.visitor.ValueDescVisitorUnit
+import net.mamoe.mirai.internal.testFramework.codegen.visitors.AnalyzeDefaultValuesMappingVisitor
+import net.mamoe.mirai.internal.testFramework.codegen.visitors.DefaultValuesMapping
 import kotlin.reflect.KParameter
-import kotlin.reflect.KProperty
-import kotlin.reflect.KProperty1
-import kotlin.reflect.full.memberProperties
-import kotlin.reflect.full.primaryConstructor
-
-interface ValueDescVisitor {
-    fun visitValue(desc: ValueDesc) {}
-
-    fun visitPlain(desc: PlainValueDesc) {
-        visitValue(desc)
-    }
-
-    fun visitArray(desc: ArrayValueDesc) {
-        visitValue(desc)
-        for (element in desc.elements) {
-            element.accept(this)
-        }
-    }
-
-    fun visitObjectArray(desc: ObjectArrayValueDesc) {
-        visitArray(desc)
-    }
-
-    fun visitCollection(desc: CollectionValueDesc) {
-        visitArray(desc)
-    }
-
-    fun visitMap(desc: MapValueDesc) {
-        visitValue(desc)
-        for ((key, value) in desc.elements.entries) {
-            key.accept(this)
-            value.accept(this)
-        }
-    }
-
-    fun visitPrimitiveArray(desc: PrimitiveArrayValueDesc) {
-        visitArray(desc)
-    }
-
-    fun <T : Any> visitClass(desc: ClassValueDesc<T>) {
-        visitValue(desc)
-        desc.properties.forEach { (_, u) ->
-            u.accept(this)
-        }
-    }
-}
 
 
-class DefaultValuesMapping(
-    val forClass: KClass<*>,
-    val mapping: MutableMap<String, Any?> = mutableMapOf()
-) {
-    operator fun get(property: KProperty<*>): Any? = mapping[property.name]
-}
-
-class AnalyzeDefaultValuesMappingVisitor : ValueDescVisitor {
-    val mappings: MutableList<DefaultValuesMapping> = mutableListOf()
-
-    override fun <T : Any> visitClass(desc: ClassValueDesc<T>) {
-        super.visitClass(desc)
-
-        if (mappings.any { it.forClass == desc.type }) return
-
-        val defaultInstance =
-            createInstanceWithMostDefaultValues(desc.type, desc.properties.mapValues { it.value.origin })
-
-        val optionalParameters = desc.type.primaryConstructor!!.parameters.filter { it.isOptional }
-
-        mappings.add(
-            DefaultValuesMapping(
-                desc.type,
-                optionalParameters.associateTo(mutableMapOf()) { param ->
-                    val value = findCorrespondingProperty(desc, param).get(defaultInstance)
-                    param.name!! to value
-                }
-            )
-        )
-    }
-
-
-    private fun <T : Any> findCorrespondingProperty(
-        desc: ClassValueDesc<T>,
-        param: KParameter
-    ) = desc.type.memberProperties.single { it.name == param.name }.cast<KProperty1<Any, Any>>()
-
-    private fun <T : Any> createInstanceWithMostDefaultValues(clazz: KClass<T>, arguments: Map<KParameter, Any?>): T {
-        val primaryConstructor = clazz.primaryConstructor ?: error("Type $clazz does not have primary constructor.")
-        return primaryConstructor.callBy(arguments.filter { !it.key.isOptional })
-    }
+fun ValueDesc.removeDefaultValues(): ValueDesc {
+    val def = AnalyzeDefaultValuesMappingVisitor()
+    this.accept(def)
+    this.accept(RemoveDefaultValuesVisitor(def.mappings))
+    return this
 }
 
 class RemoveDefaultValuesVisitor(
     private val mappings: MutableList<DefaultValuesMapping>,
-) : ValueDescVisitor {
-    override fun <T : Any> visitClass(desc: ClassValueDesc<T>) {
-        super.visitClass(desc)
+) : ValueDescVisitorUnit {
+    override fun visitValue(desc: ValueDesc, data: Nothing?) {
+        desc.acceptChildren(this, data)
+    }
+
+    override fun <T : Any> visitClass(desc: ClassValueDesc<T>, data: Nothing?) {
+        super.visitClass(desc, data)
         val mapping = mappings.find { it.forClass == desc.type }?.mapping ?: return
 
         // remove properties who have the same values as their default values, this would significantly reduce code size.
@@ -175,4 +101,4 @@ class RemoveDefaultValuesVisitor(
             else -> false
         }
     }
-}
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonTest/kotlin/utils/codegen/ConstructorCallCodegenFacade.kt b/mirai-core/src/commonTest/kotlin/testFramework/codegen/ValueDescAnalyzer.kt
similarity index 64%
rename from mirai-core/src/commonTest/kotlin/utils/codegen/ConstructorCallCodegenFacade.kt
rename to mirai-core/src/commonTest/kotlin/testFramework/codegen/ValueDescAnalyzer.kt
index 60bdbdd6d..2bea3b924 100644
--- a/mirai-core/src/commonTest/kotlin/utils/codegen/ConstructorCallCodegenFacade.kt
+++ b/mirai-core/src/commonTest/kotlin/testFramework/codegen/ValueDescAnalyzer.kt
@@ -7,9 +7,12 @@
  * https://github.com/mamoe/mirai/blob/dev/LICENSE
  */
 
-package net.mamoe.mirai.internal.utils.codegen
+@file:OptIn(ExperimentalStdlibApi::class)
+
+package net.mamoe.mirai.internal.testFramework.codegen
 
 import kotlinx.serialization.Serializable
+import net.mamoe.mirai.internal.testFramework.codegen.descriptors.*
 import net.mamoe.mirai.utils.cast
 import kotlin.reflect.KParameter
 import kotlin.reflect.KProperty1
@@ -20,7 +23,10 @@ import kotlin.reflect.full.primaryConstructor
 import kotlin.reflect.full.valueParameters
 import kotlin.reflect.typeOf
 
-object ConstructorCallCodegenFacade {
+object ValueDescAnalyzer {
+    private val anyType = typeOf<Any>()
+
+
     /**
      * Analyze [value] and give its correspondent [ValueDesc].
      */
@@ -46,17 +52,22 @@ object ConstructorCallCodegenFacade {
             return ClassValueDesc(null, value, map)
         }
 
-        ArrayValueDesc.createOrNull(value, type, null)?.let { return it }
+        CollectionLikeValueDesc.createOrNull(value, type, null)?.let { return it }
         if (value is Collection<*>) {
-            return CollectionValueDesc(null, value, arrayType = type, elementType = type.arguments.first().type!!)
+            return CollectionValueDesc(
+                null,
+                value,
+                arrayType = type,
+                elementType = type.arguments.firstOrNull()?.type ?: anyType
+            )
         } else if (value is Map<*, *>) {
             return MapValueDesc(
                 null,
                 value.cast(),
                 value.cast(),
                 type,
-                type.arguments.first().type!!,
-                type.arguments[1].type!!
+                type.arguments.firstOrNull()?.type ?: anyType,
+                type.arguments.getOrNull(1)?.type ?: anyType
             )
         }
 
@@ -70,36 +81,9 @@ object ConstructorCallCodegenFacade {
             else -> PlainValueDesc(null, value.toString(), value)
         }
     }
-
-    /**
-     * Generate source code to construct the value represented by [desc].
-     */
-    fun generate(desc: ValueDesc, context: CodegenContext = CodegenContext()): String {
-        if (context.configuration.removeDefaultValues) {
-            val def = AnalyzeDefaultValuesMappingVisitor()
-            desc.accept(def)
-            desc.accept(RemoveDefaultValuesVisitor(def.mappings))
-        }
-
-        ValueCodegen(context).generate(desc)
-        return context.getResult()
-    }
-
-    fun analyzeAndGenerate(value: Any?, type: KType, context: CodegenContext = CodegenContext()): String {
-        return generate(analyze(value, type), context)
-    }
 }
 
 @OptIn(ExperimentalStdlibApi::class)
-inline fun <reified T> ConstructorCallCodegenFacade.analyze(value: T): ValueDesc {
+inline fun <reified T> ValueDescAnalyzer.analyze(value: T): ValueDesc {
     return analyze(value, typeOf<T>())
-}
-
-@OptIn(ExperimentalStdlibApi::class)
-inline fun <reified T> ConstructorCallCodegenFacade.analyzeAndGenerate(
-    value: T,
-    context: CodegenContext = CodegenContext()
-): String {
-    return analyzeAndGenerate(value, typeOf<T>(), context)
-}
-
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/ClassValueDesc.kt b/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/ClassValueDesc.kt
new file mode 100644
index 000000000..a5693b90a
--- /dev/null
+++ b/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/ClassValueDesc.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.testFramework.codegen.descriptors
+
+import net.mamoe.mirai.internal.testFramework.codegen.visitor.ValueDescTransformer
+import net.mamoe.mirai.internal.testFramework.codegen.visitor.ValueDescVisitor
+import kotlin.reflect.KClass
+import kotlin.reflect.KParameter
+
+class ClassValueDesc<T : Any>(
+    override val parent: ValueDesc?,
+    override val origin: T,
+    val properties: MutableMap<KParameter, ValueDesc>,
+) : ValueDesc {
+    val type: KClass<out T> by lazy { origin::class }
+
+    override fun <D, R> accept(visitor: ValueDescVisitor<D, R>, data: D): R {
+        return visitor.visitClass(this, data)
+    }
+
+    override fun <D> acceptChildren(visitor: ValueDescVisitor<D, *>, data: D) {
+        properties.forEach { (_, u) ->
+            u.accept(visitor, data)
+        }
+    }
+
+    override fun <D> transformChildren(visitor: ValueDescTransformer<D>, data: D) {
+        val result = mutableMapOf<KParameter, ValueDesc>()
+        for (entry in this.properties.entries) {
+            entry.value.acceptChildren(visitor, data)
+            val newValue = entry.value.accept(visitor, data)
+            if (newValue != null) {
+                result[entry.key] = newValue
+            }
+        }
+        this.properties.clear()
+        this.properties.putAll(result)
+    }
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/CollectionLikeValueDesc.kt b/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/CollectionLikeValueDesc.kt
new file mode 100644
index 000000000..b0eb95a35
--- /dev/null
+++ b/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/CollectionLikeValueDesc.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.testFramework.codegen.descriptors
+
+import net.mamoe.mirai.internal.testFramework.codegen.visitor.ValueDescTransformer
+import net.mamoe.mirai.internal.testFramework.codegen.visitor.ValueDescVisitor
+import kotlin.reflect.KType
+import kotlin.reflect.typeOf
+
+sealed interface CollectionLikeValueDesc : ValueDesc {
+    val value: Any
+
+    val arrayType: KType
+    val elementType: KType
+    val elements: MutableList<ValueDesc>
+
+    override fun <D> acceptChildren(visitor: ValueDescVisitor<D, *>, data: D) {
+        for (element in elements) {
+            element.accept(visitor, data)
+        }
+    }
+
+    override fun <D> transformChildren(visitor: ValueDescTransformer<D>, data: D) {
+        elements.transform { it.accept(visitor, data) }
+    }
+
+    companion object {
+        @OptIn(ExperimentalStdlibApi::class)
+        fun createOrNull(array: Any, type: KType, parent: ValueDesc?): CollectionLikeValueDesc? {
+            if (array is Array<*>) return ObjectArrayValueDesc(parent, array, arrayType = type)
+            return when (array) {
+                is IntArray -> PrimitiveArrayValueDesc(parent, array, arrayType = type, elementType = typeOf<Int>())
+                is ByteArray -> PrimitiveArrayValueDesc(parent, array, arrayType = type, elementType = typeOf<Byte>())
+                is ShortArray -> PrimitiveArrayValueDesc(parent, array, arrayType = type, elementType = typeOf<Short>())
+                is CharArray -> PrimitiveArrayValueDesc(parent, array, arrayType = type, elementType = typeOf<Char>())
+                is LongArray -> PrimitiveArrayValueDesc(parent, array, arrayType = type, elementType = typeOf<Long>())
+                is FloatArray -> PrimitiveArrayValueDesc(parent, array, arrayType = type, elementType = typeOf<Float>())
+                is DoubleArray -> PrimitiveArrayValueDesc(
+                    parent,
+                    array,
+                    arrayType = type,
+                    elementType = typeOf<Double>()
+                )
+                is BooleanArray -> PrimitiveArrayValueDesc(
+                    parent,
+                    array,
+                    arrayType = type,
+                    elementType = typeOf<Boolean>()
+                )
+                else -> return null
+            }
+        }
+    }
+}
+
+fun <E> MutableList<E>.transform(transformer: (E) -> E?) {
+    val result = this.asSequence().mapNotNull(transformer).toMutableList()
+    this.clear()
+    this.addAll(result)
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/CollectionValueDesc.kt b/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/CollectionValueDesc.kt
new file mode 100644
index 000000000..3baae13ad
--- /dev/null
+++ b/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/CollectionValueDesc.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.testFramework.codegen.descriptors
+
+import net.mamoe.mirai.internal.testFramework.codegen.ValueDescAnalyzer
+import net.mamoe.mirai.internal.testFramework.codegen.visitor.ValueDescVisitor
+import kotlin.reflect.KType
+import kotlin.reflect.full.createType
+
+class CollectionValueDesc(
+    override val parent: ValueDesc?,
+    value: Collection<*>,
+    override val origin: Collection<*> = value,
+    override val arrayType: KType,
+    override val elementType: KType = arrayType.arguments.first().type ?: Any::class.createType()
+) : CollectionLikeValueDesc {
+
+    override var value: Collection<*> = value
+        set(value) {
+            field = value
+            elements.clear()
+            elements.addAll(initializeElements(value))
+        }
+
+    override val elements: MutableList<ValueDesc> by lazy {
+        initializeElements(value)
+    }
+
+    private fun initializeElements(value: Collection<*>) = value.mapTo(ArrayList(value.size)) {
+        ValueDescAnalyzer.analyze(it, elementType)
+    }
+
+
+    override fun <D, R> accept(visitor: ValueDescVisitor<D, R>, data: D): R = visitor.visitCollection(this, data)
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/MapValueDesc.kt b/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/MapValueDesc.kt
new file mode 100644
index 000000000..e928b808b
--- /dev/null
+++ b/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/MapValueDesc.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.testFramework.codegen.descriptors
+
+import net.mamoe.mirai.internal.testFramework.codegen.ValueDescAnalyzer
+import net.mamoe.mirai.internal.testFramework.codegen.visitor.ValueDescTransformer
+import net.mamoe.mirai.internal.testFramework.codegen.visitor.ValueDescVisitor
+import kotlin.reflect.KType
+import kotlin.reflect.full.createType
+
+class MapValueDesc(
+    override val parent: ValueDesc?,
+    var value: Map<Any?, Any?>,
+    override val origin: Map<Any?, Any?> = value,
+    val mapType: KType,
+    val keyType: KType = mapType.arguments.first().type ?: Any::class.createType(),
+    val valueType: KType = mapType.arguments[1].type ?: Any::class.createType(),
+) : ValueDesc {
+    val elements: MutableMap<ValueDesc, ValueDesc> by lazy {
+        value.map {
+            ValueDescAnalyzer.analyze(it.key, keyType) to ValueDescAnalyzer.analyze(
+                it.value,
+                valueType
+            )
+        }.toMap(mutableMapOf())
+    }
+
+    override fun <D, R> accept(visitor: ValueDescVisitor<D, R>, data: D): R = visitor.visitMap(this, data)
+
+    override fun <D> acceptChildren(visitor: ValueDescVisitor<D, *>, data: D) {
+        for ((key, value) in elements.entries) {
+            key.accept(visitor, data)
+            value.accept(visitor, data)
+        }
+    }
+
+    override fun <D> transformChildren(visitor: ValueDescTransformer<D>, data: D) {
+        val resultMap = mutableMapOf<ValueDesc, ValueDesc>()
+        for (entry in this.elements.entries) {
+            val newKey = entry.key.accept(visitor, data)
+            val newValue = entry.value.accept(visitor, data)
+            if (newKey != null && newValue != null) {
+                resultMap[newKey] = newValue
+            }
+        }
+        this.elements.clear()
+        this.elements.putAll(resultMap)
+    }
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/ObjectArrayValueDesc.kt b/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/ObjectArrayValueDesc.kt
new file mode 100644
index 000000000..9b93a7b34
--- /dev/null
+++ b/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/ObjectArrayValueDesc.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.testFramework.codegen.descriptors
+
+import net.mamoe.mirai.internal.testFramework.codegen.ValueDescAnalyzer
+import net.mamoe.mirai.internal.testFramework.codegen.visitor.ValueDescVisitor
+import kotlin.reflect.KType
+import kotlin.reflect.full.createType
+
+class ObjectArrayValueDesc(
+    override val parent: ValueDesc?,
+    value: Array<*>,
+    override val origin: Array<*> = value,
+    override val arrayType: KType,
+    override val elementType: KType = arrayType.arguments.first().type ?: Any::class.createType(),
+) : CollectionLikeValueDesc {
+
+    override var value: Array<*> = value
+        set(value) {
+            field = value
+            elements.clear()
+            elements.addAll(initializeElements(value))
+        }
+
+    override val elements: MutableList<ValueDesc> by lazy {
+        initializeElements(value)
+    }
+
+    private fun initializeElements(value: Array<*>) = value.mapTo(ArrayList(value.size)) {
+        ValueDescAnalyzer.analyze(it, elementType)
+    }
+
+
+    override fun <D, R> accept(visitor: ValueDescVisitor<D, R>, data: D): R = visitor.visitObjectArray(this, data)
+
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/PlainValueDesc.kt b/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/PlainValueDesc.kt
new file mode 100644
index 000000000..b9578d7a8
--- /dev/null
+++ b/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/PlainValueDesc.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.testFramework.codegen.descriptors
+
+import net.mamoe.mirai.internal.testFramework.codegen.visitor.ValueDescTransformer
+import net.mamoe.mirai.internal.testFramework.codegen.visitor.ValueDescVisitor
+
+class PlainValueDesc(
+    override val parent: ValueDesc?,
+    var value: String,
+    override val origin: Any?
+) : ValueDesc {
+    init {
+        require(value.isNotBlank())
+    }
+
+    override fun <D, R> accept(visitor: ValueDescVisitor<D, R>, data: D): R {
+        return visitor.visitPlain(this, data)
+    }
+
+    override fun <D> acceptChildren(visitor: ValueDescVisitor<D, *>, data: D) {
+    }
+
+    override fun <D> transformChildren(visitor: ValueDescTransformer<D>, data: D) {
+    }
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/PrimitiveArrayValueDesc.kt b/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/PrimitiveArrayValueDesc.kt
new file mode 100644
index 000000000..90e3df809
--- /dev/null
+++ b/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/PrimitiveArrayValueDesc.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.testFramework.codegen.descriptors
+
+import net.mamoe.mirai.internal.testFramework.codegen.ValueDescAnalyzer
+import net.mamoe.mirai.internal.testFramework.codegen.visitor.ValueDescVisitor
+import kotlin.reflect.KType
+
+class PrimitiveArrayValueDesc(
+    override val parent: ValueDesc?,
+    value: Any,
+    override val origin: Any = value,
+    override val arrayType: KType,
+    override val elementType: KType
+) : CollectionLikeValueDesc {
+    override var value: Any = value
+        set(value) {
+            field = value
+            elements.clear()
+            elements.addAll(initializeElements(value))
+        }
+
+    override val elements: MutableList<ValueDesc> by lazy {
+        initializeElements(value)
+    }
+
+    private fun initializeElements(value: Any): MutableList<ValueDesc> = when (value) {
+        is IntArray -> value.mapTo(mutableListOf()) { ValueDescAnalyzer.analyze(it, elementType) }
+        is ByteArray -> value.mapTo(mutableListOf()) { ValueDescAnalyzer.analyze(it, elementType) }
+        is ShortArray -> value.mapTo(mutableListOf()) { ValueDescAnalyzer.analyze(it, elementType) }
+        is CharArray -> value.mapTo(mutableListOf()) { ValueDescAnalyzer.analyze(it, elementType) }
+        is LongArray -> value.mapTo(mutableListOf()) { ValueDescAnalyzer.analyze(it, elementType) }
+        is FloatArray -> value.mapTo(mutableListOf()) { ValueDescAnalyzer.analyze(it, elementType) }
+        is DoubleArray -> value.mapTo(mutableListOf()) { ValueDescAnalyzer.analyze(it, elementType) }
+        is BooleanArray -> value.mapTo(mutableListOf()) { ValueDescAnalyzer.analyze(it, elementType) }
+        else -> error("$value is not an array.")
+    }
+
+    override fun <D, R> accept(visitor: ValueDescVisitor<D, R>, data: D): R {
+        return visitor.visitPrimitiveArray(this, data)
+    }
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/ValueDesc.kt b/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/ValueDesc.kt
new file mode 100644
index 000000000..aa18185d3
--- /dev/null
+++ b/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/ValueDesc.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.testFramework.codegen.descriptors
+
+import net.mamoe.mirai.internal.testFramework.codegen.visitor.ValueDescTransformer
+import net.mamoe.mirai.internal.testFramework.codegen.visitor.ValueDescTransformerNotNull
+import net.mamoe.mirai.internal.testFramework.codegen.visitor.ValueDescVisitor
+
+sealed interface ValueDesc {
+    val origin: Any?
+    val parent: ValueDesc?
+
+    fun <D, R> accept(visitor: ValueDescVisitor<D, R>, data: D): R
+    fun <D> acceptChildren(visitor: ValueDescVisitor<D, *>, data: D)
+
+    fun <D> transform(visitor: ValueDescTransformer<D>, data: D): ValueDesc? = this.accept(visitor, data)
+    fun <D> transformChildren(visitor: ValueDescTransformer<D>, data: D)
+}
+
+fun <R> ValueDesc.accept(visitor: ValueDescVisitor<Nothing?, R>): R = accept(visitor, null)
+fun ValueDesc.transform(visitor: ValueDescTransformer<Nothing?>) = transform(visitor, null)
+fun ValueDesc.transform(visitor: ValueDescTransformerNotNull<Nothing?>) = transform(visitor, null)!!
+fun <R> ValueDesc.acceptChildren(visitor: ValueDescVisitor<Nothing?, R>) = acceptChildren(visitor, null)
+fun ValueDesc.transformChildren(visitor: ValueDescTransformer<Nothing?>) = transformChildren(visitor, null)
+
+val ValueDesc.parents
+    get() = sequence {
+        var parent = parent
+        do {
+            parent ?: return@sequence
+            yield(parent)
+            parent = parent.parent
+        } while (true)
+    }
+
+inline fun <reified T : ValueDesc> ValueDesc.findParent(): T? = parents.filterIsInstance<T>().firstOrNull()
diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/test/IndenterTest.kt b/mirai-core/src/commonTest/kotlin/testFramework/codegen/test/IndenterTest.kt
new file mode 100644
index 000000000..824bbfb12
--- /dev/null
+++ b/mirai-core/src/commonTest/kotlin/testFramework/codegen/test/IndenterTest.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.testFramework.codegen.test
+
+import net.mamoe.mirai.internal.test.AbstractTest
+import net.mamoe.mirai.internal.testFramework.codegen.visitors.WordingIndenter
+import org.junit.jupiter.api.Test
+import kotlin.test.assertEquals
+
+internal class IndenterTest : AbstractTest() {
+
+    @Test
+    fun `can indentize`() {
+        assertEquals("    test", WordingIndenter.spacing(4).indentize("test"))
+    }
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/test/OptimizeByteArrayAsHexStringTransformerTest.kt b/mirai-core/src/commonTest/kotlin/testFramework/codegen/test/OptimizeByteArrayAsHexStringTransformerTest.kt
new file mode 100644
index 000000000..0e7c8f061
--- /dev/null
+++ b/mirai-core/src/commonTest/kotlin/testFramework/codegen/test/OptimizeByteArrayAsHexStringTransformerTest.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.testFramework.codegen.test
+
+import net.mamoe.mirai.internal.test.AbstractTest
+import net.mamoe.mirai.internal.testFramework.codegen.ValueDescAnalyzer
+import net.mamoe.mirai.internal.testFramework.codegen.analyze
+import net.mamoe.mirai.internal.testFramework.codegen.descriptors.transform
+import net.mamoe.mirai.internal.testFramework.codegen.visitors.OptimizeByteArrayAsHexStringTransformer
+import net.mamoe.mirai.internal.testFramework.codegen.visitors.ValueDescToStringRenderer
+import net.mamoe.mirai.internal.testFramework.codegen.visitors.renderToString
+import org.junit.jupiter.api.Test
+import kotlin.test.assertEquals
+
+internal class OptimizeByteArrayAsHexStringTransformerTest : AbstractTest() {
+
+    private inline fun <reified T> analyzeTransformAndRender(
+        value: T,
+        renderer: ValueDescToStringRenderer = ValueDescToStringRenderer()
+    ): String? {
+        return ValueDescAnalyzer.analyze(value)
+            .transform(OptimizeByteArrayAsHexStringTransformer())
+            ?.renderToString(renderer)
+    }
+
+    @Test
+    fun `can optimize as string`() {
+        assertEquals(
+            """
+            "test".toByteArray() /* 74 65 73 74 */
+        """.trimIndent(), analyzeTransformAndRender("test".toByteArray())
+        )
+    }
+
+    @Test
+    fun `can optimize as hex`() {
+        assertEquals(
+            """
+            "O".toByteArray() /* 4F 02 */
+        """.trimIndent(), analyzeTransformAndRender(byteArrayOf(0x4f, 0x02))
+        )
+    }
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/test/visitors/ValueDescAnalyzerTest.kt b/mirai-core/src/commonTest/kotlin/testFramework/codegen/test/visitors/ValueDescAnalyzerTest.kt
new file mode 100644
index 000000000..7b88fda1a
--- /dev/null
+++ b/mirai-core/src/commonTest/kotlin/testFramework/codegen/test/visitors/ValueDescAnalyzerTest.kt
@@ -0,0 +1,261 @@
+/*
+ * 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.testFramework.codegen.test.visitors
+
+import net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg
+import net.mamoe.mirai.internal.testFramework.codegen.RemoveDefaultValuesVisitor
+import net.mamoe.mirai.internal.testFramework.codegen.ValueDescAnalyzer
+import net.mamoe.mirai.internal.testFramework.codegen.analyze
+import net.mamoe.mirai.internal.testFramework.codegen.descriptors.accept
+import net.mamoe.mirai.internal.testFramework.codegen.visitors.*
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class ValueDescAnalyzerTest {
+
+    private inline fun <reified T> analyzeAndRender(
+        value: T
+    ): String {
+        return ValueDescAnalyzer.analyze(value).renderToString(
+            rendererContext = RendererContext(
+                indenter = Indenter.NoIndent,
+                classFormatter = object : ClassFormatter() {
+                    override fun formatClassProperty(
+                        context: ClassFormatterContext,
+                        propertyString: String?,
+                        valueString: String
+                    ): String = "$propertyString=$valueString"
+                }
+            ),
+        )
+    }
+
+    @Test
+    fun `test plain`() {
+        assertEquals(
+            "\"test\"",
+            analyzeAndRender("test")
+        )
+        assertEquals(
+            "1",
+            analyzeAndRender(1)
+        )
+        assertEquals(
+            "1.0",
+            analyzeAndRender(1.0)
+        )
+    }
+
+    @Test
+    fun `test array`() {
+        assertEquals(
+            "arrayOf(1, 2)",
+            analyzeAndRender(arrayOf(1, 2))
+        )
+        assertEquals(
+            "arrayOf(5.0)",
+            analyzeAndRender(arrayOf(5.0))
+        )
+        assertEquals(
+            "arrayOf(\"1\")",
+            analyzeAndRender(arrayOf("1"))
+        )
+        assertEquals(
+            """
+                arrayOf(
+                arrayOf(1), 
+                )
+            """.trimIndent(),
+            analyzeAndRender(arrayOf(arrayOf(1)))
+        )
+    }
+
+    data class TestClass(
+        val value: String
+    )
+
+    data class TestClass2(
+        val value: Any
+    )
+
+    @Test
+    fun `test class`() {
+        assertEquals(
+            """
+                ${TestClass::class.qualifiedName!!}(
+                value="test",
+                )
+            """.trimIndent(),
+            analyzeAndRender(TestClass("test"))
+        )
+        assertEquals(
+            """
+                ${TestClass2::class.qualifiedName!!}(
+                value="test",
+                )
+            """.trimIndent(),
+            analyzeAndRender(TestClass2("test"))
+        )
+        assertEquals(
+            """
+                ${TestClass2::class.qualifiedName!!}(
+                value=1,
+                )
+            """.trimIndent(),
+            analyzeAndRender(TestClass2(1))
+        )
+    }
+
+    data class TestNesting(
+        val nested: Nested
+    ) {
+        data class Nested(
+            val value: String
+        )
+    }
+
+    @Test
+    fun `test nesting`() {
+        assertEquals(
+            """
+                ${TestNesting::class.qualifiedName}(
+                nested=${TestNesting.Nested::class.qualifiedName}(
+                value="test",
+                ),
+                )
+            """.trimIndent(),
+            analyzeAndRender(TestNesting(TestNesting.Nested("test")))
+        )
+    }
+
+    @Test
+    fun `test complex nesting`() {
+        assertEquals(
+            """
+            net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.StructMsg(
+                version = 1,
+                msgType = 2,
+                msgSeq = 1630,
+                msgTime = 1630,
+                reqUin = 1230,
+                msg = net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.SystemMsg(
+                    subType = 1,
+                    msgTitle = "邀请加群",
+                    msgDescribe = "邀请你加入 %group_name%",
+                    actions = mutableListOf(
+                        net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.SystemMsgAction(
+                            name = "拒绝",
+                            result = "已拒绝",
+                            actionInfo = net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.SystemMsgActionInfo(
+                                type = 12,
+                                groupCode = 2230203,
+                            ),
+                            detailName = "拒绝",
+                        ), 
+                        net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.SystemMsgAction(
+                            name = "同意",
+                            result = "已同意",
+                            actionInfo = net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.SystemMsgActionInfo(
+                                type = 11,
+                                groupCode = 2230203,
+                            ),
+                            detailName = "同意",
+                        ), 
+                        net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.SystemMsgAction(
+                            name = "忽略",
+                            result = "已忽略",
+                            actionInfo = net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.SystemMsgActionInfo(
+                                type = 14,
+                                groupCode = 2230203,
+                            ),
+                            detailName = "忽略",
+                        ), 
+                    ),
+                    groupCode = 2230203,
+                    actionUin = 1230001,
+                    groupMsgType = 2,
+                    groupInviterRole = 1,
+                    groupInfo = net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.GroupInfo(
+                        appPrivilegeFlag = 67698880,
+                    ),
+                    reqUinNick = "user3",
+                    groupName = "testtest",
+                    actionUinNick = "user1",
+                    groupExtFlag = 1075905600,
+                    actionUinQqNick = "user1",
+                    reqUinGender = 255,
+                    c2cInviteJoinGroupFlag = 1,
+                ),
+            )
+            """.trimIndent(),
+            ValueDescAnalyzer.analyze(
+                Structmsg.StructMsg(
+                    version = 1,
+                    msgType = 2,
+                    msgSeq = 1630,
+                    msgTime = 1630,
+                    reqUin = 1230,
+                    msg = Structmsg.SystemMsg(
+                        subType = 1,
+                        msgTitle = "邀请加群",
+                        msgDescribe = "邀请你加入 %group_name%",
+                        actions = mutableListOf(
+                            Structmsg.SystemMsgAction(
+                                name = "拒绝",
+                                result = "已拒绝",
+                                actionInfo = Structmsg.SystemMsgActionInfo(
+                                    type = 12,
+                                    groupCode = 2230203,
+                                ),
+                                detailName = "拒绝",
+                            ),
+                            Structmsg.SystemMsgAction(
+                                name = "同意",
+                                result = "已同意",
+                                actionInfo = Structmsg.SystemMsgActionInfo(
+                                    type = 11,
+                                    groupCode = 2230203,
+                                ),
+                                detailName = "同意",
+                            ),
+                            Structmsg.SystemMsgAction(
+                                name = "忽略",
+                                result = "已忽略",
+                                actionInfo = Structmsg.SystemMsgActionInfo(
+                                    type = 14,
+                                    groupCode = 2230203,
+                                ),
+                                detailName = "忽略",
+                            ),
+                        ),
+                        groupCode = 2230203,
+                        actionUin = 1230001,
+                        groupMsgType = 2,
+                        groupInviterRole = 1,
+                        groupInfo = Structmsg.GroupInfo(
+                            appPrivilegeFlag = 67698880,
+                        ),
+                        reqUinNick = "user3",
+                        groupName = "testtest",
+                        actionUinNick = "user1",
+                        groupExtFlag = 1075905600,
+                        actionUinQqNick = "user1",
+                        reqUinGender = 255,
+                        c2cInviteJoinGroupFlag = 1,
+                    ),
+                )
+            ).apply {
+                val def = AnalyzeDefaultValuesMappingVisitor()
+                accept(def)
+                accept(RemoveDefaultValuesVisitor(def.mappings))
+            }.renderToString(ValueDescToStringRenderer())
+        )
+    }
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/test/visitors/ValueDescToStringRendererTest.kt b/mirai-core/src/commonTest/kotlin/testFramework/codegen/test/visitors/ValueDescToStringRendererTest.kt
new file mode 100644
index 000000000..e97a28feb
--- /dev/null
+++ b/mirai-core/src/commonTest/kotlin/testFramework/codegen/test/visitors/ValueDescToStringRendererTest.kt
@@ -0,0 +1,198 @@
+/*
+ * 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
+ */
+
+@file:OptIn(ExperimentalStdlibApi::class)
+
+package net.mamoe.mirai.internal.testFramework.codegen.test.visitors
+
+import net.mamoe.mirai.internal.testFramework.codegen.ValueDescAnalyzer
+import net.mamoe.mirai.internal.testFramework.codegen.analyze
+import net.mamoe.mirai.internal.testFramework.codegen.visitors.ValueDescToStringRenderer
+import net.mamoe.mirai.internal.testFramework.codegen.visitors.renderToString
+import org.junit.jupiter.api.Test
+import kotlin.test.assertEquals
+
+internal class ValueDescToStringRendererTest {
+    private val renderer = ValueDescToStringRenderer()
+
+    @Test
+    fun `plain value`() {
+        assertEquals("\"str\"", ValueDescAnalyzer.analyze("str").renderToString(renderer))
+        assertEquals("1", ValueDescAnalyzer.analyze(1).renderToString(renderer))
+    }
+
+    @Test
+    fun `object array value`() {
+        assertEquals(
+            "arrayOf(\"str\", \"obj\")",
+            ValueDescAnalyzer.analyze(arrayOf("str", "obj")).renderToString(renderer)
+        )
+        assertEquals("arrayOf(1, 2)", ValueDescAnalyzer.analyze(arrayOf(1, 2)).renderToString(renderer))
+    }
+
+    @Test
+    fun `object array value nested`() {
+        assertEquals(
+            """
+                arrayOf(
+                    arrayOf("str", "obj"), 
+                    arrayOf("str", "obj"), 
+                )
+            """.trimIndent(),
+            ValueDescAnalyzer.analyze(arrayOf(arrayOf("str", "obj"), arrayOf("str", "obj"))).renderToString(renderer)
+        )
+        assertEquals(
+            """
+                arrayOf(
+                    arrayOf(1, 2), 
+                    arrayOf(1, 2), 
+                )
+            """.trimIndent(),
+            ValueDescAnalyzer.analyze(arrayOf(arrayOf(1, 2), arrayOf(1, 2))).renderToString(renderer)
+        )
+    }
+
+    @Test
+    fun `primitive array value`() {
+        assertEquals("intArrayOf(1, 2)", ValueDescAnalyzer.analyze(intArrayOf(1, 2)).renderToString(renderer))
+    }
+
+    @Test
+    fun `collection value`() {
+        assertEquals("mutableListOf(1, 2)", ValueDescAnalyzer.analyze(listOf(1, 2)).renderToString(renderer))
+        assertEquals("mutableSetOf(1, 2)", ValueDescAnalyzer.analyze(setOf(1, 2)).renderToString(renderer))
+    }
+
+    @Test
+    fun `collection value nested`() {
+        assertEquals(
+            """
+                mutableListOf(
+                    mutableListOf(1, 2), 
+                    mutableListOf(1, 2), 
+                )
+            """.trimIndent(),
+            ValueDescAnalyzer.analyze(listOf(listOf(1, 2), listOf(1, 2))).renderToString(renderer)
+        )
+        assertEquals(
+            """
+                mutableSetOf(
+                    mutableListOf(1, 2), 
+                    mutableListOf(2, 2), 
+                )
+            """.trimIndent(),
+            ValueDescAnalyzer.analyze(setOf(listOf(1, 2), listOf(2, 2))).renderToString(renderer)
+        )
+    }
+
+    @Test
+    fun `map value`() {
+        assertEquals(
+            """
+            mutableMapOf(
+                1 to 2,
+                3 to 2,
+            )
+        """.trimIndent(), ValueDescAnalyzer.analyze(mapOf(1 to 2, 3 to 2)).renderToString(renderer)
+        )
+    }
+
+    @Test
+    fun `map value nested`() {
+        assertEquals(
+            """
+            |mutableMapOf(
+            |    1 to 2,
+            |    5 to mutableMapOf(
+            |        1 to 2,
+            |        3 to 2,
+            |    ),
+            |)
+        """.trimMargin(),
+            ValueDescAnalyzer.analyze(mapOf(1 to 2, 5 to mapOf(1 to 2, 3 to 2)))
+                .renderToString(renderer)
+        )
+    }
+
+    data class MyClass(
+        val str: String,
+        val int: Int,
+        val self: MyClass?,
+    )
+
+    @Test
+    fun `class value`() {
+        data class MyClass2(
+            val str: String,
+            val int: Int,
+            val self: MyClass2?,
+        )
+
+        assertEquals(
+            """
+                ${MyClass::class.qualifiedName}(
+                    str = "str",
+                    int = 1,
+                    self = null,
+                )
+            """.trimIndent(),
+            ValueDescAnalyzer.analyze(MyClass("str", 1, null)).renderToString(renderer)
+        )
+
+        assertEquals(
+            """
+                `${MyClass2::class.java.name}`(
+                    str = "str",
+                    int = 1,
+                    self = null,
+                )
+            """.trimIndent(),
+            ValueDescAnalyzer.analyze(MyClass2("str", 1, null)).renderToString(renderer)
+        )
+    }
+
+    @Test
+    fun `class value nested`() {
+        data class MyClass2(
+            val str: String,
+            val int: Int,
+            val self: MyClass2?,
+        )
+
+        assertEquals(
+            """
+                ${MyClass::class.qualifiedName}(
+                    str = "str",
+                    int = 1,
+                    self = ${MyClass::class.qualifiedName}(
+                        str = "str",
+                        int = 1,
+                        self = null,
+                    ),
+                )
+            """.trimIndent(),
+            ValueDescAnalyzer.analyze(MyClass("str", 1, MyClass("str", 1, null))).renderToString(renderer)
+        )
+
+        assertEquals(
+            """
+                `${MyClass2::class.java.name}`(
+                    str = "str",
+                    int = 1,
+                    self = `${MyClass2::class.java.name}`(
+                        str = "str",
+                        int = 1,
+                        self = null,
+                    ),
+                )
+            """.trimIndent(),
+            ValueDescAnalyzer.analyze(MyClass2("str", 1, MyClass2("str", 1, null))).renderToString(renderer)
+        )
+    }
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitor/ValueDescTransformer.kt b/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitor/ValueDescTransformer.kt
new file mode 100644
index 000000000..32d4707ed
--- /dev/null
+++ b/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitor/ValueDescTransformer.kt
@@ -0,0 +1,100 @@
+/*
+ * 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
+ */
+
+@file:OptIn(ExperimentalStdlibApi::class)
+
+package net.mamoe.mirai.internal.testFramework.codegen.visitor
+
+import net.mamoe.mirai.internal.testFramework.codegen.descriptors.*
+import kotlin.reflect.KParameter
+
+abstract class ValueDescTransformer<D> : ValueDescVisitor<D, ValueDesc?> {
+    override fun visitValue(desc: ValueDesc, data: D): ValueDesc? {
+        return desc
+    }
+
+    override fun visitObjectArray(desc: ObjectArrayValueDesc, data: D): ValueDesc? {
+        val newElements = desc.elements.mapNotNull { element ->
+            element.acceptChildren(this, data)
+            element.accept(this, data)
+        }
+        return ObjectArrayValueDesc(desc.parent, desc.value, desc.origin, desc.arrayType, desc.elementType).apply {
+            elements.clear()
+            elements.addAll(newElements)
+        }
+    }
+
+    override fun visitPrimitiveArray(desc: PrimitiveArrayValueDesc, data: D): ValueDesc? {
+        val newElements = desc.elements.mapNotNull { element ->
+            element.acceptChildren(this, data)
+            element.accept(this, data)
+        }
+        return PrimitiveArrayValueDesc(desc.parent, desc.value, desc.origin, desc.arrayType, desc.elementType).apply {
+            elements.clear()
+            elements.addAll(newElements)
+        }
+    }
+
+    override fun visitCollection(desc: CollectionValueDesc, data: D): ValueDesc? {
+        val newElements = desc.elements.mapNotNull { element ->
+            element.acceptChildren(this, data)
+            element.accept(this, data)
+        }
+        return CollectionValueDesc(desc.parent, desc.value, desc.origin, desc.arrayType, desc.elementType).apply {
+            elements.clear()
+            elements.addAll(newElements)
+        }
+    }
+
+    override fun <T : Any> visitClass(desc: ClassValueDesc<T>, data: D): ValueDesc? {
+        val resultMap = mutableMapOf<KParameter, ValueDesc>()
+        for ((param, value) in desc.properties) {
+            value.accept(this, data)?.let { resultMap.put(param, it) }
+        }
+        return ClassValueDesc(desc.parent, desc.origin, resultMap)
+    }
+}
+
+abstract class ValueDescTransformerNotNull<D> : ValueDescTransformer<D>() {
+    private fun fail(): Nothing {
+        throw IllegalStateException("ValueDescTransformerNotNull cannot return null from its 'visit' functions.")
+    }
+
+    override fun visitValue(desc: ValueDesc, data: D): ValueDesc {
+        return super.visitValue(desc, data) ?: fail()
+    }
+
+    override fun visitObjectArray(desc: ObjectArrayValueDesc, data: D): ValueDesc {
+        return super.visitObjectArray(desc, data) ?: fail()
+    }
+
+    override fun visitPrimitiveArray(desc: PrimitiveArrayValueDesc, data: D): ValueDesc {
+        return super.visitPrimitiveArray(desc, data) ?: fail()
+    }
+
+    override fun visitCollection(desc: CollectionValueDesc, data: D): ValueDesc {
+        return super.visitCollection(desc, data) ?: fail()
+    }
+
+    override fun <T : Any> visitClass(desc: ClassValueDesc<T>, data: D): ValueDesc {
+        return super.visitClass(desc, data) ?: fail()
+    }
+
+    override fun visitPlain(desc: PlainValueDesc, data: D): ValueDesc? {
+        return super.visitPlain(desc, data) ?: fail()
+    }
+
+    override fun visitArray(desc: CollectionLikeValueDesc, data: D): ValueDesc {
+        return super.visitArray(desc, data) ?: fail()
+    }
+
+    override fun visitMap(desc: MapValueDesc, data: D): ValueDesc {
+        return super.visitMap(desc, data) ?: fail()
+    }
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitor/ValueDescVisitor.kt b/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitor/ValueDescVisitor.kt
new file mode 100644
index 000000000..655eb8978
--- /dev/null
+++ b/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitor/ValueDescVisitor.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.testFramework.codegen.visitor
+
+import net.mamoe.mirai.internal.testFramework.codegen.descriptors.*
+
+interface ValueDescVisitor<D, R> {
+    fun visitValue(desc: ValueDesc, data: D): R
+
+    fun visitPlain(desc: PlainValueDesc, data: D): R {
+        return visitValue(desc, data)
+    }
+
+    fun visitArray(desc: CollectionLikeValueDesc, data: D): R {
+        return visitValue(desc, data)
+    }
+
+    fun visitObjectArray(desc: ObjectArrayValueDesc, data: D): R {
+        return visitArray(desc, data)
+    }
+
+    fun visitCollection(desc: CollectionValueDesc, data: D): R {
+        return visitArray(desc, data)
+    }
+
+    fun visitMap(desc: MapValueDesc, data: D): R {
+        return visitValue(desc, data)
+    }
+
+    fun visitPrimitiveArray(desc: PrimitiveArrayValueDesc, data: D): R {
+        return visitArray(desc, data)
+    }
+
+    fun <T : Any> visitClass(desc: ClassValueDesc<T>, data: D): R {
+        return visitValue(desc, data)
+    }
+}
diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitor/ValueDescVisitorUnit.kt b/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitor/ValueDescVisitorUnit.kt
new file mode 100644
index 000000000..b7831ce73
--- /dev/null
+++ b/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitor/ValueDescVisitorUnit.kt
@@ -0,0 +1,17 @@
+/*
+ * 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.testFramework.codegen.visitor
+
+import net.mamoe.mirai.internal.testFramework.codegen.descriptors.ValueDesc
+
+interface ValueDescVisitorUnit : ValueDescVisitor<Nothing?, Unit> {
+    override fun visitValue(desc: ValueDesc, data: Nothing?) {
+    }
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/AnalyzeDefaultValuesMappingVisitor.kt b/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/AnalyzeDefaultValuesMappingVisitor.kt
new file mode 100644
index 000000000..8e9b6740a
--- /dev/null
+++ b/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/AnalyzeDefaultValuesMappingVisitor.kt
@@ -0,0 +1,68 @@
+/*
+ * 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.testFramework.codegen.visitors
+
+import net.mamoe.mirai.internal.testFramework.codegen.descriptors.ClassValueDesc
+import net.mamoe.mirai.internal.testFramework.codegen.descriptors.ValueDesc
+import net.mamoe.mirai.internal.testFramework.codegen.visitor.ValueDescVisitorUnit
+import net.mamoe.mirai.utils.cast
+import kotlin.reflect.KClass
+import kotlin.reflect.KParameter
+import kotlin.reflect.KProperty
+import kotlin.reflect.KProperty1
+import kotlin.reflect.full.memberProperties
+import kotlin.reflect.full.primaryConstructor
+
+class DefaultValuesMapping(
+    val forClass: KClass<*>,
+    val mapping: MutableMap<String, Any?> = mutableMapOf()
+) {
+    operator fun get(property: KProperty<*>): Any? = mapping[property.name]
+}
+
+class AnalyzeDefaultValuesMappingVisitor : ValueDescVisitorUnit {
+    val mappings: MutableList<DefaultValuesMapping> = mutableListOf()
+
+    override fun visitValue(desc: ValueDesc, data: Nothing?) {
+        desc.acceptChildren(this, data)
+    }
+
+    override fun <T : Any> visitClass(desc: ClassValueDesc<T>, data: Nothing?) {
+        super.visitClass(desc, data)
+
+        if (mappings.any { it.forClass == desc.type }) return
+
+        val defaultInstance =
+            createInstanceWithMostDefaultValues(desc.type, desc.properties.mapValues { it.value.origin })
+
+        val optionalParameters = desc.type.primaryConstructor!!.parameters.filter { it.isOptional }
+
+        mappings.add(
+            DefaultValuesMapping(
+                desc.type,
+                optionalParameters.associateTo(mutableMapOf()) { param ->
+                    val value = findCorrespondingProperty(desc, param).get(defaultInstance)
+                    param.name!! to value
+                }
+            )
+        )
+    }
+
+
+    private fun <T : Any> findCorrespondingProperty(
+        desc: ClassValueDesc<T>,
+        param: KParameter
+    ) = desc.type.memberProperties.single { it.name == param.name }.cast<KProperty1<Any, Any>>()
+
+    private fun <T : Any> createInstanceWithMostDefaultValues(clazz: KClass<T>, arguments: Map<KParameter, Any?>): T {
+        val primaryConstructor = clazz.primaryConstructor ?: error("Type $clazz does not have primary constructor.")
+        return primaryConstructor.callBy(arguments.filter { !it.key.isOptional })
+    }
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/ClassFormatter.kt b/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/ClassFormatter.kt
new file mode 100644
index 000000000..068258dfd
--- /dev/null
+++ b/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/ClassFormatter.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.testFramework.codegen.visitors
+
+import net.mamoe.mirai.internal.testFramework.codegen.descriptors.ClassValueDesc
+
+data class ClassFormatterContext(
+    val desc: ClassValueDesc<*>,
+    val visitor: ValueDescToStringRenderer,
+    val rendererContext: RendererContext,
+)
+
+open class ClassFormatter {
+    open fun formatClassName(context: ClassFormatterContext): String {
+        val name = context.desc.type.qualifiedName ?: context.desc.type.java.name
+        return wrapBacktickIfNecessary(name)
+    }
+
+    open fun formatClassProperty(context: ClassFormatterContext, propertyString: String?, valueString: String): String =
+        "$propertyString = $valueString"
+
+    companion object {
+        protected fun wrapBacktickIfNecessary(name: String) = if (name.contains(' ') || name.contains('$')) {
+            "`$name`"
+        } else name
+    }
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/Indenter.kt b/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/Indenter.kt
new file mode 100644
index 000000000..e99c8f963
--- /dev/null
+++ b/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/Indenter.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.testFramework.codegen.visitors
+
+fun interface Indenter {
+    fun indentize(string: String): String
+
+    object NoIndent : Indenter {
+        override fun indentize(string: String): String = string
+    }
+}
+
+class WordingIndenter constructor(
+    private val separator: String,
+) : Indenter {
+
+    override fun indentize(string: String): String = "$separator$string"
+
+    companion object {
+        fun spacing(count: Int = 4): Indenter = WordingIndenter(" ".repeat(count))
+    }
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/OptimizeByteArrayAsHexStringTransformer.kt b/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/OptimizeByteArrayAsHexStringTransformer.kt
new file mode 100644
index 000000000..e15243f40
--- /dev/null
+++ b/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/OptimizeByteArrayAsHexStringTransformer.kt
@@ -0,0 +1,88 @@
+/*
+ * 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
+ */
+
+@file:OptIn(ExperimentalStdlibApi::class)
+
+package net.mamoe.mirai.internal.testFramework.codegen.visitors
+
+import net.mamoe.mirai.internal.testFramework.codegen.descriptors.*
+import net.mamoe.mirai.internal.testFramework.codegen.visitor.ValueDescTransformer
+import net.mamoe.mirai.internal.testFramework.codegen.visitor.ValueDescTransformerNotNull
+import net.mamoe.mirai.utils.toUHexString
+import kotlin.reflect.typeOf
+
+/**
+ * If the byte array was from [String.toByteArray], transform it to the [String].
+ * Otherwise, transform it as hex string.
+ */
+open class OptimizeByteArrayAsHexStringTransformer : ValueDescTransformerNotNull<Nothing?>() {
+    open fun transform(desc: CollectionLikeValueDesc, value: ByteArray): ValueDesc {
+        return if (isReadableString(value)) {
+            // prefers to show readable string
+            PlainValueDesc(
+                desc.parent,
+                value = "\"${
+                    value.decodeToString().escapeQuotation()
+                }\".toByteArray() /* ${value.toUHexString()} */",
+                origin = desc.origin
+            )
+        } else {
+            PlainValueDesc(
+                desc.parent,
+                value = "\"${value.toUHexString()}\".hexToBytes()",
+                origin = desc.origin
+            )
+        }
+
+    }
+
+    protected fun isReadableString(value: ByteArray) =
+        value.decodeToString().all { Character.isUnicodeIdentifierPart(it) || it.isWhitespace() }
+
+    override fun visitValue(desc: ValueDesc, data: Nothing?): ValueDesc {
+        desc.acceptChildren(this, data)
+        return super.visitValue(desc, data)
+    }
+
+    override fun visitObjectArray(desc: ObjectArrayValueDesc, data: Nothing?): ValueDesc {
+        if (desc.arrayType == arrayOfByteType) {
+            val array = desc.elements.mapNotNull { (it as? PlainValueDesc)?.value?.toByteOrNull() }.toByteArray()
+            if (array.size != desc.elements.size) return desc
+
+            return transform(desc, array)
+        }
+        return super.visitObjectArray(desc, data)
+    }
+
+    override fun visitPrimitiveArray(desc: PrimitiveArrayValueDesc, data: Nothing?): ValueDesc {
+        if (desc.value is ByteArray) {
+            return transform(desc, desc.value as ByteArray)
+        }
+        return super.visitPrimitiveArray(desc, data)
+    }
+
+    companion object {
+        private val arrayOfByteType = typeOf<Array<Byte>>()
+        private fun String.escapeQuotation(): String = buildString { this@escapeQuotation.escapeQuotationTo(this) }
+
+        private fun String.escapeQuotationTo(out: StringBuilder) {
+            for (element in this) {
+                when (element) {
+                    '\\' -> out.append("\\\\")
+                    '\n' -> out.append("\\n")
+                    '\r' -> out.append("\\r")
+                    '\t' -> out.append("\\t")
+                    '\"' -> out.append("\\\"")
+                    else -> out.append(element)
+                }
+            }
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/ValueDescToStringRenderer.kt b/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/ValueDescToStringRenderer.kt
new file mode 100644
index 000000000..a8a9c128c
--- /dev/null
+++ b/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/ValueDescToStringRenderer.kt
@@ -0,0 +1,138 @@
+/*
+ * 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.testFramework.codegen.visitors
+
+import net.mamoe.mirai.internal.testFramework.codegen.descriptors.*
+import net.mamoe.mirai.internal.testFramework.codegen.visitor.ValueDescVisitor
+
+class RendererContext(
+    val indenter: Indenter,
+    val classFormatter: ClassFormatter = ClassFormatter()
+)
+
+fun ValueDesc.renderToString(
+    renderer: ValueDescToStringRenderer = ValueDescToStringRenderer(),
+    rendererContext: RendererContext = RendererContext(WordingIndenter.spacing(4))
+): String {
+    return accept(
+        renderer,
+        rendererContext
+    )
+}
+
+open class ValueDescToStringRenderer : ValueDescVisitor<RendererContext, String> {
+    private inline val visitor get() = this
+
+    private inline fun Appendable.withIndent(indenter: Indenter, block: Appendable.() -> Unit) {
+        append(
+            buildString(block)
+                .lineSequence()
+                .map { indenter.indentize(it) }
+                .joinToString("\n")
+                .trimEnd { it != '\n' && it.isWhitespace() }
+        )
+    }
+
+    override fun visitValue(desc: ValueDesc, data: RendererContext): String {
+        return desc.toString() // tentative fallback
+    }
+
+    override fun visitPlain(desc: PlainValueDesc, data: RendererContext): String {
+        return desc.value
+    }
+
+    override fun visitArray(desc: CollectionLikeValueDesc, data: RendererContext): String = buildString {
+        val array = desc.value
+
+        fun impl(funcName: String, elements: List<ValueDesc>) {
+            if (elements.any { it !is PlainValueDesc }) {
+                // complex types
+                append(funcName)
+                append('(')
+                appendLine()
+                withIndent(data.indenter) {
+                    val list = elements.toList()
+                    list.forEach { desc ->
+                        append(desc.accept(visitor, data))
+                        appendLine(", ")
+                    }
+                }
+                append(')')
+            } else {
+                // primitive types
+                append(funcName)
+                append('(')
+                val list = elements.toList()
+                list.forEachIndexed { index, desc ->
+                    append(desc.accept(visitor, data))
+                    if (index != list.lastIndex) append(", ")
+                }
+                append(')')
+            }
+        }
+
+        when (array) {
+            is Array<*> -> impl("arrayOf", desc.elements)
+            is IntArray -> impl("intArrayOf", desc.elements)
+            is ByteArray -> impl("byteArrayOf", desc.elements)
+            is ShortArray -> impl("shortArrayOf", desc.elements)
+            is CharArray -> impl("charArrayOf", desc.elements)
+            is LongArray -> impl("longArrayOf", desc.elements)
+            is FloatArray -> impl("floatArrayOf", desc.elements)
+            is DoubleArray -> impl("doubleArrayOf", desc.elements)
+            is BooleanArray -> impl("booleanArrayOf", desc.elements)
+            is List<*> -> impl("mutableListOf", desc.elements)
+            is Set<*> -> impl("mutableSetOf", desc.elements)
+            else -> error("$array is not an array.")
+        }
+    }
+
+    override fun visitMap(desc: MapValueDesc, data: RendererContext): String = buildString {
+        appendLine("mutableMapOf(")
+        for ((key, value) in desc.elements) {
+            withIndent(data.indenter) {
+                append(key.accept(visitor, data))
+                append(" to ")
+                append(value.accept(visitor, data))
+                appendLine(",")
+            }
+        }
+        append(")")
+    }
+
+    override fun <T : Any> visitClass(desc: ClassValueDesc<T>, data: RendererContext): String = buildString {
+        val classFormatterContext = ClassFormatterContext(desc, visitor, data)
+        appendLine("${data.classFormatter.formatClassName(classFormatterContext)}(")
+        for ((param, valueDesc) in desc.properties) {
+            withIndent(data.indenter) {
+                append(
+                    data.classFormatter.formatClassProperty(
+                        classFormatterContext,
+                        param.name,
+                        valueDesc.accept(visitor, data)
+                    )
+                )
+            }
+            appendLine(",")
+        }
+        append(")")
+    }
+
+    open fun renderClassName(desc: ClassValueDesc<*>): String {
+        val name = desc.type.qualifiedName ?: desc.type.java.name
+        return wrapBacktickIfNecessary(name)
+    }
+
+    companion object {
+        protected fun wrapBacktickIfNecessary(name: String) = if (name.contains(' ') || name.contains('$')) {
+            "`$name`"
+        } else name
+    }
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonTest/kotlin/notice/Desensitizer.kt b/mirai-core/src/commonTest/kotlin/testFramework/desensitizer/Desensitizer.kt
similarity index 62%
rename from mirai-core/src/commonTest/kotlin/notice/Desensitizer.kt
rename to mirai-core/src/commonTest/kotlin/testFramework/desensitizer/Desensitizer.kt
index d06cc07d2..938768e40 100644
--- a/mirai-core/src/commonTest/kotlin/notice/Desensitizer.kt
+++ b/mirai-core/src/commonTest/kotlin/testFramework/desensitizer/Desensitizer.kt
@@ -7,12 +7,17 @@
  * https://github.com/mamoe/mirai/blob/dev/LICENSE
  */
 
-package net.mamoe.mirai.internal.notice
+package net.mamoe.mirai.internal.testFramework.desensitizer
 
 import kotlinx.serialization.decodeFromString
 import net.mamoe.mirai.Mirai
-import net.mamoe.mirai.internal.notice.Desensitizer.Companion.generateAndDesensitize
-import net.mamoe.mirai.internal.utils.codegen.*
+import net.mamoe.mirai.internal.testFramework.codegen.ValueDescAnalyzer
+import net.mamoe.mirai.internal.testFramework.codegen.analyze
+import net.mamoe.mirai.internal.testFramework.codegen.descriptors.*
+import net.mamoe.mirai.internal.testFramework.codegen.removeDefaultValues
+import net.mamoe.mirai.internal.testFramework.codegen.visitor.ValueDescTransformerNotNull
+import net.mamoe.mirai.internal.testFramework.codegen.visitors.OptimizeByteArrayAsHexStringTransformer
+import net.mamoe.mirai.internal.testFramework.codegen.visitors.renderToString
 import net.mamoe.mirai.internal.utils.io.NestedStructure
 import net.mamoe.mirai.internal.utils.io.NestedStructureDesensitizer
 import net.mamoe.mirai.internal.utils.io.ProtocolStruct
@@ -74,19 +79,20 @@ internal class Desensitizer private constructor(
         fun desensitize(string: String): String = instance.desensitize(string)
 
 
-        fun ConstructorCallCodegenFacade.generateAndDesensitize(
+        fun ValueDescAnalyzer.generateAndDesensitize(
             value: Any?,
             type: KType,
             desensitizer: Desensitizer = instance,
         ): String {
-            val a = analyze(value, type).apply {
-                accept(DesensitizationVisitor(desensitizer))
-            }
-            return generate(a)
+            return analyze(value, type)
+                .transform(OptimizeByteArrayAsHexStringTransformer())
+                .removeDefaultValues()
+                .transform(DesensitizationVisitor(desensitizer))
+                .renderToString()
         }
 
         @OptIn(ExperimentalStdlibApi::class)
-        inline fun <reified T> ConstructorCallCodegenFacade.generateAndDesensitize(
+        inline fun <reified T> ValueDescAnalyzer.generateAndDesensitize(
             value: T,
             desensitizer: Desensitizer = instance,
         ): String = generateAndDesensitize(value, typeOf<T>(), desensitizer)
@@ -161,52 +167,70 @@ private val format = Yaml {
 
 private class DesensitizationVisitor(
     private val desensitizer: Desensitizer,
-) : ValueDescVisitor {
-    override fun visitPlain(desc: PlainValueDesc) {
-        desc.value = desensitizer.desensitize(desc.value)
+) : ValueDescTransformerNotNull<Nothing?>() {
+    override fun visitValue(desc: ValueDesc, data: Nothing?): ValueDesc {
+        desc.acceptChildren(this, data)
+        return super.visitValue(desc, data)
     }
 
-    override fun visitObjectArray(desc: ObjectArrayValueDesc) {
-        if (desc.arrayType.arguments.first().type?.classifier == Byte::class) { // variance is ignored
+    override fun visitPlain(desc: PlainValueDesc, data: Nothing?): ValueDesc {
+        return PlainValueDesc(desc.parent, desensitizer.desensitize(desc.value), desc.origin)
+    }
+
+    override fun visitObjectArray(desc: ObjectArrayValueDesc, data: Nothing?): ValueDesc {
+        return if (
+            desc.arrayType.arguments.firstOrNull()?.type?.classifier == Byte::class
+            || (desc.value as? Array<*>)?.getOrNull(0) is Byte
+        ) {
             @Suppress("UNCHECKED_CAST")
-            desc.value = desensitizer.desensitize(desc.value as Array<Byte>)
+            ObjectArrayValueDesc(
+                desc.parent,
+                desensitizer.desensitize(desc.value as Array<Byte>),
+                desc.origin,
+                desc.arrayType,
+                desc.elementType
+            )
         } else {
-            for (element in desc.elements) {
-                element.accept(this)
+            super.visitObjectArray(desc, data)
+        }
+    }
+
+    override fun visitPrimitiveArray(desc: PrimitiveArrayValueDesc, data: Nothing?): ValueDesc {
+        return if (desc.value is ByteArray) {
+            PrimitiveArrayValueDesc(
+                desc.parent,
+                desensitizer.desensitize(desc.value as ByteArray),
+                desc.origin,
+                desc.arrayType,
+                desc.elementType
+            )
+        } else super.visitPrimitiveArray(desc, data)
+    }
+
+    override fun <T : Any> visitClass(desc: ClassValueDesc<T>, data: Nothing?): ValueDesc {
+        ClassValueDesc(desc.parent, desc.origin, desc.properties.toMutableMap().apply {
+            replaceAll() { key, value ->
+                val annotation = key.findAnnotation<NestedStructure>()
+                if (annotation != null && value.origin is ByteArray) {
+                    val instance = annotation.serializer.objectInstance ?: annotation.serializer.createInstance()
+
+                    val result = instance.cast<NestedStructureDesensitizer<ProtocolStruct, ProtocolStruct>>()
+                        .deserialize(desc.origin as ProtocolStruct, value.origin as ByteArray)
+                        ?: desc.origin
+
+                    val generate = ValueDescAnalyzer.analyze(result)
+                        .transform(OptimizeByteArrayAsHexStringTransformer())
+                        .transform(DesensitizationVisitor(desensitizer))
+                        .renderToString()
+                    PlainValueDesc(
+                        desc,
+                        "$generate.toByteArray(${result::class.qualifiedName}.serializer())",
+                        value.origin
+                    )
+                } else value
             }
-        }
-    }
-
-    override fun visitCollection(desc: CollectionValueDesc) {
-        for (element in desc.elements) {
-            element.accept(this)
-        }
-    }
-
-    override fun visitPrimitiveArray(desc: PrimitiveArrayValueDesc) {
-        if (desc.value is ByteArray) {
-            desc.value = desensitizer.desensitize(desc.value as ByteArray)
-        }
-    }
-
-    override fun <T : Any> visitClass(desc: ClassValueDesc<T>) {
-        super.visitClass(desc)
-        desc.properties.replaceAll() { key, value ->
-            val annotation = key.findAnnotation<NestedStructure>()
-            if (annotation != null && value.origin is ByteArray) {
-                val instance = annotation.serializer.objectInstance ?: annotation.serializer.createInstance()
-
-                val result = instance.cast<NestedStructureDesensitizer<ProtocolStruct, ProtocolStruct>>()
-                    .deserialize(desc.origin as ProtocolStruct, value.origin as ByteArray)
-                    ?: desc.origin
-
-                val generate = ConstructorCallCodegenFacade.generateAndDesensitize(result)
-                PlainValueDesc(
-                    desc,
-                    "$generate.toByteArray(${result::class.qualifiedName}.serializer())",
-                    value.origin
-                )
-            } else value
+        }).let {
+            return super.visitClass(it, data)
         }
     }
 }
\ No newline at end of file
diff --git a/mirai-core/src/commonTest/kotlin/notice/RecordingNoticeHandler.kt b/mirai-core/src/commonTest/kotlin/testFramework/notice/RecordingNoticeHandler.kt
similarity index 82%
rename from mirai-core/src/commonTest/kotlin/notice/RecordingNoticeHandler.kt
rename to mirai-core/src/commonTest/kotlin/testFramework/notice/RecordingNoticeHandler.kt
index b3e529944..fde34f062 100644
--- a/mirai-core/src/commonTest/kotlin/notice/RecordingNoticeHandler.kt
+++ b/mirai-core/src/commonTest/kotlin/testFramework/notice/RecordingNoticeHandler.kt
@@ -7,15 +7,15 @@
  * https://github.com/mamoe/mirai/blob/dev/LICENSE
  */
 
-package net.mamoe.mirai.internal.notice
+package net.mamoe.mirai.internal.testFramework.notice
 
 import kotlinx.atomicfu.atomic
 import kotlinx.coroutines.sync.Mutex
 import kotlinx.coroutines.sync.withLock
 import net.mamoe.mirai.internal.network.components.NoticePipelineContext
 import net.mamoe.mirai.internal.network.components.SimpleNoticeProcessor
-import net.mamoe.mirai.internal.notice.Desensitizer.Companion.generateAndDesensitize
-import net.mamoe.mirai.internal.utils.codegen.ConstructorCallCodegenFacade
+import net.mamoe.mirai.internal.testFramework.codegen.ValueDescAnalyzer
+import net.mamoe.mirai.internal.testFramework.desensitizer.Desensitizer.Companion.generateAndDesensitize
 import net.mamoe.mirai.internal.utils.io.ProtocolStruct
 import net.mamoe.mirai.utils.MiraiLogger
 import net.mamoe.mirai.utils.info
@@ -42,7 +42,7 @@ internal class RecordingNoticeProcessor : SimpleNoticeProcessor<ProtocolStruct>(
         lock.withLock {
             id.getAndDecrement()
             logger.info { "Recorded #${id.value} ${data::class.simpleName}" }
-            logger.info { "Desensitized: \n\n\u001B[0m" + ConstructorCallCodegenFacade.generateAndDesensitize(data) + "\n\n" }
+            logger.info { "Desensitized: \n\n\u001B[0m" + ValueDescAnalyzer.generateAndDesensitize(data) + "\n\n" }
         }
     }
 }
diff --git a/mirai-core/src/commonTest/kotlin/testFramework/package.kt b/mirai-core/src/commonTest/kotlin/testFramework/package.kt
new file mode 100644
index 000000000..9d69d364d
--- /dev/null
+++ b/mirai-core/src/commonTest/kotlin/testFramework/package.kt
@@ -0,0 +1,10 @@
+/*
+ * 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.testFramework
\ No newline at end of file
diff --git a/mirai-core/src/commonTest/kotlin/utils/codegen/ValueCodegen.kt b/mirai-core/src/commonTest/kotlin/utils/codegen/ValueCodegen.kt
deleted file mode 100644
index 6954d9120..000000000
--- a/mirai-core/src/commonTest/kotlin/utils/codegen/ValueCodegen.kt
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * 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.codegen
-
-import net.mamoe.mirai.utils.toUHexString
-
-class ValueCodegen(
-    val context: CodegenContext
-) {
-    fun generate(desc: ValueDesc) {
-        when (desc) {
-            is PlainValueDesc -> generate(desc)
-            is ObjectArrayValueDesc -> generate(desc)
-            is PrimitiveArrayValueDesc -> generate(desc)
-            is CollectionValueDesc -> generate(desc)
-            is ClassValueDesc<*> -> generate(desc)
-            is MapValueDesc -> generate(desc)
-        }
-    }
-
-    fun generate(desc: PlainValueDesc) {
-        context.append(desc.value)
-    }
-
-    fun generate(desc: MapValueDesc) {
-        context.run {
-            appendLine("mutableMapOf(")
-            for ((key, value) in desc.elements) {
-                generate(key)
-                append(" to ")
-                generate(value)
-                appendLine(",")
-            }
-            append(")")
-        }
-    }
-
-    fun <T : Any> generate(desc: ClassValueDesc<T>) {
-        context.run {
-            appendLine("${desc.type.qualifiedName}(")
-            for ((param, valueDesc) in desc.properties) {
-                append(param.name)
-                append("=")
-                generate(valueDesc)
-                appendLine(",")
-            }
-            append(")")
-        }
-    }
-
-    fun generate(desc: ArrayValueDesc) {
-        val array = desc.value
-
-        fun impl(funcName: String, elements: List<ValueDesc>) {
-            context.run {
-                append(funcName)
-                append('(')
-                val list = elements.toList()
-                list.forEachIndexed { index, desc ->
-                    generate(desc)
-                    if (index != list.lastIndex) append(", ")
-                }
-                append(')')
-            }
-        }
-
-        return when (array) {
-            is Array<*> -> impl("arrayOf", desc.elements)
-            is IntArray -> impl("intArrayOf", desc.elements)
-            is ByteArray -> {
-                if (array.size == 0) {
-                    context.append("net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY") // let IDE to shorten references.
-                    return
-                } else {
-                    if (array.decodeToString().all { Character.isUnicodeIdentifierPart(it) || it.isWhitespace() }) {
-                        // prefers to show readable string
-                        context.append(
-                            "\"${
-                                array.decodeToString().escapeQuotation()
-                            }\".toByteArray() /* ${array.toUHexString()} */"
-                        )
-                    } else {
-                        context.append("\"${array.toUHexString()}\".hexToBytes()")
-                    }
-                    return
-                }
-            }
-            is ShortArray -> impl("shortArrayOf", desc.elements)
-            is CharArray -> impl("charArrayOf", desc.elements)
-            is LongArray -> impl("longArrayOf", desc.elements)
-            is FloatArray -> impl("floatArrayOf", desc.elements)
-            is DoubleArray -> impl("doubleArrayOf", desc.elements)
-            is BooleanArray -> impl("booleanArrayOf", desc.elements)
-            is List<*> -> impl("mutableListOf", desc.elements)
-            is Set<*> -> impl("mutableSetOf", desc.elements)
-            else -> error("$array is not an array.")
-        }
-    }
-}
-
-class CodegenContext(
-    val sb: StringBuilder = StringBuilder(),
-    val configuration: CodegenConfiguration = CodegenConfiguration()
-) : Appendable by sb {
-    fun getResult(): String {
-        return sb.toString()
-    }
-}
-
-class CodegenConfiguration(
-    var removeDefaultValues: Boolean = true,
-)
-
-
-private fun String.escapeQuotation(): String = buildString { this@escapeQuotation.escapeQuotationTo(this) }
-
-private fun String.escapeQuotationTo(out: StringBuilder) {
-    for (i in 0 until length) {
-        when (val ch = this[i]) {
-            '\\' -> out.append("\\\\")
-            '\n' -> out.append("\\n")
-            '\r' -> out.append("\\r")
-            '\t' -> out.append("\\t")
-            '\"' -> out.append("\\\"")
-            else -> out.append(ch)
-        }
-    }
-}
-
diff --git a/mirai-core/src/commonTest/kotlin/utils/codegen/ValueDesc.kt b/mirai-core/src/commonTest/kotlin/utils/codegen/ValueDesc.kt
deleted file mode 100644
index 767cf6926..000000000
--- a/mirai-core/src/commonTest/kotlin/utils/codegen/ValueDesc.kt
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * 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.codegen
-
-import kotlin.reflect.KClass
-import kotlin.reflect.KParameter
-import kotlin.reflect.KType
-import kotlin.reflect.full.createType
-import kotlin.reflect.typeOf
-
-sealed interface ValueDesc {
-    val origin: Any?
-    val parent: ValueDesc?
-
-    fun accept(visitor: ValueDescVisitor)
-}
-
-val ValueDesc.parents
-    get() = sequence {
-        var parent = parent
-        do {
-            parent ?: return@sequence
-            yield(parent)
-            parent = parent.parent
-        } while (true);
-    }
-
-inline fun <reified T : ValueDesc> ValueDesc.findParent(): T? = parents.filterIsInstance<T>().firstOrNull()
-
-sealed interface ArrayValueDesc : ValueDesc {
-    val value: Any
-
-    val arrayType: KType
-    val elementType: KType
-    val elements: MutableList<ValueDesc>
-
-    companion object {
-        @OptIn(ExperimentalStdlibApi::class)
-        fun createOrNull(array: Any, type: KType, parent: ValueDesc?): ArrayValueDesc? {
-            if (array is Array<*>) return ObjectArrayValueDesc(parent, array, arrayType = type)
-            return when (array) {
-                is IntArray -> PrimitiveArrayValueDesc(parent, array, arrayType = type, elementType = typeOf<Int>())
-                is ByteArray -> PrimitiveArrayValueDesc(parent, array, arrayType = type, elementType = typeOf<Byte>())
-                is ShortArray -> PrimitiveArrayValueDesc(parent, array, arrayType = type, elementType = typeOf<Short>())
-                is CharArray -> PrimitiveArrayValueDesc(parent, array, arrayType = type, elementType = typeOf<Char>())
-                is LongArray -> PrimitiveArrayValueDesc(parent, array, arrayType = type, elementType = typeOf<Long>())
-                is FloatArray -> PrimitiveArrayValueDesc(parent, array, arrayType = type, elementType = typeOf<Float>())
-                is DoubleArray -> PrimitiveArrayValueDesc(
-                    parent,
-                    array,
-                    arrayType = type,
-                    elementType = typeOf<Double>()
-                )
-                is BooleanArray -> PrimitiveArrayValueDesc(
-                    parent,
-                    array,
-                    arrayType = type,
-                    elementType = typeOf<Boolean>()
-                )
-                else -> return null
-            }
-        }
-    }
-}
-
-class ObjectArrayValueDesc(
-    override val parent: ValueDesc?,
-    override var value: Array<*>,
-    override val origin: Array<*> = value,
-    override val arrayType: KType,
-    override val elementType: KType = arrayType.arguments.first().type ?: Any::class.createType(),
-) : ArrayValueDesc {
-    override val elements: MutableList<ValueDesc> by lazy {
-        value.mapTo(mutableListOf()) {
-            ConstructorCallCodegenFacade.analyze(it, elementType)
-        }
-    }
-
-    override fun accept(visitor: ValueDescVisitor) {
-        visitor.visitObjectArray(this)
-    }
-}
-
-class CollectionValueDesc(
-    override val parent: ValueDesc?,
-    override var value: Collection<*>,
-    override val origin: Collection<*> = value,
-    override val arrayType: KType,
-    override val elementType: KType = arrayType.arguments.first().type ?: Any::class.createType()
-) : ArrayValueDesc {
-    override val elements: MutableList<ValueDesc> by lazy {
-        value.mapTo(mutableListOf()) {
-            ConstructorCallCodegenFacade.analyze(it, elementType)
-        }
-    }
-
-    override fun accept(visitor: ValueDescVisitor) {
-        visitor.visitCollection(this)
-    }
-}
-
-class MapValueDesc(
-    override val parent: ValueDesc?,
-    var value: Map<Any?, Any?>,
-    override val origin: Map<Any?, Any?> = value,
-    val mapType: KType,
-    val keyType: KType = mapType.arguments.first().type ?: Any::class.createType(),
-    val valueType: KType = mapType.arguments[1].type ?: Any::class.createType(),
-) : ValueDesc {
-    val elements: MutableMap<ValueDesc, ValueDesc> by lazy {
-        value.map {
-            ConstructorCallCodegenFacade.analyze(it.key, keyType) to ConstructorCallCodegenFacade.analyze(
-                it.value,
-                valueType
-            )
-        }.toMap(mutableMapOf())
-    }
-
-    override fun accept(visitor: ValueDescVisitor) {
-        visitor.visitMap(this)
-    }
-}
-
-class PrimitiveArrayValueDesc(
-    override val parent: ValueDesc?,
-    override var value: Any,
-    override val origin: Any = value,
-    override val arrayType: KType,
-    override val elementType: KType
-) : ArrayValueDesc {
-    override val elements: MutableList<ValueDesc> by lazy {
-        when (val value = value) {
-            is IntArray -> value.mapTo(mutableListOf()) { ConstructorCallCodegenFacade.analyze(it, elementType) }
-            is ByteArray -> value.mapTo(mutableListOf()) { ConstructorCallCodegenFacade.analyze(it, elementType) }
-            is ShortArray -> value.mapTo(mutableListOf()) { ConstructorCallCodegenFacade.analyze(it, elementType) }
-            is CharArray -> value.mapTo(mutableListOf()) { ConstructorCallCodegenFacade.analyze(it, elementType) }
-            is LongArray -> value.mapTo(mutableListOf()) { ConstructorCallCodegenFacade.analyze(it, elementType) }
-            is FloatArray -> value.mapTo(mutableListOf()) { ConstructorCallCodegenFacade.analyze(it, elementType) }
-            is DoubleArray -> value.mapTo(mutableListOf()) { ConstructorCallCodegenFacade.analyze(it, elementType) }
-            is BooleanArray -> value.mapTo(mutableListOf()) { ConstructorCallCodegenFacade.analyze(it, elementType) }
-            else -> error("$value is not an array.")
-        }
-    }
-
-    override fun accept(visitor: ValueDescVisitor) {
-        visitor.visitPrimitiveArray(this)
-    }
-}
-
-class PlainValueDesc(
-    override val parent: ValueDesc?,
-    var value: String,
-    override val origin: Any?
-) : ValueDesc {
-    init {
-        require(value.isNotBlank())
-    }
-
-    override fun accept(visitor: ValueDescVisitor) {
-        visitor.visitPlain(this)
-    }
-}
-
-class ClassValueDesc<T : Any>(
-    override val parent: ValueDesc?,
-    override val origin: T,
-    val properties: MutableMap<KParameter, ValueDesc>,
-) : ValueDesc {
-    val type: KClass<out T> by lazy { origin::class }
-
-    override fun accept(visitor: ValueDescVisitor) {
-        visitor.visitClass(this)
-    }
-}
\ No newline at end of file
diff --git a/mirai-core/src/commonTest/kotlin/utils/codegen/test/ConstructorCallCodegenTest.kt b/mirai-core/src/commonTest/kotlin/utils/codegen/test/ConstructorCallCodegenTest.kt
deleted file mode 100644
index fec8533cf..000000000
--- a/mirai-core/src/commonTest/kotlin/utils/codegen/test/ConstructorCallCodegenTest.kt
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * 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.codegen.test
-
-import net.mamoe.mirai.internal.utils.codegen.ConstructorCallCodegenFacade
-import net.mamoe.mirai.internal.utils.codegen.analyzeAndGenerate
-import kotlin.test.Test
-import kotlin.test.assertEquals
-
-class ConstructorCallCodegenTest {
-
-    @Test
-    fun `test plain`() {
-        assertEquals(
-            "\"test\"",
-            ConstructorCallCodegenFacade.analyzeAndGenerate("test")
-        )
-        assertEquals(
-            "1",
-            ConstructorCallCodegenFacade.analyzeAndGenerate(1)
-        )
-        assertEquals(
-            "1.0",
-            ConstructorCallCodegenFacade.analyzeAndGenerate(1.0)
-        )
-    }
-
-    @Test
-    fun `test array`() {
-        assertEquals(
-            "arrayOf(1, 2)",
-            ConstructorCallCodegenFacade.analyzeAndGenerate(arrayOf(1, 2))
-        )
-        assertEquals(
-            "arrayOf(5.0)",
-            ConstructorCallCodegenFacade.analyzeAndGenerate(arrayOf(5.0))
-        )
-        assertEquals(
-            "arrayOf(\"1\")",
-            ConstructorCallCodegenFacade.analyzeAndGenerate(arrayOf("1"))
-        )
-        assertEquals(
-            "arrayOf(arrayOf(1))",
-            ConstructorCallCodegenFacade.analyzeAndGenerate(arrayOf(arrayOf(1)))
-        )
-    }
-
-    data class TestClass(
-        val value: String
-    )
-
-    data class TestClass2(
-        val value: Any
-    )
-
-    @Test
-    fun `test class`() {
-        assertEquals(
-            """
-                ${TestClass::class.qualifiedName!!}(
-                value="test",
-                )
-            """.trimIndent(),
-            ConstructorCallCodegenFacade.analyzeAndGenerate(TestClass("test"))
-        )
-        assertEquals(
-            """
-                ${TestClass2::class.qualifiedName!!}(
-                value="test",
-                )
-            """.trimIndent(),
-            ConstructorCallCodegenFacade.analyzeAndGenerate(TestClass2("test"))
-        )
-        assertEquals(
-            """
-                ${TestClass2::class.qualifiedName!!}(
-                value=1,
-                )
-            """.trimIndent(),
-            ConstructorCallCodegenFacade.analyzeAndGenerate(TestClass2(1))
-        )
-    }
-
-    data class TestNesting(
-        val nested: Nested
-    ) {
-        data class Nested(
-            val value: String
-        )
-    }
-
-    @Test
-    fun `test nesting`() {
-        assertEquals(
-            """
-                net.mamoe.mirai.internal.utils.codegen.test.ConstructorCallCodegenTest.TestNesting(
-                nested=net.mamoe.mirai.internal.utils.codegen.test.ConstructorCallCodegenTest.TestNesting.Nested(
-                value="test",
-                ),
-                )
-            """.trimIndent(),
-            ConstructorCallCodegenFacade.analyzeAndGenerate(TestNesting(TestNesting.Nested("test")))
-        )
-    }
-}
\ No newline at end of file
diff --git a/mirai-core/src/jvmTest/kotlin/bootstrap/RunRecorder.kt b/mirai-core/src/jvmTest/kotlin/bootstrap/RunRecorder.kt
index abd154fca..ebffce213 100644
--- a/mirai-core/src/jvmTest/kotlin/bootstrap/RunRecorder.kt
+++ b/mirai-core/src/jvmTest/kotlin/bootstrap/RunRecorder.kt
@@ -14,8 +14,8 @@ import net.mamoe.mirai.Bot
 import net.mamoe.mirai.BotFactory
 import net.mamoe.mirai.internal.asQQAndroidBot
 import net.mamoe.mirai.internal.network.components.NoticeProcessorPipeline
-import net.mamoe.mirai.internal.notice.Desensitizer
-import net.mamoe.mirai.internal.notice.RecordingNoticeProcessor
+import net.mamoe.mirai.internal.testFramework.desensitizer.Desensitizer
+import net.mamoe.mirai.internal.testFramework.notice.RecordingNoticeProcessor
 import net.mamoe.mirai.utils.BotConfiguration
 import net.mamoe.mirai.utils.readResource
 import net.mamoe.yamlkt.Yaml
diff --git a/mirai-core/src/jvmTest/kotlin/utils/StructureToStringLegacy.kt b/mirai-core/src/jvmTest/kotlin/utils/StructureToStringTransformerLegacy.kt
similarity index 97%
rename from mirai-core/src/jvmTest/kotlin/utils/StructureToStringLegacy.kt
rename to mirai-core/src/jvmTest/kotlin/utils/StructureToStringTransformerLegacy.kt
index 175e76545..e5c289e6a 100644
--- a/mirai-core/src/jvmTest/kotlin/utils/StructureToStringLegacy.kt
+++ b/mirai-core/src/jvmTest/kotlin/utils/StructureToStringTransformerLegacy.kt
@@ -10,9 +10,6 @@
 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
@@ -21,8 +18,7 @@ import kotlin.reflect.KProperty1
 import kotlin.reflect.full.hasAnnotation
 import kotlin.reflect.jvm.javaField
 
-// Kept for souvenir. Not used.
-internal class StructureToStringLegacy : StructureToStringTransformer {
+internal class StructureToStringTransformerLegacy : StructureToStringTransformer {
     override fun transform(any: Any?): String = any._miraiContentToString()
 
     private val indent: String = " ".repeat(4)
diff --git a/mirai-core/src/jvmTest/kotlin/utils/StructureToStringTransformerNew.kt b/mirai-core/src/jvmTest/kotlin/utils/StructureToStringTransformerNew.kt
new file mode 100644
index 000000000..744b86ad4
--- /dev/null
+++ b/mirai-core/src/jvmTest/kotlin/utils/StructureToStringTransformerNew.kt
@@ -0,0 +1,29 @@
+/*
+ * 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 net.mamoe.mirai.internal.testFramework.codegen.ValueDescAnalyzer
+import net.mamoe.mirai.internal.testFramework.codegen.analyze
+import net.mamoe.mirai.internal.testFramework.codegen.descriptors.transform
+import net.mamoe.mirai.internal.testFramework.codegen.removeDefaultValues
+import net.mamoe.mirai.internal.testFramework.codegen.visitors.OptimizeByteArrayAsHexStringTransformer
+import net.mamoe.mirai.internal.testFramework.codegen.visitors.renderToString
+
+internal class StructureToStringTransformerNew : StructureToStringTransformer {
+    private val legacy = StructureToStringTransformerLegacy()
+
+    override fun transform(any: Any?): String =
+        kotlin.runCatching {
+            ValueDescAnalyzer.analyze(any)
+                .transform(OptimizeByteArrayAsHexStringTransformer())
+                ?.removeDefaultValues()
+                ?.renderToString()
+        }.getOrNull() ?: legacy.transform(any)
+}
\ No newline at end of file
diff --git a/mirai-core/src/jvmTest/kotlin/utils/test/StructureToStringTransformerNewTest.kt b/mirai-core/src/jvmTest/kotlin/utils/test/StructureToStringTransformerNewTest.kt
new file mode 100644
index 000000000..1b0f37dcf
--- /dev/null
+++ b/mirai-core/src/jvmTest/kotlin/utils/test/StructureToStringTransformerNewTest.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.test
+
+import net.mamoe.mirai.internal.test.AbstractTest
+import net.mamoe.mirai.internal.utils.structureToString
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+internal class StructureToStringTransformerNewTest : AbstractTest() {
+
+    data class MyClass(
+        val value: String
+    )
+
+    @Test
+    fun `can load service`() {
+        assertEquals(
+            """
+            ${MyClass::class.qualifiedName}(
+                value = "1",
+            )
+        """.trimIndent(),
+            MyClass("1").structureToString()
+        )
+    }
+}
\ No newline at end of file
diff --git a/mirai-core/src/jvmTest/resources/META-INF/services/net.mamoe.mirai.internal.utils.StructureToStringTransformer b/mirai-core/src/jvmTest/resources/META-INF/services/net.mamoe.mirai.internal.utils.StructureToStringTransformer
new file mode 100644
index 000000000..1b0ca2479
--- /dev/null
+++ b/mirai-core/src/jvmTest/resources/META-INF/services/net.mamoe.mirai.internal.utils.StructureToStringTransformer
@@ -0,0 +1,10 @@
+#
+# 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
+#
+
+net.mamoe.mirai.internal.utils.StructureToStringTransformerNew
\ No newline at end of file