mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-05 23:50:08 +08:00
Rewrite and generalize ConstructorCallCodegen for multipurpose usage.
This commit is contained in:
parent
bf98ab7858
commit
3a2663104b
@ -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?
|
||||
|
@ -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()}" }
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
||||
}
|
@ -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) {
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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()
|
@ -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"))
|
||||
}
|
||||
}
|
@ -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))
|
||||
)
|
||||
}
|
||||
}
|
@ -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())
|
||||
)
|
||||
}
|
||||
}
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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?) {
|
||||
}
|
||||
}
|
@ -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 })
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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))
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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" }
|
||||
}
|
||||
}
|
||||
}
|
10
mirai-core/src/commonTest/kotlin/testFramework/package.kt
Normal file
10
mirai-core/src/commonTest/kotlin/testFramework/package.kt
Normal file
@ -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
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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")))
|
||||
)
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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)
|
@ -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)
|
||||
}
|
@ -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()
|
||||
)
|
||||
}
|
||||
}
|
@ -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
|
Loading…
Reference in New Issue
Block a user