Skip to content

Commit 979ab47

Browse files
committed
Make required virtual methods abstract
1 parent a222268 commit 979ab47

File tree

150 files changed

+3910
-1659
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

150 files changed

+3910
-1659
lines changed

kt/api-generator/src/main/kotlin/godot/codegen/generation/rule/ApiRule.kt

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ import godot.tools.common.constants.TO_GODOT_NAME_UTIL_FUNCTION
2727

2828
class EnrichedCoreRule : GodotApiRule<ApiTask>() {
2929
override fun apply(task: ApiTask, context: GenerationContext) {
30-
val coreTypes = context.api.builtinClasses.associate { it.name to (it.enums?.toEnriched(GenerationType(it.name)) ?: listOf()) }
30+
val coreTypes = context.api.builtinClasses.associate {
31+
it.name to (it.enums?.toEnriched(GenerationType(it.name)) ?: listOf())
32+
}
3133
val globalEnumList = context.api.globalEnums.toEnriched()
3234
val globalEnumMap = globalEnumList.associateBy { it.identifier }
3335
val nativeStructureMap = mutableMapOf<String, EnrichedNativeStructure>()
@@ -53,8 +55,8 @@ class EnrichedCoreRule : GodotApiRule<ApiTask>() {
5355
class EnrichedClassRule : GodotApiRule<ApiTask>() {
5456
override fun apply(task: ApiTask, context: GenerationContext) {
5557
val classes = context.api.classes
56-
val classList = classes.toEnriched().filter { it.apiType == ApiType.CORE }
57-
val classMap = classList.associateBy { it.identifier }
58+
var classList = classes.toEnriched().filter { it.apiType == ApiType.CORE }
59+
var classMap = classList.associateBy { it.identifier }
5860

5961
classes.forEach {
6062
val enrichedChild = classMap[it.name]
@@ -69,6 +71,14 @@ class EnrichedClassRule : GodotApiRule<ApiTask>() {
6971
classMap[it.type]?.makeSingleton()
7072
}
7173

74+
75+
classList = classList
76+
.filter { clazz ->// Remove class extending singletons
77+
val parent = clazz.parent
78+
parent == null || !parent.isSingleton
79+
}
80+
classMap = classList.associateBy { it.identifier }
81+
7282
context.classMap += classMap
7383
context.classList += classList
7484

kt/api-generator/src/main/kotlin/godot/codegen/generation/rule/ClassRule.kt

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import godot.codegen.generation.task.EnrichedEnumTask
1111
import godot.codegen.generation.task.EnrichedMethodTask
1212
import godot.codegen.generation.task.EnrichedPropertyTask
1313
import godot.codegen.generation.task.SignalTask
14+
import godot.codegen.models.enriched.EnrichedMethod
1415
import godot.codegen.models.traits.addKdoc
1516
import godot.tools.common.constants.GODOT_BASE_TYPE
1617
import godot.tools.common.constants.KT_OBJECT
@@ -31,19 +32,7 @@ class MemberRule : GodotApiRule<EnrichedClassTask>() {
3132
}
3233

3334
for (method in clazz.methods) {
34-
if (context.isNativeStructure(method.type.identifier)) {
35-
continue
36-
}
37-
var shouldGenerate = true
38-
for (argument in method.arguments) {
39-
if (context.isNativeStructure(argument.type.identifier)) {
40-
shouldGenerate = false
41-
break
42-
}
43-
}
44-
if (!shouldGenerate) {
45-
continue
46-
}
35+
if(!canGenerateMethod(context, method)) continue
4736

4837
if (method.isStatic) {
4938
task.enrichedStaticMethods.add(EnrichedMethodTask(method, clazz))
@@ -69,15 +58,42 @@ class MemberRule : GodotApiRule<EnrichedClassTask>() {
6958
if (task.clazz.isSingleton) {
7059
generateSingletonConstructor(context)
7160
} else {
72-
addModifiers(KModifier.OPEN)
73-
generateClassConstructor(task.clazz.isInstantiable, context)
61+
if (clazz.isAbstract) {
62+
addModifiers(KModifier.ABSTRACT)
63+
} else {
64+
addModifiers(KModifier.OPEN)
65+
}
66+
generateClassConstructor(clazz.isInstantiable, context)
7467
}
7568

7669
addKdoc(clazz)
7770
addAnnotation(GODOT_BASE_TYPE)
71+
72+
val parent = task.clazz.parent?: return@configure
73+
for (method in parent.methods.filter { it.isAbstract }) {
74+
if(!canGenerateMethod(context, method)) continue
75+
val overrideMethod = method.override()
76+
task.enrichedMethods.add(EnrichedMethodTask(overrideMethod, clazz))
77+
}
7878
}
7979

80-
private fun TypeSpec.Builder.generateClassConstructor(isInstantiable: Boolean, context: GenerationContext): TypeSpec.Builder {
80+
private fun canGenerateMethod(context: GenerationContext, method: EnrichedMethod): Boolean {
81+
if (context.isNativeStructure(method.type.identifier)) {
82+
return false
83+
}
84+
for (argument in method.arguments) {
85+
if (context.isNativeStructure(argument.type.identifier)) {
86+
return false
87+
}
88+
}
89+
return true
90+
}
91+
92+
private fun TypeSpec.Builder.generateClassConstructor(
93+
isInstantiable: Boolean,
94+
context: GenerationContext
95+
): TypeSpec.Builder {
96+
8197
if (!isInstantiable) {
8298
primaryConstructor(
8399
FunSpec.constructorBuilder()
@@ -116,20 +132,20 @@ class MemberRule : GodotApiRule<EnrichedClassTask>() {
116132
}
117133

118134
class BindingRule : GodotApiRule<EnrichedClassTask>() {
119-
override fun apply(classTask: EnrichedClassTask, context: GenerationContext) = configure(classTask.builder) {
120-
val clazz = classTask.clazz
135+
override fun apply(task: EnrichedClassTask, context: GenerationContext) = configure(task.builder) {
136+
val clazz = task.clazz
121137
clazz.methods
122138
.filter { !it.isVirtual }
123139
.onEach {
124-
classTask.bindings.addProperty(
140+
task.bindings.addProperty(
125141
PropertySpec
126142
.builder(it.voidPtrVariableName, VOID_PTR)
127143
.addModifiers(KModifier.INTERNAL)
128144
.initializer(
129145
"%T.getMethodBindPtr(%S,·%S,·%L)",
130146
TYPE_MANAGER,
131147
clazz.identifier,
132-
it.godotName,
148+
it.originalName,
133149
it.hash
134150
)
135151
.build()

kt/api-generator/src/main/kotlin/godot/codegen/generation/rule/MethodRule.kt

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,11 @@ import godot.tools.common.constants.godotCorePackage
2929

3030
interface BaseMethodeRule {
3131
fun FunSpec.Builder.configureMethod(method: EnrichedMethod, clazz: EnrichedClass, context: GenerationContext) {
32-
33-
addKdoc(method)
3432
// This method already exist in the Kotlin class Any. We have to override it because Godot uses the same name in Object.
3533
if (method.name == "toString") {
3634
addModifiers(KModifier.OVERRIDE)
3735
}
3836

39-
// Godot doesn't override its methods, they are either final or meant to be implemented by script or extension.
40-
if (method.isVirtual) {
41-
addModifiers(KModifier.OPEN)
42-
} else {
43-
addModifiers(KModifier.FINAL)
44-
}
45-
4637
val methodTypeName = method.getCastedType()
4738
val shouldReturn = method.getTypeName() != UNIT
4839
if (shouldReturn) {
@@ -51,15 +42,32 @@ interface BaseMethodeRule {
5142

5243
generateParameters(method, context)
5344

54-
if (!method.isVirtual) {
55-
writeCode(method, clazz)
56-
} else {
45+
// Godot can override virtual methods in child classes, we have to create a dummy final implementation for theM
46+
if (method.isOverride) {
47+
addModifiers(KModifier.OVERRIDE)
5748
addStatement(
5849
"%L·%T(%S)",
5950
"throw",
6051
NotImplementedError::class,
61-
"${method.name} is not implemented for ${clazz.identifier}"
52+
"${clazz.identifier}::${method.name} can't be called from the JVM."
6253
)
54+
addKdoc("Virtual method inherited from base class implemented in non-JVM code. Don't call it.")
55+
} else if (method.isAbstract) {
56+
addModifiers(KModifier.ABSTRACT)
57+
addKdoc(method)
58+
} else if (method.isVirtual) {
59+
addModifiers(KModifier.OPEN)
60+
addStatement(
61+
"%L·%T(%S)",
62+
"throw",
63+
NotImplementedError::class,
64+
"${clazz.identifier}::${method.name} is not implemented."
65+
)
66+
addKdoc(method)
67+
} else {
68+
addModifiers(KModifier.FINAL)
69+
writeCode(method, clazz)
70+
addKdoc(method)
6371
}
6472
}
6573

@@ -215,7 +223,7 @@ class StringOnlyRule : GodotApiRule<EnrichedClassTask>(), BaseMethodeRule {
215223
}
216224

217225
private fun createStringOnlyMethod(method: EnrichedMethod, clazz: EnrichedClass, context: GenerationContext): EnrichedMethodTask? {
218-
if (method.isVirtual) {
226+
if (method.isVirtual || method.isOverride) {
219227
return null
220228
}
221229
if (method.arguments.none { it.type.identifier == GodotTypes.stringName || it.type.identifier == GodotTypes.nodePath }) {

kt/api-generator/src/main/kotlin/godot/codegen/generation/rule/RegistrationRule.kt

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,20 @@ import godot.tools.common.constants.godotCorePackage
1111

1212
class RegistrationRule() : GodotApiRule<ApiTask>() {
1313
override fun apply(task: ApiTask, context: GenerationContext) {
14-
val coreTypes = context.classList
14+
val coreTypes = context
15+
.classList
1516
.filter { it.isCoreClass() }
1617

17-
val apiTypes = context.classList
18+
val apiTypes = context
19+
.classList
1820
.filter { !it.isCoreClass() }
19-
.filter { //Remove class extending singletons
20-
val parent = it.parent
21-
parent == null || parent.isSingleton == false
22-
}
21+
2322

2423
val registrationTask = RegistrationTask()
2524
(coreTypes + apiTypes)
2625
.filter { clazz -> //Remove class extending singletons
2726
val parent = clazz.parent
28-
parent == null || parent.isSingleton == false
27+
parent == null || !parent.isSingleton
2928
}.onEach { clazz ->
3029
registrationTask.addVariantMapping(clazz)
3130
registrationTask.addClassRegistering(clazz)
@@ -35,7 +34,7 @@ class RegistrationRule() : GodotApiRule<ApiTask>() {
3534
task.registrationFiles.add(registrationTask)
3635
}
3736

38-
fun RegistrationTask.addVariantMapping(enrichedClass: EnrichedClass) {
37+
private fun RegistrationTask.addVariantMapping(enrichedClass: EnrichedClass) {
3938
variantMapper.addStatement(
4039
"%M[%T::class] = %T",
4140
MemberName(godotCorePackage, "variantMapper"),
@@ -44,24 +43,41 @@ class RegistrationRule() : GodotApiRule<ApiTask>() {
4443
)
4544
}
4645

47-
fun RegistrationTask.addClassRegistering(clazz: EnrichedClass) {
46+
private fun RegistrationTask.addClassRegistering(clazz: EnrichedClass) {
4847
val formatString: String
4948
if (clazz.isSingleton) {
5049
engineTypes.addStatement("%T.registerSingleton(%S)·{·%T·}", TYPE_MANAGER, clazz.identifier, clazz.className)
5150
formatString = "%T.registerEngineType(%S,·%T::class)·{·%T·}"
51+
engineTypes.addStatement(
52+
formatString,
53+
TYPE_MANAGER,
54+
clazz.identifier,
55+
clazz.className,
56+
clazz.className
57+
)
5258
} else {
53-
formatString = "%T.registerEngineType(%S,·%T::class,·::%T)"
59+
if(clazz.isAbstract) {
60+
formatString = "%T.registerEngineType(%S,·%T::class,·null)"
61+
engineTypes.addStatement(
62+
formatString,
63+
TYPE_MANAGER,
64+
clazz.identifier,
65+
clazz.className,
66+
)
67+
} else {
68+
formatString = "%T.registerEngineType(%S,·%T::class,·::%T)"
69+
engineTypes.addStatement(
70+
formatString,
71+
TYPE_MANAGER,
72+
clazz.identifier,
73+
clazz.className,
74+
clazz.className
75+
)
76+
}
5477
}
55-
engineTypes.addStatement(
56-
formatString,
57-
TYPE_MANAGER,
58-
clazz.identifier,
59-
clazz.className,
60-
clazz.className
61-
)
6278
}
6379

64-
fun RegistrationTask.addMethodBindings(clazz: EnrichedClass) {
80+
private fun RegistrationTask.addMethodBindings(clazz: EnrichedClass) {
6581
engineMethods.addStatement(
6682
"%T",
6783
clazz.className.nestedClass(methodBindingsInnerClassName)

kt/api-generator/src/main/kotlin/godot/codegen/models/enriched/EnrichedClass.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ class EnrichedClass(model: Class) : TypeGenerationTrait, DocumentedGenerationTra
2727
val properties = model.properties?.toEnriched() ?: listOf()
2828
val methods = model.methods?.toEnriched() ?: listOf()
2929

30+
val isAbstract = methods.any { it.isAbstract }
31+
3032
override var description = model.description
3133
val additionalImports = mutableSetOf<ClassName>()
3234

kt/api-generator/src/main/kotlin/godot/codegen/models/enriched/EnrichedMethod.kt

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,15 @@ import godot.codegen.workarounds.sanitizeApiType
1010
import godot.common.constants.Constraints
1111
import godot.common.extensions.convertToCamelCase
1212

13-
class EnrichedMethod(model: Method) : CallableGeneratorTrait, DocumentedGenerationTrait {
13+
class EnrichedMethod(private val model: Method, override: Boolean = false) : CallableGeneratorTrait, DocumentedGenerationTrait {
14+
enum class Modifier {
15+
DEFAULT,
16+
STATIC,
17+
VIRTUAL,
18+
ABSTRACT,
19+
OVERRIDE
20+
}
21+
1422
override val type = GenerationType(model.returnValue?.type?.sanitizeApiType() ?: "void")
1523
override val nullable = type.isObjectSubClass() || type.isVariant()
1624
override val genericParameters = emptyList<ClassName>()
@@ -29,21 +37,42 @@ class EnrichedMethod(model: Method) : CallableGeneratorTrait, DocumentedGenerati
2937
override var description = model.description
3038

3139
val hash = model.hash
32-
val isVirtual = model.isVirtual
33-
val isStatic = model.isStatic
34-
val godotName = model.name
40+
private val modifier = run {
41+
if (override) {
42+
Modifier.OVERRIDE
43+
} else if (model.isVirtual) {
44+
if(model.isRequired) {
45+
Modifier.ABSTRACT
46+
} else {
47+
Modifier.VIRTUAL
48+
}
49+
50+
} else if (model.isStatic) {
51+
Modifier.STATIC
3552

53+
} else {
54+
Modifier.DEFAULT
55+
}
56+
}
57+
val isVirtual: Boolean
58+
get() = modifier == Modifier.VIRTUAL || modifier == Modifier.ABSTRACT
59+
val isAbstract: Boolean
60+
get() = modifier == Modifier.ABSTRACT
61+
val isStatic: Boolean
62+
get() = modifier == Modifier.STATIC
63+
val isOverride: Boolean
64+
get() = modifier == Modifier.OVERRIDE
65+
66+
val originalName = model.name
3667
init {
3768
if (arguments.size > Constraints.MAX_FUNCTION_ARG_COUNT) {
3869
throw TooManyMethodArgument(model)
3970
}
4071
}
72+
73+
fun override(): EnrichedMethod {
74+
return EnrichedMethod(model, true)
75+
}
4176
}
4277

4378
fun List<Method>.toEnriched() = map { EnrichedMethod(it) }
44-
45-
fun EnrichedMethod.isSameSignature(other: EnrichedMethod): Boolean {
46-
return name == other.name &&
47-
type == other.type
48-
arguments == other.arguments
49-
}

0 commit comments

Comments
 (0)