Skip to content

Commit 9e77fb8

Browse files
committed
Add MethodCallable.kt
1 parent 0a936fc commit 9e77fb8

File tree

27 files changed

+945
-2236
lines changed

27 files changed

+945
-2236
lines changed

kt/api-generator/src/main/kotlin/godot/codegen/generationEntry.kt

+5-7
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import godot.codegen.models.ApiDescription
66
import godot.codegen.services.IApiGenerationService
77
import godot.codegen.services.impl.ApiGenerationService
88
import godot.codegen.services.impl.AwaitGenerationService
9-
import godot.codegen.services.impl.LambdaCallableGenerationService
9+
import godot.codegen.services.impl.ConnectorGenerationService
10+
import godot.codegen.services.impl.CallableGenerationService
1011
import godot.codegen.services.impl.SignalGenerationService
1112
import java.io.File
1213

@@ -15,12 +16,9 @@ fun generateApiFrom(jsonSource: File, coreDir: File, apiDir: File) {
1516
val generationService: IApiGenerationService = ApiGenerationService(apiDescription)
1617
generationService.generateApi(coreDir, apiDir)
1718

18-
LambdaCallableGenerationService.generate(coreDir)
19-
SignalGenerationService.generateCore(coreDir)
20-
}
21-
22-
fun generateExtension(outputDir: File) {
23-
SignalGenerationService.generateExtension(outputDir)
19+
SignalGenerationService.generate(coreDir)
20+
CallableGenerationService.generate(coreDir)
21+
ConnectorGenerationService.generate(coreDir)
2422
}
2523

2624
fun generateCoroutine(outputDir: File) {

kt/api-generator/src/main/kotlin/godot/codegen/poet/GenericClassNameInfo.kt

+122-14
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,35 @@ import com.squareup.kotlinpoet.KModifier
77
import com.squareup.kotlinpoet.LambdaTypeName
88
import com.squareup.kotlinpoet.ParameterSpec
99
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
10+
import com.squareup.kotlinpoet.TypeName
1011
import com.squareup.kotlinpoet.TypeSpec
1112
import com.squareup.kotlinpoet.TypeVariableName
1213

13-
// Helper that provide utilities to handle generic classes.
14+
/**
15+
* Provides utilities to handle generic classes and generate KotlinPoet builders for them.
16+
*
17+
* @property className The base [ClassName] representing the class.
18+
* @param numberOfGenericParameters The number of generic type parameters for the class.
19+
*/
1420
class GenericClassNameInfo(
1521
val className: ClassName,
1622
numberOfGenericParameters: Int
1723
) {
24+
/**
25+
* Return list of generic argument type names.
26+
*
27+
* Example: genericTypes = [P0, P1]
28+
*/
1829
val genericTypes = Array(numberOfGenericParameters) {
1930
TypeVariableName("P$it")
2031
}.toList()
32+
33+
34+
/**
35+
* Parameterized [ClassName] with generic type variables.
36+
*
37+
* Example: MyClass<P0, P1>
38+
*/
2139
val genericClassName = className.run {
2240
if (genericTypes.isNotEmpty()) {
2341
parameterizedBy(genericTypes)
@@ -26,7 +44,18 @@ class GenericClassNameInfo(
2644
}
2745
}
2846

47+
/**
48+
* List of reified type variables for inline functions.
49+
*
50+
* Example: [reified P0, reified P1]
51+
*/
2952
val reifiedTypes = genericTypes.map { it.copy(reified = true) }
53+
54+
/**
55+
* Parameterized [ClassName] with reified generic type variables.
56+
*
57+
* Example: MyClass<reified P0, reified P1>
58+
*/
3059
val reifiedClassName = className.run {
3160
if (reifiedTypes.isNotEmpty()) {
3261
parameterizedBy(reifiedTypes)
@@ -35,6 +64,11 @@ class GenericClassNameInfo(
3564
}
3665
}
3766

67+
/**
68+
* Parameterized [ClassName] with generic parameters erased to [ANY].
69+
*
70+
* Example: MyClass<Any, Any>
71+
*/
3872
val erasedGenericClassName = className.run {
3973
if (genericTypes.isNotEmpty()) {
4074
parameterizedBy(genericTypes.map { ANY })
@@ -43,28 +77,97 @@ class GenericClassNameInfo(
4377
}
4478
}
4579

46-
fun toTypeSpecBuilder() = TypeSpec
47-
.classBuilder(className)
48-
.addTypeVariables(genericTypes)
80+
/**
81+
* Combine optional [prefix], core [genericTypes], and optional [suffix] into one list.
82+
*/
83+
private fun allTypeVariables(
84+
prefix: List<TypeVariableName> = emptyList(),
85+
suffix: List<TypeVariableName> = emptyList()
86+
) = prefix + genericTypes + suffix
87+
88+
/**
89+
* Builds a [TypeSpec.Builder] for the class with type variables.
90+
*
91+
* Example: toTypeSpecBuilder(prefix, suffix) -> TypeSpec.classBuilder("MyClass").addTypeVariables(prefix + [P0,P1] + suffix)
92+
*/
93+
fun toTypeSpecBuilder(
94+
prefix: List<TypeVariableName> = emptyList(),
95+
suffix: List<TypeVariableName> = emptyList()
96+
) = TypeSpec.classBuilder(className.simpleName)
97+
.addTypeVariables(allTypeVariables(prefix, suffix))
98+
99+
/**
100+
* Builds a [FunSpec.Builder] with generic type variables.
101+
*
102+
* Example: builder("foo", prefix, suffix) -> builder("foo").addTypeVariables(prefix + [P0,P1] + suffix)
103+
*/
104+
fun toFunSpecBuilder(
105+
name: String,
106+
prefix: List<TypeVariableName> = emptyList(),
107+
suffix: List<TypeVariableName> = emptyList()
108+
) = FunSpec.builder(name)
109+
.addTypeVariables(allTypeVariables(prefix, suffix))
49110

50-
fun toFunSpecBuilder(name: String) = FunSpec
51-
.builder(name)
52-
.addTypeVariables(genericTypes)
53111

54-
fun toExtensionFunSpecBuilder(name: String) = toFunSpecBuilder(name).receiver(genericClassName)
55112

56-
fun toReifiedFunSpecBuilder(name: String) = FunSpec
57-
.builder(name)
58-
.addTypeVariables(reifiedTypes)
113+
/**
114+
* Builds an extension [FunSpec.Builder] on [genericClassName].
115+
*
116+
* Example: fun <X,P0,P1,Y> MyClass<P0,P1>.foo() {...}
117+
*/
118+
fun toExtensionFunSpecBuilder(
119+
name: String,
120+
prefix: List<TypeVariableName> = emptyList(),
121+
suffix: List<TypeVariableName> = emptyList()
122+
) = toFunSpecBuilder(name, prefix, suffix)
123+
.receiver(genericClassName)
124+
125+
126+
/**
127+
* Begin building a reified inline FunSpec with reified type parameters.
128+
*
129+
* Example: inline fun <X, reified P0, reified P1, Y> foo() {...}
130+
*/
131+
fun toReifiedFunSpecBuilder(
132+
name: String,
133+
prefix: List<TypeVariableName> = emptyList(),
134+
suffix: List<TypeVariableName> = emptyList()
135+
) = FunSpec.builder(name)
59136
.addModifiers(KModifier.INLINE)
137+
.addTypeVariables(prefix + reifiedTypes + suffix)
138+
139+
140+
/**
141+
* Begin building a reified inline extension FunSpec on the class.
142+
*
143+
* Example: inline fun <reified P0, reified P1> MyClass<P0, P1>.foo() {...}
144+
*/
145+
fun toReifiedExtensionFunSpecBuilder(
146+
name: String,
147+
prefix: List<TypeVariableName> = emptyList(),
148+
suffix: List<TypeVariableName> = emptyList()
149+
) = toReifiedFunSpecBuilder(name, prefix, suffix)
150+
.receiver(reifiedClassName)
60151

61-
fun toReifiedExtensionFunSpecBuilder(name: String) = toReifiedFunSpecBuilder(name).receiver(reifiedClassName)
62152

153+
/**
154+
* Generate a list of parameters matching the generic type variables.
155+
*
156+
* Example: [ParameterSpec("p0", P0), ParameterSpec("p1", P1)]
157+
*/
63158
fun toParameterSpecList() = genericTypes
64159
.mapIndexed { index: Int, typeVariableName: TypeVariableName ->
65160
ParameterSpec.builder("p$index", typeVariableName).build()
66161
}
67162

163+
/**
164+
* Build a comma-separated argument string based on a template.
165+
*
166+
* @param template String template where occurrences of `{$indexKey}` will be replaced by index
167+
* @param indexKey placeholder key in template (e.g., "%d")
168+
*
169+
* Example: toArgumentsString("p%{i}", "{i}") -> "p0, p1"
170+
*/
68171
fun toArgumentsString(template: String, indexKey: String): String {
69172
return buildString {
70173
genericTypes.mapIndexed { index: Int, typeVariableName: TypeVariableName ->
@@ -74,8 +177,13 @@ class GenericClassNameInfo(
74177
}
75178
}
76179

77-
fun toLambdaTypeName(returnType: TypeVariableName) = LambdaTypeName.get(
78-
receiver = null,
180+
/**
181+
* Create a LambdaTypeName taking generic types as parameters and returning [returnType].
182+
*
183+
* Example: toLambdaTypeName(R) -> (p0: P0, p1: P1) -> R
184+
*/
185+
fun toLambdaTypeName(returnType: TypeName, receiver: TypeName? = null) = LambdaTypeName.get(
186+
receiver = receiver,
79187
parameters = genericTypes
80188
.mapIndexed { index: Int, typeVariableName: TypeVariableName ->
81189
ParameterSpec.builder("p$index", typeVariableName).build()

kt/api-generator/src/main/kotlin/godot/codegen/services/ILambdaCallableGenerationService.kt renamed to kt/api-generator/src/main/kotlin/godot/codegen/services/ICallableGenerationService.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ package godot.codegen.services
22

33
import java.io.File
44

5-
interface ILambdaCallableGenerationService {
5+
interface ICallableGenerationService {
66
fun generate(outputDir: File)
77
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package godot.codegen.services
2+
3+
import java.io.File
4+
5+
interface IConnectorGenerationService {
6+
fun generate(output: File)
7+
}

kt/api-generator/src/main/kotlin/godot/codegen/services/ISignalGenerationService.kt

+1-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,5 @@ package godot.codegen.services
33
import java.io.File
44

55
interface ISignalGenerationService {
6-
fun generateCore(outputDir: File)
7-
fun generateExtension(outputDir: File)
6+
fun generate(outputDir: File)
87
}
+6-75
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import com.squareup.kotlinpoet.PropertySpec
1616
import com.squareup.kotlinpoet.TypeSpec
1717
import com.squareup.kotlinpoet.TypeVariableName
1818
import godot.codegen.poet.GenericClassNameInfo
19-
import godot.codegen.services.ILambdaCallableGenerationService
19+
import godot.codegen.services.ICallableGenerationService
2020
import godot.common.constants.Constraints
2121
import godot.tools.common.constants.GodotFunctions
2222
import godot.tools.common.constants.GodotKotlinJvmTypes
@@ -26,24 +26,19 @@ import godot.tools.common.constants.godotInteropPackage
2626
import java.io.File
2727

2828

29-
object LambdaCallableGenerationService : ILambdaCallableGenerationService {
29+
object CallableGenerationService : ICallableGenerationService {
3030
private const val FUNCTION_PARAMETER_NAME = "function"
3131
private const val LAMBDA_CALLABLE_NAME = "LambdaCallable"
3232
private const val LAMBDA_CONTAINER_NAME = "LambdaContainer"
33-
private const val CALLABLE_FUNCTION_NAME = "callable"
34-
private const val JAVA_CREATE_METHOD_NAME = "javaCreate"
3533
private const val CONTAINER_ARGUMENT_NAME = "container"
34+
private const val CALLABLE_FUNCTION_NAME = "callable"
3635
private const val VARIANT_TYPE_RETURN_NAME = "returnConverter"
3736
private const val VARIANT_TYPE_ARGUMENT_NAME = "typeConverters"
3837
private val LAMBDA_CALLABLE_CLASS_NAME = ClassName(godotCorePackage, LAMBDA_CALLABLE_NAME)
3938
private val LAMBDA_CONTAINER_CLASS_NAME = ClassName(godotCorePackage, LAMBDA_CONTAINER_NAME)
4039
private val returnTypeParameter = TypeVariableName("R", ANY.copy(nullable = true))
4140
private val variantConverterClassName = ClassName(godotInteropPackage, GodotKotlinJvmTypes.variantConverter)
4241

43-
//Java
44-
private val REFLECTION_CLASS_NAME = ClassName("kotlin.jvm.internal", "Reflection")
45-
private val JAVA_CLASS_CLASS_NAME = ClassName("java.lang", "Class")
46-
4742
override fun generate(outputDir: File) {
4843
val callableFileSpec = FileSpec.builder(godotCorePackage, "LambdaCallables")
4944
val containerFileSpec = FileSpec.builder(godotCorePackage, "LambdaContainers")
@@ -280,9 +275,6 @@ object LambdaCallableGenerationService : ILambdaCallableGenerationService {
280275
++remainingParameters
281276
}
282277

283-
val genericClassNameInfo = GenericClassNameInfo(lambdaCallableClassName, argCount)
284-
lambdaCallableClassBuilder.addType(generateKtCallableCompanion(argCount, genericClassNameInfo))
285-
286278
containerFileSpec.addType(lambdaContainerClassBuilder.build())
287279
callableFileSpec.addType(lambdaCallableClassBuilder.build())
288280

@@ -308,7 +300,7 @@ object LambdaCallableGenerationService : ILambdaCallableGenerationService {
308300
buildString {
309301
append("return·$LAMBDA_CALLABLE_NAME$argCount(")
310302
append("%M.getOrDefault(%T::class,·%T),·arrayOf(")
311-
for (typeParameter in genericParameters) {
303+
genericParameters.forEach { _ ->
312304
append("%M[%T::class]!!,·")
313305
}
314306
append("),·")
@@ -346,6 +338,7 @@ object LambdaCallableGenerationService : ILambdaCallableGenerationService {
346338
AnnotationSpec
347339
.builder(ClassName("kotlin", "Suppress"))
348340
.addMember("\"PackageDirectoryMismatch\", \"UNCHECKED_CAST\"")
341+
.addMember("\"unused\"")
349342
.build()
350343
)
351344
.build()
@@ -356,72 +349,10 @@ object LambdaCallableGenerationService : ILambdaCallableGenerationService {
356349
AnnotationSpec
357350
.builder(ClassName("kotlin", "Suppress"))
358351
.addMember("\"PackageDirectoryMismatch\", \"UNCHECKED_CAST\"")
352+
.addMember("\"unused\"")
359353
.build()
360354
)
361355
.build()
362356
.writeTo(outputDir)
363357
}
364-
365-
// JAVA BRIDGE FUNCTION
366-
private fun generateKtCallableCompanion(argCount: Int, genericClassNameInfo: GenericClassNameInfo): TypeSpec {
367-
val variantMapperMember = MemberName(godotCorePackage, "variantMapper")
368-
369-
return TypeSpec
370-
.companionObjectBuilder()
371-
.addFunction(
372-
FunSpec
373-
.builder(JAVA_CREATE_METHOD_NAME)
374-
.addTypeVariable(returnTypeParameter)
375-
.addTypeVariables(genericClassNameInfo.genericTypes)
376-
.addParameter(
377-
ParameterSpec
378-
.builder("returnClass", JAVA_CLASS_CLASS_NAME.parameterizedBy(returnTypeParameter))
379-
.build()
380-
)
381-
.addParameters(genericClassNameInfo.toParameterSpecList().map {
382-
ParameterSpec
383-
.builder(it.name + "Class", JAVA_CLASS_CLASS_NAME.parameterizedBy(it.type))
384-
.build()
385-
})
386-
.addParameter(
387-
ParameterSpec
388-
.builder(
389-
FUNCTION_PARAMETER_NAME,
390-
genericClassNameInfo.toLambdaTypeName(returnTypeParameter)
391-
)
392-
.build()
393-
)
394-
.addCode(
395-
CodeBlock.of(
396-
buildString {
397-
append("return·$LAMBDA_CALLABLE_NAME$argCount(")
398-
append("%M.getOrDefault(%T.getOrCreateKotlinClass(returnClass),·%T),·arrayOf(")
399-
genericClassNameInfo.toParameterSpecList().forEach {
400-
append("%M[%T.getOrCreateKotlinClass(${it.name}Class)]!!,·")
401-
}
402-
append("),·")
403-
append(FUNCTION_PARAMETER_NAME)
404-
append(')')
405-
},
406-
variantMapperMember,
407-
REFLECTION_CLASS_NAME,
408-
VARIANT_PARSER_NIL,
409-
*genericClassNameInfo.genericTypes
410-
.flatMap {
411-
listOf(variantMapperMember, REFLECTION_CLASS_NAME)
412-
}
413-
.toTypedArray()
414-
)
415-
)
416-
.addAnnotation(JvmStatic::class)
417-
.addAnnotation(
418-
AnnotationSpec
419-
.builder(JvmName::class)
420-
.addMember("\"create\"")
421-
.build()
422-
)
423-
.build()
424-
)
425-
.build()
426-
}
427358
}

0 commit comments

Comments
 (0)