From 0a3ffbe5947c87c314b41d830e2eafc1699e06e6 Mon Sep 17 00:00:00 2001 From: chippmann Date: Sun, 3 Nov 2024 11:51:59 +0300 Subject: [PATCH 1/5] Add support for abstract class registration --- .../filebuilder/ClassRegistrarFileBuilder.kt | 58 +++++++------------ .../entrygenerator/model/RegisteredClass.kt | 10 +--- .../src/main/kotlin/godot/core/KtClass.kt | 3 +- .../kotlin/godot/registration/Registration.kt | 9 ++- 4 files changed, 33 insertions(+), 47 deletions(-) diff --git a/kt/entry-generation/godot-entry-generator/src/main/kotlin/godot/entrygenerator/filebuilder/ClassRegistrarFileBuilder.kt b/kt/entry-generation/godot-entry-generator/src/main/kotlin/godot/entrygenerator/filebuilder/ClassRegistrarFileBuilder.kt index f367f6104f..524004c73b 100644 --- a/kt/entry-generation/godot-entry-generator/src/main/kotlin/godot/entrygenerator/filebuilder/ClassRegistrarFileBuilder.kt +++ b/kt/entry-generation/godot-entry-generator/src/main/kotlin/godot/entrygenerator/filebuilder/ClassRegistrarFileBuilder.kt @@ -45,11 +45,6 @@ class ClassRegistrarFileBuilder( ) } } - .let { classBuilder -> - if (registeredClass.isAbstract) { - classBuilder.addKdoc("Registrar for abstract class. Does not register any members as it's only used for default value providing if any properties with default values are provided in the abstract class. Members of this abstract class are registered by the inheriting registrars") - } else classBuilder - } private val className = ClassName(registeredClass.containingPackage, registeredClass.name) @@ -59,29 +54,24 @@ class ClassRegistrarFileBuilder( .addParameter("registry", ClassName(godotRegistrationPackage, GodotKotlinJvmTypes.classRegistry)) .beginControlFlow("with(registry)") //START: with registry .let { funSpecBuilder -> - if (!registeredClass.isAbstract) { - val superClasses = registeredClass.supertypes.mapNotNull { supertype -> - //Used to implement script inheritance methods, so we remove base types and abstract parents. - val value = if (supertype is RegisteredClass && !supertype.isAbstract) { - "\"${supertype.registeredName}\"" - } else { - null - } - value - }.reduceOrNull { statement, name -> "$statement,$name" } ?: "" - funSpecBuilder.beginControlFlow( - "registerClass<%T>(listOf($superClasses),·%T::class,·${registeredClass.isTool},·%S,·%S,·%S,·%S)·{", - className, - className, - registeredClass.godotBaseClass, - registeredClass.registeredName, - registeredClass.relativeSourcePath, - compilationTimeRelativeRegistrationFilePath, - ) //START: registerClass - } else { - funSpecBuilder - .addComment("Abstract classes don't need to have any members to be registered") - } + val superClasses = registeredClass.supertypes.mapNotNull { supertype -> + //Used to implement script inheritance methods, so we remove base types + val value = if (supertype is RegisteredClass) { + "\"${supertype.registeredName}\"" + } else { + null + } + value + }.reduceOrNull { statement, name -> "$statement,$name" } ?: "" + funSpecBuilder.beginControlFlow( + "registerClass<%T>(listOf($superClasses),·%T::class,·${registeredClass.isAbstract},·${registeredClass.isTool},·%S,·%S,·%S,·%S)·{", + className, + className, + registeredClass.godotBaseClass, + registeredClass.registeredName, + registeredClass.relativeSourcePath, + compilationTimeRelativeRegistrationFilePath, + ) //START: registerClass } @@ -92,18 +82,14 @@ class ClassRegistrarFileBuilder( if (!registeredClass.isAbstract) { ConstructorRegistrationGenerator.generate(registeredClass, className, registerClassControlFlow) - FunctionRegistrationGenerator.generate(registeredClass, className, registerClassControlFlow) - SignalRegistrationGenerator.generate(registeredClass, className, registerClassControlFlow) - PropertyRegistrationGenerator.generate(registeredClass, className, registerClassControlFlow) } + FunctionRegistrationGenerator.generate(registeredClass, className, registerClassControlFlow) + SignalRegistrationGenerator.generate(registeredClass, className, registerClassControlFlow) + PropertyRegistrationGenerator.generate(registeredClass, className, registerClassControlFlow) classRegistrarBuilder.addFunction( registerClassControlFlow - .let { funSpecBuilder -> - if (!registeredClass.isAbstract) { - funSpecBuilder.endControlFlow() //END: registerClass - } else funSpecBuilder - } + .endControlFlow() //END: registerClass .endControlFlow() //END: with registry .build() ) diff --git a/kt/entry-generation/godot-entry-generator/src/main/kotlin/godot/entrygenerator/model/RegisteredClass.kt b/kt/entry-generation/godot-entry-generator/src/main/kotlin/godot/entrygenerator/model/RegisteredClass.kt index 31d4b0931c..5635a257a1 100644 --- a/kt/entry-generation/godot-entry-generator/src/main/kotlin/godot/entrygenerator/model/RegisteredClass.kt +++ b/kt/entry-generation/godot-entry-generator/src/main/kotlin/godot/entrygenerator/model/RegisteredClass.kt @@ -23,11 +23,7 @@ data class RegisteredClass( get() = annotations.getAnnotation() != null internal val godotBaseClass: String - get() = if (isAbstract) { - "" - } else { - supertypes - .first { it.annotations.hasAnnotation() } - .name - } + get() = supertypes + .first { it.annotations.hasAnnotation() } + .name } diff --git a/kt/godot-library/src/main/kotlin/godot/core/KtClass.kt b/kt/godot-library/src/main/kotlin/godot/core/KtClass.kt index 243a2e9ed8..225ef3e336 100644 --- a/kt/godot-library/src/main/kotlin/godot/core/KtClass.kt +++ b/kt/godot-library/src/main/kotlin/godot/core/KtClass.kt @@ -13,7 +13,8 @@ data class KtClass( private val _functions: Map>, private val _notificationFunctions: List Unit>, private val _signalInfos: Map, - val baseGodotClass: String + val baseGodotClass: String, + val isAbstract: Boolean, ) { val registeredSupertypes: Array get() = _registeredSupertypes.toTypedArray() diff --git a/kt/godot-library/src/main/kotlin/godot/registration/Registration.kt b/kt/godot-library/src/main/kotlin/godot/registration/Registration.kt index 7acb047795..9cf7281de0 100644 --- a/kt/godot-library/src/main/kotlin/godot/registration/Registration.kt +++ b/kt/godot-library/src/main/kotlin/godot/registration/Registration.kt @@ -77,7 +77,8 @@ class ClassBuilderDsl( private val relativeSourcePath: String, private val compilationTimeRelativeRegistrationFilePath: String, private val superClasses: List, - private val baseGodotClass: String + private val baseGodotClass: String, + private val isAbstract: Boolean, ) { private val constructors = mutableMapOf>() @@ -1290,7 +1291,8 @@ class ClassBuilderDsl( _functions = functions, _notificationFunctions = notificationFunctions, _signalInfos = signals, - baseGodotClass = baseGodotClass + baseGodotClass = baseGodotClass, + isAbstract = isAbstract, ) } } @@ -1305,6 +1307,7 @@ class ClassRegistry( fun registerClass( superClass: List, kClass: KClass, + isAbstract: Boolean = false, isTool: Boolean = false, baseGodotClass: String, registeredName: String, @@ -1312,7 +1315,7 @@ class ClassRegistry( compilationTimeRelativeRegistrationFilePath: String, cb: ClassBuilderDsl.() -> Unit ) { - val builder = ClassBuilderDsl(registeredName, relativeSourcePath, compilationTimeRelativeRegistrationFilePath, superClass, baseGodotClass) + val builder = ClassBuilderDsl(registeredName, relativeSourcePath, compilationTimeRelativeRegistrationFilePath, superClass, baseGodotClass, isAbstract) builder.cb() TypeManager.registerUserType(registeredName, kClass) registerClass(builder.build()) From 2c11f9e200ba0539996db3cfe0957cf40ee84e3f Mon Sep 17 00:00:00 2001 From: chippmann Date: Sun, 3 Nov 2024 12:07:32 +0300 Subject: [PATCH 2/5] Remove check for abstract classes --- .../src/main/kotlin/godot/registration/Registration.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kt/godot-library/src/main/kotlin/godot/registration/Registration.kt b/kt/godot-library/src/main/kotlin/godot/registration/Registration.kt index 9cf7281de0..a611eec621 100644 --- a/kt/godot-library/src/main/kotlin/godot/registration/Registration.kt +++ b/kt/godot-library/src/main/kotlin/godot/registration/Registration.kt @@ -1275,7 +1275,9 @@ class ClassBuilderDsl( } internal fun build(): KtClass { - check(constructors.isNotEmpty()) { "Please provide at least one constructor." } + if (!isAbstract) { + check(constructors.isNotEmpty()) { "Please provide at least one constructor." } + } // Constraints.MAX_CONSTRUCTOR_ARG_COUNT + 1 because we have no arg constructor. val constructorArray = arrayOfNulls>(Constraints.MAX_CONSTRUCTOR_ARG_COUNT + 1) constructors.forEach { From 779d7b5912a07ca0eaf176c2d5ba953296e4ffa2 Mon Sep 17 00:00:00 2001 From: chippmann Date: Sun, 3 Nov 2024 12:14:13 +0300 Subject: [PATCH 3/5] Remove default constructor check --- .../godot/entrygenerator/EntryGenerator.kt | 2 -- .../checks/DefaultConstructorCheck.kt | 20 ------------------- 2 files changed, 22 deletions(-) delete mode 100644 kt/entry-generation/godot-entry-generator/src/main/kotlin/godot/entrygenerator/checks/DefaultConstructorCheck.kt diff --git a/kt/entry-generation/godot-entry-generator/src/main/kotlin/godot/entrygenerator/EntryGenerator.kt b/kt/entry-generation/godot-entry-generator/src/main/kotlin/godot/entrygenerator/EntryGenerator.kt index cb0faed873..9d7e962534 100644 --- a/kt/entry-generation/godot-entry-generator/src/main/kotlin/godot/entrygenerator/EntryGenerator.kt +++ b/kt/entry-generation/godot-entry-generator/src/main/kotlin/godot/entrygenerator/EntryGenerator.kt @@ -2,7 +2,6 @@ package godot.entrygenerator import godot.entrygenerator.checks.ConstructorArgCountCheck import godot.entrygenerator.checks.ConstructorOverloadingCheck -import godot.entrygenerator.checks.DefaultConstructorCheck import godot.entrygenerator.checks.FunctionArgCountCheck import godot.entrygenerator.checks.LateinitPropertyCheck import godot.entrygenerator.checks.NullablePropertyCheck @@ -106,7 +105,6 @@ object EntryGenerator { sourceFiles: List ): Boolean { return listOf( - DefaultConstructorCheck(logger, sourceFiles).execute(), ConstructorArgCountCheck(logger, sourceFiles).execute(), ConstructorOverloadingCheck(logger, sourceFiles).execute(), diff --git a/kt/entry-generation/godot-entry-generator/src/main/kotlin/godot/entrygenerator/checks/DefaultConstructorCheck.kt b/kt/entry-generation/godot-entry-generator/src/main/kotlin/godot/entrygenerator/checks/DefaultConstructorCheck.kt deleted file mode 100644 index f862368da9..0000000000 --- a/kt/entry-generation/godot-entry-generator/src/main/kotlin/godot/entrygenerator/checks/DefaultConstructorCheck.kt +++ /dev/null @@ -1,20 +0,0 @@ -package godot.entrygenerator.checks - -import godot.entrygenerator.model.SourceFile -import godot.entrygenerator.utils.Logger - -class DefaultConstructorCheck(logger: Logger, sourceFiles: List): BaseCheck(logger, sourceFiles) { - override fun execute(): Boolean { - var hasIssue = false - sourceFiles - .flatMap { it.registeredClasses } - .filter { !it.isAbstract } - .forEach { registeredClass -> - if (registeredClass.constructors.none { it.parameters.isEmpty() }) { - hasIssue = true - logger.error(registeredClass, "RegisteredClass does not have a public default constructor") - } - } - return hasIssue - } -} From c8cb9403ffde890df39f261fcd09dce00ac09a25 Mon Sep 17 00:00:00 2001 From: chippmann Date: Sun, 3 Nov 2024 12:23:51 +0300 Subject: [PATCH 4/5] Remove requirement for constructor presence --- .../src/main/kotlin/godot/registration/Registration.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/kt/godot-library/src/main/kotlin/godot/registration/Registration.kt b/kt/godot-library/src/main/kotlin/godot/registration/Registration.kt index a611eec621..dc8c300b45 100644 --- a/kt/godot-library/src/main/kotlin/godot/registration/Registration.kt +++ b/kt/godot-library/src/main/kotlin/godot/registration/Registration.kt @@ -1275,9 +1275,6 @@ class ClassBuilderDsl( } internal fun build(): KtClass { - if (!isAbstract) { - check(constructors.isNotEmpty()) { "Please provide at least one constructor." } - } // Constraints.MAX_CONSTRUCTOR_ARG_COUNT + 1 because we have no arg constructor. val constructorArray = arrayOfNulls>(Constraints.MAX_CONSTRUCTOR_ARG_COUNT + 1) constructors.forEach { From 7cdbf51d67e72d3ee85c9b970185180c09680ca9 Mon Sep 17 00:00:00 2001 From: chippmann Date: Sun, 3 Nov 2024 12:32:06 +0300 Subject: [PATCH 5/5] Ensure we only process classes and not interfaces --- .../annotation/processor/ext/ksClassDeclarationExt.kt | 3 ++- .../processor/visitor/RegistrationAnnotationVisitor.kt | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/kt/entry-generation/godot-kotlin-symbol-processor/src/main/kotlin/godot/annotation/processor/ext/ksClassDeclarationExt.kt b/kt/entry-generation/godot-kotlin-symbol-processor/src/main/kotlin/godot/annotation/processor/ext/ksClassDeclarationExt.kt index d5754d456d..1e772ae641 100644 --- a/kt/entry-generation/godot-kotlin-symbol-processor/src/main/kotlin/godot/annotation/processor/ext/ksClassDeclarationExt.kt +++ b/kt/entry-generation/godot-kotlin-symbol-processor/src/main/kotlin/godot/annotation/processor/ext/ksClassDeclarationExt.kt @@ -9,6 +9,7 @@ import com.google.devtools.ksp.symbol.ClassKind import com.google.devtools.ksp.symbol.KSClassDeclaration import com.google.devtools.ksp.symbol.KSFunctionDeclaration import com.google.devtools.ksp.symbol.KSPropertyDeclaration +import com.google.devtools.ksp.symbol.Modifier import godot.annotation.GodotApiMember import godot.annotation.Member import godot.annotation.processor.Settings @@ -95,7 +96,7 @@ internal fun KSClassDeclaration.mapToClazz( functions = registeredFunctions, signals = registeredSignals, properties = registeredProperties, - isAbstract = isAbstract(), + isAbstract = this.modifiers.contains(Modifier.ABSTRACT), isFqNameRegistrationEnabled = settings.isFqNameRegistrationEnabled, classNamePrefix = settings.classPrefix, symbolProcessorSource = this diff --git a/kt/entry-generation/godot-kotlin-symbol-processor/src/main/kotlin/godot/annotation/processor/visitor/RegistrationAnnotationVisitor.kt b/kt/entry-generation/godot-kotlin-symbol-processor/src/main/kotlin/godot/annotation/processor/visitor/RegistrationAnnotationVisitor.kt index 250ce840a6..aed980cd36 100644 --- a/kt/entry-generation/godot-kotlin-symbol-processor/src/main/kotlin/godot/annotation/processor/visitor/RegistrationAnnotationVisitor.kt +++ b/kt/entry-generation/godot-kotlin-symbol-processor/src/main/kotlin/godot/annotation/processor/visitor/RegistrationAnnotationVisitor.kt @@ -1,5 +1,6 @@ package godot.annotation.processor.visitor +import com.google.devtools.ksp.symbol.ClassKind import com.google.devtools.ksp.symbol.KSClassDeclaration import com.google.devtools.ksp.symbol.KSFile import com.google.devtools.ksp.symbol.KSVisitorVoid @@ -36,9 +37,11 @@ internal class RegistrationAnnotationVisitor( if (declaration.hasCompilationErrors()) { null } else { - val clazz = declaration.mapToClazz(settings) - if (clazz is RegisteredClass) { - clazz + if (declaration.classKind == ClassKind.CLASS) { + val clazz = declaration.mapToClazz(settings) + if (clazz is RegisteredClass) { + clazz + } else null } else null } }