From eab4803b0e889c89d3eb5979bb9ecf63b8ab9eb8 Mon Sep 17 00:00:00 2001 From: RimuruChan Date: Mon, 3 Jul 2023 00:21:15 +0800 Subject: [PATCH 1/9] =?UTF-8?q?refactor:=20=E4=BF=AE=E6=94=B9=E5=8C=85?= =?UTF-8?q?=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/com/liteldev/headeroutput/HeaderOutput.kt | 4 ++-- .../headeroutput/config/{ => origindata}/MemberTypeData.kt | 2 +- .../headeroutput/config/{ => origindata}/TypeData.kt | 2 +- .../config/{ => origindata}/VariableTypeData.kt | 6 ++---- .../kotlin/com/liteldev/headeroutput/entity/BaseType.kt | 4 ++-- .../kotlin/com/liteldev/headeroutput/entity/ClassType.kt | 4 ++-- .../com/liteldev/headeroutput/entity/NamespaceType.kt | 2 +- .../kotlin/com/liteldev/headeroutput/entity/StructType.kt | 4 ++-- src/main/kotlin/com/liteldev/headeroutput/utils.kt | 2 +- 9 files changed, 14 insertions(+), 16 deletions(-) rename src/main/kotlin/com/liteldev/headeroutput/config/{ => origindata}/MemberTypeData.kt (98%) rename src/main/kotlin/com/liteldev/headeroutput/config/{ => origindata}/TypeData.kt (94%) rename src/main/kotlin/com/liteldev/headeroutput/config/{ => origindata}/VariableTypeData.kt (86%) diff --git a/src/main/kotlin/com/liteldev/headeroutput/HeaderOutput.kt b/src/main/kotlin/com/liteldev/headeroutput/HeaderOutput.kt index 090d94d..7684c00 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/HeaderOutput.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/HeaderOutput.kt @@ -1,8 +1,8 @@ package com.liteldev.headeroutput import com.liteldev.headeroutput.config.GeneratorConfig -import com.liteldev.headeroutput.config.MemberTypeData -import com.liteldev.headeroutput.config.TypeData +import com.liteldev.headeroutput.config.origindata.MemberTypeData +import com.liteldev.headeroutput.config.origindata.TypeData import com.liteldev.headeroutput.entity.* import com.liteldev.headeroutput.generate.ClassGenerator import com.liteldev.headeroutput.generate.NamespaceGenerator diff --git a/src/main/kotlin/com/liteldev/headeroutput/config/MemberTypeData.kt b/src/main/kotlin/com/liteldev/headeroutput/config/origindata/MemberTypeData.kt similarity index 98% rename from src/main/kotlin/com/liteldev/headeroutput/config/MemberTypeData.kt rename to src/main/kotlin/com/liteldev/headeroutput/config/origindata/MemberTypeData.kt index d571830..6e71378 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/config/MemberTypeData.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/config/origindata/MemberTypeData.kt @@ -1,4 +1,4 @@ -package com.liteldev.headeroutput.config +package com.liteldev.headeroutput.config.origindata import com.liteldev.headeroutput.appendSpace import com.liteldev.headeroutput.entity.AccessType diff --git a/src/main/kotlin/com/liteldev/headeroutput/config/TypeData.kt b/src/main/kotlin/com/liteldev/headeroutput/config/origindata/TypeData.kt similarity index 94% rename from src/main/kotlin/com/liteldev/headeroutput/config/TypeData.kt rename to src/main/kotlin/com/liteldev/headeroutput/config/origindata/TypeData.kt index 0f03bd2..ad27b8b 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/config/TypeData.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/config/origindata/TypeData.kt @@ -1,4 +1,4 @@ -package com.liteldev.headeroutput.config +package com.liteldev.headeroutput.config.origindata import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/src/main/kotlin/com/liteldev/headeroutput/config/VariableTypeData.kt b/src/main/kotlin/com/liteldev/headeroutput/config/origindata/VariableTypeData.kt similarity index 86% rename from src/main/kotlin/com/liteldev/headeroutput/config/VariableTypeData.kt rename to src/main/kotlin/com/liteldev/headeroutput/config/origindata/VariableTypeData.kt index ccf4b16..0374fc6 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/config/VariableTypeData.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/config/origindata/VariableTypeData.kt @@ -1,4 +1,4 @@ -package com.liteldev.headeroutput.config +package com.liteldev.headeroutput.config.origindata import com.liteldev.headeroutput.entity.VarSymbolType import kotlinx.serialization.SerialName @@ -16,9 +16,7 @@ data class VariableTypeData( other as VariableTypeData if (Name != other.Name) return false - if (Type != other.Type) return false - - return true + return Type == other.Type } override fun hashCode(): Int { diff --git a/src/main/kotlin/com/liteldev/headeroutput/entity/BaseType.kt b/src/main/kotlin/com/liteldev/headeroutput/entity/BaseType.kt index 23e733d..f700976 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/entity/BaseType.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/entity/BaseType.kt @@ -1,8 +1,8 @@ package com.liteldev.headeroutput.entity import com.liteldev.headeroutput.HeaderOutput -import com.liteldev.headeroutput.config.MemberTypeData -import com.liteldev.headeroutput.config.TypeData +import com.liteldev.headeroutput.config.origindata.MemberTypeData +import com.liteldev.headeroutput.config.origindata.TypeData import com.liteldev.headeroutput.parent import com.liteldev.headeroutput.relativePath import com.liteldev.headeroutput.substring diff --git a/src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt b/src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt index cec6731..7c18ca3 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt @@ -1,8 +1,8 @@ package com.liteldev.headeroutput.entity import com.liteldev.headeroutput.HeaderOutput -import com.liteldev.headeroutput.config.MemberTypeData -import com.liteldev.headeroutput.config.TypeData +import com.liteldev.headeroutput.config.origindata.MemberTypeData +import com.liteldev.headeroutput.config.origindata.TypeData open class ClassType( name: String, typeData: TypeData, diff --git a/src/main/kotlin/com/liteldev/headeroutput/entity/NamespaceType.kt b/src/main/kotlin/com/liteldev/headeroutput/entity/NamespaceType.kt index 51613ac..6343aed 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/entity/NamespaceType.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/entity/NamespaceType.kt @@ -1,6 +1,6 @@ package com.liteldev.headeroutput.entity -import com.liteldev.headeroutput.config.TypeData +import com.liteldev.headeroutput.config.origindata.TypeData class NamespaceType( name: String, typeData: TypeData diff --git a/src/main/kotlin/com/liteldev/headeroutput/entity/StructType.kt b/src/main/kotlin/com/liteldev/headeroutput/entity/StructType.kt index 0913948..8eb68d8 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/entity/StructType.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/entity/StructType.kt @@ -1,7 +1,7 @@ package com.liteldev.headeroutput.entity -import com.liteldev.headeroutput.config.MemberTypeData -import com.liteldev.headeroutput.config.TypeData +import com.liteldev.headeroutput.config.origindata.MemberTypeData +import com.liteldev.headeroutput.config.origindata.TypeData class StructType( name: String, typeData: TypeData, diff --git a/src/main/kotlin/com/liteldev/headeroutput/utils.kt b/src/main/kotlin/com/liteldev/headeroutput/utils.kt index 9c91320..82c8d92 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/utils.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/utils.kt @@ -1,6 +1,6 @@ package com.liteldev.headeroutput -import com.liteldev.headeroutput.config.TypeData +import com.liteldev.headeroutput.config.origindata.TypeData import java.nio.file.Paths fun String.relativePath(path: String) = Paths.get(this).relativize(Paths.get(path)).toString().replace("\\", "/") From d2ba8f2340cbceccf1f40d7191a4c1be0820fd3d Mon Sep 17 00:00:00 2001 From: RimuruChan Date: Mon, 3 Jul 2023 15:56:40 +0800 Subject: [PATCH 2/9] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E6=9E=B6?= =?UTF-8?q?=E6=9E=84=EF=BC=8C=E5=8E=BB=E9=99=A4=E6=97=A7=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E8=BD=AC=E7=A7=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/liteldev/headeroutput/HeaderOutput.kt | 189 ++++++------------ .../com/liteldev/headeroutput/TypeManager.kt | 75 +++++++ .../headeroutput/config/GeneratorConfig.kt | 35 ++-- .../config/GeneratorConfigData.kt | 19 ++ .../config/origindata/MemberTypeData.kt | 21 +- .../config/origindata/VariableTypeData.kt | 2 +- .../liteldev/headeroutput/entity/BaseType.kt | 92 ++------- .../liteldev/headeroutput/entity/ClassType.kt | 41 +--- .../liteldev/headeroutput/entity/EnumType.kt | 2 +- .../headeroutput/entity/NamespaceType.kt | 7 +- .../headeroutput/entity/StructType.kt | 6 +- .../headeroutput/generate/ClassGenerator.kt | 52 +++-- .../headeroutput/generate/Generator.kt | 7 + .../generate/NamespaceGenerator.kt | 38 ++-- .../headeroutput/generate/StructGenerator.kt | 46 ++--- .../kotlin/com/liteldev/headeroutput/utils.kt | 18 -- 16 files changed, 287 insertions(+), 363 deletions(-) create mode 100644 src/main/kotlin/com/liteldev/headeroutput/TypeManager.kt create mode 100644 src/main/kotlin/com/liteldev/headeroutput/config/GeneratorConfigData.kt create mode 100644 src/main/kotlin/com/liteldev/headeroutput/generate/Generator.kt diff --git a/src/main/kotlin/com/liteldev/headeroutput/HeaderOutput.kt b/src/main/kotlin/com/liteldev/headeroutput/HeaderOutput.kt index 7684c00..8eef28e 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/HeaderOutput.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/HeaderOutput.kt @@ -11,197 +11,128 @@ import kotlinx.cli.ArgParser import kotlinx.cli.ArgType import kotlinx.cli.default import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.* import java.io.File - @OptIn(ExperimentalSerializationApi::class) private val json = Json { explicitNulls = false } -object HeaderOutput { - - private lateinit var JSON_PATH: String - lateinit var OLD_PATH: String - lateinit var GENERATE_PATH: String - private lateinit var CONFIG_PATH: String +fun main(args: Array) { + HeaderOutput.run(args) +} +object HeaderOutput { private lateinit var originData: JsonObject - private lateinit var funcListOfTypes: Map - lateinit var generatorConfig: GeneratorConfig - lateinit var realClassNameList: List - lateinit var realStructNameList: List - - val classMap = mutableMapOf() - val structMap = mutableMapOf() - val namespaceMap = mutableMapOf() + val notExistBaseType = mutableSetOf() - @JvmStatic - fun main(args: Array) { + fun run(args: Array) { if (!readCommandLineArgs(args)) return - loadConfig() + GeneratorConfig.loadConfig() loadOriginData() - loadIdentifier() - loadFuncListOfTypes() - - funcListOfTypes.forEach { (typeName, type) -> - when { - isNameSpace(typeName, type) -> { - namespaceMap[typeName] = NamespaceType(typeName, type).also { - runCatching { - it.readOldExtra() - it.readComments("namespace") - }.onFailure { - println("Warning: $typeName not found in old") - } - } - } - - realStructNameList.contains(typeName) -> { - structMap[typeName] = StructType(typeName, type).also { - runCatching { - it.readOldExtra() - it.readComments("struct") - }.onFailure { - println("Warning: $typeName not found in old") - } - } - } - - else/*realClassNameList.contains(typeName)*/ -> { - classMap[typeName] = ClassType(typeName, type).also { - runCatching { - it.readOldExtra() - it.readComments("class") - }.onFailure { - println("Warning: $typeName not found in old") - } - } - } - } - } - - //link every class - val rootClasses = mutableMapOf() - classMap.values.forEach { classType -> - classType.initIncludeList() - classType.constructLinkedClassMap(rootClasses) - } - - structMap.values.forEach { structType -> - structType.initIncludeList() - } + loadIdentifiedTypes() + loadTypes() - namespaceMap.values.forEach { namespaceType -> - namespaceType.initIncludeList() - } + TypeManager.initInclusionList() println("Warning: these class has no information in originData but used by other classes\n$notExistBaseType") - //println(namespaceMap.keys) - File(GENERATE_PATH).mkdirs() + File(GeneratorConfig.generatePath).mkdirs() - ClassGenerator.generate() - StructGenerator.generate() - NamespaceGenerator.generate() - - File(OLD_PATH).listFiles()?.filter { it.isFile }?.forEach { - val origin = it.readText() - if (!origin.contains("#define AUTO_GENERATED")) { - val dest = File(GENERATE_PATH, it.name) - if (dest.isFile) - println("Warning: ${dest.name} is already exist") - it.copyTo(dest, true) + TypeManager.getAllTypes().forEach { type -> + when { + type.isNamespace() -> NamespaceGenerator.generate(type) + type.isStruct() -> StructGenerator.generate(type) + type.isClass() -> ClassGenerator.generate(type) } } - - val oldFileNames = (File(OLD_PATH).listFiles()?.map { it.name } ?: arrayListOf()).toSet() - val newFileNames = (File(GENERATE_PATH).listFiles()?.map { it.name } ?: arrayListOf()).toSet() - - println("Deleted:\t" + oldFileNames.subtract(newFileNames)) - println("Modified:\t" + oldFileNames.intersect(newFileNames)) - println("Addition:\t" + newFileNames.subtract(oldFileNames)) } private fun readCommandLineArgs(args: Array): Boolean { val parser = ArgParser("HeaderOutput") val configPath by parser.option(ArgType.String, "config", "c", "The config file path").default("./config.json") - val oldPath by parser.option(ArgType.String, "old", "o", "The old header path").default("./old") val generatePath by parser.option(ArgType.String, "generate", "g", "The generate header files path") .default("./header") val jsonPath by parser.option(ArgType.String, "json", "j", "The original data json file path") .default("./header.json") parser.parse(args) - CONFIG_PATH = configPath - OLD_PATH = oldPath - GENERATE_PATH = generatePath - JSON_PATH = jsonPath - if (!File(CONFIG_PATH).isFile) { + GeneratorConfig.configPath = configPath + GeneratorConfig.generatePath = generatePath + GeneratorConfig.jsonPath = jsonPath + if (!File(GeneratorConfig.configPath).isFile) { println("Invalid config file path") return false } - if (!File(OLD_PATH).isDirectory) { - println("Invalid old header files path") - return false - } - if (!File(GENERATE_PATH).isDirectory) { + if (!File(GeneratorConfig.generatePath).isDirectory) { try { - File(GENERATE_PATH).mkdirs() + File(GeneratorConfig.generatePath).mkdirs() } catch (e: Exception) { println("Fail to create generate header files path") return false } } - if (!File(JSON_PATH).isFile) { + if (!File(GeneratorConfig.jsonPath).isFile) { println("Invalid original data json file path") return false } return true } - private fun loadConfig() { - println("Loading config...") - val configText = File(this.CONFIG_PATH).readText() - generatorConfig = json.decodeFromString(configText) - } private fun loadOriginData() { println("Loading origin data...") - val configText = File(JSON_PATH).readText() + val configText = File(GeneratorConfig.jsonPath).readText() originData = Json.parseToJsonElement(configText).jsonObject } - private fun loadFuncListOfTypes() { - println("Loading func list of types...") - funcListOfTypes = originData["classes"]?.jsonObject?.filter { (k, _) -> - generatorConfig.exclusion.generation.regex.find { k.matches(Regex(it)) } == null + private fun loadTypes() { + println("Loading types...") + originData["classes"]?.jsonObject?.filter { (k, _) -> + GeneratorConfig.generationExcludeRegexList.find { k.matches(Regex(it)) } == null }?.mapValues { entry -> json.decodeFromJsonElement(entry.value).also { type -> var counter = 0 type.virtual?.forEach { memberType -> - run { - if (memberType.name == "" && !memberType.isUnknownFunction()) - memberType.symbolType = SymbolNodeType.Unknown - if (memberType.isUnknownFunction()) { - memberType.storageClass = StorageClassType.Virtual - memberType.addFlag(MemberTypeData.PTR_CALL) - memberType.name = "void __unk_vfn_${counter}" - } - counter++ + // 对于没有名字的虚函数,将其标记为未知函数,并且将其名字设置为 __unk_vfn_0, __unk_vfn_1, ... + if (memberType.name == "" && !memberType.isUnknownFunction()) + memberType.symbolType = SymbolNodeType.Unknown + if (memberType.isUnknownFunction()) { + memberType.storageClass = StorageClassType.Virtual + memberType.addFlag(MemberTypeData.FLAG_PTR_CALL) + memberType.name = "void __unk_vfn_${counter}" } + counter++ } } - } ?: mapOf() + }?.forEach { (typeName, type) -> + TypeManager.addType( + typeName, + when { + isNameSpace(typeName, type) -> + NamespaceType(typeName, type) + + isStruct(typeName) -> + StructType(typeName, type) + + isClass(typeName) -> + ClassType(typeName, type) + + else -> { + println("Warning: $typeName is not a namespace, struct or class") + ClassType(typeName, type) + } + } + ) + } } - private fun loadIdentifier() { + private fun loadIdentifiedTypes() { println("Loading identifier...") val identifier = originData["identifier"]?.jsonObject - realClassNameList = + TypeManager.classNameList = (identifier?.get("class")?.jsonArray).orEmpty().map { it.jsonPrimitive.content } - realStructNameList = + TypeManager.structNameList = (identifier?.get("struct")?.jsonArray).orEmpty().map { it.jsonPrimitive.content } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/liteldev/headeroutput/TypeManager.kt b/src/main/kotlin/com/liteldev/headeroutput/TypeManager.kt new file mode 100644 index 0000000..a454b2c --- /dev/null +++ b/src/main/kotlin/com/liteldev/headeroutput/TypeManager.kt @@ -0,0 +1,75 @@ +package com.liteldev.headeroutput + +import com.liteldev.headeroutput.config.origindata.TypeData +import com.liteldev.headeroutput.entity.BaseType +import com.liteldev.headeroutput.entity.ClassType +import com.liteldev.headeroutput.entity.NamespaceType +import com.liteldev.headeroutput.entity.StructType + +object TypeManager { + private val typeMap = hashMapOf() + private val nestingMap = hashMapOf() + + lateinit var classNameList: List + lateinit var structNameList: List + + fun addType(fullName: String, type: BaseType) { + typeMap[fullName] = type + } + + fun getType(fullName: String): BaseType? { + return typeMap[fullName] + } + + fun hasType(fullName: String): Boolean { + return typeMap.containsKey(fullName) + } + + fun getAllTypes(): List { + return typeMap.values.toList() + } + + fun initInclusionList() { + typeMap.forEach { (_, type) -> + type.initIncludeList() + } + } + + fun generateNestingMap() { + typeMap.forEach { (fullName, type) -> + val parent = type.typeData.parentTypes?.firstOrNull() + if (parent != null) { + nestingMap[fullName] = typeMap[parent]!! + } + } + } + +} + +fun BaseType.isClass(): Boolean = this is ClassType && !this.isStruct() + +fun BaseType.isStruct() = this is StructType +fun BaseType.isNamespace() = this is NamespaceType + +fun isNameSpace(typeName: String, typeData: TypeData): Boolean { + if (typeData.privateTypes != null + || typeData.privateStaticTypes != null + || typeData.protectedTypes != null + || typeData.protectedStaticTypes != null + || typeData.publicStaticTypes != null + || typeData.virtual != null + || typeData.vtblEntry != null + ) { + return false + } + val isClassOrStruct = isStruct(typeName) || isClass(typeName) + return (typeData.publicTypes?.find { it.isPtrCall() } == null && !isClassOrStruct) +} + +fun isStruct(typeName: String): Boolean { + return TypeManager.structNameList.contains(typeName) +} + +fun isClass(typeName: String): Boolean { + return TypeManager.classNameList.contains(typeName) +} diff --git a/src/main/kotlin/com/liteldev/headeroutput/config/GeneratorConfig.kt b/src/main/kotlin/com/liteldev/headeroutput/config/GeneratorConfig.kt index 9eee731..cb42d04 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/config/GeneratorConfig.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/config/GeneratorConfig.kt @@ -1,19 +1,28 @@ package com.liteldev.headeroutput.config -import kotlinx.serialization.Serializable +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json +import java.io.File -@Serializable -data class GeneratorConfig( - var exclusion: Exclusion -) { - @Serializable - data class Exclusion( - var generation: Generation, var inclusion: Inclusion - ) { - @Serializable - data class Generation(var regex: MutableList) +object GeneratorConfig { + @OptIn(ExperimentalSerializationApi::class) + private val json = Json { explicitNulls = false } - @Serializable - data class Inclusion(var regex: MutableList) + lateinit var jsonPath: String + lateinit var generatePath: String + lateinit var configPath: String + lateinit var generationExcludeRegexList: MutableList + lateinit var inclusionExcludeRegexList: MutableList + + private lateinit var generatorConfigData: GeneratorConfigData + + + fun loadConfig() { + println("Loading config...") + val configText = File(configPath).readText() + generatorConfigData = json.decodeFromString(configText) + generationExcludeRegexList = generatorConfigData.exclusion.generation.regex + inclusionExcludeRegexList = generatorConfigData.exclusion.inclusion.regex } } diff --git a/src/main/kotlin/com/liteldev/headeroutput/config/GeneratorConfigData.kt b/src/main/kotlin/com/liteldev/headeroutput/config/GeneratorConfigData.kt new file mode 100644 index 0000000..4bef3c3 --- /dev/null +++ b/src/main/kotlin/com/liteldev/headeroutput/config/GeneratorConfigData.kt @@ -0,0 +1,19 @@ +package com.liteldev.headeroutput.config + +import kotlinx.serialization.Serializable + +@Serializable +data class GeneratorConfigData( + var exclusion: Exclusion +) { + @Serializable + data class Exclusion( + var generation: Generation, var inclusion: Inclusion + ) { + @Serializable + data class Generation(var regex: MutableList) + + @Serializable + data class Inclusion(var regex: MutableList) + } +} diff --git a/src/main/kotlin/com/liteldev/headeroutput/config/origindata/MemberTypeData.kt b/src/main/kotlin/com/liteldev/headeroutput/config/origindata/MemberTypeData.kt index 6e71378..cde2087 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/config/origindata/MemberTypeData.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/config/origindata/MemberTypeData.kt @@ -26,7 +26,6 @@ data class MemberTypeData( fun genFuncString( namespace: Boolean = false, useFakeSymbol: Boolean = false, - comment: String = "", vIndex: Int = -1 ): String { val symbol = @@ -42,7 +41,6 @@ data class MemberTypeData( if (symbol.isNotEmpty()) { ret.appendSpace(START_BLANK_SPACE + 1).append("* @symbol ${symbol.replace("@", "\\@")}\n") } - if (comment.isNotEmpty()) ret.appendSpace(START_BLANK_SPACE + 1).append("*\n").append(comment) ret.appendSpace(START_BLANK_SPACE + 1).append("*/\n") ret.appendSpace(START_BLANK_SPACE) @@ -90,8 +88,6 @@ data class MemberTypeData( ) } - fun isConst() = flags and CONST == CONST - fun isConstructor() = symbolType == SymbolNodeType.Constructor fun isDestructor() = symbolType == SymbolNodeType.Destructor @@ -102,9 +98,10 @@ data class MemberTypeData( fun isStaticGlobalVariable() = symbolType == SymbolNodeType.StaticVar - fun isPtrCall() = flags and PTR_CALL == PTR_CALL + fun isConst() = hasFlag(FLAG_CONST) + fun isPtrCall() = hasFlag(FLAG_PTR_CALL) - fun isPureCall() = flags and PURE_CALL == PURE_CALL + fun isPureCall() = hasFlag(FLAG_PURE_CALL) fun addFlag(flag: Int) { if (flags and flag != flag) flags += flag @@ -114,18 +111,18 @@ data class MemberTypeData( if (flags and flag == flag) flags -= flag } + fun hasFlag(flag: Int) = flags and flag == flag + fun isVirtual() = storageClass == StorageClassType.Virtual companion object { //[0] const //[1] __ptr64 spec //[2] isPureCall - const val CONST = 1 shl 0 - const val PTR_CALL = 1 shl 1 - const val PURE_CALL = 1 shl 2 + const val FLAG_CONST = 1 shl 0 + const val FLAG_PTR_CALL = 1 shl 1 + const val FLAG_PURE_CALL = 1 shl 2 const val START_BLANK_SPACE = 4 } - - -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/liteldev/headeroutput/config/origindata/VariableTypeData.kt b/src/main/kotlin/com/liteldev/headeroutput/config/origindata/VariableTypeData.kt index 0374fc6..f3561ef 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/config/origindata/VariableTypeData.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/config/origindata/VariableTypeData.kt @@ -24,4 +24,4 @@ data class VariableTypeData( result = 31 * result + Type.value return result } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/liteldev/headeroutput/entity/BaseType.kt b/src/main/kotlin/com/liteldev/headeroutput/entity/BaseType.kt index f700976..85a4e06 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/entity/BaseType.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/entity/BaseType.kt @@ -1,75 +1,20 @@ package com.liteldev.headeroutput.entity import com.liteldev.headeroutput.HeaderOutput +import com.liteldev.headeroutput.TypeManager +import com.liteldev.headeroutput.config.GeneratorConfig import com.liteldev.headeroutput.config.origindata.MemberTypeData import com.liteldev.headeroutput.config.origindata.TypeData import com.liteldev.headeroutput.parent import com.liteldev.headeroutput.relativePath -import com.liteldev.headeroutput.substring -import java.io.File abstract class BaseType( var name: String, var typeData: TypeData, - val includeList: MutableSet = mutableSetOf(), - var beforeExtra: String = "", - var afterExtra: String = "", - var comment: String = "", - private var memberComments: MutableMap = mutableMapOf(), ) { + private val includeList: MutableSet = mutableSetOf() - abstract fun getPath(): String - - fun readOldExtra() { - val origin = File(HeaderOutput.OLD_PATH, getPath()).readText().replace("\r\n", "\n") - beforeExtra = origin.substring( - "#define BEFORE_EXTRA\n", - "\n#undef BEFORE_EXTRA" - ) - afterExtra = origin.substring( - "#define AFTER_EXTRA\n", - "\n#undef AFTER_EXTRA" - ) - } - - fun readComments(flag: String) { - val regex = Regex("/\\*\\*\n([\\S\\s]+)\\*/\n$flag", RegexOption.MULTILINE) - var origin = File(HeaderOutput.OLD_PATH, getPath()).readText().replace("\r\n", "\n") - origin = origin.substring("#undef BEFORE_EXTRA\n", "\n#define AFTER_EXTRA") + - origin.substringAfter("#undef AFTER_EXTRA\n") - comment = regex.find(origin)?.groupValues?.get(0)?.substring("", "\n$flag") ?: "" - val classBody = origin.substring("$flag $name ", "\n};") - var inComment = false - val comment = StringBuilder() - var symbol: String? = null - classBody.lines().forEach { - when { - it.contains("/*") -> inComment = true - it.contains("*/") -> { - inComment = false - symbol?.let { s -> - memberComments[s] = comment.toString() - symbol = null - } - comment.clear() - } - - inComment -> { - when { - it.contains("@symbol") -> symbol = it.substringAfter("@symbol ", "").trim().replace("\\@", "@") - it.contains("@vftbl") -> {} - it.contains("@hash") -> {} - else -> comment.append(it).append("\n") - } - } - } - } - } - - fun getCommentOf(member: MemberTypeData): String { - val symbol = member.symbol - return this.memberComments[symbol] ?: "" - } + fun getPath(): String = "./$name.hpp" private fun readIncludeClassFromMembers(list: List): Set { val retList = mutableSetOf() @@ -86,33 +31,26 @@ abstract class BaseType( } } return retList.filter { inclusion -> - HeaderOutput.generatorConfig.exclusion.inclusion.regex.find { + GeneratorConfig.inclusionExcludeRegexList.find { inclusion.matches( Regex(it) ) } == null - } - .toSet() + }.toSet() } private fun readList(list: List) = readIncludeClassFromMembers(list).filter { - val ret = - HeaderOutput.classMap.contains(it) || HeaderOutput.structMap.contains(it) || HeaderOutput.namespaceMap.contains( - it - ) - if (!ret) { - HeaderOutput.notExistBaseType.add(it) + TypeManager.hasType(it).apply { + if (!this) { + HeaderOutput.notExistBaseType.add(it) + } } - ret - }.map { HeaderOutput.classMap[it] ?: HeaderOutput.structMap[it] ?: HeaderOutput.namespaceMap[it]!! } - .let(includeList::addAll) - - fun getGlobalHeaderPath() = "llapi/Global.h" + }.mapNotNull { TypeManager.getType(it) }.let(includeList::addAll) fun getRelativeInclusions(): String { val include = StringBuilder() includeList.forEach { - include.appendLine("#include \"${(getPath().parent()).relativePath(it.getPath())}\"") + include.appendLine("""#include "${(getPath().parent()).relativePath(it.getPath())}"""") } return include.toString() } @@ -127,4 +65,8 @@ abstract class BaseType( typeData.privateStaticTypes?.let(::readList) includeList.removeIf { it === this } } -} \ No newline at end of file + + companion object { + const val GLOBAL_HEADER_PATH = "llapi/Global.h" + } +} diff --git a/src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt b/src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt index 7c18ca3..65341de 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt @@ -1,13 +1,11 @@ package com.liteldev.headeroutput.entity -import com.liteldev.headeroutput.HeaderOutput import com.liteldev.headeroutput.config.origindata.MemberTypeData import com.liteldev.headeroutput.config.origindata.TypeData open class ClassType( name: String, typeData: TypeData, var parent: ClassType? = null, - private val children: MutableMap = mutableMapOf(), ) : BaseType(name, typeData) { // TODO: Fix in header generator @@ -19,15 +17,6 @@ open class ClassType( } } - override fun getPath(): String { - return "./$name.hpp" - /*if (parent == null) { - "./$name" - } else { - parent!!.getPath() + "/" + name - }*/ - } - override fun hashCode(): Int { return name.hashCode() } @@ -35,22 +24,9 @@ open class ClassType( override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false - other as ClassType - if (name != other.name) return false - if (typeData != other.typeData) return false - - return true - } - - fun constructLinkedClassMap(rootClasses: MutableMap) { - typeData.parentTypes?.also { parentNames -> - parent = HeaderOutput.classMap[parentNames[0]]?.also { - it.children[name] = this - } - } ?: run { rootClasses[name] = this } - parent?.let(includeList::add) + return typeData == other.typeData } open fun genAntiReconstruction(): String { @@ -98,7 +74,7 @@ open class ClassType( var counter = 0 typeData.virtual?.forEach { if (it.namespace.isEmpty() || it.namespace == name) - sb.appendLine(it.genFuncString(comment = this.getCommentOf(it), vIndex = counter)) + sb.appendLine(it.genFuncString(vIndex = counter)) counter++ } @@ -107,7 +83,6 @@ open class ClassType( typeData.virtualUnordered?.sortedBy { it.name }?.forEach { sb.appendLine( it.genFuncString( - comment = getCommentOf(it), useFakeSymbol = true ) ) @@ -116,10 +91,10 @@ open class ClassType( } typeData.publicTypes?.sortedBy { it.name }?.forEach { - sb.appendLine(it.genFuncString(comment = this.getCommentOf(it))) + sb.appendLine(it.genFuncString()) } typeData.publicStaticTypes?.sortedBy { it.name }?.forEach { - sb.appendLine(it.genFuncString(comment = this.getCommentOf(it))) + sb.appendLine(it.genFuncString()) } if (sb.equals("public:\n")) return "" @@ -141,11 +116,11 @@ open class ClassType( sb.appendLine("protected:") typeData.protectedTypes?.sortedBy { it.name }?.forEach { if ((genFunc && !it.isStaticGlobalVariable()) || (!genFunc && it.isStaticGlobalVariable())) - sb.appendLine(it.genFuncString(comment = this.getCommentOf(it))) + sb.appendLine(it.genFuncString()) } typeData.protectedStaticTypes?.sortedBy { it.name }?.forEach { if ((genFunc && !it.isStaticGlobalVariable()) || (!genFunc && it.isStaticGlobalVariable())) - sb.appendLine(it.genFuncString(comment = this.getCommentOf(it))) + sb.appendLine(it.genFuncString()) } if (sb.equals("protected:\n") || sb.equals("//protected:\n")) return "" @@ -167,11 +142,11 @@ open class ClassType( sb.appendLine("private:") typeData.privateTypes?.sortedBy { it.name }?.forEach { if ((genFunc && !it.isStaticGlobalVariable()) || (!genFunc && it.isStaticGlobalVariable())) - sb.appendLine(it.genFuncString(comment = this.getCommentOf(it))) + sb.appendLine(it.genFuncString()) } typeData.privateStaticTypes?.sortedBy { it.name }?.forEach { if ((genFunc && !it.isStaticGlobalVariable()) || (!genFunc && it.isStaticGlobalVariable())) - sb.appendLine(it.genFuncString(comment = this.getCommentOf(it))) + sb.appendLine(it.genFuncString()) } if (sb.equals("private:\n") || sb.equals("//private:\n")) return "" diff --git a/src/main/kotlin/com/liteldev/headeroutput/entity/EnumType.kt b/src/main/kotlin/com/liteldev/headeroutput/entity/EnumType.kt index 3192003..9cc2891 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/entity/EnumType.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/entity/EnumType.kt @@ -107,4 +107,4 @@ open class IntEnumSerializer(serialName: String, private val clazz: KClass return clazz.java.enumConstants.firstOrNull { it.value == decodeInt } ?: throw IllegalArgumentException("No enum constant found for value $decodeInt") } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/liteldev/headeroutput/entity/NamespaceType.kt b/src/main/kotlin/com/liteldev/headeroutput/entity/NamespaceType.kt index 6343aed..4f70c24 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/entity/NamespaceType.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/entity/NamespaceType.kt @@ -5,15 +5,12 @@ import com.liteldev.headeroutput.config.origindata.TypeData class NamespaceType( name: String, typeData: TypeData ) : BaseType(name, typeData) { - override fun getPath(): String { - return "./$name.hpp" - } fun genPublic(): String { val sb = StringBuilder() typeData.publicTypes?.sortedBy { it.name }?.forEach { - sb.appendLine(it.genFuncString(namespace = true, comment = this.getCommentOf(it))) + sb.appendLine(it.genFuncString(namespace = true)) } return sb.toString() } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/liteldev/headeroutput/entity/StructType.kt b/src/main/kotlin/com/liteldev/headeroutput/entity/StructType.kt index 8eb68d8..9b5baaa 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/entity/StructType.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/entity/StructType.kt @@ -6,9 +6,6 @@ import com.liteldev.headeroutput.config.origindata.TypeData class StructType( name: String, typeData: TypeData, ) : ClassType(name, typeData) { - override fun getPath(): String { - return "./$name.hpp" - } override fun genAntiReconstruction(): String { val public = arrayListOf() @@ -44,5 +41,4 @@ class StructType( sb.appendLine() return sb.toString() } - -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/liteldev/headeroutput/generate/ClassGenerator.kt b/src/main/kotlin/com/liteldev/headeroutput/generate/ClassGenerator.kt index 191380b..9af01de 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/generate/ClassGenerator.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/generate/ClassGenerator.kt @@ -1,42 +1,40 @@ package com.liteldev.headeroutput.generate -import com.liteldev.headeroutput.HeaderOutput +import com.liteldev.headeroutput.config.GeneratorConfig +import com.liteldev.headeroutput.entity.BaseType +import com.liteldev.headeroutput.entity.ClassType import java.io.File -object ClassGenerator { +object ClassGenerator : Generator { - fun generate() { - HeaderOutput.classMap.forEach { (name, classType) -> - val hpp = File(HeaderOutput.GENERATE_PATH, classType.getPath()) - hpp.writeText( - """ + override fun generate(type: BaseType) { + if (type !is ClassType) { + println("ClassGenerator: ${type.name} is not ClassType") + return + } + val name = type.name + val hpp = File(GeneratorConfig.generatePath, type.getPath()) + hpp.writeText( + """ /** * @file $name.hpp * */ #pragma once #define AUTO_GENERATED -#include "${classType.getGlobalHeaderPath()}" -${classType.getRelativeInclusions()} -#define BEFORE_EXTRA -${classType.beforeExtra} -#undef BEFORE_EXTRA +#include "${BaseType.GLOBAL_HEADER_PATH}" +${type.getRelativeInclusions()} -${classType.comment} -class $name ${run { if (classType.parent != null) ": public ${classType.parent!!.name} " else "" }}{ +class $name ${run { if (type.parent != null) ": public ${type.parent!!.name} " else "" }}{ -#define AFTER_EXTRA -${classType.afterExtra} -#undef AFTER_EXTRA """.trimIndent() - ) - hpp.appendText(classType.genAntiReconstruction()) - hpp.appendText(classType.genPublic()) - hpp.appendText(classType.genProtected()) - hpp.appendText(classType.genPrivate()) - hpp.appendText(classType.genProtected(genFunc = false)) - hpp.appendText(classType.genPrivate(genFunc = false)) - hpp.appendText("};\n") - } + ) + hpp.appendText(type.genAntiReconstruction()) + hpp.appendText(type.genPublic()) + hpp.appendText(type.genProtected()) + hpp.appendText(type.genPrivate()) + hpp.appendText(type.genProtected(genFunc = false)) + hpp.appendText(type.genPrivate(genFunc = false)) + hpp.appendText("};\n") } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/liteldev/headeroutput/generate/Generator.kt b/src/main/kotlin/com/liteldev/headeroutput/generate/Generator.kt new file mode 100644 index 0000000..e47c427 --- /dev/null +++ b/src/main/kotlin/com/liteldev/headeroutput/generate/Generator.kt @@ -0,0 +1,7 @@ +package com.liteldev.headeroutput.generate + +import com.liteldev.headeroutput.entity.BaseType + +interface Generator { + fun generate(type: BaseType) +} diff --git a/src/main/kotlin/com/liteldev/headeroutput/generate/NamespaceGenerator.kt b/src/main/kotlin/com/liteldev/headeroutput/generate/NamespaceGenerator.kt index 9123fa6..86c87b9 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/generate/NamespaceGenerator.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/generate/NamespaceGenerator.kt @@ -1,37 +1,35 @@ package com.liteldev.headeroutput.generate -import com.liteldev.headeroutput.HeaderOutput +import com.liteldev.headeroutput.config.GeneratorConfig +import com.liteldev.headeroutput.entity.BaseType +import com.liteldev.headeroutput.entity.NamespaceType import java.io.File -object NamespaceGenerator { +object NamespaceGenerator : Generator { - fun generate() { - HeaderOutput.namespaceMap.forEach { (name, namespaceType) -> - val file = File(HeaderOutput.GENERATE_PATH, namespaceType.getPath()) - file.writeText( - """ + override fun generate(type: BaseType) { + if (type !is NamespaceType) { + println("NamespaceGenerator: ${type.name} is not NamespaceType") + return + } + val name = type.name + val file = File(GeneratorConfig.generatePath, type.getPath()) + file.writeText( + """ /** * @file $name.hpp * */ #pragma once #define AUTO_GENERATED -#include "${namespaceType.getGlobalHeaderPath()}" -${namespaceType.getRelativeInclusions()} -#define BEFORE_EXTRA -${namespaceType.beforeExtra} -#undef BEFORE_EXTRA +#include "${BaseType.GLOBAL_HEADER_PATH}" +${type.getRelativeInclusions()} -${namespaceType.comment} namespace $name { -#define AFTER_EXTRA -${namespaceType.afterExtra} -#undef AFTER_EXTRA -${namespaceType.genPublic()} +${type.genPublic()} }; """.trimIndent() - ) - } + ) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/liteldev/headeroutput/generate/StructGenerator.kt b/src/main/kotlin/com/liteldev/headeroutput/generate/StructGenerator.kt index 5ca28d2..84048db 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/generate/StructGenerator.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/generate/StructGenerator.kt @@ -1,40 +1,38 @@ package com.liteldev.headeroutput.generate -import com.liteldev.headeroutput.HeaderOutput +import com.liteldev.headeroutput.config.GeneratorConfig +import com.liteldev.headeroutput.entity.BaseType +import com.liteldev.headeroutput.entity.StructType import java.io.File -object StructGenerator { +object StructGenerator : Generator { - fun generate() { - HeaderOutput.structMap.forEach { (name, structType) -> - val hpp = File(HeaderOutput.GENERATE_PATH, structType.getPath()) - hpp.writeText( - """ + override fun generate(type: BaseType) { + if (type !is StructType) { + println("StructGenerator: ${type.name} is not StructType") + return + } + val name = type.name + val hpp = File(GeneratorConfig.generatePath, type.getPath()) + hpp.writeText( + """ /** * @file $name.hpp * */ #pragma once #define AUTO_GENERATED -#include "${structType.getGlobalHeaderPath()}" -${structType.getRelativeInclusions()} -#define BEFORE_EXTRA -${structType.beforeExtra} -#undef BEFORE_EXTRA +#include "${BaseType.GLOBAL_HEADER_PATH}" +${type.getRelativeInclusions()} -${structType.comment} struct $name { -#define AFTER_EXTRA -${structType.afterExtra} -#undef AFTER_EXTRA """.trimIndent() - ) - hpp.appendText(structType.genAntiReconstruction()) - hpp.appendText(structType.genPublic()) - hpp.appendText(structType.genProtected()) - hpp.appendText(structType.genPrivate()) - hpp.appendText("};") - } + ) + hpp.appendText(type.genAntiReconstruction()) + hpp.appendText(type.genPublic()) + hpp.appendText(type.genProtected()) + hpp.appendText(type.genPrivate()) + hpp.appendText("};") } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/liteldev/headeroutput/utils.kt b/src/main/kotlin/com/liteldev/headeroutput/utils.kt index 82c8d92..da754b1 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/utils.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/utils.kt @@ -1,6 +1,5 @@ package com.liteldev.headeroutput -import com.liteldev.headeroutput.config.origindata.TypeData import java.nio.file.Paths fun String.relativePath(path: String) = Paths.get(this).relativize(Paths.get(path)).toString().replace("\\", "/") @@ -17,20 +16,3 @@ fun StringBuilder.appendSpace(count: Int): StringBuilder { } return this } - -fun isNameSpace(typeName: String, typeData: TypeData): Boolean { - if (typeData.privateTypes != null - || typeData.privateStaticTypes != null - || typeData.protectedTypes != null - || typeData.protectedStaticTypes != null - || typeData.publicStaticTypes != null - || typeData.virtual != null - || typeData.vtblEntry != null - ) { - return false - } - val containsInIdentifierList = - HeaderOutput.realStructNameList.contains(typeName) || HeaderOutput.realClassNameList.contains(typeName) - return (typeData.publicTypes?.find { it.isPtrCall() } == null - && !containsInIdentifierList) -} \ No newline at end of file From c6260bfcb725ea669be1ab243be519eb7416ce73 Mon Sep 17 00:00:00 2001 From: RimuruChan Date: Wed, 12 Jul 2023 18:31:27 +0800 Subject: [PATCH 3/9] =?UTF-8?q?feat:=20=E9=80=82=E9=85=8D=E5=B5=8C?= =?UTF-8?q?=E5=A5=97=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.json | 2 +- .../liteldev/headeroutput/HeaderGenerator.kt | 83 +++++++++++++++++++ .../com/liteldev/headeroutput/HeaderOutput.kt | 28 +++---- .../com/liteldev/headeroutput/TypeManager.kt | 64 ++++++++++++-- .../liteldev/headeroutput/entity/BaseType.kt | 79 +++++++++++++++++- .../liteldev/headeroutput/entity/ClassType.kt | 47 ++++++++--- .../headeroutput/entity/NamespaceType.kt | 14 ++++ .../headeroutput/entity/StructType.kt | 22 ++++- .../headeroutput/generate/ClassGenerator.kt | 40 --------- .../headeroutput/generate/Generator.kt | 7 -- .../generate/NamespaceGenerator.kt | 35 -------- .../headeroutput/generate/StructGenerator.kt | 38 --------- .../kotlin/com/liteldev/headeroutput/utils.kt | 4 + 13 files changed, 302 insertions(+), 161 deletions(-) create mode 100644 src/main/kotlin/com/liteldev/headeroutput/HeaderGenerator.kt delete mode 100644 src/main/kotlin/com/liteldev/headeroutput/generate/ClassGenerator.kt delete mode 100644 src/main/kotlin/com/liteldev/headeroutput/generate/Generator.kt delete mode 100644 src/main/kotlin/com/liteldev/headeroutput/generate/NamespaceGenerator.kt delete mode 100644 src/main/kotlin/com/liteldev/headeroutput/generate/StructGenerator.kt diff --git a/config.json b/config.json index 2fe881c..f0a173b 100644 --- a/config.json +++ b/config.json @@ -5,7 +5,7 @@ "^$", "^struct .*$", "^class .*$", - ".*(@|::|\\s|<).*", + ".*(@|\\s|<).*", "(std|gsl|mce|glm|entt|rapidjson|type_info|leveldb)" ] }, diff --git a/src/main/kotlin/com/liteldev/headeroutput/HeaderGenerator.kt b/src/main/kotlin/com/liteldev/headeroutput/HeaderGenerator.kt new file mode 100644 index 0000000..69ce2ad --- /dev/null +++ b/src/main/kotlin/com/liteldev/headeroutput/HeaderGenerator.kt @@ -0,0 +1,83 @@ +package com.liteldev.headeroutput + +import com.liteldev.headeroutput.config.GeneratorConfig +import com.liteldev.headeroutput.entity.BaseType +import java.io.File + +object HeaderGenerator { + + const val HEADER_SUFFIX = "h" + private const val PREDEFINE_FILE_NAME = "_HeaderOutputPredefine.$HEADER_SUFFIX" + + private val HEADER_TEMPLATE = """ +#pragma once + +#include "$PREDEFINE_FILE_NAME" + + +""".trimIndent() + + fun generate() { + TypeManager.nestingMap.forEach { (_, baseType) -> + generate(baseType) + } + createPredefineFile() + } + + private fun generate(type: BaseType) { + when { + type.isNamespace() -> generateNamespace(type) + else -> { + val sb = StringBuilder() + var parentPath = GeneratorConfig.generatePath + if (type.outerType != null) { + parentPath += type.outerType!!.getPath().removeSuffix(".$HEADER_SUFFIX") + sb.appendLine("namespace ${type.outerType!!.name} {") + sb.appendLine() + sb.appendLine(type.generateTypeDefine()) + sb.appendLine("};") + } else { + sb.appendLine(type.generateTypeDefine()) + } + + val targetFile = File(parentPath, type.getPath()) + targetFile.writeText(HEADER_TEMPLATE + sb.toString()) + } + } + } + + private fun generateNamespace(type: BaseType) { + assert(type.isNamespace()) { "${type.name} is not namespace" } + + val file = File(GeneratorConfig.generatePath, type.getPath()) + file.writeText(HEADER_TEMPLATE + type.generateTypeDefine()) + + if (type.innerTypes.isNotEmpty()) { + File(GeneratorConfig.generatePath, type.getPath().removeSuffix(".$HEADER_SUFFIX")).also { it.mkdirs() } + type.innerTypes.forEach { innerType -> + generate(innerType) + } + } + } + + private fun createPredefineFile() { + val file = File(GeneratorConfig.generatePath, PREDEFINE_FILE_NAME) + file.writeText( + """ +#pragma once + +#define MCAPI __declspec(dllimport) + +#include +#include +#include +#include +#include +#include +#include +#include + +""".trimIndent() + ) + } +} diff --git a/src/main/kotlin/com/liteldev/headeroutput/HeaderOutput.kt b/src/main/kotlin/com/liteldev/headeroutput/HeaderOutput.kt index 8eef28e..e3f49a8 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/HeaderOutput.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/HeaderOutput.kt @@ -4,9 +4,6 @@ import com.liteldev.headeroutput.config.GeneratorConfig import com.liteldev.headeroutput.config.origindata.MemberTypeData import com.liteldev.headeroutput.config.origindata.TypeData import com.liteldev.headeroutput.entity.* -import com.liteldev.headeroutput.generate.ClassGenerator -import com.liteldev.headeroutput.generate.NamespaceGenerator -import com.liteldev.headeroutput.generate.StructGenerator import kotlinx.cli.ArgParser import kotlinx.cli.ArgType import kotlinx.cli.default @@ -34,27 +31,24 @@ object HeaderOutput { loadIdentifiedTypes() loadTypes() + TypeManager.initParents() TypeManager.initInclusionList() println("Warning: these class has no information in originData but used by other classes\n$notExistBaseType") File(GeneratorConfig.generatePath).mkdirs() - TypeManager.getAllTypes().forEach { type -> - when { - type.isNamespace() -> NamespaceGenerator.generate(type) - type.isStruct() -> StructGenerator.generate(type) - type.isClass() -> ClassGenerator.generate(type) - } - } + TypeManager.generateNestingMap() + + HeaderGenerator.generate() } private fun readCommandLineArgs(args: Array): Boolean { val parser = ArgParser("HeaderOutput") val configPath by parser.option(ArgType.String, "config", "c", "The config file path").default("./config.json") - val generatePath by parser.option(ArgType.String, "generate", "g", "The generate header files path") + val generatePath by parser.option(ArgType.String, "output-dir", "o", "The header output path") .default("./header") - val jsonPath by parser.option(ArgType.String, "json", "j", "The original data json file path") + val jsonPath by parser.option(ArgType.String, "input", "i", "The original data json file path") .default("./header.json") parser.parse(args) GeneratorConfig.configPath = configPath @@ -65,9 +59,7 @@ object HeaderOutput { return false } if (!File(GeneratorConfig.generatePath).isDirectory) { - try { - File(GeneratorConfig.generatePath).mkdirs() - } catch (e: Exception) { + if (!File(GeneratorConfig.generatePath).mkdirs()) { println("Fail to create generate header files path") return false } @@ -87,6 +79,7 @@ object HeaderOutput { } private fun loadTypes() { + val notIdentifiedTypes = mutableSetOf() println("Loading types...") originData["classes"]?.jsonObject?.filter { (k, _) -> GeneratorConfig.generationExcludeRegexList.find { k.matches(Regex(it)) } == null @@ -119,12 +112,15 @@ object HeaderOutput { ClassType(typeName, type) else -> { - println("Warning: $typeName is not a namespace, struct or class") + notIdentifiedTypes.add(typeName) ClassType(typeName, type) } } ) } + println( + "Warning: can not determine these types' type. Treat them as class type\n$notIdentifiedTypes" + ) } private fun loadIdentifiedTypes() { diff --git a/src/main/kotlin/com/liteldev/headeroutput/TypeManager.kt b/src/main/kotlin/com/liteldev/headeroutput/TypeManager.kt index a454b2c..be486b2 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/TypeManager.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/TypeManager.kt @@ -8,8 +8,8 @@ import com.liteldev.headeroutput.entity.StructType object TypeManager { private val typeMap = hashMapOf() - private val nestingMap = hashMapOf() + val nestingMap = hashMapOf() lateinit var classNameList: List lateinit var structNameList: List @@ -29,18 +29,72 @@ object TypeManager { return typeMap.values.toList() } + fun initParents() { + typeMap.values.filter { it.isClass() }.forEach { type -> + type as ClassType + type.typeData.parentTypes?.getOrNull(0)?.run { typeMap[this] }?.let { type.parents.add(it) } + /* + fixme: Fix in header generator: recursive parent + type.typeData.parentTypes?.forEach { parent -> + getType(parent)?.let { type.parents.add(it) } + } + */ + } + } + fun initInclusionList() { typeMap.forEach { (_, type) -> type.initIncludeList() } } + /** + * @param type: the type to be created, must be a inner type + */ + private fun createDummyClass(name: String): BaseType { + assert(!hasType(name)) { "type $name already exists" } + + val dummyClass = + ClassType(name, TypeData(null, null, null, null, null, null, null, null, null, null, null)) + + if (name.contains("::")) { + val parentName = name.substringBeforeLast("::") + if (!hasType(parentName)) { + createDummyClass(parentName) + } + + val parentType = getType(parentName)!! + parentType.innerTypes.add(dummyClass) + dummyClass.outerType = parentType + } else { + nestingMap[name] = dummyClass + } + + addType(name, dummyClass) + return dummyClass + } + fun generateNestingMap() { - typeMap.forEach { (fullName, type) -> - val parent = type.typeData.parentTypes?.firstOrNull() - if (parent != null) { - nestingMap[fullName] = typeMap[parent]!! + typeMap.filter { !it.key.contains("::") } + .forEach { (key, value) -> + nestingMap[key] = value + value.constructInnerTypeList() + } + // 收集所有形成嵌套关系的类,检查哪些类没有被收集到 + val allNestingType = nestingMap.values.flatMap { it.innerTypes }.toMutableSet() + allNestingType.addAll(nestingMap.values) + val allType = typeMap.values.toSet() + val notNestingType = allType - allNestingType + println("Warning: these class has no nesting relationship\n${notNestingType.map { it.name }}") + // generate a dummy class for each not nesting class + notNestingType.forEach { + val parentName = it.name.substringBeforeLast("::") + if (!hasType(parentName)) { + createDummyClass(parentName) } + val parentType = getType(parentName)!! + parentType.innerTypes.add(it) + it.outerType = parentType } } diff --git a/src/main/kotlin/com/liteldev/headeroutput/entity/BaseType.kt b/src/main/kotlin/com/liteldev/headeroutput/entity/BaseType.kt index 85a4e06..64fb480 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/entity/BaseType.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/entity/BaseType.kt @@ -1,5 +1,6 @@ package com.liteldev.headeroutput.entity +import com.liteldev.headeroutput.HeaderGenerator.HEADER_SUFFIX import com.liteldev.headeroutput.HeaderOutput import com.liteldev.headeroutput.TypeManager import com.liteldev.headeroutput.config.GeneratorConfig @@ -7,6 +8,7 @@ import com.liteldev.headeroutput.config.origindata.MemberTypeData import com.liteldev.headeroutput.config.origindata.TypeData import com.liteldev.headeroutput.parent import com.liteldev.headeroutput.relativePath +import java.util.* abstract class BaseType( var name: String, @@ -14,7 +16,28 @@ abstract class BaseType( ) { private val includeList: MutableSet = mutableSetOf() - fun getPath(): String = "./$name.hpp" + var outerType: BaseType? = null + val innerTypes: MutableSet = mutableSetOf() + val referenceTypes: MutableSet = mutableSetOf() + + val simpleName = name.substringAfterLast("::") + val fullEscapeName = name.replace("::", "_") + val fullEscapeNameUpper = fullEscapeName.uppercase(Locale.getDefault()) + + open fun getPath(): String = "$simpleName.$HEADER_SUFFIX" + + fun constructInnerTypeList(outerType: BaseType? = null) { + this.outerType = outerType + TypeManager.getAllTypes().filter { + if (!it.name.startsWith(this.name + "::")) + false + else + !it.name.substring(this.name.length + 2).contains("::") + }.forEach { + innerTypes.add(it) + it.constructInnerTypeList(this) + } + } private fun readIncludeClassFromMembers(list: List): Set { val retList = mutableSetOf() @@ -55,7 +78,26 @@ abstract class BaseType( return include.toString() } - open fun initIncludeList() { +// fun collectAllReferencedType(): Set { +// val retList = mutableSetOf() +// retList.addAll(includeList) +// innerTypeList.forEach { +// retList.addAll(it.collectAllReferencedType()) +// } +// return retList +// } + + abstract fun generateTypeDefine(): String + + fun generateInnerTypeDefine(): String { + val sb = StringBuilder("\n") + innerTypes.forEach { + sb.appendLine(it.generateTypeDefine()) + } + return if (sb.isNotBlank()) sb.toString() else "" + } + + fun initIncludeList() { typeData.virtual?.let(::readList) typeData.publicTypes?.let(::readList) typeData.publicStaticTypes?.let(::readList) @@ -66,6 +108,39 @@ abstract class BaseType( includeList.removeIf { it === this } } +// private fun collectSelfReferencedType() { +// val referencedTypeNames = mutableSetOf() +// val list = mutableListOf() +// typeData.virtual?.let(list::addAll) +// typeData.publicTypes?.let(list::addAll) +// typeData.publicStaticTypes?.let(list::addAll) +// typeData.protectedTypes?.let(list::addAll) +// typeData.protectedStaticTypes?.let(list::addAll) +// typeData.privateTypes?.let(list::addAll) +// typeData.privateStaticTypes?.let(list::addAll) +// list.forEach { memberType -> +// memberType.params?.forEach { param -> +// param.Name?.let { it -> +// Regex("(\\w+::\\w+)").findAll(it).forEach { +// referencedTypeNames.add(it.groupValues[1]) +// } +// } +// } +// Regex("(\\w+)::").findAll(memberType.valType.Name ?: "").forEach { +// referencedTypeNames.add(it.groupValues[1]) +// } +// } +// return referencedTypeNames.filter { inclusion -> +// GeneratorConfig.inclusionExcludeRegexList.find { +// inclusion.matches( +// Regex(it) +// ) +// } == null +// }.toSet() +// +// } + + companion object { const val GLOBAL_HEADER_PATH = "llapi/Global.h" } diff --git a/src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt b/src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt index 65341de..74937f2 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt @@ -5,10 +5,11 @@ import com.liteldev.headeroutput.config.origindata.TypeData open class ClassType( name: String, typeData: TypeData, - var parent: ClassType? = null, ) : BaseType(name, typeData) { - // TODO: Fix in header generator + val parents = arrayListOf() + + // fixme: Fix in header generator init { typeData.virtual?.forEach { virtual -> typeData.virtualUnordered?.removeIf { unordered -> @@ -21,12 +22,32 @@ open class ClassType( return name.hashCode() } - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - other as ClassType - if (name != other.name) return false - return typeData == other.typeData + override fun generateTypeDefine(): String { + val sb = StringBuilder( + "class $simpleName ${genParents()}{\n" + ) + if (innerTypes.isNotEmpty()) { + sb.appendLine("public:") + sb.append(generateInnerTypeDefine().replace("\n", "\n ")) + } + sb.append(genAntiReconstruction()) + sb.append(genPublic()) + sb.append(genProtected()) + sb.append(genPrivate()) + sb.append(genProtected(genFunc = false)) + sb.append(genPrivate(genFunc = false)) + sb.appendLine("};") + return sb.toString() + } + + fun genParents(): String { + if (parents.isEmpty()) { + return "" + } + val sb = StringBuilder(": ") + parents.joinToString(", ") { "public ${it.name}" }.let(sb::append) + sb.append(" ") + return sb.toString() } open fun genAntiReconstruction(): String { @@ -51,16 +72,16 @@ open class ClassType( val sb = StringBuilder() if (genOperator || genEmptyParamConstructor || genMoveConstructor) { sb.appendLine() - sb.appendLine("#ifndef DISABLE_CONSTRUCTOR_PREVENTION_${name.uppercase()}") + sb.appendLine("#ifndef DISABLE_CONSTRUCTOR_PREVENTION_$fullEscapeNameUpper") sb.appendLine("public:") if (genOperator) { - sb.appendLine(" class $name& operator=(class $name const &) = delete;") + sb.appendLine(" $simpleName& operator=($simpleName const &) = delete;") } if (genMoveConstructor) { - sb.appendLine(" $name(class $name const &) = delete;") + sb.appendLine(" $simpleName($simpleName const &) = delete;") } if (genEmptyParamConstructor) { - sb.appendLine(" $name() = delete;") + sb.appendLine(" $simpleName() = delete;") } sb.appendLine("#endif") } @@ -79,7 +100,7 @@ open class ClassType( } if (typeData.virtualUnordered?.isNotEmpty() == true) { - sb.appendLine("#ifdef ENABLE_VIRTUAL_FAKESYMBOL_${name.uppercase()}") + sb.appendLine("#ifdef ENABLE_VIRTUAL_FAKESYMBOL_${fullEscapeNameUpper}") typeData.virtualUnordered?.sortedBy { it.name }?.forEach { sb.appendLine( it.genFuncString( diff --git a/src/main/kotlin/com/liteldev/headeroutput/entity/NamespaceType.kt b/src/main/kotlin/com/liteldev/headeroutput/entity/NamespaceType.kt index 4f70c24..b5fe5e7 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/entity/NamespaceType.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/entity/NamespaceType.kt @@ -1,5 +1,6 @@ package com.liteldev.headeroutput.entity +import com.liteldev.headeroutput.HeaderGenerator.HEADER_SUFFIX import com.liteldev.headeroutput.config.origindata.TypeData class NamespaceType( @@ -11,6 +12,19 @@ class NamespaceType( typeData.publicTypes?.sortedBy { it.name }?.forEach { sb.appendLine(it.genFuncString(namespace = true)) } + sb.appendLine() return sb.toString() } + + override fun generateTypeDefine(): String { + val sb = StringBuilder() + sb.append("namespace $name {\n") + sb.append(genPublic()) + sb.appendLine("};") + return sb.toString() + } + + override fun getPath(): String { + return name.replace("::", "/") + ".$HEADER_SUFFIX" + } } diff --git a/src/main/kotlin/com/liteldev/headeroutput/entity/StructType.kt b/src/main/kotlin/com/liteldev/headeroutput/entity/StructType.kt index 9b5baaa..300c2e0 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/entity/StructType.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/entity/StructType.kt @@ -25,20 +25,34 @@ class StructType( val sb = StringBuilder() if (genOperator || genEmptyParamConstructor || genMoveConstructor) { sb.appendLine() - sb.appendLine("#ifndef DISABLE_CONSTRUCTOR_PREVENTION_${name.uppercase()}") + sb.appendLine("#ifndef DISABLE_CONSTRUCTOR_PREVENTION_${fullEscapeNameUpper}") sb.appendLine("public:") if (genOperator) { - sb.appendLine(" struct $name& operator=(struct $name const &) = delete;") + sb.appendLine(" $simpleName& operator=($simpleName const &) = delete;") } if (genMoveConstructor) { - sb.appendLine(" $name(struct $name const &) = delete;") + sb.appendLine(" $simpleName($simpleName const &) = delete;") } if (genEmptyParamConstructor) { - sb.appendLine(" $name() = delete;") + sb.appendLine(" $simpleName() = delete;") } sb.appendLine("#endif") } sb.appendLine() return sb.toString() } + + override fun generateTypeDefine(): String { + val sb = StringBuilder("struct $simpleName {\n") + if (innerTypes.isNotEmpty()) { + sb.appendLine("public:") + sb.append(generateInnerTypeDefine().replace("\n", "\n ")) + } + sb.append(genAntiReconstruction()) + sb.append(genPublic()) + sb.append(genProtected()) + sb.append(genPrivate()) + sb.appendLine("};") + return sb.toString() + } } diff --git a/src/main/kotlin/com/liteldev/headeroutput/generate/ClassGenerator.kt b/src/main/kotlin/com/liteldev/headeroutput/generate/ClassGenerator.kt deleted file mode 100644 index 9af01de..0000000 --- a/src/main/kotlin/com/liteldev/headeroutput/generate/ClassGenerator.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.liteldev.headeroutput.generate - -import com.liteldev.headeroutput.config.GeneratorConfig -import com.liteldev.headeroutput.entity.BaseType -import com.liteldev.headeroutput.entity.ClassType -import java.io.File - -object ClassGenerator : Generator { - - override fun generate(type: BaseType) { - if (type !is ClassType) { - println("ClassGenerator: ${type.name} is not ClassType") - return - } - val name = type.name - val hpp = File(GeneratorConfig.generatePath, type.getPath()) - hpp.writeText( - """ -/** - * @file $name.hpp - * - */ -#pragma once -#define AUTO_GENERATED -#include "${BaseType.GLOBAL_HEADER_PATH}" -${type.getRelativeInclusions()} - -class $name ${run { if (type.parent != null) ": public ${type.parent!!.name} " else "" }}{ - -""".trimIndent() - ) - hpp.appendText(type.genAntiReconstruction()) - hpp.appendText(type.genPublic()) - hpp.appendText(type.genProtected()) - hpp.appendText(type.genPrivate()) - hpp.appendText(type.genProtected(genFunc = false)) - hpp.appendText(type.genPrivate(genFunc = false)) - hpp.appendText("};\n") - } -} diff --git a/src/main/kotlin/com/liteldev/headeroutput/generate/Generator.kt b/src/main/kotlin/com/liteldev/headeroutput/generate/Generator.kt deleted file mode 100644 index e47c427..0000000 --- a/src/main/kotlin/com/liteldev/headeroutput/generate/Generator.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.liteldev.headeroutput.generate - -import com.liteldev.headeroutput.entity.BaseType - -interface Generator { - fun generate(type: BaseType) -} diff --git a/src/main/kotlin/com/liteldev/headeroutput/generate/NamespaceGenerator.kt b/src/main/kotlin/com/liteldev/headeroutput/generate/NamespaceGenerator.kt deleted file mode 100644 index 86c87b9..0000000 --- a/src/main/kotlin/com/liteldev/headeroutput/generate/NamespaceGenerator.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.liteldev.headeroutput.generate - -import com.liteldev.headeroutput.config.GeneratorConfig -import com.liteldev.headeroutput.entity.BaseType -import com.liteldev.headeroutput.entity.NamespaceType -import java.io.File - -object NamespaceGenerator : Generator { - - override fun generate(type: BaseType) { - if (type !is NamespaceType) { - println("NamespaceGenerator: ${type.name} is not NamespaceType") - return - } - val name = type.name - val file = File(GeneratorConfig.generatePath, type.getPath()) - file.writeText( - """ -/** - * @file $name.hpp - * - */ -#pragma once -#define AUTO_GENERATED -#include "${BaseType.GLOBAL_HEADER_PATH}" -${type.getRelativeInclusions()} - -namespace $name { - -${type.genPublic()} -}; -""".trimIndent() - ) - } -} diff --git a/src/main/kotlin/com/liteldev/headeroutput/generate/StructGenerator.kt b/src/main/kotlin/com/liteldev/headeroutput/generate/StructGenerator.kt deleted file mode 100644 index 84048db..0000000 --- a/src/main/kotlin/com/liteldev/headeroutput/generate/StructGenerator.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.liteldev.headeroutput.generate - -import com.liteldev.headeroutput.config.GeneratorConfig -import com.liteldev.headeroutput.entity.BaseType -import com.liteldev.headeroutput.entity.StructType -import java.io.File - -object StructGenerator : Generator { - - override fun generate(type: BaseType) { - if (type !is StructType) { - println("StructGenerator: ${type.name} is not StructType") - return - } - val name = type.name - val hpp = File(GeneratorConfig.generatePath, type.getPath()) - hpp.writeText( - """ -/** - * @file $name.hpp - * - */ -#pragma once -#define AUTO_GENERATED -#include "${BaseType.GLOBAL_HEADER_PATH}" -${type.getRelativeInclusions()} - -struct $name { - -""".trimIndent() - ) - hpp.appendText(type.genAntiReconstruction()) - hpp.appendText(type.genPublic()) - hpp.appendText(type.genProtected()) - hpp.appendText(type.genPrivate()) - hpp.appendText("};") - } -} diff --git a/src/main/kotlin/com/liteldev/headeroutput/utils.kt b/src/main/kotlin/com/liteldev/headeroutput/utils.kt index da754b1..d755457 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/utils.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/utils.kt @@ -16,3 +16,7 @@ fun StringBuilder.appendSpace(count: Int): StringBuilder { } return this } + +//fun removeTypeModifier(type: String): String { +// return type.replace("const ", "").replace("volatile ", "").replace("restrict ", "") +//} From 3c71e80b66437e3ba9bb9af851651a15a62587a8 Mon Sep 17 00:00:00 2001 From: RimuruChan Date: Fri, 14 Jul 2023 15:35:37 +0800 Subject: [PATCH 4/9] =?UTF-8?q?feat:=20=E4=B8=BA=E5=A4=B4=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E9=80=82=E9=85=8D=E8=87=AA=E5=8A=A8=E5=BC=95=E5=85=A5?= =?UTF-8?q?=E7=94=9F=E6=88=90=E5=A4=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../liteldev/headeroutput/HeaderGenerator.kt | 30 ++-- .../com/liteldev/headeroutput/HeaderOutput.kt | 23 ++- .../com/liteldev/headeroutput/TypeManager.kt | 40 ++---- .../{entity => config/origindata}/EnumType.kt | 2 +- .../config/origindata/MemberTypeData.kt | 3 - .../config/origindata/VariableTypeData.kt | 1 - .../liteldev/headeroutput/entity/BaseType.kt | 132 ++++++------------ .../liteldev/headeroutput/entity/ClassType.kt | 18 ++- .../headeroutput/entity/NamespaceType.kt | 9 +- .../headeroutput/entity/StructType.kt | 4 +- .../kotlin/com/liteldev/headeroutput/utils.kt | 77 ++++++++-- src/test/kotlin/TestClearSpecifier.kt | 6 + 12 files changed, 174 insertions(+), 171 deletions(-) rename src/main/kotlin/com/liteldev/headeroutput/{entity => config/origindata}/EnumType.kt (98%) create mode 100644 src/test/kotlin/TestClearSpecifier.kt diff --git a/src/main/kotlin/com/liteldev/headeroutput/HeaderGenerator.kt b/src/main/kotlin/com/liteldev/headeroutput/HeaderGenerator.kt index 69ce2ad..75f020c 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/HeaderGenerator.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/HeaderGenerator.kt @@ -12,16 +12,15 @@ object HeaderGenerator { private val HEADER_TEMPLATE = """ #pragma once -#include "$PREDEFINE_FILE_NAME" - """.trimIndent() fun generate() { + File(GeneratorConfig.generatePath).mkdirs() + createPredefineFile() TypeManager.nestingMap.forEach { (_, baseType) -> generate(baseType) } - createPredefineFile() } private fun generate(type: BaseType) { @@ -29,9 +28,7 @@ object HeaderGenerator { type.isNamespace() -> generateNamespace(type) else -> { val sb = StringBuilder() - var parentPath = GeneratorConfig.generatePath if (type.outerType != null) { - parentPath += type.outerType!!.getPath().removeSuffix(".$HEADER_SUFFIX") sb.appendLine("namespace ${type.outerType!!.name} {") sb.appendLine() sb.appendLine(type.generateTypeDefine()) @@ -39,9 +36,17 @@ object HeaderGenerator { } else { sb.appendLine(type.generateTypeDefine()) } + val file = File(GeneratorConfig.generatePath, type.getPath()) + file.writeText( + HEADER_TEMPLATE + """ +#include "${type.getPath().relativePath(PREDEFINE_FILE_NAME)}" + +// auto generated inclusion list +${type.includeList.sorted().joinToString("\n") { "#include \"$it\"" }} + - val targetFile = File(parentPath, type.getPath()) - targetFile.writeText(HEADER_TEMPLATE + sb.toString()) + """.trimIndent() + sb.toString() + ) } } } @@ -50,7 +55,16 @@ object HeaderGenerator { assert(type.isNamespace()) { "${type.name} is not namespace" } val file = File(GeneratorConfig.generatePath, type.getPath()) - file.writeText(HEADER_TEMPLATE + type.generateTypeDefine()) + file.writeText( + HEADER_TEMPLATE + """ +#include "${type.getPath().relativePath(PREDEFINE_FILE_NAME)}" + +// auto generated inclusion list +${type.includeList.sorted().joinToString("\n") { "#include \"$it\"" }} + + + """.trimIndent() + type.generateTypeDefine() + ) if (type.innerTypes.isNotEmpty()) { File(GeneratorConfig.generatePath, type.getPath().removeSuffix(".$HEADER_SUFFIX")).also { it.mkdirs() } diff --git a/src/main/kotlin/com/liteldev/headeroutput/HeaderOutput.kt b/src/main/kotlin/com/liteldev/headeroutput/HeaderOutput.kt index e3f49a8..97f0590 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/HeaderOutput.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/HeaderOutput.kt @@ -2,8 +2,12 @@ package com.liteldev.headeroutput import com.liteldev.headeroutput.config.GeneratorConfig import com.liteldev.headeroutput.config.origindata.MemberTypeData +import com.liteldev.headeroutput.config.origindata.StorageClassType +import com.liteldev.headeroutput.config.origindata.SymbolNodeType import com.liteldev.headeroutput.config.origindata.TypeData -import com.liteldev.headeroutput.entity.* +import com.liteldev.headeroutput.entity.ClassType +import com.liteldev.headeroutput.entity.NamespaceType +import com.liteldev.headeroutput.entity.StructType import kotlinx.cli.ArgParser import kotlinx.cli.ArgType import kotlinx.cli.default @@ -14,16 +18,11 @@ import java.io.File @OptIn(ExperimentalSerializationApi::class) private val json = Json { explicitNulls = false } -fun main(args: Array) { - HeaderOutput.run(args) -} - object HeaderOutput { private lateinit var originData: JsonObject - val notExistBaseType = mutableSetOf() - - fun run(args: Array) { + @JvmStatic + fun main(args: Array) { if (!readCommandLineArgs(args)) return GeneratorConfig.loadConfig() @@ -32,14 +31,10 @@ object HeaderOutput { loadTypes() TypeManager.initParents() + TypeManager.initReferences() + TypeManager.initNestingMap() TypeManager.initInclusionList() - println("Warning: these class has no information in originData but used by other classes\n$notExistBaseType") - - File(GeneratorConfig.generatePath).mkdirs() - - TypeManager.generateNestingMap() - HeaderGenerator.generate() } diff --git a/src/main/kotlin/com/liteldev/headeroutput/TypeManager.kt b/src/main/kotlin/com/liteldev/headeroutput/TypeManager.kt index be486b2..a283106 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/TypeManager.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/TypeManager.kt @@ -3,8 +3,6 @@ package com.liteldev.headeroutput import com.liteldev.headeroutput.config.origindata.TypeData import com.liteldev.headeroutput.entity.BaseType import com.liteldev.headeroutput.entity.ClassType -import com.liteldev.headeroutput.entity.NamespaceType -import com.liteldev.headeroutput.entity.StructType object TypeManager { private val typeMap = hashMapOf() @@ -42,6 +40,12 @@ object TypeManager { } } + fun initReferences() { + typeMap.values.forEach { type -> + type.collectSelfReferencedType() + } + } + fun initInclusionList() { typeMap.forEach { (_, type) -> type.initIncludeList() @@ -49,7 +53,7 @@ object TypeManager { } /** - * @param type: the type to be created, must be a inner type + * @param name: the type's name to be created, must be an inner type */ private fun createDummyClass(name: String): BaseType { assert(!hasType(name)) { "type $name already exists" } @@ -74,7 +78,7 @@ object TypeManager { return dummyClass } - fun generateNestingMap() { + fun initNestingMap() { typeMap.filter { !it.key.contains("::") } .forEach { (key, value) -> nestingMap[key] = value @@ -99,31 +103,3 @@ object TypeManager { } } - -fun BaseType.isClass(): Boolean = this is ClassType && !this.isStruct() - -fun BaseType.isStruct() = this is StructType -fun BaseType.isNamespace() = this is NamespaceType - -fun isNameSpace(typeName: String, typeData: TypeData): Boolean { - if (typeData.privateTypes != null - || typeData.privateStaticTypes != null - || typeData.protectedTypes != null - || typeData.protectedStaticTypes != null - || typeData.publicStaticTypes != null - || typeData.virtual != null - || typeData.vtblEntry != null - ) { - return false - } - val isClassOrStruct = isStruct(typeName) || isClass(typeName) - return (typeData.publicTypes?.find { it.isPtrCall() } == null && !isClassOrStruct) -} - -fun isStruct(typeName: String): Boolean { - return TypeManager.structNameList.contains(typeName) -} - -fun isClass(typeName: String): Boolean { - return TypeManager.classNameList.contains(typeName) -} diff --git a/src/main/kotlin/com/liteldev/headeroutput/entity/EnumType.kt b/src/main/kotlin/com/liteldev/headeroutput/config/origindata/EnumType.kt similarity index 98% rename from src/main/kotlin/com/liteldev/headeroutput/entity/EnumType.kt rename to src/main/kotlin/com/liteldev/headeroutput/config/origindata/EnumType.kt index 9cc2891..4ca3f68 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/entity/EnumType.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/config/origindata/EnumType.kt @@ -1,4 +1,4 @@ -package com.liteldev.headeroutput.entity +package com.liteldev.headeroutput.config.origindata import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable diff --git a/src/main/kotlin/com/liteldev/headeroutput/config/origindata/MemberTypeData.kt b/src/main/kotlin/com/liteldev/headeroutput/config/origindata/MemberTypeData.kt index cde2087..ce8e331 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/config/origindata/MemberTypeData.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/config/origindata/MemberTypeData.kt @@ -1,9 +1,6 @@ package com.liteldev.headeroutput.config.origindata import com.liteldev.headeroutput.appendSpace -import com.liteldev.headeroutput.entity.AccessType -import com.liteldev.headeroutput.entity.StorageClassType -import com.liteldev.headeroutput.entity.SymbolNodeType import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/src/main/kotlin/com/liteldev/headeroutput/config/origindata/VariableTypeData.kt b/src/main/kotlin/com/liteldev/headeroutput/config/origindata/VariableTypeData.kt index f3561ef..c61d506 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/config/origindata/VariableTypeData.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/config/origindata/VariableTypeData.kt @@ -1,6 +1,5 @@ package com.liteldev.headeroutput.config.origindata -import com.liteldev.headeroutput.entity.VarSymbolType import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/src/main/kotlin/com/liteldev/headeroutput/entity/BaseType.kt b/src/main/kotlin/com/liteldev/headeroutput/entity/BaseType.kt index 64fb480..d5cca99 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/entity/BaseType.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/entity/BaseType.kt @@ -1,20 +1,18 @@ package com.liteldev.headeroutput.entity import com.liteldev.headeroutput.HeaderGenerator.HEADER_SUFFIX -import com.liteldev.headeroutput.HeaderOutput import com.liteldev.headeroutput.TypeManager -import com.liteldev.headeroutput.config.GeneratorConfig import com.liteldev.headeroutput.config.origindata.MemberTypeData import com.liteldev.headeroutput.config.origindata.TypeData -import com.liteldev.headeroutput.parent -import com.liteldev.headeroutput.relativePath +import com.liteldev.headeroutput.getTopLevelFileType +import com.liteldev.headeroutput.removeTypeSpecifier import java.util.* abstract class BaseType( var name: String, var typeData: TypeData, ) { - private val includeList: MutableSet = mutableSetOf() + lateinit var includeList: MutableSet var outerType: BaseType? = null val innerTypes: MutableSet = mutableSetOf() @@ -24,7 +22,12 @@ abstract class BaseType( val fullEscapeName = name.replace("::", "_") val fullEscapeNameUpper = fullEscapeName.uppercase(Locale.getDefault()) - open fun getPath(): String = "$simpleName.$HEADER_SUFFIX" + open fun getPath(): String { + if (outerType == null) { + return "./$simpleName.$HEADER_SUFFIX" + } + return "./" + getTopLevelFileType().name.replace("::", "/") + "." + HEADER_SUFFIX + } fun constructInnerTypeList(outerType: BaseType? = null) { this.outerType = outerType @@ -39,56 +42,19 @@ abstract class BaseType( } } - private fun readIncludeClassFromMembers(list: List): Set { - val retList = mutableSetOf() - list.forEach { memberType -> - memberType.params?.forEach { param -> - param.Name?.let { it -> - Regex("(\\w+)::").findAll(it).forEach { - retList.add(it.groupValues[1]) - } - } - } - Regex("(\\w+)::").findAll(memberType.valType.Name ?: "").forEach { - retList.add(it.groupValues[1]) - } - } - return retList.filter { inclusion -> - GeneratorConfig.inclusionExcludeRegexList.find { - inclusion.matches( - Regex(it) - ) - } == null - }.toSet() - } - - private fun readList(list: List) = readIncludeClassFromMembers(list).filter { - TypeManager.hasType(it).apply { - if (!this) { - HeaderOutput.notExistBaseType.add(it) - } - } - }.mapNotNull { TypeManager.getType(it) }.let(includeList::addAll) - - fun getRelativeInclusions(): String { - val include = StringBuilder() - includeList.forEach { - include.appendLine("""#include "${(getPath().parent()).relativePath(it.getPath())}"""") + fun collectAllReferencedType(): Set { + val retList = mutableSetOf() + retList.addAll(referenceTypes) + innerTypes.forEach { + retList.addAll(it.collectAllReferencedType()) } - return include.toString() + return retList } -// fun collectAllReferencedType(): Set { -// val retList = mutableSetOf() -// retList.addAll(includeList) -// innerTypeList.forEach { -// retList.addAll(it.collectAllReferencedType()) -// } -// return retList -// } - abstract fun generateTypeDefine(): String + abstract fun initIncludeList() + fun generateInnerTypeDefine(): String { val sb = StringBuilder("\n") innerTypes.forEach { @@ -97,49 +63,31 @@ abstract class BaseType( return if (sb.isNotBlank()) sb.toString() else "" } - fun initIncludeList() { - typeData.virtual?.let(::readList) - typeData.publicTypes?.let(::readList) - typeData.publicStaticTypes?.let(::readList) - typeData.protectedTypes?.let(::readList) - typeData.protectedStaticTypes?.let(::readList) - typeData.privateTypes?.let(::readList) - typeData.privateStaticTypes?.let(::readList) - includeList.removeIf { it === this } + fun collectSelfReferencedType() { + val referencedTypeNames = mutableSetOf() + val list = mutableListOf() + typeData.virtual?.let(list::addAll) + typeData.publicTypes?.let(list::addAll) + typeData.publicStaticTypes?.let(list::addAll) + typeData.protectedTypes?.let(list::addAll) + typeData.protectedStaticTypes?.let(list::addAll) + typeData.privateTypes?.let(list::addAll) + typeData.privateStaticTypes?.let(list::addAll) + list.forEach { memberType -> + memberType.params?.forEach { param -> + referencedTypeNames.add(removeTypeSpecifier(param.Name ?: "")) + } + memberType.valType.Name?.let { + referencedTypeNames.add(removeTypeSpecifier(it)) + } + } + referencedTypeNames.forEach { + TypeManager.getType(it)?.let { type -> + referenceTypes.add(type) + } + } } -// private fun collectSelfReferencedType() { -// val referencedTypeNames = mutableSetOf() -// val list = mutableListOf() -// typeData.virtual?.let(list::addAll) -// typeData.publicTypes?.let(list::addAll) -// typeData.publicStaticTypes?.let(list::addAll) -// typeData.protectedTypes?.let(list::addAll) -// typeData.protectedStaticTypes?.let(list::addAll) -// typeData.privateTypes?.let(list::addAll) -// typeData.privateStaticTypes?.let(list::addAll) -// list.forEach { memberType -> -// memberType.params?.forEach { param -> -// param.Name?.let { it -> -// Regex("(\\w+::\\w+)").findAll(it).forEach { -// referencedTypeNames.add(it.groupValues[1]) -// } -// } -// } -// Regex("(\\w+)::").findAll(memberType.valType.Name ?: "").forEach { -// referencedTypeNames.add(it.groupValues[1]) -// } -// } -// return referencedTypeNames.filter { inclusion -> -// GeneratorConfig.inclusionExcludeRegexList.find { -// inclusion.matches( -// Regex(it) -// ) -// } == null -// }.toSet() -// -// } - companion object { const val GLOBAL_HEADER_PATH = "llapi/Global.h" diff --git a/src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt b/src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt index 74937f2..0f95575 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt @@ -2,6 +2,7 @@ package com.liteldev.headeroutput.entity import com.liteldev.headeroutput.config.origindata.MemberTypeData import com.liteldev.headeroutput.config.origindata.TypeData +import com.liteldev.headeroutput.relativePath open class ClassType( name: String, typeData: TypeData, @@ -18,10 +19,6 @@ open class ClassType( } } - override fun hashCode(): Int { - return name.hashCode() - } - override fun generateTypeDefine(): String { val sb = StringBuilder( "class $simpleName ${genParents()}{\n" @@ -40,6 +37,15 @@ open class ClassType( return sb.toString() } + override fun initIncludeList() { + includeList = referenceTypes.filter { !it.name.startsWith(this.name + "::") } + .map { this.getPath().relativePath(it.getPath()) }.toMutableSet() + if (parents.isNotEmpty()) { + includeList.addAll(parents.map { this.getPath().relativePath(it.getPath()) }) + } + includeList.remove("") + } + fun genParents(): String { if (parents.isEmpty()) { return "" @@ -63,9 +69,9 @@ open class ClassType( size == 1 && this[0].Name == "class $name const &" } == true && it.valType.Name == "class $name &" } == null - val genEmptyParamConstructor = public.find { it.name == name && it.params?.isEmpty() ?: true } == null + val genEmptyParamConstructor = public.find { it.name == simpleName && it.params?.isEmpty() ?: true } == null val genMoveConstructor = public.find { - it.name == name && it.params?.run { + it.name == simpleName && it.params?.run { size == 1 && this[0].Name == "class $name const &" } == true } == null diff --git a/src/main/kotlin/com/liteldev/headeroutput/entity/NamespaceType.kt b/src/main/kotlin/com/liteldev/headeroutput/entity/NamespaceType.kt index b5fe5e7..0c95d49 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/entity/NamespaceType.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/entity/NamespaceType.kt @@ -2,6 +2,7 @@ package com.liteldev.headeroutput.entity import com.liteldev.headeroutput.HeaderGenerator.HEADER_SUFFIX import com.liteldev.headeroutput.config.origindata.TypeData +import com.liteldev.headeroutput.relativePath class NamespaceType( name: String, typeData: TypeData @@ -25,6 +26,12 @@ class NamespaceType( } override fun getPath(): String { - return name.replace("::", "/") + ".$HEADER_SUFFIX" + return "./" + name.replace("::", "/") + ".$HEADER_SUFFIX" + } + + override fun initIncludeList() { + includeList = referenceTypes.map { this.getPath().relativePath(it.getPath()) }.toMutableSet() + includeList.remove(this.getPath().relativePath(this.getPath())) + includeList.remove("") } } diff --git a/src/main/kotlin/com/liteldev/headeroutput/entity/StructType.kt b/src/main/kotlin/com/liteldev/headeroutput/entity/StructType.kt index 300c2e0..f7bf294 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/entity/StructType.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/entity/StructType.kt @@ -16,9 +16,9 @@ class StructType( size == 1 && this[0].Name == "struct $name const &" } == true && it.valType.Name == "struct $name &" } == null - val genEmptyParamConstructor = public.find { it.name == name && it.params?.isEmpty() ?: true } == null + val genEmptyParamConstructor = public.find { it.name == simpleName && it.params?.isEmpty() ?: true } == null val genMoveConstructor = public.find { - it.name == name && it.params?.run { + it.name == simpleName && it.params?.run { size == 1 && this[0].Name == "struct $name const &" } == true } == null diff --git a/src/main/kotlin/com/liteldev/headeroutput/utils.kt b/src/main/kotlin/com/liteldev/headeroutput/utils.kt index d755457..c2cf10f 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/utils.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/utils.kt @@ -1,22 +1,77 @@ package com.liteldev.headeroutput +import com.liteldev.headeroutput.config.origindata.TypeData +import com.liteldev.headeroutput.entity.BaseType +import com.liteldev.headeroutput.entity.ClassType +import com.liteldev.headeroutput.entity.NamespaceType +import com.liteldev.headeroutput.entity.StructType import java.nio.file.Paths -fun String.relativePath(path: String) = Paths.get(this).relativize(Paths.get(path)).toString().replace("\\", "/") +fun String.relativePath(path: String): String { + // fixme: 修复路径前多余的 `../` + return Paths.get(this).relativize(Paths.get(path)).toString().replace("\\", "/").removePrefix("../") +} + +fun StringBuilder.appendSpace(count: Int): StringBuilder = append(" ".repeat(count)) -fun String.parent() = "$this/.." +fun BaseType.isClass(): Boolean = this is ClassType && !this.isStruct() -fun String.substring(startStr: String, endStr: String): String { - return substringAfter(startStr, "").substringBefore(endStr, "") +fun BaseType.isStruct() = this is StructType +fun BaseType.isNamespace() = this is NamespaceType + +fun BaseType.getTopLevelFileType(): BaseType { + outerType ?: return this + if (isNamespace()) { + return this + } + var outer = outerType!! + if (outer.isNamespace()) { + return this + } + while (outer.outerType != null) { + if (outer.outerType!!.isNamespace()) { + return outer + } + outer = outer.outerType!! + } + return outer } -fun StringBuilder.appendSpace(count: Int): StringBuilder { - for (i in 0 until count) { - this.append(" ") +fun isNameSpace(typeName: String, typeData: TypeData): Boolean { + if (typeData.privateTypes != null + || typeData.privateStaticTypes != null + || typeData.protectedTypes != null + || typeData.protectedStaticTypes != null + || typeData.publicStaticTypes != null + || typeData.virtual != null + || typeData.vtblEntry != null + ) { + return false } - return this + val isClassOrStruct = isStruct(typeName) || isClass(typeName) + return (typeData.publicTypes?.find { it.isPtrCall() } == null && !isClassOrStruct) +} + +fun isStruct(typeName: String): Boolean { + return TypeManager.structNameList.contains(typeName) +} + +fun isClass(typeName: String): Boolean { + return TypeManager.classNameList.contains(typeName) } -//fun removeTypeModifier(type: String): String { -// return type.replace("const ", "").replace("volatile ", "").replace("restrict ", "") -//} +fun removeTypeSpecifier(type: String): String { + var result = type.replace("*", "").replace("&", "").trim() + while (result.contains(" ")) { + result = when { + result.startsWith("const ") -> result.substring(6).trim() + result.endsWith(" const") -> result.substring(0, result.length - 6).trim() + + result.startsWith("class ") -> result.substring(6).trim() + result.startsWith("struct ") -> result.substring(7).trim() + + else -> break + } + } + return result +} diff --git a/src/test/kotlin/TestClearSpecifier.kt b/src/test/kotlin/TestClearSpecifier.kt new file mode 100644 index 0000000..53aff59 --- /dev/null +++ b/src/test/kotlin/TestClearSpecifier.kt @@ -0,0 +1,6 @@ +import com.liteldev.headeroutput.removeTypeSpecifier + +fun main() { + removeTypeSpecifier("const Mojang::Sima && const *").let(::println) + removeTypeSpecifier("class WeakEntityRef const &").let(::println) +} From 62f1d1a1d6be1af43098d9b6b69bb8841a8cc6a8 Mon Sep 17 00:00:00 2001 From: RimuruChan Date: Mon, 17 Jul 2023 15:22:16 +0800 Subject: [PATCH 5/9] refactor: remove not necessary MANIFEST.MF --- src/main/kotlin/META-INF/MANIFEST.MF | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 src/main/kotlin/META-INF/MANIFEST.MF diff --git a/src/main/kotlin/META-INF/MANIFEST.MF b/src/main/kotlin/META-INF/MANIFEST.MF deleted file mode 100644 index 751d381..0000000 --- a/src/main/kotlin/META-INF/MANIFEST.MF +++ /dev/null @@ -1,3 +0,0 @@ -Manifest-Version: 1.0 -Main-Class: com.liteldev.headeroutput.HeaderOutput - From 1debe8798610f0066496464300bd4f461ab755c3 Mon Sep 17 00:00:00 2001 From: RimuruChan Date: Tue, 18 Jul 2023 20:27:47 +0800 Subject: [PATCH 6/9] feat: auto generate enum/referenced classes' declarations refactor: simplified code --- build.gradle.kts | 12 +- gradle.properties | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- .../liteldev/headeroutput/HeaderGenerator.kt | 7 +- .../com/liteldev/headeroutput/HeaderOutput.kt | 125 ++++++++++++------ .../com/liteldev/headeroutput/TypeManager.kt | 70 +++++----- .../headeroutput/config/GeneratorConfig.kt | 4 + .../config/origindata/TypeData.kt | 42 +++++- .../liteldev/headeroutput/entity/BaseType.kt | 87 ++++-------- .../liteldev/headeroutput/entity/ClassType.kt | 90 ++++++------- .../liteldev/headeroutput/entity/EnumType.kt | 10 ++ .../headeroutput/entity/NamespaceType.kt | 17 ++- .../headeroutput/entity/StructType.kt | 36 +---- .../kotlin/com/liteldev/headeroutput/utils.kt | 57 ++------ src/test/kotlin/TestClearSpecifier.kt | 6 - 15 files changed, 281 insertions(+), 286 deletions(-) create mode 100644 src/main/kotlin/com/liteldev/headeroutput/entity/EnumType.kt delete mode 100644 src/test/kotlin/TestClearSpecifier.kt diff --git a/build.gradle.kts b/build.gradle.kts index f3ac69b..4ab6911 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,8 +1,8 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar plugins { - kotlin("jvm") version "1.7.10" - kotlin("plugin.serialization") version "1.7.10" + kotlin("jvm") version "1.9.0" + kotlin("plugin.serialization") version "1.9.0" id("com.github.johnrengelman.shadow") version "7.1.2" java distribution @@ -23,12 +23,12 @@ repositories { dependencies { implementation(kotlin("stdlib")) - implementation("org.jetbrains.kotlin:kotlin-reflect:1.7.10") - implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.0") + implementation("org.jetbrains.kotlin:kotlin-reflect:1.9.0") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0") implementation("org.jetbrains.kotlinx:kotlinx-cli:0.3.5") - testImplementation(platform("org.junit:junit-bom:5.9.0")) - testImplementation("org.junit.jupiter:junit-jupiter") + testImplementation(platform("org.junit:junit-bom:5.9.2")) + testImplementation("org.junit.jupiter:junit-jupiter:5.9.2") } tasks.test { diff --git a/gradle.properties b/gradle.properties index 29e08e8..7fc6f1f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -kotlin.code.style=official \ No newline at end of file +kotlin.code.style=official diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ae04661..84a0b92 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/kotlin/com/liteldev/headeroutput/HeaderGenerator.kt b/src/main/kotlin/com/liteldev/headeroutput/HeaderGenerator.kt index 75f020c..2d85fa9 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/HeaderGenerator.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/HeaderGenerator.kt @@ -39,7 +39,7 @@ object HeaderGenerator { val file = File(GeneratorConfig.generatePath, type.getPath()) file.writeText( HEADER_TEMPLATE + """ -#include "${type.getPath().relativePath(PREDEFINE_FILE_NAME)}" +#include "${type.getPath().relativePathTo(PREDEFINE_FILE_NAME)}" // auto generated inclusion list ${type.includeList.sorted().joinToString("\n") { "#include \"$it\"" }} @@ -57,7 +57,7 @@ ${type.includeList.sorted().joinToString("\n") { "#include \"$it\"" }} val file = File(GeneratorConfig.generatePath, type.getPath()) file.writeText( HEADER_TEMPLATE + """ -#include "${type.getPath().relativePath(PREDEFINE_FILE_NAME)}" +#include "${type.getPath().relativePathTo(PREDEFINE_FILE_NAME)}" // auto generated inclusion list ${type.includeList.sorted().joinToString("\n") { "#include \"$it\"" }} @@ -82,8 +82,11 @@ ${type.includeList.sorted().joinToString("\n") { "#include \"$it\"" }} #define MCAPI __declspec(dllimport) +#include +#include #include #include +#include #include #include #include diff --git a/src/main/kotlin/com/liteldev/headeroutput/HeaderOutput.kt b/src/main/kotlin/com/liteldev/headeroutput/HeaderOutput.kt index 97f0590..147bfc6 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/HeaderOutput.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/HeaderOutput.kt @@ -20,6 +20,9 @@ private val json = Json { explicitNulls = false } object HeaderOutput { private lateinit var originData: JsonObject + private lateinit var classNameList: MutableSet + private lateinit var structNameList: MutableSet + private lateinit var typeDataMap: MutableMap @JvmStatic fun main(args: Array) { @@ -28,7 +31,7 @@ object HeaderOutput { GeneratorConfig.loadConfig() loadOriginData() loadIdentifiedTypes() - loadTypes() + constructTypes() TypeManager.initParents() TypeManager.initReferences() @@ -71,59 +74,93 @@ object HeaderOutput { println("Loading origin data...") val configText = File(GeneratorConfig.jsonPath).readText() originData = Json.parseToJsonElement(configText).jsonObject + typeDataMap = originData["classes"]?.jsonObject?.mapValues { entry -> + json.decodeFromJsonElement(entry.value) + }?.toMutableMap() ?: mutableMapOf() + typeDataMap.values.forEach { type -> + var counter = 0 + type.virtual?.forEach { + // 对于没有名字的虚函数,将其标记为未知函数,并且将其名字设置为 __unk_vfn_0, __unk_vfn_1, ... + if (it.name == "" && !it.isUnknownFunction()) + it.symbolType = SymbolNodeType.Unknown + if (it.isUnknownFunction()) { + it.storageClass = StorageClassType.Virtual + it.addFlag(MemberTypeData.FLAG_PTR_CALL) + it.name = "void __unk_vfn_${counter}" + } + counter++ + + } + } } - private fun loadTypes() { + private fun constructTypes() { val notIdentifiedTypes = mutableSetOf() println("Loading types...") - originData["classes"]?.jsonObject?.filter { (k, _) -> - GeneratorConfig.generationExcludeRegexList.find { k.matches(Regex(it)) } == null - }?.mapValues { entry -> - json.decodeFromJsonElement(entry.value).also { type -> - var counter = 0 - type.virtual?.forEach { memberType -> - // 对于没有名字的虚函数,将其标记为未知函数,并且将其名字设置为 __unk_vfn_0, __unk_vfn_1, ... - if (memberType.name == "" && !memberType.isUnknownFunction()) - memberType.symbolType = SymbolNodeType.Unknown - if (memberType.isUnknownFunction()) { - memberType.storageClass = StorageClassType.Virtual - memberType.addFlag(MemberTypeData.FLAG_PTR_CALL) - memberType.name = "void __unk_vfn_${counter}" + typeDataMap + .filterNot { (k, _) -> GeneratorConfig.isExcludedFromGeneration(k) } + .forEach { (typeName, type) -> + TypeManager.addType( + typeName, + when { + isStruct(typeName) -> StructType(typeName, type) + isClass(typeName) -> ClassType(typeName, type) + isNameSpace(typeName, type) -> NamespaceType(typeName, type) + else -> { + notIdentifiedTypes.add(typeName) + ClassType(typeName, type) + } } - counter++ - } + ) } - }?.forEach { (typeName, type) -> - TypeManager.addType( - typeName, - when { - isNameSpace(typeName, type) -> - NamespaceType(typeName, type) - - isStruct(typeName) -> - StructType(typeName, type) - - isClass(typeName) -> - ClassType(typeName, type) - - else -> { - notIdentifiedTypes.add(typeName) - ClassType(typeName, type) - } - } - ) - } - println( - "Warning: can not determine these types' type. Treat them as class type\n$notIdentifiedTypes" - ) + println("Warning: can not determine these types' type. Treat them as class type\n$notIdentifiedTypes") } private fun loadIdentifiedTypes() { println("Loading identifier...") val identifier = originData["identifier"]?.jsonObject - TypeManager.classNameList = - (identifier?.get("class")?.jsonArray).orEmpty().map { it.jsonPrimitive.content } - TypeManager.structNameList = - (identifier?.get("struct")?.jsonArray).orEmpty().map { it.jsonPrimitive.content } + classNameList = + (identifier?.get("class")?.jsonArray).orEmpty().map { it.jsonPrimitive.content }.toMutableSet() + structNameList = + (identifier?.get("struct")?.jsonArray).orEmpty().map { it.jsonPrimitive.content }.toMutableSet() + + // check if any type is not identified but derived from other types + val referencedTypes = typeDataMap.values + .flatMap { it.parentTypes.orEmpty() + it.collectReferencedTypes().keys } + .filter { it in originData["classes"]?.jsonObject?.keys.orEmpty() } + .toMutableSet() + .also { + it.removeAll(classNameList) + it.removeAll(structNameList) + } + + if (referencedTypes.isNotEmpty()) { + println( + "Warning: these types are referenced from other types but not identified. Treat them as class type\n$referencedTypes" + ) + classNameList.addAll(referencedTypes) + } + } + + private fun isNameSpace(typeName: String, typeData: TypeData): Boolean { + if (isStruct(typeName) || isClass(typeName)) + return false + if (listOf( + typeData.privateTypes, + typeData.privateStaticTypes, + typeData.protectedTypes, + typeData.protectedStaticTypes, + typeData.publicStaticTypes, + typeData.virtual, + typeData.vtblEntry + ).any { it != null } + ) return false + return typeData.publicTypes?.none { it.isPtrCall() } == true } + + private fun isStruct(typeName: String) = structNameList.contains(typeName) + + + private fun isClass(typeName: String) = classNameList.contains(typeName) + } diff --git a/src/main/kotlin/com/liteldev/headeroutput/TypeManager.kt b/src/main/kotlin/com/liteldev/headeroutput/TypeManager.kt index a283106..846dbca 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/TypeManager.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/TypeManager.kt @@ -1,15 +1,17 @@ package com.liteldev.headeroutput +import com.liteldev.headeroutput.config.GeneratorConfig import com.liteldev.headeroutput.config.origindata.TypeData import com.liteldev.headeroutput.entity.BaseType +import com.liteldev.headeroutput.entity.BaseType.TypeKind import com.liteldev.headeroutput.entity.ClassType +import com.liteldev.headeroutput.entity.EnumType +import com.liteldev.headeroutput.entity.StructType object TypeManager { private val typeMap = hashMapOf() val nestingMap = hashMapOf() - lateinit var classNameList: List - lateinit var structNameList: List fun addType(fullName: String, type: BaseType) { typeMap[fullName] = type @@ -41,7 +43,8 @@ object TypeManager { } fun initReferences() { - typeMap.values.forEach { type -> + // copy to avoid ConcurrentModificationException due to enum is being added in `collectSelfReferencedType` + typeMap.values.toMutableSet().forEach { type -> type.collectSelfReferencedType() } } @@ -52,32 +55,6 @@ object TypeManager { } } - /** - * @param name: the type's name to be created, must be an inner type - */ - private fun createDummyClass(name: String): BaseType { - assert(!hasType(name)) { "type $name already exists" } - - val dummyClass = - ClassType(name, TypeData(null, null, null, null, null, null, null, null, null, null, null)) - - if (name.contains("::")) { - val parentName = name.substringBeforeLast("::") - if (!hasType(parentName)) { - createDummyClass(parentName) - } - - val parentType = getType(parentName)!! - parentType.innerTypes.add(dummyClass) - dummyClass.outerType = parentType - } else { - nestingMap[name] = dummyClass - } - - addType(name, dummyClass) - return dummyClass - } - fun initNestingMap() { typeMap.filter { !it.key.contains("::") } .forEach { (key, value) -> @@ -96,10 +73,43 @@ object TypeManager { if (!hasType(parentName)) { createDummyClass(parentName) } - val parentType = getType(parentName)!! + val parentType = getType(parentName) ?: return@forEach parentType.innerTypes.add(it) it.outerType = parentType } } + /** + * @param name: the type's name to be created, must be an inner type + */ + fun createDummyClass(name: String, type: TypeKind = TypeKind.CLASS): BaseType? { + assert(!hasType(name)) { "type $name already exists" } + + if (GeneratorConfig.isExcludedFromGeneration(name)) { + return null + } + + val dummyClass = when (type) { + TypeKind.CLASS -> ClassType(name, TypeData.empty()) + TypeKind.STRUCT -> StructType(name, TypeData.empty()) + TypeKind.ENUM -> EnumType(name) + else -> throw IllegalArgumentException("type $type is not supported") + } + + if (name.contains("::")) { + val parentName = name.substringBeforeLast("::") + if (!hasType(parentName)) { + createDummyClass(parentName) + } + + val parentType = getType(parentName) ?: return null + parentType.innerTypes.add(dummyClass) + dummyClass.outerType = parentType + } else { + nestingMap[name] = dummyClass + } + + addType(name, dummyClass) + return dummyClass + } } diff --git a/src/main/kotlin/com/liteldev/headeroutput/config/GeneratorConfig.kt b/src/main/kotlin/com/liteldev/headeroutput/config/GeneratorConfig.kt index cb42d04..12620bd 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/config/GeneratorConfig.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/config/GeneratorConfig.kt @@ -25,4 +25,8 @@ object GeneratorConfig { generationExcludeRegexList = generatorConfigData.exclusion.generation.regex inclusionExcludeRegexList = generatorConfigData.exclusion.inclusion.regex } + + fun isExcludedFromGeneration(name: String): Boolean { + return generationExcludeRegexList.any { name.matches(it.toRegex()) } + } } diff --git a/src/main/kotlin/com/liteldev/headeroutput/config/origindata/TypeData.kt b/src/main/kotlin/com/liteldev/headeroutput/config/origindata/TypeData.kt index ad27b8b..8a3ea01 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/config/origindata/TypeData.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/config/origindata/TypeData.kt @@ -1,7 +1,9 @@ package com.liteldev.headeroutput.config.origindata +import com.liteldev.headeroutput.entity.BaseType import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import java.util.* @Serializable data class TypeData( @@ -16,4 +18,42 @@ data class TypeData( val virtual: List?, @SerialName("virtual.unordered") val virtualUnordered: MutableList?, @SerialName("vtbl_entry") val vtblEntry: List? -) +) { + + fun collectAllFunction() = listOfNotNull( + privateTypes, + privateStaticTypes, + protectedTypes, + protectedStaticTypes, + publicTypes, + publicStaticTypes, + virtual, + virtualUnordered, + ).flatten() + + fun collectInstanceFunction() = listOfNotNull( + privateTypes, + protectedTypes, + publicTypes, + virtual, + virtualUnordered, + ).flatten() + + fun collectReferencedTypes(): Map { + val typeRegex = Regex("(struct|class|enum)\\s+([a-zA-Z0-9_]+(?:::[a-zA-Z0-9_]+)*)") + return collectAllFunction().flatMap { memberType -> + (memberType.params?.mapNotNull { it.Name } ?: emptyList()) + listOfNotNull(memberType.valType.Name) + }.mapNotNull { name -> + typeRegex.find(name) + ?.let { + it.groupValues[2] to BaseType.TypeKind.valueOf(it.groupValues[1].uppercase(Locale.getDefault())) + } + }.toMap() + } + + companion object { + fun empty(): TypeData { + return TypeData(null, null, null, null, null, null, null, null, null, null, null) + } + } +} diff --git a/src/main/kotlin/com/liteldev/headeroutput/entity/BaseType.kt b/src/main/kotlin/com/liteldev/headeroutput/entity/BaseType.kt index d5cca99..6f6c28f 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/entity/BaseType.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/entity/BaseType.kt @@ -2,94 +2,65 @@ package com.liteldev.headeroutput.entity import com.liteldev.headeroutput.HeaderGenerator.HEADER_SUFFIX import com.liteldev.headeroutput.TypeManager -import com.liteldev.headeroutput.config.origindata.MemberTypeData import com.liteldev.headeroutput.config.origindata.TypeData import com.liteldev.headeroutput.getTopLevelFileType -import com.liteldev.headeroutput.removeTypeSpecifier import java.util.* abstract class BaseType( var name: String, + type: TypeKind, var typeData: TypeData, ) { - lateinit var includeList: MutableSet + var type: TypeKind = type + protected set var outerType: BaseType? = null + val innerTypes: MutableSet = mutableSetOf() val referenceTypes: MutableSet = mutableSetOf() + val includeList: MutableSet = mutableSetOf() val simpleName = name.substringAfterLast("::") val fullEscapeName = name.replace("::", "_") - val fullEscapeNameUpper = fullEscapeName.uppercase(Locale.getDefault()) + val fullUpperEscapeName = fullEscapeName.uppercase(Locale.getDefault()) - open fun getPath(): String { - if (outerType == null) { - return "./$simpleName.$HEADER_SUFFIX" - } - return "./" + getTopLevelFileType().name.replace("::", "/") + "." + HEADER_SUFFIX + abstract fun generateTypeDefine(): String + + open fun initIncludeList() {} + + fun getPath(): String { + return "./${getTopLevelFileType().name.replace("::", "/")}.$HEADER_SUFFIX" } fun constructInnerTypeList(outerType: BaseType? = null) { this.outerType = outerType - TypeManager.getAllTypes().filter { - if (!it.name.startsWith(this.name + "::")) - false - else - !it.name.substring(this.name.length + 2).contains("::") - }.forEach { - innerTypes.add(it) - it.constructInnerTypeList(this) - } - } - - fun collectAllReferencedType(): Set { - val retList = mutableSetOf() - retList.addAll(referenceTypes) - innerTypes.forEach { - retList.addAll(it.collectAllReferencedType()) - } - return retList + innerTypes.addAll( + TypeManager.getAllTypes().filter { + it.name.startsWith(this.name + "::") && !it.name.substring(this.name.length + 2).contains("::") + }.onEach { + it.constructInnerTypeList(this) + } + ) } - abstract fun generateTypeDefine(): String - - abstract fun initIncludeList() + private fun collectAllReferencedType(): Set = + referenceTypes + innerTypes.flatMap { it.collectAllReferencedType() } fun generateInnerTypeDefine(): String { - val sb = StringBuilder("\n") - innerTypes.forEach { - sb.appendLine(it.generateTypeDefine()) - } - return if (sb.isNotBlank()) sb.toString() else "" + val generatedTypes = innerTypes.joinToString(separator = "\n") { it.generateTypeDefine() } + return if (generatedTypes.isNotBlank()) "\n$generatedTypes" else "" } fun collectSelfReferencedType() { - val referencedTypeNames = mutableSetOf() - val list = mutableListOf() - typeData.virtual?.let(list::addAll) - typeData.publicTypes?.let(list::addAll) - typeData.publicStaticTypes?.let(list::addAll) - typeData.protectedTypes?.let(list::addAll) - typeData.protectedStaticTypes?.let(list::addAll) - typeData.privateTypes?.let(list::addAll) - typeData.privateStaticTypes?.let(list::addAll) - list.forEach { memberType -> - memberType.params?.forEach { param -> - referencedTypeNames.add(removeTypeSpecifier(param.Name ?: "")) - } - memberType.valType.Name?.let { - referencedTypeNames.add(removeTypeSpecifier(it)) - } - } - referencedTypeNames.forEach { - TypeManager.getType(it)?.let { type -> - referenceTypes.add(type) + typeData.collectReferencedTypes().forEach { (name, kind) -> + if (!TypeManager.hasType(name)) { + TypeManager.createDummyClass(name, kind) } + TypeManager.getType(name)?.let(referenceTypes::add) } } - - companion object { - const val GLOBAL_HEADER_PATH = "llapi/Global.h" + enum class TypeKind { + CLASS, STRUCT, ENUM, UNION, NAMESPACE } } diff --git a/src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt b/src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt index 0f95575..d3e33f4 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt @@ -1,12 +1,11 @@ package com.liteldev.headeroutput.entity -import com.liteldev.headeroutput.config.origindata.MemberTypeData import com.liteldev.headeroutput.config.origindata.TypeData -import com.liteldev.headeroutput.relativePath +import com.liteldev.headeroutput.relativePathTo open class ClassType( name: String, typeData: TypeData, -) : BaseType(name, typeData) { +) : BaseType(name, TypeKind.CLASS, typeData) { val parents = arrayListOf() @@ -38,11 +37,13 @@ open class ClassType( } override fun initIncludeList() { - includeList = referenceTypes.filter { !it.name.startsWith(this.name + "::") } - .map { this.getPath().relativePath(it.getPath()) }.toMutableSet() + // not include self, inner type, and types can forward declare + referenceTypes.filter { !it.name.startsWith(this.name + "::") && it.name.contains("::") } + .map { this.getPath().relativePathTo(it.getPath()) }.let(includeList::addAll) if (parents.isNotEmpty()) { - includeList.addAll(parents.map { this.getPath().relativePath(it.getPath()) }) + includeList.addAll(parents.map { this.getPath().relativePathTo(it.getPath()) }) } + includeList.remove(this.getPath().relativePathTo(this.getPath())) includeList.remove("") } @@ -56,43 +57,44 @@ open class ClassType( return sb.toString() } - open fun genAntiReconstruction(): String { - val public = arrayListOf() - typeData.virtual?.let(public::addAll) - typeData.publicTypes?.let(public::addAll) - typeData.protectedTypes?.let(public::addAll) - typeData.privateTypes?.let(public::addAll) - public.filter { it.isConstructor() || (it.isOperator() && it.name == "operator=") } - .let(public::addAll) - val genOperator = public.find { - it.isOperator() && it.params?.run { - size == 1 && this[0].Name == "class $name const &" - } == true && it.valType.Name == "class $name &" - } == null - val genEmptyParamConstructor = public.find { it.name == simpleName && it.params?.isEmpty() ?: true } == null - val genMoveConstructor = public.find { - it.name == simpleName && it.params?.run { - size == 1 && this[0].Name == "class $name const &" + fun genAntiReconstruction(): String { + val classType = if (this.type == TypeKind.STRUCT) "struct" else "class" + val public = typeData.collectInstanceFunction() + .filter { it.isConstructor() || (it.isOperator() && it.name == "operator=") } + val genOperator = public.none { + it.isOperator() && it.params?.let { params -> + params.size == 1 && params[0].Name == "$classType $name const &" + } == true && it.valType.Name == "$classType $name &" + } + val genEmptyParamConstructor = public.none { it.name == simpleName && it.params?.isEmpty() ?: true } + val genMoveConstructor = public.none { + it.name == simpleName && it.params?.let { params -> + params.size == 1 && params[0].Name == "$classType $name const &" } == true - } == null - val sb = StringBuilder() - if (genOperator || genEmptyParamConstructor || genMoveConstructor) { - sb.appendLine() - sb.appendLine("#ifndef DISABLE_CONSTRUCTOR_PREVENTION_$fullEscapeNameUpper") - sb.appendLine("public:") - if (genOperator) { - sb.appendLine(" $simpleName& operator=($simpleName const &) = delete;") - } - if (genMoveConstructor) { - sb.appendLine(" $simpleName($simpleName const &) = delete;") - } - if (genEmptyParamConstructor) { - sb.appendLine(" $simpleName() = delete;") - } - sb.appendLine("#endif") } - sb.appendLine() - return sb.toString() + return if (!genOperator && !genEmptyParamConstructor && !genMoveConstructor) { + "\n" + } else + StringBuilder( + """ + +#ifndef DISABLE_CONSTRUCTOR_PREVENTION_$fullUpperEscapeName +public: + + """.trimIndent() + ).apply { + if (genOperator) { + appendLine(" $simpleName& operator=($simpleName const &) = delete;") + } + if (genMoveConstructor) { + appendLine(" $simpleName($simpleName const &) = delete;") + } + if (genEmptyParamConstructor) { + appendLine(" $simpleName() = delete;") + } + appendLine("#endif") + appendLine() + }.toString() } fun genPublic(): String { @@ -106,7 +108,7 @@ open class ClassType( } if (typeData.virtualUnordered?.isNotEmpty() == true) { - sb.appendLine("#ifdef ENABLE_VIRTUAL_FAKESYMBOL_${fullEscapeNameUpper}") + sb.appendLine("#ifdef ENABLE_VIRTUAL_FAKESYMBOL_${fullUpperEscapeName}") typeData.virtualUnordered?.sortedBy { it.name }?.forEach { sb.appendLine( it.genFuncString( @@ -157,9 +159,7 @@ open class ClassType( } fun genPrivate(genFunc: Boolean = true): String { - if ((typeData.privateTypes == null || typeData.privateTypes?.isEmpty() == true) - && (typeData.privateStaticTypes == null || typeData.privateStaticTypes?.isEmpty() == true) - ) { + if ((typeData.privateTypes?.isEmpty() != false) && (typeData.privateStaticTypes?.isEmpty() != false)) { return "" } val sb = StringBuilder() diff --git a/src/main/kotlin/com/liteldev/headeroutput/entity/EnumType.kt b/src/main/kotlin/com/liteldev/headeroutput/entity/EnumType.kt new file mode 100644 index 0000000..bf6bb4a --- /dev/null +++ b/src/main/kotlin/com/liteldev/headeroutput/entity/EnumType.kt @@ -0,0 +1,10 @@ +package com.liteldev.headeroutput.entity + +import com.liteldev.headeroutput.config.origindata.TypeData + +class EnumType(name: String) : BaseType(name, TypeKind.ENUM, TypeData.empty()) { + override fun generateTypeDefine(): String { + return "enum class $simpleName {};\n" + } + +} diff --git a/src/main/kotlin/com/liteldev/headeroutput/entity/NamespaceType.kt b/src/main/kotlin/com/liteldev/headeroutput/entity/NamespaceType.kt index 0c95d49..ca61cb8 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/entity/NamespaceType.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/entity/NamespaceType.kt @@ -1,12 +1,11 @@ package com.liteldev.headeroutput.entity -import com.liteldev.headeroutput.HeaderGenerator.HEADER_SUFFIX import com.liteldev.headeroutput.config.origindata.TypeData -import com.liteldev.headeroutput.relativePath +import com.liteldev.headeroutput.relativePathTo class NamespaceType( name: String, typeData: TypeData -) : BaseType(name, typeData) { +) : BaseType(name, TypeKind.NAMESPACE, typeData) { fun genPublic(): String { val sb = StringBuilder() @@ -25,13 +24,13 @@ class NamespaceType( return sb.toString() } - override fun getPath(): String { - return "./" + name.replace("::", "/") + ".$HEADER_SUFFIX" - } - override fun initIncludeList() { - includeList = referenceTypes.map { this.getPath().relativePath(it.getPath()) }.toMutableSet() - includeList.remove(this.getPath().relativePath(this.getPath())) + referenceTypes + // not include types can forward declare + .filter { it.name.contains("::") } + .map { this.getPath().relativePathTo(it.getPath()) } + .let(includeList::addAll) + includeList.remove(this.getPath().relativePathTo(this.getPath())) includeList.remove("") } } diff --git a/src/main/kotlin/com/liteldev/headeroutput/entity/StructType.kt b/src/main/kotlin/com/liteldev/headeroutput/entity/StructType.kt index f7bf294..6c00360 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/entity/StructType.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/entity/StructType.kt @@ -1,45 +1,13 @@ package com.liteldev.headeroutput.entity -import com.liteldev.headeroutput.config.origindata.MemberTypeData import com.liteldev.headeroutput.config.origindata.TypeData class StructType( name: String, typeData: TypeData, ) : ClassType(name, typeData) { - override fun genAntiReconstruction(): String { - val public = arrayListOf() - typeData.publicTypes?.filter { it.isConstructor() || (it.isOperator() && it.name == "operator=") } - ?.let(public::addAll) - val genOperator = public.find { - it.isOperator() && it.params?.run { - size == 1 && this[0].Name == "struct $name const &" - } == true && it.valType.Name == "struct $name &" - } == null - val genEmptyParamConstructor = public.find { it.name == simpleName && it.params?.isEmpty() ?: true } == null - val genMoveConstructor = public.find { - it.name == simpleName && it.params?.run { - size == 1 && this[0].Name == "struct $name const &" - } == true - } == null - val sb = StringBuilder() - if (genOperator || genEmptyParamConstructor || genMoveConstructor) { - sb.appendLine() - sb.appendLine("#ifndef DISABLE_CONSTRUCTOR_PREVENTION_${fullEscapeNameUpper}") - sb.appendLine("public:") - if (genOperator) { - sb.appendLine(" $simpleName& operator=($simpleName const &) = delete;") - } - if (genMoveConstructor) { - sb.appendLine(" $simpleName($simpleName const &) = delete;") - } - if (genEmptyParamConstructor) { - sb.appendLine(" $simpleName() = delete;") - } - sb.appendLine("#endif") - } - sb.appendLine() - return sb.toString() + init { + type = TypeKind.STRUCT } override fun generateTypeDefine(): String { diff --git a/src/main/kotlin/com/liteldev/headeroutput/utils.kt b/src/main/kotlin/com/liteldev/headeroutput/utils.kt index c2cf10f..3de52f6 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/utils.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/utils.kt @@ -1,23 +1,21 @@ package com.liteldev.headeroutput -import com.liteldev.headeroutput.config.origindata.TypeData import com.liteldev.headeroutput.entity.BaseType -import com.liteldev.headeroutput.entity.ClassType -import com.liteldev.headeroutput.entity.NamespaceType -import com.liteldev.headeroutput.entity.StructType import java.nio.file.Paths -fun String.relativePath(path: String): String { - // fixme: 修复路径前多余的 `../` - return Paths.get(this).relativize(Paths.get(path)).toString().replace("\\", "/").removePrefix("../") +fun String.relativePathTo(path: String): String { + return Paths.get(this.substringBeforeLast("/")).relativize(Paths.get(path)).toString().replace("\\", "/") } fun StringBuilder.appendSpace(count: Int): StringBuilder = append(" ".repeat(count)) -fun BaseType.isClass(): Boolean = this is ClassType && !this.isStruct() +fun BaseType.isClass(): Boolean = this.type == BaseType.TypeKind.CLASS -fun BaseType.isStruct() = this is StructType -fun BaseType.isNamespace() = this is NamespaceType +fun BaseType.isStruct() = this.type == BaseType.TypeKind.STRUCT + +fun BaseType.isNamespace() = this.type == BaseType.TypeKind.NAMESPACE + +fun BaseType.isEnum() = this.type == BaseType.TypeKind.ENUM fun BaseType.getTopLevelFileType(): BaseType { outerType ?: return this @@ -36,42 +34,3 @@ fun BaseType.getTopLevelFileType(): BaseType { } return outer } - -fun isNameSpace(typeName: String, typeData: TypeData): Boolean { - if (typeData.privateTypes != null - || typeData.privateStaticTypes != null - || typeData.protectedTypes != null - || typeData.protectedStaticTypes != null - || typeData.publicStaticTypes != null - || typeData.virtual != null - || typeData.vtblEntry != null - ) { - return false - } - val isClassOrStruct = isStruct(typeName) || isClass(typeName) - return (typeData.publicTypes?.find { it.isPtrCall() } == null && !isClassOrStruct) -} - -fun isStruct(typeName: String): Boolean { - return TypeManager.structNameList.contains(typeName) -} - -fun isClass(typeName: String): Boolean { - return TypeManager.classNameList.contains(typeName) -} - -fun removeTypeSpecifier(type: String): String { - var result = type.replace("*", "").replace("&", "").trim() - while (result.contains(" ")) { - result = when { - result.startsWith("const ") -> result.substring(6).trim() - result.endsWith(" const") -> result.substring(0, result.length - 6).trim() - - result.startsWith("class ") -> result.substring(6).trim() - result.startsWith("struct ") -> result.substring(7).trim() - - else -> break - } - } - return result -} diff --git a/src/test/kotlin/TestClearSpecifier.kt b/src/test/kotlin/TestClearSpecifier.kt deleted file mode 100644 index 53aff59..0000000 --- a/src/test/kotlin/TestClearSpecifier.kt +++ /dev/null @@ -1,6 +0,0 @@ -import com.liteldev.headeroutput.removeTypeSpecifier - -fun main() { - removeTypeSpecifier("const Mojang::Sima && const *").let(::println) - removeTypeSpecifier("class WeakEntityRef const &").let(::println) -} From 4523c8ae83e4fb51e186723937ba46a453c636dd Mon Sep 17 00:00:00 2001 From: RimuruChan Date: Tue, 18 Jul 2023 20:36:42 +0800 Subject: [PATCH 7/9] feat: add more stl header pre-include --- .../liteldev/headeroutput/HeaderGenerator.kt | 54 +++++++++++++++---- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/src/main/kotlin/com/liteldev/headeroutput/HeaderGenerator.kt b/src/main/kotlin/com/liteldev/headeroutput/HeaderGenerator.kt index 2d85fa9..dee20cd 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/HeaderGenerator.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/HeaderGenerator.kt @@ -82,17 +82,49 @@ ${type.includeList.sorted().joinToString("\n") { "#include \"$it\"" }} #define MCAPI __declspec(dllimport) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include // STL general algorithms +#include // STL array container +#include // STL bitset container +#include // Character handling functions +#include // C Error codes +#include // C localization library +#include // Common mathematics functions +#include // C++11 Time library +#include // Complex number type +#include // C Standard Input/Output library +#include // General purpose utilities: program control, dynamic memory allocation, random numbers, sort and search +#include // C string handling +#include // C Time library +#include // Wide character type +#include // Wide character classification +#include // STL double ended queue container +#include // Exception handling classes +#include // STL forward list container +#include // File stream classes +#include // STL Function objects +#include // Input/Output manipulators +#include // Base input/output stream classes +#include // Input/Output forward declarations +#include // Standard Input/Output stream objects +#include // Basic input stream classes +#include // Numeric limits +#include // STL linear list container +#include // STL map container +#include // STL unique_ptr, shared_ptr, weak_ptr +#include // STL optional type +#include // Basic output stream classes +#include // STL queue and priority_queue container +#include // STL set and multiset container +#include // String stream classes +#include // STL stack container +#include // Standard exception objects +#include // Stream buffer classes +#include // String class +#include // STL string_view type +#include // STL unordered map container +#include // STL unordered set container +#include // STL utility components +#include // STL dynamic array container """.trimIndent() ) From 065a2df792b1696069d31de91d7edcb72c19e8a1 Mon Sep 17 00:00:00 2001 From: RimuruChan Date: Wed, 19 Jul 2023 17:56:08 +0800 Subject: [PATCH 8/9] fix: collect all reference instead of self only --- config.json | 2 +- .../headeroutput/config/origindata/TypeData.kt | 14 +++++++------- .../com/liteldev/headeroutput/entity/BaseType.kt | 2 +- .../com/liteldev/headeroutput/entity/ClassType.kt | 2 +- .../liteldev/headeroutput/entity/NamespaceType.kt | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/config.json b/config.json index f0a173b..5b993f6 100644 --- a/config.json +++ b/config.json @@ -6,7 +6,7 @@ "^struct .*$", "^class .*$", ".*(@|\\s|<).*", - "(std|gsl|mce|glm|entt|rapidjson|type_info|leveldb)" + "^(?:std|gsl|mce|glm|entt|rapidjson|type_info|leveldb|asio)" ] }, "inclusion": { diff --git a/src/main/kotlin/com/liteldev/headeroutput/config/origindata/TypeData.kt b/src/main/kotlin/com/liteldev/headeroutput/config/origindata/TypeData.kt index 8a3ea01..a7b1a14 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/config/origindata/TypeData.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/config/origindata/TypeData.kt @@ -39,19 +39,19 @@ data class TypeData( virtualUnordered, ).flatten() + private fun matchTypes(name: String) = typeMatchRegex.findAll(name) + .map { it.groupValues[2] to BaseType.TypeKind.valueOf(it.groupValues[1].uppercase(Locale.getDefault())) } + + fun collectReferencedTypes(): Map { - val typeRegex = Regex("(struct|class|enum)\\s+([a-zA-Z0-9_]+(?:::[a-zA-Z0-9_]+)*)") return collectAllFunction().flatMap { memberType -> (memberType.params?.mapNotNull { it.Name } ?: emptyList()) + listOfNotNull(memberType.valType.Name) - }.mapNotNull { name -> - typeRegex.find(name) - ?.let { - it.groupValues[2] to BaseType.TypeKind.valueOf(it.groupValues[1].uppercase(Locale.getDefault())) - } - }.toMap() + }.flatMap(::matchTypes).toMap() } companion object { + val typeMatchRegex = Regex("(struct|class|enum)\\s+([a-zA-Z0-9_]+(?:::[a-zA-Z0-9_]+)*)") + fun empty(): TypeData { return TypeData(null, null, null, null, null, null, null, null, null, null, null) } diff --git a/src/main/kotlin/com/liteldev/headeroutput/entity/BaseType.kt b/src/main/kotlin/com/liteldev/headeroutput/entity/BaseType.kt index 6f6c28f..338fbef 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/entity/BaseType.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/entity/BaseType.kt @@ -43,7 +43,7 @@ abstract class BaseType( ) } - private fun collectAllReferencedType(): Set = + protected fun collectAllReferencedType(): Set = referenceTypes + innerTypes.flatMap { it.collectAllReferencedType() } fun generateInnerTypeDefine(): String { diff --git a/src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt b/src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt index d3e33f4..86408bc 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt @@ -38,7 +38,7 @@ open class ClassType( override fun initIncludeList() { // not include self, inner type, and types can forward declare - referenceTypes.filter { !it.name.startsWith(this.name + "::") && it.name.contains("::") } + collectAllReferencedType().filter { !it.name.startsWith(this.name + "::") && it.name.contains("::") } .map { this.getPath().relativePathTo(it.getPath()) }.let(includeList::addAll) if (parents.isNotEmpty()) { includeList.addAll(parents.map { this.getPath().relativePathTo(it.getPath()) }) diff --git a/src/main/kotlin/com/liteldev/headeroutput/entity/NamespaceType.kt b/src/main/kotlin/com/liteldev/headeroutput/entity/NamespaceType.kt index ca61cb8..00f2c37 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/entity/NamespaceType.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/entity/NamespaceType.kt @@ -25,7 +25,7 @@ class NamespaceType( } override fun initIncludeList() { - referenceTypes + collectAllReferencedType() // not include types can forward declare .filter { it.name.contains("::") } .map { this.getPath().relativePathTo(it.getPath()) } From d3aaf3b6079bef04753c25e9fd8bc0d991ffd588 Mon Sep 17 00:00:00 2001 From: RimuruChan Date: Thu, 20 Jul 2023 14:34:51 +0800 Subject: [PATCH 9/9] fix: consider include template class even in global namespace --- src/main/kotlin/com/liteldev/headeroutput/TypeManager.kt | 5 +++-- .../com/liteldev/headeroutput/config/origindata/TypeData.kt | 2 ++ .../kotlin/com/liteldev/headeroutput/entity/ClassType.kt | 6 ++++-- .../kotlin/com/liteldev/headeroutput/entity/StructType.kt | 4 ++-- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/com/liteldev/headeroutput/TypeManager.kt b/src/main/kotlin/com/liteldev/headeroutput/TypeManager.kt index 846dbca..32eac5b 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/TypeManager.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/TypeManager.kt @@ -12,6 +12,7 @@ object TypeManager { private val typeMap = hashMapOf() val nestingMap = hashMapOf() + val template = hashSetOf() fun addType(fullName: String, type: BaseType) { typeMap[fullName] = type @@ -90,8 +91,8 @@ object TypeManager { } val dummyClass = when (type) { - TypeKind.CLASS -> ClassType(name, TypeData.empty()) - TypeKind.STRUCT -> StructType(name, TypeData.empty()) + TypeKind.CLASS -> ClassType(name, TypeData.empty(), template.contains(name)) + TypeKind.STRUCT -> StructType(name, TypeData.empty(), template.contains(name)) TypeKind.ENUM -> EnumType(name) else -> throw IllegalArgumentException("type $type is not supported") } diff --git a/src/main/kotlin/com/liteldev/headeroutput/config/origindata/TypeData.kt b/src/main/kotlin/com/liteldev/headeroutput/config/origindata/TypeData.kt index a7b1a14..429aa0a 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/config/origindata/TypeData.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/config/origindata/TypeData.kt @@ -1,5 +1,6 @@ package com.liteldev.headeroutput.config.origindata +import com.liteldev.headeroutput.TypeManager import com.liteldev.headeroutput.entity.BaseType import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -41,6 +42,7 @@ data class TypeData( private fun matchTypes(name: String) = typeMatchRegex.findAll(name) .map { it.groupValues[2] to BaseType.TypeKind.valueOf(it.groupValues[1].uppercase(Locale.getDefault())) } + .onEach { (typeName, _) -> if (name.contains("$typeName<")) TypeManager.template.add(typeName) } // detects template class fun collectReferencedTypes(): Map { diff --git a/src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt b/src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt index 86408bc..bbf98ef 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/entity/ClassType.kt @@ -4,7 +4,7 @@ import com.liteldev.headeroutput.config.origindata.TypeData import com.liteldev.headeroutput.relativePathTo open class ClassType( - name: String, typeData: TypeData, + name: String, typeData: TypeData, private val isTemplateClass: Boolean = false, ) : BaseType(name, TypeKind.CLASS, typeData) { val parents = arrayListOf() @@ -38,7 +38,9 @@ open class ClassType( override fun initIncludeList() { // not include self, inner type, and types can forward declare - collectAllReferencedType().filter { !it.name.startsWith(this.name + "::") && it.name.contains("::") } + collectAllReferencedType().filter { + !it.name.startsWith(this.name + "::") && (it.name.contains("::") || (it as? ClassType)?.isTemplateClass == true) + } .map { this.getPath().relativePathTo(it.getPath()) }.let(includeList::addAll) if (parents.isNotEmpty()) { includeList.addAll(parents.map { this.getPath().relativePathTo(it.getPath()) }) diff --git a/src/main/kotlin/com/liteldev/headeroutput/entity/StructType.kt b/src/main/kotlin/com/liteldev/headeroutput/entity/StructType.kt index 6c00360..3260ce0 100644 --- a/src/main/kotlin/com/liteldev/headeroutput/entity/StructType.kt +++ b/src/main/kotlin/com/liteldev/headeroutput/entity/StructType.kt @@ -3,8 +3,8 @@ package com.liteldev.headeroutput.entity import com.liteldev.headeroutput.config.origindata.TypeData class StructType( - name: String, typeData: TypeData, -) : ClassType(name, typeData) { + name: String, typeData: TypeData, isTemplate: Boolean = false +) : ClassType(name, typeData, isTemplate) { init { type = TypeKind.STRUCT