diff --git a/fxgl-core/src/main/kotlin/com/almasb/fxgl/core/reflect/ReflectionFunctionCaller.kt b/fxgl-core/src/main/kotlin/com/almasb/fxgl/core/reflect/ReflectionFunctionCaller.kt index 76d21b6f1d..6b8ba336be 100644 --- a/fxgl-core/src/main/kotlin/com/almasb/fxgl/core/reflect/ReflectionFunctionCaller.kt +++ b/fxgl-core/src/main/kotlin/com/almasb/fxgl/core/reflect/ReflectionFunctionCaller.kt @@ -104,6 +104,26 @@ class ReflectionFunctionCaller { return defaultFunctionHandler.apply(functionName, args.toList()) } + fun invoke(functionName: String, args: List): Any { + return invoke(functionName, args.toTypedArray()) + } + + /** + * An equivalent of [call], but allows any type of arguments, not just String. + * + * @return 0 for void type functions, otherwise return value from the invoked function + */ + fun invoke(functionName: String, args: Array): Any { + val function = functions[FunctionSignature(functionName, args.size)] + + if (function != null) { + // void returns null, but Any is expected, so we return 0 in such cases + return function.method.invoke(function.functionCallTarget, *args) ?: 0 + } + + return defaultFunctionHandler.apply(functionName, args.toList().map { it.toString() }) + } + private data class FunctionSignature(val name: String, val paramCount: Int) /** diff --git a/fxgl-core/src/test/kotlin/com/almasb/fxgl/core/reflect/ReflectionUtilsTest.kt b/fxgl-core/src/test/kotlin/com/almasb/fxgl/core/reflect/ReflectionUtilsTest.kt index 089c0bcde5..a32ea8eb8d 100644 --- a/fxgl-core/src/test/kotlin/com/almasb/fxgl/core/reflect/ReflectionUtilsTest.kt +++ b/fxgl-core/src/test/kotlin/com/almasb/fxgl/core/reflect/ReflectionUtilsTest.kt @@ -224,6 +224,42 @@ class ReflectionUtilsTest { } } + @Test + fun `ReflectionFunctionCaller can invoke functions with non-String arguments`() { + val value = 335 + val data = TestClass2("world") + + val obj = TestClass3() + val rfc = ReflectionFunctionCaller().also { it.addFunctionCallTarget(obj) } + + val result = rfc.invoke("someFunction", arrayOf(value, data)) + + assertThat(result, `is`("335world")) + } + + @Test + fun `ReflectionFunctionCaller fails if incorrect types of arguments`() { + val value = 335 + val data = TestClass2("world") + + val obj = TestClass3() + val rfc = ReflectionFunctionCaller().also { it.addFunctionCallTarget(obj) } + + assertThrows { + rfc.invoke("someFunction", arrayOf(data, value)) + } + } + + @Test + fun `ReflectionFunctionCaller fails if incorrect number of arguments`() { + val obj = TestClass3() + val rfc = ReflectionFunctionCaller().also { it.addFunctionCallTarget(obj) } + + assertThrows { + rfc.invoke("someFunction", arrayOf()) + } + } + @Retention(AnnotationRetention.RUNTIME) annotation class Ann @@ -256,4 +292,10 @@ class ReflectionUtilsTest { } class TestClass2(val s: String) : TestClass1() + + class TestClass3 { + fun someFunction(value: Int, data: TestClass2): String { + return "" + value + data.s + } + } } \ No newline at end of file