Skip to content

Commit

Permalink
Make jni call safer.
Browse files Browse the repository at this point in the history
  • Loading branch information
CedNaru committed Sep 16, 2024
1 parent 359d785 commit e54dc51
Show file tree
Hide file tree
Showing 26 changed files with 211 additions and 138 deletions.
38 changes: 38 additions & 0 deletions src/jni/methods.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#ifndef GODOT_JVM_METHODS_H
#define GODOT_JVM_METHODS_H

#include <jni.h>

namespace jni {
typedef jmethodID MethodID;

struct VoidMethodID {
jmethodID methodId = nullptr;
};

struct BooleanMethodID {
jmethodID methodId = nullptr;
};

struct IntMethodID {
jmethodID methodId = nullptr;
};

struct LongMethodID {
jmethodID methodId = nullptr;
};

struct FloatMethodID {
jmethodID methodId = nullptr;
};

struct DoubleMethodID {
jmethodID methodId = nullptr;
};

struct ObjectMethodID {
jmethodID methodId = nullptr;
};
}// namespace jni

#endif// GODOT_JVM_METHODS_H
37 changes: 7 additions & 30 deletions src/jni/types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,49 +60,26 @@ namespace jni {
return obj == nullptr;
}

jint JObject::call_int_method(Env& env, MethodId method, jvalue* args) const {
auto ret = env.env->CallIntMethodA((jclass) obj, method, args);
env.check_exceptions();
return ret;
}

jdouble JObject::call_double_method(Env& env, MethodId method, jvalue* args) const {
jdouble ret = env.env->CallDoubleMethodA((jclass) obj, method, args);
env.check_exceptions();
return ret;
}

jlong JObject::call_long_method(Env& env, MethodId method, jvalue* args) const {
auto ret = env.env->CallLongMethodA((jclass) obj, method, args);
env.check_exceptions();
return ret;
}

jboolean JObject::call_boolean_method(Env& env, MethodId method, jvalue* args) const {
auto ret = env.env->CallBooleanMethodA((jclass) obj, method, args);
env.check_exceptions();
return ret;
}

bool JObject::is_same_object(Env& env, const JObject& other) const {
return env.is_same_object(obj, other.obj);
}

MethodId JClass::get_method_id(Env& env, const char* name, const char* signature) {
MethodID JClass::get_method_id(Env& env, const char* name, const char* signature) {
auto id = env.env->GetMethodID((jclass) obj, name, signature);
JVM_CRASH_COND_MSG(id == nullptr, vformat("Method not found: %s with signature: %s", name, signature));
env.check_exceptions();
return id;
}

MethodId JClass::get_static_method_id(Env& env, const char* name, const char* signature) {
MethodID JClass::get_static_method_id(Env& env, const char* name, const char* signature) {
auto id = env.env->GetStaticMethodID((jclass) obj, name, signature);
JVM_CRASH_COND_MSG(id == nullptr, vformat("Method not found: %s with signature: %s", name, signature));
env.check_exceptions();
return id;
}

FieldId JClass::get_static_field_id(Env& env, const char* name, const char* signature) {
FieldID JClass::get_static_field_id(Env& env, const char* name, const char* signature) {
auto id = env.env->GetStaticFieldID((jclass) obj, name, signature);
JVM_CRASH_COND_MSG(id == nullptr, vformat("Field not found: %s with signature: %s", name, signature));
env.check_exceptions();
Expand All @@ -114,24 +91,24 @@ namespace jni {
env.check_exceptions();
}

MethodId JClass::get_constructor_method_id(Env& env, const char* signature) {
MethodID JClass::get_constructor_method_id(Env& env, const char* signature) {
return get_method_id(env, "<init>", signature);
}

JObject JClass::new_instance(Env& env, MethodId ctor, jvalue* args) {
JObject JClass::new_instance(Env& env, MethodID ctor, jvalue* args) {
auto ret = env.env->NewObjectA((jclass) obj, ctor, args);
JVM_CRASH_COND_MSG(ret == nullptr, "Failed to instantiated object!");
env.check_exceptions();
return JObject(ret);
}

JObject JClass::call_static_object_method(Env& env, MethodId method, jvalue* args) {
JObject JClass::call_static_object_method(Env& env, MethodID method, jvalue* args) {
auto ret = env.env->CallStaticObjectMethodA((jclass) obj, method, args);
env.check_exceptions();
return JObject(ret);
}

JObject JClass::get_static_object_field(Env& env, FieldId field) {
JObject JClass::get_static_object_field(Env& env, FieldID field) {
auto value = env.env->GetStaticObjectField((jclass) obj, field);
env.check_exceptions();
return JObject(value);
Expand Down
112 changes: 81 additions & 31 deletions src/jni/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define GODOT_LOADER_JOBJECT_H

#include "env.h"
#include "methods.h"

#include <core/templates/vector.h>
#include <jni.h>
Expand Down Expand Up @@ -35,8 +36,7 @@ namespace jni {
JValue() = default;
};

typedef jmethodID MethodId;
typedef jfieldID FieldId;
typedef jfieldID FieldID;

class JObject {
public:
Expand Down Expand Up @@ -64,60 +64,98 @@ namespace jni {
void delete_local_ref(Env& p_env);

template<bool CHECK_EXCEPTION = true>
void call_void_method(Env& env, MethodId method, jvalue* args = {}) const;
void call_void_method(Env& p_env, VoidMethodID method, jvalue* args = {}) const;

template<bool CHECK_EXCEPTION = true>
JObject call_object_method(Env& env, MethodId method, jvalue* args = {}) const;
jboolean call_boolean_method(Env& p_env, BooleanMethodID method, jvalue* args = {}) const;

jint call_int_method(Env& env, MethodId method, jvalue* args = {}) const;
template<bool CHECK_EXCEPTION = true>
jint call_int_method(Env& p_env, IntMethodID method, jvalue* args = {}) const;

template<bool CHECK_EXCEPTION = true>
jlong call_long_method(Env& p_env, LongMethodID method, jvalue* args = {}) const;

jlong call_long_method(Env& env, MethodId method, jvalue* args = {}) const;
template<bool CHECK_EXCEPTION = true>
jdouble call_float_method(Env& p_env, FloatMethodID method, jvalue* args = {}) const;

jdouble call_double_method(Env& env, MethodId method, jvalue* args = {}) const;
template<bool CHECK_EXCEPTION = true>
jdouble call_double_method(Env& p_env, DoubleMethodID method, jvalue* args = {}) const;

jboolean call_boolean_method(Env& env, MethodId method, jvalue* args = {}) const;
template<bool CHECK_EXCEPTION = true>
JObject call_object_method(Env& p_env, ObjectMethodID method, jvalue* args = {}) const;

bool is_null();

bool is_same_object(Env& env, const JObject& other) const;
};

template<bool CHECK_EXCEPTION>
void JObject::call_void_method(Env& env, MethodId method, jvalue* args) const {
env.env->CallVoidMethodA((jclass) obj, method, args);
#ifdef DEV_ENABLED
env.check_exceptions();
#define CHECK_EXCEPTION_TEMPLATE p_env.check_exceptions()
#else
if constexpr (CHECK_EXCEPTION){
env.check_exceptions();
}
#define CHECK_EXCEPTION_TEMPLATE \
if constexpr (CHECK_EXCEPTION) { p_env.check_exceptions(); } \
(void) 0
#endif

template<bool CHECK_EXCEPTION>
void JObject::call_void_method(Env& p_env, VoidMethodID method, jvalue* args) const {
p_env.env->CallVoidMethodA((jobject) obj, method.methodId, args);
CHECK_EXCEPTION_TEMPLATE;
}

template<bool CHECK_EXCEPTION>
JObject JObject::call_object_method(Env& env, MethodId method, jvalue* args) const {
JObject ret = env.env->CallObjectMethodA((jclass) obj, method, args);
#ifdef DEV_ENABLED
env.check_exceptions();
#else
if constexpr (CHECK_EXCEPTION){
env.check_exceptions();
}
#endif
jboolean JObject::call_boolean_method(Env& p_env, BooleanMethodID method, jvalue* args) const {
return p_env.env->CallBooleanMethodA((jobject) obj, method.methodId, args);
CHECK_EXCEPTION_TEMPLATE;
}

template<bool CHECK_EXCEPTION>
jint JObject::call_int_method(Env& p_env, IntMethodID method, jvalue* args) const {
return p_env.env->CallIntMethodA((jobject) obj, method.methodId, args);
CHECK_EXCEPTION_TEMPLATE;
}

template<bool CHECK_EXCEPTION>
jlong JObject::call_long_method(Env& p_env, LongMethodID method, jvalue* args) const {
return p_env.env->CallLongMethodA((jobject) obj, method.methodId, args);
CHECK_EXCEPTION_TEMPLATE;
}

template<bool CHECK_EXCEPTION>
jdouble JObject::call_float_method(Env& p_env, FloatMethodID method, jvalue* args) const {
jdouble ret = p_env.env->CallFloatMethodA((jobject) obj, method.methodId, args);
CHECK_EXCEPTION_TEMPLATE;
return ret;
}

template<bool CHECK_EXCEPTION>
jdouble JObject::call_double_method(Env& p_env, DoubleMethodID method, jvalue* args) const {
jdouble ret = p_env.env->CallDoubleMethodA((jobject) obj, method.methodId, args);
CHECK_EXCEPTION_TEMPLATE;
return ret;
}

template<bool CHECK_EXCEPTION>
JObject JObject::call_object_method(Env& p_env, ObjectMethodID method, jvalue* args) const {
return p_env.env->CallObjectMethodA((jobject) obj, method.methodId, args);
CHECK_EXCEPTION_TEMPLATE;
}

class JString : public JObject {
public:
JString() = default;

JString(jstring string) : JObject(string) {}

explicit JString(JObject jObject) : JObject(jObject) {};
};

class JArray : public JObject {
public:
JArray() = default;

JArray(jarray array) : JObject(array) {}

explicit JArray(JObject jObject) : JObject(jObject) {};

int length(Env& env);
Expand All @@ -126,7 +164,9 @@ namespace jni {
class JObjectArray : public JArray {
public:
JObjectArray() = default;

JObjectArray(jarray array) : JArray(array) {}

explicit JObjectArray(JObject jObject) : JArray(jObject) {};

void set(Env& env, int index, JObject value);
Expand All @@ -138,7 +178,9 @@ namespace jni {
public:
JByteArray() = default;
JByteArray(Env& env, jsize size = 0);

JByteArray(jbyteArray array) : JArray(array) {}

explicit JByteArray(JObject jObject) : JArray(jObject) {};

void get_array_elements(Env& env, jbyte* arr, jsize size);
Expand All @@ -149,7 +191,9 @@ namespace jni {
public:
JIntArray() = default;
JIntArray(Env& env, jsize size = 0);

JIntArray(jbyteArray array) : JArray(array) {}

explicit JIntArray(JObject jObject) : JArray(jObject) {};

void get_array_elements(Env& env, jint* arr, jsize size);
Expand All @@ -160,7 +204,9 @@ namespace jni {
public:
JLongArray() = default;
JLongArray(Env& env, jsize size = 0);

JLongArray(jbyteArray array) : JArray(array) {}

explicit JLongArray(JObject jObject) : JArray(jObject) {};

void get_array_elements(Env& env, jlong* arr, jsize size);
Expand All @@ -171,7 +217,9 @@ namespace jni {
public:
JFloatArray() = default;
JFloatArray(Env& env, jsize size = 0);

JFloatArray(jbyteArray array) : JArray(array) {}

explicit JFloatArray(JObject jObject) : JArray(jObject) {};

void get_array_elements(Env& env, jfloat* arr, jsize size);
Expand All @@ -182,7 +230,9 @@ namespace jni {
public:
JDoubleArray() = default;
JDoubleArray(Env& env, jsize size = 0);

JDoubleArray(jbyteArray array) : JArray(array) {}

explicit JDoubleArray(JObject jObject) : JArray(jObject) {};

void get_array_elements(Env& env, jdouble* arr, jsize size);
Expand All @@ -203,23 +253,23 @@ namespace jni {

JClass() : JClass((jclass) nullptr) {}

JObject new_instance(Env& env, MethodId ctor, jvalue* args = {});
JObject new_instance(Env& env, MethodID ctor, jvalue* args = {});

JObjectArray new_object_array(Env& env, int size, JObject initial = {});

MethodId get_constructor_method_id(Env& env, const char* signature);
MethodID get_constructor_method_id(Env& env, const char* signature);

MethodId get_method_id(Env& env, const char* name, const char* signature);
MethodID get_method_id(Env& env, const char* name, const char* signature);

MethodId get_static_method_id(Env& env, const char* name, const char* signature);
MethodID get_static_method_id(Env& env, const char* name, const char* signature);

FieldId get_static_field_id(Env& env, const char* name, const char* signature);
FieldID get_static_field_id(Env& env, const char* name, const char* signature);

void register_natives(Env& env, Vector<JNativeMethod> methods);

JObject call_static_object_method(Env& env, MethodId method, jvalue* args = {});
JObject call_static_object_method(Env& env, MethodID method, jvalue* args = {});

JObject get_static_object_field(Env& env, FieldId field);
JObject get_static_object_field(Env& env, FieldID field);

bool is_assignable_from(Env& env, JClass p_other) const;
};
Expand Down
2 changes: 1 addition & 1 deletion src/jvm_wrapper/bootstrap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ void Bootstrap::init(jni::Env& p_env, const String& p_project_path, const String
jni::JObject project_path = p_env.new_string(p_project_path.utf8().get_data());
jni::JObject jar_file {p_env.new_string(p_jar_file.utf8().get_data())};
jvalue args[3] = {jni::to_jni_arg(project_path), jni::to_jni_arg(jar_file), jni::to_jni_arg(p_class_loader)};
wrapped.call_void_method(p_env, INIT, args);
wrapped.call_object_method(p_env, INIT, args);
}

void Bootstrap::finish(jni::Env& p_env) {
Expand Down
4 changes: 2 additions & 2 deletions src/jvm_wrapper/bootstrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ JVM_INSTANCE_WRAPPER(Bootstrap, "godot.runtime.Bootstrap") {
JVM_CLASS(Bootstrap)

// clang-format off
JNI_METHOD(INIT)
JNI_METHOD(FINISH)
JNI_OBJECT_METHOD(INIT)
JNI_VOID_METHOD(FINISH)

INIT_JNI_BINDINGS(
INIT_JNI_METHOD(INIT, "init", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V")
Expand Down
2 changes: 1 addition & 1 deletion src/jvm_wrapper/bridge/callable_bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

#include "bridges_utils.h"
#include "constraints.h"
#include "jvm_wrapper/kotlin_callable_custom.h"
#include "jvm_wrapper/memory/transfer_context.h"
#include "kotlin_callable_custom.h"

using namespace bridges;

Expand Down
2 changes: 1 addition & 1 deletion src/jvm_wrapper/bridge/gd_print_bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace bridges {
SINGLETON_CLASS(GDPrintBridge)

// clang-format off
JNI_METHOD(PRINT_STACKTRACE)
JNI_OBJECT_METHOD(PRINT_STACKTRACE)

INIT_JNI_BINDINGS(
INIT_JNI_METHOD(PRINT_STACKTRACE, "getStacktrace", "()Ljava/lang/String;")
Expand Down
3 changes: 2 additions & 1 deletion src/jvm_wrapper/bridge/kt_callable_bridge.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "kt_callable_bridge.h"
#include "kotlin_callable_custom.h"

#include "jvm_wrapper/kotlin_callable_custom.h"

using namespace bridges;

Expand Down
Loading

0 comments on commit e54dc51

Please sign in to comment.