Skip to content

Commit

Permalink
Test Foreign API
Browse files Browse the repository at this point in the history
  • Loading branch information
CedNaru committed Oct 26, 2024
1 parent ac2a1bd commit 8649e80
Show file tree
Hide file tree
Showing 31 changed files with 135 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ supertypes = [
godot.Node,
godot.Object,
godot.core.KtObject,
godot.common.interop.IdentityPointer,
godot.common.interop.ValuePointer,
kotlin.Any
]
signals = [
Expand Down
2 changes: 2 additions & 0 deletions harness/bunnymark/benchmarks/BunnymarkV3/gdj/Bunny.gdj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ supertypes = [
godot.Node,
godot.Object,
godot.core.KtObject,
godot.common.interop.IdentityPointer,
godot.common.interop.ValuePointer,
kotlin.Any
]
signals = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ supertypes = [
godot.Node,
godot.Object,
godot.core.KtObject,
godot.common.interop.IdentityPointer,
godot.common.interop.ValuePointer,
kotlin.Any
]
signals = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ supertypes = [
godot.Node,
godot.Object,
godot.core.KtObject,
godot.common.interop.IdentityPointer,
godot.common.interop.ValuePointer,
kotlin.Any
]
signals = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ supertypes = [
godot.Node,
godot.Object,
godot.core.KtObject,
godot.common.interop.IdentityPointer,
godot.common.interop.ValuePointer,
kotlin.Any
]
signals = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import godot.annotation.RegisterClass
import godot.annotation.RegisterFunction
import godot.annotation.RegisterSignal
import godot.core.Vector2
import godot.signals.signal
import godot.core.signal1

@RegisterClass("BunnymarkV1DrawTexture")
class BunnymarkV1DrawTexture : Node2D() {

@RegisterSignal
val benchmarkFinished by signal<Int>("bunnyCount")
val benchmarkFinished by signal1<Int>("bunnyCount")

data class Bunny(var position: Vector2, var speed: Vector2)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ import godot.*
import godot.annotation.RegisterClass
import godot.annotation.RegisterFunction
import godot.annotation.RegisterSignal
import godot.signals.signal

@RegisterClass("BunnymarkV1Sprites")
class BunnymarkV1Sprites : Node2D() {

@RegisterSignal
val benchmarkFinished by signal<Int>("bunnyCount")
val benchmarkFinished by signal1<Int>("bunnyCount")

private data class Bunny(var sprite: Sprite2D, var speed: Vector2)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ import godot.*
import godot.annotation.RegisterClass
import godot.annotation.RegisterFunction
import godot.annotation.RegisterSignal
import godot.signals.signal

@RegisterClass("BunnymarkV2")
class BunnymarkV2 : Node2D() {

@RegisterSignal
val benchmarkFinished by signal<Int>("bunnyCount")
val benchmarkFinished by signal1<Int>("bunnyCount")

private val gravity = 500
private val bunnySpeeds = mutableListOf<Vector2>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import godot.annotation.RegisterFunction
import godot.annotation.RegisterSignal
import godot.benchmark.bunnymark.v3.Bunny
import godot.core.Vector2
import godot.signals.signal
import godot.core.signal1

@RegisterClass("BunnymarkV3")
class BunnymarkV3 : Node2D() {

@RegisterSignal
val benchmarkFinished by signal<Int>("bunnyCount")
val benchmarkFinished by signal1<Int>("bunnyCount")

private val randomNumberGenerator = RandomNumberGenerator()
private val bunnyTexture = ResourceLoader.load("res://images/godot_bunny.png") as Texture2D
Expand Down
2 changes: 1 addition & 1 deletion kt/api-generator/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ plugins {
}

kotlin {
jvmToolchain(11)
jvmToolchain(22)
}

gradlePlugin {
Expand Down
2 changes: 1 addition & 1 deletion kt/build-logic/convention/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ plugins {
}

kotlin {
jvmToolchain(11)
jvmToolchain(22)
}

dependencies {
Expand Down
2 changes: 1 addition & 1 deletion kt/common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ version = fullGodotKotlinJvmVersion
group = "com.utopia-rise"

kotlin {
jvmToolchain(11)
jvmToolchain(22)
}

publishing {
Expand Down
2 changes: 1 addition & 1 deletion kt/entry-generation/godot-entry-generator/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ plugins {
}

kotlin {
jvmToolchain(11)
jvmToolchain(22)
}

dependencies {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ plugins {
}

kotlin {
jvmToolchain(11)
jvmToolchain(22)
}

dependencies {
Expand Down
2 changes: 1 addition & 1 deletion kt/godot-coroutine-library/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ kotlinDefinitions {


kotlin {
jvmToolchain(11)
jvmToolchain(22)
}

java {
Expand Down
13 changes: 12 additions & 1 deletion kt/godot-library/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,25 @@ apiGenerator {
}

kotlin {
jvmToolchain(11)
jvmToolchain(22)
}

java {
withSourcesJar()
withJavadocJar()
}

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
kotlinOptions {
jvmTarget = "22" // Make sure this matches the JVM version
}
}

tasks.withType<JavaCompile>().configureEach {
options.compilerArgs.add("--enable-preview")
options.release.set(22) // Match the Java version you are using
}

dependencies {
// added here as a transitive dependency so the user can use reflection
// we need to add it here so reflection is available where the code is loaded (Bootstrap.kt) otherwise it will not work
Expand Down
29 changes: 27 additions & 2 deletions kt/godot-library/src/main/kotlin/godot/core/Functions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ package godot.core

import godot.core.memory.TransferContext
import godot.common.extensions.convertToSnakeCase
import godot.common.interop.VoidPtr
import godot.core.memory.MemoryManager
import java.lang.foreign.Arena
import java.lang.foreign.FunctionDescriptor
import java.lang.foreign.Linker
import java.lang.foreign.MemorySegment
import java.lang.foreign.ValueLayout
import java.lang.invoke.MethodHandle
import java.lang.invoke.MethodHandles
import java.lang.invoke.MethodType

data class KtFunctionInfo(
val name: String,
Expand Down Expand Up @@ -31,7 +41,8 @@ abstract class KtFunction<T : KtObject, R : Any?>(
private val types: Array<VariantConverter> = parameterTypes.toList().toTypedArray()
val registrationName = functionInfo.name.convertToSnakeCase()

fun invoke(instance: T): Unit = withParameters(types) {
fun invoke(id: Long): Unit = withParameters(types) {
val instance = MemoryManager.getScriptInstance(id) as T
try {
invokeKt(instance)
} catch (t: Throwable) {
Expand All @@ -51,7 +62,21 @@ abstract class KtFunction<T : KtObject, R : Any?>(
ret
}

internal companion object : ParametersReader()
fun getInvokeStub() = linker.upcallStub(
mh.bindTo(this),
FunctionDescriptor.ofVoid(ValueLayout.JAVA_LONG),
Arena.global()
).address()

internal companion object : ParametersReader() {
private val linker = Linker.nativeLinker()

private val mh: MethodHandle = MethodHandles.lookup().findVirtual(
KtFunction::class.java,
"invoke",
MethodType.methodType(Void.TYPE, Long::class.javaPrimitiveType),
)
}

internal abstract fun invokeKt(instance: T): R
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,11 @@ internal object MemoryManager {
ObjectDB.remove(ObjectID(id))
}

fun getScriptInstance(id: Long) = lock.read {
ObjectDB[ObjectID(id)]!!.instance!!
}


fun getInstanceOrCreate(ptr: VoidPtr, id: Long, constructorIndex: Int): KtObject {
val objectID = ObjectID(id)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package godot.core.memory

import godot.core.KtObject
import godot.core.LongStringQueue
import godot.common.interop.ObjectID
import godot.core.VariantConverter
import godot.common.constants.Constraints
import godot.common.interop.ObjectID
import godot.common.interop.VoidPtr
import godot.common.util.threadLocal
import godot.core.KtObject
import godot.core.LongStringQueue
import godot.core.VariantConverter
import kotlincompile.definitions.GodotJvmBuildConfig
import java.lang.foreign.FunctionDescriptor
import java.lang.foreign.Linker
import java.lang.foreign.MemorySegment
import java.lang.foreign.ValueLayout
import java.lang.invoke.MethodHandle
import java.nio.ByteBuffer
import java.nio.ByteOrder

Expand Down Expand Up @@ -76,11 +81,7 @@ internal object TransferContext {
}

fun callMethod(ptr: VoidPtr, methodPtr: VoidPtr, expectedReturnType: VariantConverter) {
icall(
ptr,
methodPtr,
expectedReturnType.id
)
handle.invoke(ptr, methodPtr)
}

fun initializeKtObject(obj: KtObject) {
Expand All @@ -89,5 +90,12 @@ internal object TransferContext {
obj.objectID = ObjectID(buffer.long)
}

private external fun icall(ptr: VoidPtr, methodPtr: VoidPtr, expectedReturnType: Int)

lateinit var handle: MethodHandle

fun createIcallStub(icallAddress: VoidPtr) {
val segment = MemorySegment.ofAddress(icallAddress)
val descriptor = FunctionDescriptor.ofVoid(ValueLayout.JAVA_LONG, ValueLayout.JAVA_LONG)
handle = Linker.nativeLinker().downcallHandle(segment, descriptor)
}
}
2 changes: 1 addition & 1 deletion kt/plugins/godot-gradle-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ plugins {
}

kotlin {
jvmToolchain(11)
jvmToolchain(22)
}

gradlePlugin {
Expand Down
2 changes: 1 addition & 1 deletion kt/plugins/godot-intellij-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ group = "com.utopia-rise"
val intellijVersion: String = project.properties["godot.plugins.intellij.version"]?.toString() ?: libs.versions.ideaPluginDefaultIntellijVersion.get()

kotlin {
jvmToolchain(21)
jvmToolchain(22)
}

dependencies {
Expand Down
2 changes: 1 addition & 1 deletion kt/plugins/godot-plugins-common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ plugins {
}

kotlin {
jvmToolchain(11)
jvmToolchain(22)
}

repositories {
Expand Down
2 changes: 1 addition & 1 deletion kt/tools-common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ version = fullGodotKotlinJvmVersion
group = "com.utopia-rise"

kotlin {
jvmToolchain(11)
jvmToolchain(22)
}

dependencies {
Expand Down
2 changes: 1 addition & 1 deletion kt/utils/godot-build-props/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ plugins {
}

kotlin {
jvmToolchain(11)
jvmToolchain(22)
}

tasks {
Expand Down
10 changes: 8 additions & 2 deletions src/jvm_wrapper/memory/transfer_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ SharedBuffer* TransferContext::get_and_rewind_buffer(jni::Env& p_env) {
return &shared_buffer;
}

void TransferContext::create_icall_stub(jni::Env& p_env) {
uintptr_t ptr = reinterpret_cast<uintptr_t>(&TransferContext::icall);
jvalue call_args[1] = {jni::to_jni_arg(ptr)};
wrapped.call_object_method(p_env, ICALL_STUB, call_args);
}

void TransferContext::read_return_value(jni::Env& p_env, Variant& r_ret) {
SharedBuffer* buffer {get_and_rewind_buffer(p_env)};
BufferToVariant::read_variant(buffer, r_ret);
Expand Down Expand Up @@ -59,15 +65,15 @@ void TransferContext::write_object_data(jni::Env& p_env, uintptr_t ptr, ObjectID
buffer->increment_position(encode_uint64(id, buffer->get_cursor()));
}

void TransferContext::icall(JNIEnv* rawEnv, jobject instance, jlong j_ptr, jlong j_method_ptr, jint expectedReturnType) {
void TransferContext::icall(uint64_t j_ptr, uint64_t j_method_ptr) {
if (unlikely(stack_offset == -1)) {
for (int i = 0; i < MAX_STACK_SIZE; i++) {
variant_args_ptr[i] = &variant_args[i];
}
stack_offset = 0;
}

jni::Env env {rawEnv};
jni::Env env {jni::Jvm::current_env()};

SharedBuffer* buffer {get_instance().get_and_rewind_buffer(env)};
uint32_t args_size {read_args_size(buffer)};
Expand Down
7 changes: 5 additions & 2 deletions src/jvm_wrapper/memory/transfer_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ JVM_SINGLETON_WRAPPER(TransferContext, "godot.core.memory.TransferContext") {
SINGLETON_CLASS(TransferContext)

JNI_OBJECT_METHOD(GET_BUFFER)
JNI_OBJECT_METHOD(ICALL_STUB)

INIT_JNI_BINDINGS(
INIT_JNI_METHOD(GET_BUFFER, "getBuffer", "()Ljava/nio/ByteBuffer;")
INIT_NATIVE_METHOD("icall", "(JJI)V", TransferContext::icall)
INIT_JNI_METHOD(ICALL_STUB, "createIcallStub", "(J)V")
)

public:
Expand All @@ -23,7 +24,9 @@ JVM_SINGLETON_WRAPPER(TransferContext, "godot.core.memory.TransferContext") {
uint32_t read_args(jni::Env& p_env, Variant* args);
void write_object_data(jni::Env& p_env, uintptr_t ptr, ObjectID id);

static void icall(JNIEnv* rawEnv, jobject instance, jlong j_ptr, jlong j_method_ptr, jint expectedReturnType);
void create_icall_stub(jni::Env& p_env);

static void icall(uint64_t j_ptr, uint64_t j_method_ptr);

private:
SharedBuffer* get_and_rewind_buffer(jni::Env& p_env);
Expand Down
Loading

0 comments on commit 8649e80

Please sign in to comment.