2016-06-18 14:46:12 +02:00
|
|
|
/**************************************************************************/
|
|
|
|
/* java_class_wrapper.cpp */
|
|
|
|
/**************************************************************************/
|
|
|
|
/* This file is part of: */
|
|
|
|
/* GODOT ENGINE */
|
|
|
|
/* https://godotengine.org */
|
|
|
|
/**************************************************************************/
|
|
|
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
|
|
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
|
|
|
/* */
|
|
|
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
|
|
|
/* a copy of this software and associated documentation files (the */
|
|
|
|
/* "Software"), to deal in the Software without restriction, including */
|
|
|
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
|
|
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
|
|
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
|
|
|
/* the following conditions: */
|
|
|
|
/* */
|
|
|
|
/* The above copyright notice and this permission notice shall be */
|
|
|
|
/* included in all copies or substantial portions of the Software. */
|
|
|
|
/* */
|
|
|
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
|
|
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
|
|
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
|
|
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
|
|
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
|
|
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
|
|
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
|
|
|
/**************************************************************************/
|
2018-01-05 00:50:27 +01:00
|
|
|
|
2020-01-19 20:02:40 +01:00
|
|
|
#include "api/java_class_wrapper.h"
|
2022-02-16 13:56:32 +01:00
|
|
|
|
2024-11-20 10:10:17 -08:00
|
|
|
#include "jni_utils.h"
|
2014-09-02 23:13:40 -03:00
|
|
|
#include "thread_jandroid.h"
|
|
|
|
|
2020-02-19 16:27:19 -03:00
|
|
|
bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error, Variant &ret) {
|
2022-05-13 15:04:37 +02:00
|
|
|
HashMap<StringName, List<MethodInfo>>::Iterator M = methods.find(p_method);
|
2022-02-16 13:56:32 +01:00
|
|
|
if (!M) {
|
2014-09-02 23:13:40 -03:00
|
|
|
return false;
|
2022-02-16 13:56:32 +01:00
|
|
|
}
|
2014-09-02 23:13:40 -03:00
|
|
|
|
2021-01-19 13:29:41 +01:00
|
|
|
JNIEnv *env = get_jni_env();
|
2022-05-30 22:13:49 +01:00
|
|
|
ERR_FAIL_NULL_V(env, false);
|
2014-09-02 23:13:40 -03:00
|
|
|
|
2020-04-02 01:20:12 +02:00
|
|
|
MethodInfo *method = nullptr;
|
2022-05-13 15:04:37 +02:00
|
|
|
for (MethodInfo &E : M->value) {
|
2024-08-20 16:53:57 -05:00
|
|
|
if (!p_instance && !E._static && !E._constructor) {
|
2020-02-19 16:27:19 -03:00
|
|
|
r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
|
2014-09-02 23:13:40 -03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-07-15 23:45:57 -04:00
|
|
|
int pc = E.param_types.size();
|
2023-09-29 19:19:46 +03:00
|
|
|
if (p_argcount < pc) {
|
2020-02-19 16:27:19 -03:00
|
|
|
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
|
2023-09-29 19:19:46 +03:00
|
|
|
r_error.expected = pc;
|
2014-09-02 23:13:40 -03:00
|
|
|
continue;
|
|
|
|
}
|
2023-09-29 19:19:46 +03:00
|
|
|
if (p_argcount > pc) {
|
2020-02-19 16:27:19 -03:00
|
|
|
r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
|
2023-09-29 19:19:46 +03:00
|
|
|
r_error.expected = pc;
|
2014-09-02 23:13:40 -03:00
|
|
|
continue;
|
|
|
|
}
|
2021-07-15 23:45:57 -04:00
|
|
|
uint32_t *ptypes = E.param_types.ptrw();
|
2014-09-02 23:13:40 -03:00
|
|
|
bool valid = true;
|
|
|
|
|
|
|
|
for (int i = 0; i < pc; i++) {
|
|
|
|
Variant::Type arg_expected = Variant::NIL;
|
|
|
|
switch (ptypes[i]) {
|
|
|
|
case ARG_TYPE_VOID: {
|
|
|
|
//bug?
|
|
|
|
} break;
|
|
|
|
case ARG_TYPE_BOOLEAN: {
|
2022-02-16 13:56:32 +01:00
|
|
|
if (p_args[i]->get_type() != Variant::BOOL) {
|
2014-09-02 23:13:40 -03:00
|
|
|
arg_expected = Variant::BOOL;
|
2022-02-16 13:56:32 +01:00
|
|
|
}
|
2014-09-02 23:13:40 -03:00
|
|
|
} break;
|
|
|
|
case ARG_NUMBER_CLASS_BIT | ARG_TYPE_BYTE:
|
|
|
|
case ARG_NUMBER_CLASS_BIT | ARG_TYPE_CHAR:
|
|
|
|
case ARG_NUMBER_CLASS_BIT | ARG_TYPE_SHORT:
|
|
|
|
case ARG_NUMBER_CLASS_BIT | ARG_TYPE_INT:
|
|
|
|
case ARG_NUMBER_CLASS_BIT | ARG_TYPE_LONG:
|
|
|
|
case ARG_TYPE_BYTE:
|
|
|
|
case ARG_TYPE_CHAR:
|
|
|
|
case ARG_TYPE_SHORT:
|
|
|
|
case ARG_TYPE_INT:
|
|
|
|
case ARG_TYPE_LONG: {
|
2022-02-16 13:56:32 +01:00
|
|
|
if (!p_args[i]->is_num()) {
|
2014-09-02 23:13:40 -03:00
|
|
|
arg_expected = Variant::INT;
|
2022-02-16 13:56:32 +01:00
|
|
|
}
|
2014-09-02 23:13:40 -03:00
|
|
|
} break;
|
|
|
|
case ARG_NUMBER_CLASS_BIT | ARG_TYPE_FLOAT:
|
|
|
|
case ARG_NUMBER_CLASS_BIT | ARG_TYPE_DOUBLE:
|
|
|
|
case ARG_TYPE_FLOAT:
|
|
|
|
case ARG_TYPE_DOUBLE: {
|
2022-02-16 13:56:32 +01:00
|
|
|
if (!p_args[i]->is_num()) {
|
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 15:20:53 -03:00
|
|
|
arg_expected = Variant::FLOAT;
|
2022-02-16 13:56:32 +01:00
|
|
|
}
|
2014-09-02 23:13:40 -03:00
|
|
|
} break;
|
2024-11-20 10:10:17 -08:00
|
|
|
case ARG_TYPE_STRING:
|
|
|
|
case ARG_TYPE_CHARSEQUENCE: {
|
2023-12-28 14:44:23 -08:00
|
|
|
if (!p_args[i]->is_string()) {
|
2014-09-02 23:13:40 -03:00
|
|
|
arg_expected = Variant::STRING;
|
2022-02-16 13:56:32 +01:00
|
|
|
}
|
2014-09-02 23:13:40 -03:00
|
|
|
} break;
|
2024-11-20 10:10:17 -08:00
|
|
|
case ARG_TYPE_CALLABLE: {
|
|
|
|
if (p_args[i]->get_type() != Variant::CALLABLE) {
|
|
|
|
arg_expected = Variant::CALLABLE;
|
|
|
|
}
|
|
|
|
} break;
|
2014-09-02 23:13:40 -03:00
|
|
|
case ARG_TYPE_CLASS: {
|
2025-03-06 16:52:40 -06:00
|
|
|
String cn = E.param_sigs[i].operator String();
|
|
|
|
if (cn.begins_with("L") && cn.ends_with(";")) {
|
|
|
|
cn = cn.substr(1, cn.length() - 2);
|
|
|
|
}
|
|
|
|
if (cn == "org/godotengine/godot/Dictionary") {
|
|
|
|
if (p_args[i]->get_type() != Variant::DICTIONARY) {
|
|
|
|
arg_expected = Variant::DICTIONARY;
|
|
|
|
}
|
|
|
|
} else if (p_args[i]->get_type() != Variant::OBJECT && p_args[i]->get_type() != Variant::NIL) {
|
2014-09-02 23:13:40 -03:00
|
|
|
arg_expected = Variant::OBJECT;
|
2022-02-16 13:56:32 +01:00
|
|
|
} else {
|
2021-06-04 18:03:15 +02:00
|
|
|
Ref<RefCounted> ref = *p_args[i];
|
2024-08-20 16:53:57 -05:00
|
|
|
if (ref.is_valid()) {
|
2017-08-26 17:10:58 +02:00
|
|
|
if (Object::cast_to<JavaObject>(ref.ptr())) {
|
2014-09-02 23:13:40 -03:00
|
|
|
Ref<JavaObject> jo = ref;
|
2024-08-20 16:53:57 -05:00
|
|
|
jclass c = env->FindClass(cn.utf8().get_data());
|
2014-09-02 23:13:40 -03:00
|
|
|
if (!c || !env->IsInstanceOf(jo->instance, c)) {
|
|
|
|
arg_expected = Variant::OBJECT;
|
|
|
|
} else {
|
|
|
|
//ok
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
arg_expected = Variant::OBJECT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} break;
|
2025-02-13 12:41:57 -06:00
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_BOOLEAN: {
|
|
|
|
if (p_args[i]->get_type() == Variant::ARRAY) {
|
|
|
|
Array arr = *p_args[i];
|
|
|
|
if (arr.is_typed() && arr.get_typed_builtin() != Variant::BOOL) {
|
|
|
|
arg_expected = Variant::ARRAY;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
arg_expected = Variant::ARRAY;
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_BYTE:
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_CHAR: {
|
|
|
|
if (p_args[i]->get_type() != Variant::PACKED_BYTE_ARRAY) {
|
|
|
|
arg_expected = Variant::PACKED_BYTE_ARRAY;
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_SHORT:
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_INT: {
|
|
|
|
if (p_args[i]->get_type() == Variant::ARRAY) {
|
|
|
|
Array arr = *p_args[i];
|
|
|
|
if (arr.is_typed() && arr.get_typed_builtin() != Variant::INT) {
|
|
|
|
arg_expected = Variant::ARRAY;
|
|
|
|
}
|
|
|
|
} else if (p_args[i]->get_type() != Variant::PACKED_INT32_ARRAY) {
|
|
|
|
arg_expected = Variant::ARRAY;
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_LONG: {
|
|
|
|
if (p_args[i]->get_type() == Variant::ARRAY) {
|
|
|
|
Array arr = *p_args[i];
|
|
|
|
if (arr.is_typed() && arr.get_typed_builtin() != Variant::INT) {
|
|
|
|
arg_expected = Variant::ARRAY;
|
|
|
|
}
|
|
|
|
} else if (p_args[i]->get_type() != Variant::PACKED_INT64_ARRAY) {
|
|
|
|
arg_expected = Variant::ARRAY;
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_FLOAT: {
|
|
|
|
if (p_args[i]->get_type() == Variant::ARRAY) {
|
|
|
|
Array arr = *p_args[i];
|
|
|
|
if (arr.is_typed() && arr.get_typed_builtin() != Variant::FLOAT) {
|
|
|
|
arg_expected = Variant::ARRAY;
|
|
|
|
}
|
|
|
|
} else if (p_args[i]->get_type() != Variant::PACKED_FLOAT32_ARRAY) {
|
|
|
|
arg_expected = Variant::ARRAY;
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_DOUBLE: {
|
|
|
|
if (p_args[i]->get_type() == Variant::ARRAY) {
|
|
|
|
Array arr = *p_args[i];
|
|
|
|
if (arr.is_typed() && arr.get_typed_builtin() != Variant::FLOAT) {
|
|
|
|
arg_expected = Variant::ARRAY;
|
|
|
|
}
|
|
|
|
} else if (p_args[i]->get_type() != Variant::PACKED_FLOAT64_ARRAY) {
|
|
|
|
arg_expected = Variant::ARRAY;
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_STRING:
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_CHARSEQUENCE: {
|
|
|
|
if (p_args[i]->get_type() == Variant::ARRAY) {
|
|
|
|
Array arr = *p_args[i];
|
|
|
|
if (arr.is_typed() && arr.get_typed_builtin() != Variant::STRING) {
|
|
|
|
arg_expected = Variant::ARRAY;
|
|
|
|
}
|
|
|
|
} else if (p_args[i]->get_type() != Variant::PACKED_STRING_ARRAY) {
|
|
|
|
arg_expected = Variant::ARRAY;
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_CALLABLE: {
|
|
|
|
if (p_args[i]->get_type() == Variant::ARRAY) {
|
|
|
|
Array arr = *p_args[i];
|
|
|
|
if (arr.is_typed() && arr.get_typed_builtin() != Variant::CALLABLE) {
|
|
|
|
arg_expected = Variant::ARRAY;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
arg_expected = Variant::ARRAY;
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_CLASS: {
|
|
|
|
if (p_args[i]->get_type() == Variant::ARRAY) {
|
|
|
|
Array arr = *p_args[i];
|
|
|
|
if (arr.is_typed() && arr.get_typed_builtin() != Variant::OBJECT) {
|
|
|
|
arg_expected = Variant::ARRAY;
|
|
|
|
} else {
|
|
|
|
String cn = E.param_sigs[i].operator String();
|
|
|
|
if (cn.begins_with("[L") && cn.ends_with(";")) {
|
|
|
|
cn = cn.substr(2, cn.length() - 3);
|
|
|
|
}
|
|
|
|
jclass c = env->FindClass(cn.utf8().get_data());
|
|
|
|
if (c) {
|
|
|
|
for (int j = 0; j < arr.size(); j++) {
|
|
|
|
Ref<JavaObject> jo = arr[j];
|
|
|
|
if (jo.is_valid()) {
|
|
|
|
if (!env->IsInstanceOf(jo->instance, c)) {
|
|
|
|
arg_expected = Variant::ARRAY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
arg_expected = Variant::ARRAY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
arg_expected = Variant::ARRAY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
arg_expected = Variant::ARRAY;
|
|
|
|
}
|
|
|
|
} break;
|
2014-09-02 23:13:40 -03:00
|
|
|
default: {
|
2022-02-16 13:56:32 +01:00
|
|
|
if (p_args[i]->get_type() != Variant::ARRAY) {
|
2014-09-02 23:13:40 -03:00
|
|
|
arg_expected = Variant::ARRAY;
|
2022-02-16 13:56:32 +01:00
|
|
|
}
|
2014-09-02 23:13:40 -03:00
|
|
|
} break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (arg_expected != Variant::NIL) {
|
2020-02-19 16:27:19 -03:00
|
|
|
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
|
2014-09-02 23:13:40 -03:00
|
|
|
r_error.argument = i;
|
|
|
|
r_error.expected = arg_expected;
|
|
|
|
valid = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2022-02-16 13:56:32 +01:00
|
|
|
if (!valid) {
|
2014-09-02 23:13:40 -03:00
|
|
|
continue;
|
2022-02-16 13:56:32 +01:00
|
|
|
}
|
2014-09-02 23:13:40 -03:00
|
|
|
|
2021-07-15 23:45:57 -04:00
|
|
|
method = &E;
|
2014-09-02 23:13:40 -03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-02-16 13:56:32 +01:00
|
|
|
if (!method) {
|
2025-02-06 11:24:20 -06:00
|
|
|
if (r_error.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
|
|
|
|
ERR_PRINT(vformat(R"(Cannot call static function "%s" on Java class "%s" directly. Make an instance instead.)", p_method, java_class_name));
|
|
|
|
}
|
2014-09-02 23:13:40 -03:00
|
|
|
return true; //no version convinces
|
2022-02-16 13:56:32 +01:00
|
|
|
}
|
2014-09-02 23:13:40 -03:00
|
|
|
|
2020-02-19 16:27:19 -03:00
|
|
|
r_error.error = Callable::CallError::CALL_OK;
|
2014-09-02 23:13:40 -03:00
|
|
|
|
2020-04-02 01:20:12 +02:00
|
|
|
jvalue *argv = nullptr;
|
2014-09-02 23:13:40 -03:00
|
|
|
|
|
|
|
if (method->param_types.size()) {
|
|
|
|
argv = (jvalue *)alloca(sizeof(jvalue) * method->param_types.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
List<jobject> to_free;
|
|
|
|
for (int i = 0; i < method->param_types.size(); i++) {
|
|
|
|
switch (method->param_types[i]) {
|
|
|
|
case ARG_TYPE_VOID: {
|
|
|
|
//can't happen
|
2020-04-02 01:20:12 +02:00
|
|
|
argv[i].l = nullptr; //I hope this works
|
2014-09-02 23:13:40 -03:00
|
|
|
} break;
|
|
|
|
|
|
|
|
case ARG_TYPE_BOOLEAN: {
|
|
|
|
argv[i].z = *p_args[i];
|
|
|
|
} break;
|
|
|
|
case ARG_TYPE_BYTE: {
|
|
|
|
argv[i].b = *p_args[i];
|
|
|
|
} break;
|
|
|
|
case ARG_TYPE_CHAR: {
|
|
|
|
argv[i].c = *p_args[i];
|
|
|
|
} break;
|
|
|
|
case ARG_TYPE_SHORT: {
|
|
|
|
argv[i].s = *p_args[i];
|
|
|
|
} break;
|
|
|
|
case ARG_TYPE_INT: {
|
|
|
|
argv[i].i = *p_args[i];
|
|
|
|
} break;
|
|
|
|
case ARG_TYPE_LONG: {
|
2017-07-25 12:28:31 +02:00
|
|
|
argv[i].j = (int64_t)*p_args[i];
|
2014-09-02 23:13:40 -03:00
|
|
|
} break;
|
|
|
|
case ARG_TYPE_FLOAT: {
|
|
|
|
argv[i].f = *p_args[i];
|
|
|
|
} break;
|
|
|
|
case ARG_TYPE_DOUBLE: {
|
|
|
|
argv[i].d = *p_args[i];
|
|
|
|
} break;
|
|
|
|
case ARG_NUMBER_CLASS_BIT | ARG_TYPE_BOOLEAN: {
|
|
|
|
jclass bclass = env->FindClass("java/lang/Boolean");
|
|
|
|
jmethodID ctor = env->GetMethodID(bclass, "<init>", "(Z)V");
|
|
|
|
jvalue val;
|
|
|
|
val.z = (bool)(*p_args[i]);
|
|
|
|
jobject obj = env->NewObjectA(bclass, ctor, &val);
|
|
|
|
argv[i].l = obj;
|
|
|
|
to_free.push_back(obj);
|
|
|
|
} break;
|
|
|
|
case ARG_NUMBER_CLASS_BIT | ARG_TYPE_BYTE: {
|
|
|
|
jclass bclass = env->FindClass("java/lang/Byte");
|
|
|
|
jmethodID ctor = env->GetMethodID(bclass, "<init>", "(B)V");
|
|
|
|
jvalue val;
|
|
|
|
val.b = (int)(*p_args[i]);
|
|
|
|
jobject obj = env->NewObjectA(bclass, ctor, &val);
|
|
|
|
argv[i].l = obj;
|
|
|
|
to_free.push_back(obj);
|
|
|
|
} break;
|
|
|
|
case ARG_NUMBER_CLASS_BIT | ARG_TYPE_CHAR: {
|
|
|
|
jclass bclass = env->FindClass("java/lang/Character");
|
|
|
|
jmethodID ctor = env->GetMethodID(bclass, "<init>", "(C)V");
|
|
|
|
jvalue val;
|
|
|
|
val.c = (int)(*p_args[i]);
|
|
|
|
jobject obj = env->NewObjectA(bclass, ctor, &val);
|
|
|
|
argv[i].l = obj;
|
|
|
|
to_free.push_back(obj);
|
|
|
|
} break;
|
|
|
|
case ARG_NUMBER_CLASS_BIT | ARG_TYPE_SHORT: {
|
|
|
|
jclass bclass = env->FindClass("java/lang/Short");
|
|
|
|
jmethodID ctor = env->GetMethodID(bclass, "<init>", "(S)V");
|
|
|
|
jvalue val;
|
|
|
|
val.s = (int)(*p_args[i]);
|
|
|
|
jobject obj = env->NewObjectA(bclass, ctor, &val);
|
|
|
|
argv[i].l = obj;
|
|
|
|
to_free.push_back(obj);
|
|
|
|
} break;
|
|
|
|
case ARG_NUMBER_CLASS_BIT | ARG_TYPE_INT: {
|
|
|
|
jclass bclass = env->FindClass("java/lang/Integer");
|
|
|
|
jmethodID ctor = env->GetMethodID(bclass, "<init>", "(I)V");
|
|
|
|
jvalue val;
|
|
|
|
val.i = (int)(*p_args[i]);
|
|
|
|
jobject obj = env->NewObjectA(bclass, ctor, &val);
|
|
|
|
argv[i].l = obj;
|
|
|
|
to_free.push_back(obj);
|
|
|
|
} break;
|
|
|
|
case ARG_NUMBER_CLASS_BIT | ARG_TYPE_LONG: {
|
|
|
|
jclass bclass = env->FindClass("java/lang/Long");
|
|
|
|
jmethodID ctor = env->GetMethodID(bclass, "<init>", "(J)V");
|
|
|
|
jvalue val;
|
|
|
|
val.j = (int64_t)(*p_args[i]);
|
|
|
|
jobject obj = env->NewObjectA(bclass, ctor, &val);
|
|
|
|
argv[i].l = obj;
|
|
|
|
to_free.push_back(obj);
|
|
|
|
} break;
|
|
|
|
case ARG_NUMBER_CLASS_BIT | ARG_TYPE_FLOAT: {
|
|
|
|
jclass bclass = env->FindClass("java/lang/Float");
|
|
|
|
jmethodID ctor = env->GetMethodID(bclass, "<init>", "(F)V");
|
|
|
|
jvalue val;
|
|
|
|
val.f = (float)(*p_args[i]);
|
|
|
|
jobject obj = env->NewObjectA(bclass, ctor, &val);
|
|
|
|
argv[i].l = obj;
|
|
|
|
to_free.push_back(obj);
|
|
|
|
} break;
|
|
|
|
case ARG_NUMBER_CLASS_BIT | ARG_TYPE_DOUBLE: {
|
|
|
|
jclass bclass = env->FindClass("java/lang/Double");
|
|
|
|
jmethodID ctor = env->GetMethodID(bclass, "<init>", "(D)V");
|
|
|
|
jvalue val;
|
|
|
|
val.d = (double)(*p_args[i]);
|
|
|
|
jobject obj = env->NewObjectA(bclass, ctor, &val);
|
|
|
|
argv[i].l = obj;
|
|
|
|
to_free.push_back(obj);
|
|
|
|
} break;
|
2024-11-20 10:10:17 -08:00
|
|
|
case ARG_TYPE_STRING:
|
|
|
|
case ARG_TYPE_CHARSEQUENCE: {
|
2014-09-02 23:13:40 -03:00
|
|
|
String s = *p_args[i];
|
|
|
|
jstring jStr = env->NewStringUTF(s.utf8().get_data());
|
|
|
|
argv[i].l = jStr;
|
|
|
|
to_free.push_back(jStr);
|
|
|
|
} break;
|
2024-11-20 10:10:17 -08:00
|
|
|
case ARG_TYPE_CALLABLE: {
|
|
|
|
jobject jcallable = callable_to_jcallable(env, *p_args[i]);
|
|
|
|
argv[i].l = jcallable;
|
|
|
|
to_free.push_back(jcallable);
|
|
|
|
} break;
|
2014-09-02 23:13:40 -03:00
|
|
|
case ARG_TYPE_CLASS: {
|
2025-03-06 16:52:40 -06:00
|
|
|
if (p_args[i]->get_type() == Variant::DICTIONARY) {
|
|
|
|
argv[i].l = _variant_to_jvalue(env, Variant::DICTIONARY, p_args[i]).obj;
|
2014-09-02 23:13:40 -03:00
|
|
|
} else {
|
2025-03-06 16:52:40 -06:00
|
|
|
Ref<JavaObject> jo = *p_args[i];
|
|
|
|
if (jo.is_valid()) {
|
|
|
|
argv[i].l = jo->instance;
|
|
|
|
} else {
|
|
|
|
argv[i].l = nullptr; //I hope this works
|
|
|
|
}
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_BOOLEAN: {
|
|
|
|
Array arr = *p_args[i];
|
|
|
|
jbooleanArray a = env->NewBooleanArray(arr.size());
|
|
|
|
for (int j = 0; j < arr.size(); j++) {
|
|
|
|
jboolean val = arr[j];
|
|
|
|
env->SetBooleanArrayRegion(a, j, 1, &val);
|
|
|
|
}
|
|
|
|
argv[i].l = a;
|
|
|
|
to_free.push_back(a);
|
|
|
|
|
|
|
|
} break;
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_BYTE: {
|
2025-02-13 12:41:57 -06:00
|
|
|
jbyteArray a = nullptr;
|
|
|
|
|
|
|
|
if (p_args[i]->get_type() == Variant::ARRAY) {
|
|
|
|
Array arr = *p_args[i];
|
|
|
|
a = env->NewByteArray(arr.size());
|
|
|
|
for (int j = 0; j < arr.size(); j++) {
|
|
|
|
jbyte val = arr[j];
|
|
|
|
env->SetByteArrayRegion(a, j, 1, &val);
|
|
|
|
}
|
|
|
|
} else if (p_args[i]->get_type() == Variant::PACKED_BYTE_ARRAY) {
|
|
|
|
PackedByteArray arr = *p_args[i];
|
|
|
|
a = env->NewByteArray(arr.size());
|
|
|
|
env->SetByteArrayRegion(a, 0, arr.size(), (const jbyte *)arr.ptr());
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
2025-02-13 12:41:57 -06:00
|
|
|
|
2014-09-02 23:13:40 -03:00
|
|
|
argv[i].l = a;
|
|
|
|
to_free.push_back(a);
|
|
|
|
|
|
|
|
} break;
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_CHAR: {
|
2025-02-13 12:41:57 -06:00
|
|
|
jcharArray a = nullptr;
|
|
|
|
|
|
|
|
if (p_args[i]->get_type() == Variant::ARRAY) {
|
|
|
|
Array arr = *p_args[i];
|
|
|
|
a = env->NewCharArray(arr.size());
|
|
|
|
for (int j = 0; j < arr.size(); j++) {
|
|
|
|
jchar val = arr[j];
|
|
|
|
env->SetCharArrayRegion(a, j, 1, &val);
|
|
|
|
}
|
|
|
|
} else if (p_args[i]->get_type() == Variant::PACKED_BYTE_ARRAY) {
|
|
|
|
PackedByteArray arr = *p_args[i];
|
|
|
|
// The data is expected to be UTF-16 encoded, so the length is half the size of the byte array.
|
|
|
|
int size = arr.size() / 2;
|
|
|
|
a = env->NewCharArray(size);
|
|
|
|
env->SetCharArrayRegion(a, 0, size, (const jchar *)arr.ptr());
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
2025-02-13 12:41:57 -06:00
|
|
|
|
2014-09-02 23:13:40 -03:00
|
|
|
argv[i].l = a;
|
|
|
|
to_free.push_back(a);
|
|
|
|
|
|
|
|
} break;
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_SHORT: {
|
2025-02-13 12:41:57 -06:00
|
|
|
jshortArray a = nullptr;
|
|
|
|
|
|
|
|
if (p_args[i]->get_type() == Variant::ARRAY) {
|
|
|
|
Array arr = *p_args[i];
|
|
|
|
a = env->NewShortArray(arr.size());
|
|
|
|
for (int j = 0; j < arr.size(); j++) {
|
|
|
|
jshort val = arr[j];
|
|
|
|
env->SetShortArrayRegion(a, j, 1, &val);
|
|
|
|
}
|
|
|
|
} else if (p_args[i]->get_type() == Variant::PACKED_INT32_ARRAY) {
|
|
|
|
PackedInt32Array arr = *p_args[i];
|
|
|
|
a = env->NewShortArray(arr.size());
|
|
|
|
for (int j = 0; j < arr.size(); j++) {
|
|
|
|
jshort val = arr[j];
|
|
|
|
env->SetShortArrayRegion(a, j, 1, &val);
|
|
|
|
}
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
2025-02-13 12:41:57 -06:00
|
|
|
|
2014-09-02 23:13:40 -03:00
|
|
|
argv[i].l = a;
|
|
|
|
to_free.push_back(a);
|
|
|
|
|
|
|
|
} break;
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_INT: {
|
2025-02-13 12:41:57 -06:00
|
|
|
jintArray a = nullptr;
|
|
|
|
|
|
|
|
if (p_args[i]->get_type() == Variant::ARRAY) {
|
|
|
|
Array arr = *p_args[i];
|
|
|
|
a = env->NewIntArray(arr.size());
|
|
|
|
for (int j = 0; j < arr.size(); j++) {
|
|
|
|
jint val = arr[j];
|
|
|
|
env->SetIntArrayRegion(a, j, 1, &val);
|
|
|
|
}
|
|
|
|
} else if (p_args[i]->get_type() == Variant::PACKED_INT32_ARRAY) {
|
|
|
|
PackedInt32Array arr = *p_args[i];
|
|
|
|
a = env->NewIntArray(arr.size());
|
|
|
|
env->SetIntArrayRegion(a, 0, arr.size(), arr.ptr());
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
2025-02-13 12:41:57 -06:00
|
|
|
|
2014-09-02 23:13:40 -03:00
|
|
|
argv[i].l = a;
|
|
|
|
to_free.push_back(a);
|
|
|
|
} break;
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_LONG: {
|
2025-02-13 12:41:57 -06:00
|
|
|
jlongArray a = nullptr;
|
|
|
|
|
|
|
|
if (p_args[i]->get_type() == Variant::ARRAY) {
|
|
|
|
Array arr = *p_args[i];
|
|
|
|
a = env->NewLongArray(arr.size());
|
|
|
|
for (int j = 0; j < arr.size(); j++) {
|
|
|
|
jlong val = (int64_t)arr[j];
|
|
|
|
env->SetLongArrayRegion(a, j, 1, &val);
|
|
|
|
}
|
|
|
|
} else if (p_args[i]->get_type() == Variant::PACKED_INT64_ARRAY) {
|
|
|
|
PackedInt64Array arr = *p_args[i];
|
|
|
|
a = env->NewLongArray(arr.size());
|
|
|
|
env->SetLongArrayRegion(a, 0, arr.size(), arr.ptr());
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
2025-02-13 12:41:57 -06:00
|
|
|
|
2014-09-02 23:13:40 -03:00
|
|
|
argv[i].l = a;
|
|
|
|
to_free.push_back(a);
|
|
|
|
|
|
|
|
} break;
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_FLOAT: {
|
2025-02-13 12:41:57 -06:00
|
|
|
jfloatArray a = nullptr;
|
|
|
|
|
|
|
|
if (p_args[i]->get_type() == Variant::ARRAY) {
|
|
|
|
Array arr = *p_args[i];
|
|
|
|
a = env->NewFloatArray(arr.size());
|
|
|
|
for (int j = 0; j < arr.size(); j++) {
|
|
|
|
jfloat val = arr[j];
|
|
|
|
env->SetFloatArrayRegion(a, j, 1, &val);
|
|
|
|
}
|
|
|
|
} else if (p_args[i]->get_type() == Variant::PACKED_FLOAT32_ARRAY) {
|
|
|
|
PackedFloat32Array arr = *p_args[i];
|
|
|
|
a = env->NewFloatArray(arr.size());
|
|
|
|
env->SetFloatArrayRegion(a, 0, arr.size(), arr.ptr());
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
2025-02-13 12:41:57 -06:00
|
|
|
|
2014-09-02 23:13:40 -03:00
|
|
|
argv[i].l = a;
|
|
|
|
to_free.push_back(a);
|
|
|
|
|
|
|
|
} break;
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_DOUBLE: {
|
2025-02-13 12:41:57 -06:00
|
|
|
jdoubleArray a = nullptr;
|
|
|
|
|
|
|
|
if (p_args[i]->get_type() == Variant::ARRAY) {
|
|
|
|
Array arr = *p_args[i];
|
|
|
|
a = env->NewDoubleArray(arr.size());
|
|
|
|
for (int j = 0; j < arr.size(); j++) {
|
|
|
|
jdouble val = arr[j];
|
|
|
|
env->SetDoubleArrayRegion(a, j, 1, &val);
|
|
|
|
}
|
|
|
|
} else if (p_args[i]->get_type() == Variant::PACKED_FLOAT64_ARRAY) {
|
|
|
|
PackedFloat64Array arr = *p_args[i];
|
|
|
|
a = env->NewDoubleArray(arr.size());
|
|
|
|
env->SetDoubleArrayRegion(a, 0, arr.size(), arr.ptr());
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
2025-02-13 12:41:57 -06:00
|
|
|
|
2014-09-02 23:13:40 -03:00
|
|
|
argv[i].l = a;
|
|
|
|
to_free.push_back(a);
|
|
|
|
|
|
|
|
} break;
|
2024-11-20 10:10:17 -08:00
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_STRING:
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_CHARSEQUENCE: {
|
2025-02-13 12:41:57 -06:00
|
|
|
jobjectArray a = nullptr;
|
|
|
|
|
|
|
|
if (p_args[i]->get_type() == Variant::ARRAY) {
|
|
|
|
Array arr = *p_args[i];
|
|
|
|
a = env->NewObjectArray(arr.size(), env->FindClass("java/lang/String"), nullptr);
|
|
|
|
for (int j = 0; j < arr.size(); j++) {
|
|
|
|
String s = arr[j];
|
|
|
|
jstring jStr = env->NewStringUTF(s.utf8().get_data());
|
|
|
|
env->SetObjectArrayElement(a, j, jStr);
|
|
|
|
to_free.push_back(jStr);
|
|
|
|
}
|
|
|
|
} else if (p_args[i]->get_type() == Variant::PACKED_STRING_ARRAY) {
|
|
|
|
PackedStringArray arr = *p_args[i];
|
|
|
|
a = env->NewObjectArray(arr.size(), env->FindClass("java/lang/String"), nullptr);
|
|
|
|
for (int j = 0; j < arr.size(); j++) {
|
|
|
|
String s = arr[j];
|
|
|
|
jstring jStr = env->NewStringUTF(s.utf8().get_data());
|
|
|
|
env->SetObjectArrayElement(a, j, jStr);
|
|
|
|
to_free.push_back(jStr);
|
|
|
|
}
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
argv[i].l = a;
|
|
|
|
to_free.push_back(a);
|
|
|
|
} break;
|
2024-11-20 10:10:17 -08:00
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_CALLABLE: {
|
|
|
|
Array arr = *p_args[i];
|
|
|
|
jobjectArray jarr = env->NewObjectArray(arr.size(), env->FindClass("org/godotengine/godot/variant/Callable"), nullptr);
|
|
|
|
for (int j = 0; j < arr.size(); j++) {
|
|
|
|
Variant callable = arr[j];
|
|
|
|
jobject jcallable = callable_to_jcallable(env, callable);
|
|
|
|
env->SetObjectArrayElement(jarr, j, jcallable);
|
|
|
|
to_free.push_back(jcallable);
|
|
|
|
}
|
|
|
|
|
|
|
|
argv[i].l = jarr;
|
|
|
|
to_free.push_back(jarr);
|
|
|
|
} break;
|
2014-09-02 23:13:40 -03:00
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_CLASS: {
|
2025-02-13 12:41:57 -06:00
|
|
|
String cn = method->param_sigs[i].operator String();
|
|
|
|
if (cn.begins_with("[L") && cn.ends_with(";")) {
|
|
|
|
cn = cn.substr(2, cn.length() - 3);
|
|
|
|
}
|
|
|
|
jclass c = env->FindClass(cn.utf8().get_data());
|
|
|
|
if (c) {
|
|
|
|
Array arr = *p_args[i];
|
|
|
|
jobjectArray jarr = env->NewObjectArray(arr.size(), c, nullptr);
|
|
|
|
for (int j = 0; j < arr.size(); j++) {
|
|
|
|
Ref<JavaObject> jo = arr[j];
|
|
|
|
env->SetObjectArrayElement(jarr, j, jo->instance);
|
|
|
|
}
|
|
|
|
|
|
|
|
argv[i].l = jarr;
|
|
|
|
to_free.push_back(jarr);
|
|
|
|
}
|
2014-09-02 23:13:40 -03:00
|
|
|
} break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-19 16:27:19 -03:00
|
|
|
r_error.error = Callable::CallError::CALL_OK;
|
2014-09-02 23:13:40 -03:00
|
|
|
bool success = true;
|
|
|
|
|
|
|
|
switch (method->return_type) {
|
|
|
|
case ARG_TYPE_VOID: {
|
|
|
|
if (method->_static) {
|
|
|
|
env->CallStaticVoidMethodA(_class, method->method, argv);
|
|
|
|
} else {
|
|
|
|
env->CallVoidMethodA(p_instance->instance, method->method, argv);
|
|
|
|
}
|
|
|
|
ret = Variant();
|
|
|
|
|
|
|
|
} break;
|
|
|
|
case ARG_TYPE_BOOLEAN: {
|
|
|
|
if (method->_static) {
|
|
|
|
ret = env->CallStaticBooleanMethodA(_class, method->method, argv);
|
|
|
|
} else {
|
|
|
|
ret = env->CallBooleanMethodA(p_instance->instance, method->method, argv);
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case ARG_TYPE_BYTE: {
|
|
|
|
if (method->_static) {
|
|
|
|
ret = env->CallStaticByteMethodA(_class, method->method, argv);
|
|
|
|
} else {
|
|
|
|
ret = env->CallByteMethodA(p_instance->instance, method->method, argv);
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case ARG_TYPE_CHAR: {
|
|
|
|
if (method->_static) {
|
|
|
|
ret = env->CallStaticCharMethodA(_class, method->method, argv);
|
|
|
|
} else {
|
|
|
|
ret = env->CallCharMethodA(p_instance->instance, method->method, argv);
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case ARG_TYPE_SHORT: {
|
|
|
|
if (method->_static) {
|
|
|
|
ret = env->CallStaticShortMethodA(_class, method->method, argv);
|
|
|
|
} else {
|
|
|
|
ret = env->CallShortMethodA(p_instance->instance, method->method, argv);
|
|
|
|
}
|
|
|
|
|
|
|
|
} break;
|
|
|
|
case ARG_TYPE_INT: {
|
|
|
|
if (method->_static) {
|
|
|
|
ret = env->CallStaticIntMethodA(_class, method->method, argv);
|
|
|
|
} else {
|
|
|
|
ret = env->CallIntMethodA(p_instance->instance, method->method, argv);
|
|
|
|
}
|
|
|
|
|
|
|
|
} break;
|
|
|
|
case ARG_TYPE_LONG: {
|
|
|
|
if (method->_static) {
|
2017-07-25 12:28:31 +02:00
|
|
|
ret = (int64_t)env->CallStaticLongMethodA(_class, method->method, argv);
|
2014-09-02 23:13:40 -03:00
|
|
|
} else {
|
2017-07-25 12:28:31 +02:00
|
|
|
ret = (int64_t)env->CallLongMethodA(p_instance->instance, method->method, argv);
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
} break;
|
|
|
|
case ARG_TYPE_FLOAT: {
|
|
|
|
if (method->_static) {
|
|
|
|
ret = env->CallStaticFloatMethodA(_class, method->method, argv);
|
|
|
|
} else {
|
|
|
|
ret = env->CallFloatMethodA(p_instance->instance, method->method, argv);
|
|
|
|
}
|
|
|
|
|
|
|
|
} break;
|
|
|
|
case ARG_TYPE_DOUBLE: {
|
|
|
|
if (method->_static) {
|
|
|
|
ret = env->CallStaticDoubleMethodA(_class, method->method, argv);
|
|
|
|
} else {
|
|
|
|
ret = env->CallDoubleMethodA(p_instance->instance, method->method, argv);
|
|
|
|
}
|
|
|
|
|
|
|
|
} break;
|
|
|
|
default: {
|
|
|
|
jobject obj;
|
2024-08-20 16:53:57 -05:00
|
|
|
if (method->_constructor) {
|
2024-11-20 10:10:17 -08:00
|
|
|
obj = env->NewObjectA(_class, method->method, argv);
|
2024-08-20 16:53:57 -05:00
|
|
|
} else if (method->_static) {
|
2014-09-02 23:13:40 -03:00
|
|
|
obj = env->CallStaticObjectMethodA(_class, method->method, argv);
|
|
|
|
} else {
|
|
|
|
obj = env->CallObjectMethodA(p_instance->instance, method->method, argv);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!obj) {
|
|
|
|
ret = Variant();
|
|
|
|
} else {
|
|
|
|
if (!_convert_object_to_variant(env, obj, ret, method->return_type)) {
|
|
|
|
ret = Variant();
|
2020-02-19 16:27:19 -03:00
|
|
|
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
|
2014-09-02 23:13:40 -03:00
|
|
|
success = false;
|
|
|
|
}
|
|
|
|
env->DeleteLocalRef(obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
} break;
|
|
|
|
}
|
|
|
|
|
2021-07-15 23:45:57 -04:00
|
|
|
for (jobject &E : to_free) {
|
|
|
|
env->DeleteLocalRef(E);
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
|
|
|
|
2025-02-14 09:27:40 -06:00
|
|
|
jobject exception = env->ExceptionOccurred();
|
|
|
|
if (exception) {
|
|
|
|
env->ExceptionClear();
|
|
|
|
|
|
|
|
jclass java_class = env->GetObjectClass(exception);
|
|
|
|
Ref<JavaClass> java_class_wrapped = JavaClassWrapper::singleton->wrap_jclass(java_class);
|
|
|
|
env->DeleteLocalRef(java_class);
|
|
|
|
|
|
|
|
JavaClassWrapper::singleton->exception.instantiate(java_class_wrapped, exception);
|
|
|
|
env->DeleteLocalRef(exception);
|
|
|
|
} else {
|
|
|
|
JavaClassWrapper::singleton->exception.unref();
|
|
|
|
}
|
|
|
|
|
2014-09-02 23:13:40 -03:00
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
2024-11-20 10:10:17 -08:00
|
|
|
bool JavaClass::_get(const StringName &p_name, Variant &r_ret) const {
|
|
|
|
if (constant_map.has(p_name)) {
|
|
|
|
r_ret = constant_map[p_name];
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-03-09 14:58:40 +01:00
|
|
|
Variant JavaClass::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
|
2014-09-02 23:13:40 -03:00
|
|
|
Variant ret;
|
2024-08-20 16:53:57 -05:00
|
|
|
|
|
|
|
String method = (p_method == java_constructor_name) ? "<init>" : p_method;
|
|
|
|
bool found = _call_method(nullptr, method, p_args, p_argcount, r_error, ret);
|
2014-09-02 23:13:40 -03:00
|
|
|
if (found) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-03-09 14:58:40 +01:00
|
|
|
return RefCounted::callp(p_method, p_args, p_argcount, r_error);
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
|
|
|
|
2024-08-20 16:53:57 -05:00
|
|
|
String JavaClass::get_java_class_name() const {
|
|
|
|
return java_class_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
TypedArray<Dictionary> JavaClass::get_java_method_list() const {
|
|
|
|
TypedArray<Dictionary> method_list;
|
|
|
|
|
|
|
|
for (const KeyValue<StringName, List<MethodInfo>> &item : methods) {
|
|
|
|
for (const MethodInfo &mi : item.value) {
|
|
|
|
Dictionary method;
|
|
|
|
|
|
|
|
method["name"] = mi._constructor ? java_constructor_name : String(item.key);
|
|
|
|
method["id"] = (uint64_t)mi.method;
|
|
|
|
method["default_args"] = Array();
|
|
|
|
method["flags"] = METHOD_FLAGS_DEFAULT & (mi._static || mi._constructor ? METHOD_FLAG_STATIC : METHOD_FLAG_NORMAL);
|
|
|
|
|
|
|
|
{
|
|
|
|
Array a;
|
|
|
|
|
|
|
|
for (uint32_t argtype : mi.param_types) {
|
|
|
|
Dictionary d;
|
|
|
|
|
|
|
|
Variant::Type t = Variant::NIL;
|
|
|
|
float likelihood = 0.0;
|
|
|
|
_convert_to_variant_type(argtype, t, likelihood);
|
|
|
|
d["type"] = t;
|
|
|
|
if (t == Variant::OBJECT) {
|
|
|
|
d["hint"] = PROPERTY_HINT_RESOURCE_TYPE;
|
|
|
|
d["hint_string"] = "JavaObject";
|
|
|
|
} else {
|
|
|
|
d["hint"] = 0;
|
|
|
|
d["hint_string"] = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
a.push_back(d);
|
|
|
|
}
|
|
|
|
|
|
|
|
method["args"] = a;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
Dictionary d;
|
|
|
|
|
|
|
|
if (mi._constructor) {
|
|
|
|
d["type"] = Variant::OBJECT;
|
|
|
|
d["hint"] = PROPERTY_HINT_RESOURCE_TYPE;
|
|
|
|
d["hint_string"] = "JavaObject";
|
|
|
|
} else {
|
|
|
|
Variant::Type t = Variant::NIL;
|
|
|
|
float likelihood = 0.0;
|
|
|
|
_convert_to_variant_type(mi.return_type, t, likelihood);
|
|
|
|
d["type"] = t;
|
|
|
|
if (t == Variant::OBJECT) {
|
|
|
|
d["hint"] = PROPERTY_HINT_RESOURCE_TYPE;
|
|
|
|
d["hint_string"] = "JavaObject";
|
|
|
|
} else {
|
|
|
|
d["hint"] = 0;
|
|
|
|
d["hint_string"] = "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
method["return_type"] = d;
|
|
|
|
}
|
|
|
|
|
|
|
|
method_list.push_back(method);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return method_list;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ref<JavaClass> JavaClass::get_java_parent_class() const {
|
|
|
|
ERR_FAIL_NULL_V(_class, Ref<JavaClass>());
|
|
|
|
|
|
|
|
JNIEnv *env = get_jni_env();
|
|
|
|
ERR_FAIL_NULL_V(env, Ref<JavaClass>());
|
|
|
|
|
|
|
|
jclass superclass = (jclass)env->CallObjectMethod(_class, JavaClassWrapper::singleton->Class_getSuperclass);
|
|
|
|
if (!superclass) {
|
|
|
|
return Ref<JavaClass>();
|
|
|
|
}
|
|
|
|
|
|
|
|
Ref<JavaClass> ret = JavaClassWrapper::singleton->wrap_jclass(superclass);
|
|
|
|
env->DeleteLocalRef(superclass);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
String JavaClass::to_string() {
|
|
|
|
return "<JavaClass:" + java_class_name + ">";
|
|
|
|
}
|
|
|
|
|
2014-09-02 23:13:40 -03:00
|
|
|
JavaClass::JavaClass() {
|
|
|
|
}
|
|
|
|
|
2024-08-20 16:53:57 -05:00
|
|
|
JavaClass::~JavaClass() {
|
|
|
|
if (_class) {
|
|
|
|
JNIEnv *env = get_jni_env();
|
|
|
|
ERR_FAIL_NULL(env);
|
|
|
|
|
|
|
|
env->DeleteGlobalRef(_class);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-02 23:13:40 -03:00
|
|
|
/////////////////////
|
|
|
|
|
2022-03-09 14:58:40 +01:00
|
|
|
Variant JavaObject::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
|
2024-08-20 16:53:57 -05:00
|
|
|
if (instance) {
|
|
|
|
Ref<JavaClass> c = base_class;
|
|
|
|
while (c.is_valid()) {
|
|
|
|
Variant ret;
|
|
|
|
bool found = c->_call_method(this, p_method, p_args, p_argcount, r_error, ret);
|
|
|
|
if (found) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
c = c->get_java_parent_class();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return RefCounted::callp(p_method, p_args, p_argcount, r_error);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ref<JavaClass> JavaObject::get_java_class() const {
|
|
|
|
return base_class;
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
|
|
|
|
2024-08-20 16:53:57 -05:00
|
|
|
String JavaObject::to_string() {
|
|
|
|
if (base_class.is_valid() && instance) {
|
|
|
|
return "<JavaObject:" + base_class->java_class_name + " \"" + (String)call("toString") + "\">";
|
|
|
|
}
|
|
|
|
return RefCounted::to_string();
|
|
|
|
}
|
|
|
|
|
|
|
|
JavaObject::JavaObject() {
|
|
|
|
}
|
|
|
|
|
|
|
|
JavaObject::JavaObject(const Ref<JavaClass> &p_base, jobject p_instance) {
|
|
|
|
JNIEnv *env = get_jni_env();
|
|
|
|
ERR_FAIL_NULL(env);
|
|
|
|
|
|
|
|
base_class = p_base;
|
|
|
|
instance = env->NewGlobalRef(p_instance);
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
JavaObject::~JavaObject() {
|
2024-08-20 16:53:57 -05:00
|
|
|
if (instance) {
|
|
|
|
JNIEnv *env = get_jni_env();
|
|
|
|
ERR_FAIL_NULL(env);
|
|
|
|
|
|
|
|
env->DeleteGlobalRef(instance);
|
|
|
|
}
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////
|
|
|
|
|
|
|
|
bool JavaClassWrapper::_get_type_sig(JNIEnv *env, jobject obj, uint32_t &sig, String &strsig) {
|
|
|
|
jstring name2 = (jstring)env->CallObjectMethod(obj, Class_getName);
|
2019-02-26 23:54:45 +03:00
|
|
|
String str_type = jstring_to_string(name2, env);
|
2014-09-02 23:13:40 -03:00
|
|
|
env->DeleteLocalRef(name2);
|
|
|
|
uint32_t t = 0;
|
|
|
|
|
|
|
|
if (str_type.begins_with("[")) {
|
|
|
|
t = JavaClass::ARG_ARRAY_BIT;
|
|
|
|
strsig = "[";
|
2025-02-26 11:41:11 +01:00
|
|
|
str_type = str_type.substr(1);
|
2014-09-02 23:13:40 -03:00
|
|
|
if (str_type.begins_with("[")) {
|
|
|
|
print_line("Nested arrays not supported for type: " + str_type);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (str_type.begins_with("L")) {
|
|
|
|
str_type = str_type.substr(1, str_type.length() - 2); //ok it's a class
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (str_type == "void" || str_type == "V") {
|
|
|
|
t |= JavaClass::ARG_TYPE_VOID;
|
|
|
|
strsig += "V";
|
|
|
|
} else if (str_type == "boolean" || str_type == "Z") {
|
|
|
|
t |= JavaClass::ARG_TYPE_BOOLEAN;
|
|
|
|
strsig += "Z";
|
|
|
|
} else if (str_type == "byte" || str_type == "B") {
|
|
|
|
t |= JavaClass::ARG_TYPE_BYTE;
|
|
|
|
strsig += "B";
|
|
|
|
} else if (str_type == "char" || str_type == "C") {
|
|
|
|
t |= JavaClass::ARG_TYPE_CHAR;
|
|
|
|
strsig += "C";
|
|
|
|
} else if (str_type == "short" || str_type == "S") {
|
|
|
|
t |= JavaClass::ARG_TYPE_SHORT;
|
|
|
|
strsig += "S";
|
|
|
|
} else if (str_type == "int" || str_type == "I") {
|
|
|
|
t |= JavaClass::ARG_TYPE_INT;
|
|
|
|
strsig += "I";
|
|
|
|
} else if (str_type == "long" || str_type == "J") {
|
|
|
|
t |= JavaClass::ARG_TYPE_LONG;
|
|
|
|
strsig += "J";
|
|
|
|
} else if (str_type == "float" || str_type == "F") {
|
|
|
|
t |= JavaClass::ARG_TYPE_FLOAT;
|
|
|
|
strsig += "F";
|
|
|
|
} else if (str_type == "double" || str_type == "D") {
|
|
|
|
t |= JavaClass::ARG_TYPE_DOUBLE;
|
|
|
|
strsig += "D";
|
|
|
|
} else if (str_type == "java.lang.String") {
|
|
|
|
t |= JavaClass::ARG_TYPE_STRING;
|
|
|
|
strsig += "Ljava/lang/String;";
|
2024-11-20 10:10:17 -08:00
|
|
|
} else if (str_type == "java.lang.CharSequence") {
|
|
|
|
t |= JavaClass::ARG_TYPE_CHARSEQUENCE;
|
|
|
|
strsig += "Ljava/lang/CharSequence;";
|
|
|
|
} else if (str_type == "org.godotengine.godot.variant.Callable") {
|
|
|
|
t |= JavaClass::ARG_TYPE_CALLABLE;
|
|
|
|
strsig += "Lorg/godotengine/godot/variant/Callable;";
|
2014-09-02 23:13:40 -03:00
|
|
|
} else if (str_type == "java.lang.Boolean") {
|
|
|
|
t |= JavaClass::ARG_TYPE_BOOLEAN | JavaClass::ARG_NUMBER_CLASS_BIT;
|
|
|
|
strsig += "Ljava/lang/Boolean;";
|
|
|
|
} else if (str_type == "java.lang.Byte") {
|
|
|
|
t |= JavaClass::ARG_TYPE_BYTE | JavaClass::ARG_NUMBER_CLASS_BIT;
|
|
|
|
strsig += "Ljava/lang/Byte;";
|
|
|
|
} else if (str_type == "java.lang.Character") {
|
|
|
|
t |= JavaClass::ARG_TYPE_CHAR | JavaClass::ARG_NUMBER_CLASS_BIT;
|
|
|
|
strsig += "Ljava/lang/Character;";
|
|
|
|
} else if (str_type == "java.lang.Short") {
|
|
|
|
t |= JavaClass::ARG_TYPE_SHORT | JavaClass::ARG_NUMBER_CLASS_BIT;
|
|
|
|
strsig += "Ljava/lang/Short;";
|
|
|
|
} else if (str_type == "java.lang.Integer") {
|
|
|
|
t |= JavaClass::ARG_TYPE_INT | JavaClass::ARG_NUMBER_CLASS_BIT;
|
|
|
|
strsig += "Ljava/lang/Integer;";
|
|
|
|
} else if (str_type == "java.lang.Long") {
|
|
|
|
t |= JavaClass::ARG_TYPE_LONG | JavaClass::ARG_NUMBER_CLASS_BIT;
|
|
|
|
strsig += "Ljava/lang/Long;";
|
|
|
|
} else if (str_type == "java.lang.Float") {
|
|
|
|
t |= JavaClass::ARG_TYPE_FLOAT | JavaClass::ARG_NUMBER_CLASS_BIT;
|
|
|
|
strsig += "Ljava/lang/Float;";
|
|
|
|
} else if (str_type == "java.lang.Double") {
|
|
|
|
t |= JavaClass::ARG_TYPE_DOUBLE | JavaClass::ARG_NUMBER_CLASS_BIT;
|
|
|
|
strsig += "Ljava/lang/Double;";
|
|
|
|
} else {
|
|
|
|
//a class likely
|
2024-05-28 12:15:00 +02:00
|
|
|
strsig += "L" + str_type.replace_char('.', '/') + ";";
|
2014-09-02 23:13:40 -03:00
|
|
|
t |= JavaClass::ARG_TYPE_CLASS;
|
|
|
|
}
|
|
|
|
|
|
|
|
sig = t;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &var, uint32_t p_sig) {
|
|
|
|
if (!obj) {
|
|
|
|
var = Variant(); //seems null is just null...
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (p_sig) {
|
|
|
|
case ARG_TYPE_VOID: {
|
|
|
|
return Variant();
|
|
|
|
} break;
|
|
|
|
case ARG_TYPE_BOOLEAN | ARG_NUMBER_CLASS_BIT: {
|
|
|
|
var = env->CallBooleanMethod(obj, JavaClassWrapper::singleton->Boolean_booleanValue);
|
|
|
|
return true;
|
|
|
|
} break;
|
|
|
|
case ARG_TYPE_BYTE | ARG_NUMBER_CLASS_BIT: {
|
|
|
|
var = env->CallByteMethod(obj, JavaClassWrapper::singleton->Byte_byteValue);
|
|
|
|
return true;
|
|
|
|
|
|
|
|
} break;
|
|
|
|
case ARG_TYPE_CHAR | ARG_NUMBER_CLASS_BIT: {
|
|
|
|
var = env->CallCharMethod(obj, JavaClassWrapper::singleton->Character_characterValue);
|
|
|
|
return true;
|
|
|
|
|
|
|
|
} break;
|
|
|
|
case ARG_TYPE_SHORT | ARG_NUMBER_CLASS_BIT: {
|
|
|
|
var = env->CallShortMethod(obj, JavaClassWrapper::singleton->Short_shortValue);
|
|
|
|
return true;
|
|
|
|
|
|
|
|
} break;
|
|
|
|
case ARG_TYPE_INT | ARG_NUMBER_CLASS_BIT: {
|
|
|
|
var = env->CallIntMethod(obj, JavaClassWrapper::singleton->Integer_integerValue);
|
|
|
|
return true;
|
|
|
|
|
|
|
|
} break;
|
|
|
|
case ARG_TYPE_LONG | ARG_NUMBER_CLASS_BIT: {
|
2017-07-25 12:28:31 +02:00
|
|
|
var = (int64_t)env->CallLongMethod(obj, JavaClassWrapper::singleton->Long_longValue);
|
2014-09-02 23:13:40 -03:00
|
|
|
return true;
|
|
|
|
|
|
|
|
} break;
|
|
|
|
case ARG_TYPE_FLOAT | ARG_NUMBER_CLASS_BIT: {
|
|
|
|
var = env->CallFloatMethod(obj, JavaClassWrapper::singleton->Float_floatValue);
|
|
|
|
return true;
|
|
|
|
|
|
|
|
} break;
|
|
|
|
case ARG_TYPE_DOUBLE | ARG_NUMBER_CLASS_BIT: {
|
|
|
|
var = env->CallDoubleMethod(obj, JavaClassWrapper::singleton->Double_doubleValue);
|
|
|
|
return true;
|
|
|
|
} break;
|
|
|
|
case ARG_TYPE_STRING: {
|
2019-02-26 23:54:45 +03:00
|
|
|
var = jstring_to_string((jstring)obj, env);
|
2014-09-02 23:13:40 -03:00
|
|
|
return true;
|
|
|
|
} break;
|
2024-11-20 10:10:17 -08:00
|
|
|
case ARG_TYPE_CHARSEQUENCE: {
|
|
|
|
var = charsequence_to_string(env, obj);
|
|
|
|
return true;
|
|
|
|
} break;
|
|
|
|
case ARG_TYPE_CALLABLE: {
|
|
|
|
var = jcallable_to_callable(env, obj);
|
|
|
|
return true;
|
|
|
|
} break;
|
2014-09-02 23:13:40 -03:00
|
|
|
case ARG_TYPE_CLASS: {
|
2024-08-20 16:53:57 -05:00
|
|
|
jclass java_class = env->GetObjectClass(obj);
|
|
|
|
Ref<JavaClass> java_class_wrapped = JavaClassWrapper::singleton->wrap_jclass(java_class);
|
|
|
|
env->DeleteLocalRef(java_class);
|
|
|
|
|
|
|
|
if (java_class_wrapped.is_valid()) {
|
2025-03-06 16:52:40 -06:00
|
|
|
String cn = java_class_wrapped->get_java_class_name();
|
2025-03-14 18:01:12 -05:00
|
|
|
if (cn == "org.godotengine.godot.Dictionary" || cn == "java.util.HashMap") {
|
2025-03-06 16:52:40 -06:00
|
|
|
var = _jobject_to_variant(env, obj);
|
|
|
|
} else {
|
|
|
|
Ref<JavaObject> ret = Ref<JavaObject>(memnew(JavaObject(java_class_wrapped, obj)));
|
|
|
|
var = ret;
|
|
|
|
}
|
2024-08-20 16:53:57 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-09-02 23:13:40 -03:00
|
|
|
return false;
|
|
|
|
} break;
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_VOID: {
|
|
|
|
var = Array(); // ?
|
|
|
|
return true;
|
|
|
|
} break;
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_BOOLEAN: {
|
|
|
|
Array ret;
|
|
|
|
jobjectArray arr = (jobjectArray)obj;
|
|
|
|
|
|
|
|
int count = env->GetArrayLength(arr);
|
2025-02-27 16:50:51 -06:00
|
|
|
ret.resize(count);
|
2014-09-02 23:13:40 -03:00
|
|
|
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
jboolean val;
|
2025-02-27 16:50:51 -06:00
|
|
|
env->GetBooleanArrayRegion((jbooleanArray)arr, i, 1, &val);
|
|
|
|
ret[i] = (bool)val;
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
var = ret;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
} break;
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_BYTE: {
|
2025-02-27 16:50:51 -06:00
|
|
|
PackedByteArray ret;
|
2014-09-02 23:13:40 -03:00
|
|
|
jobjectArray arr = (jobjectArray)obj;
|
|
|
|
|
|
|
|
int count = env->GetArrayLength(arr);
|
2025-02-27 16:50:51 -06:00
|
|
|
ret.resize(count);
|
|
|
|
env->GetByteArrayRegion((jbyteArray)arr, 0, count, (int8_t *)ret.ptrw());
|
2014-09-02 23:13:40 -03:00
|
|
|
|
|
|
|
var = ret;
|
|
|
|
return true;
|
|
|
|
} break;
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_CHAR: {
|
2025-02-27 16:50:51 -06:00
|
|
|
PackedByteArray ret;
|
2014-09-02 23:13:40 -03:00
|
|
|
jobjectArray arr = (jobjectArray)obj;
|
|
|
|
|
|
|
|
int count = env->GetArrayLength(arr);
|
2025-02-27 16:50:51 -06:00
|
|
|
// Char arrays are UTF-16 encoded, so it's double the length.
|
|
|
|
ret.resize(count * 2);
|
|
|
|
env->GetCharArrayRegion((jcharArray)arr, 0, count, (jchar *)ret.ptrw());
|
2014-09-02 23:13:40 -03:00
|
|
|
|
|
|
|
var = ret;
|
|
|
|
return true;
|
|
|
|
} break;
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_SHORT: {
|
2025-02-27 16:50:51 -06:00
|
|
|
PackedInt32Array ret;
|
2014-09-02 23:13:40 -03:00
|
|
|
jobjectArray arr = (jobjectArray)obj;
|
|
|
|
|
|
|
|
int count = env->GetArrayLength(arr);
|
2025-02-27 16:50:51 -06:00
|
|
|
ret.resize(count);
|
2014-09-02 23:13:40 -03:00
|
|
|
|
2025-02-27 16:50:51 -06:00
|
|
|
int32_t *ptr = ret.ptrw();
|
2014-09-02 23:13:40 -03:00
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
jshort val;
|
2025-02-27 16:50:51 -06:00
|
|
|
env->GetShortArrayRegion((jshortArray)arr, i, 1, &val);
|
|
|
|
ptr[i] = val;
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
var = ret;
|
|
|
|
return true;
|
|
|
|
} break;
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_INT: {
|
2025-02-27 16:50:51 -06:00
|
|
|
PackedInt32Array ret;
|
2014-09-02 23:13:40 -03:00
|
|
|
jobjectArray arr = (jobjectArray)obj;
|
|
|
|
|
|
|
|
int count = env->GetArrayLength(arr);
|
2025-02-27 16:50:51 -06:00
|
|
|
ret.resize(count);
|
|
|
|
env->GetIntArrayRegion((jintArray)arr, 0, count, ret.ptrw());
|
2014-09-02 23:13:40 -03:00
|
|
|
|
|
|
|
var = ret;
|
|
|
|
return true;
|
|
|
|
} break;
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_LONG: {
|
2025-02-27 16:50:51 -06:00
|
|
|
PackedInt64Array ret;
|
2014-09-02 23:13:40 -03:00
|
|
|
jobjectArray arr = (jobjectArray)obj;
|
|
|
|
|
|
|
|
int count = env->GetArrayLength(arr);
|
2025-02-27 16:50:51 -06:00
|
|
|
ret.resize(count);
|
|
|
|
env->GetLongArrayRegion((jlongArray)arr, 0, count, ret.ptrw());
|
2014-09-02 23:13:40 -03:00
|
|
|
|
|
|
|
var = ret;
|
|
|
|
return true;
|
|
|
|
} break;
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_FLOAT: {
|
2025-02-27 16:50:51 -06:00
|
|
|
PackedFloat32Array ret;
|
2014-09-02 23:13:40 -03:00
|
|
|
jobjectArray arr = (jobjectArray)obj;
|
|
|
|
|
|
|
|
int count = env->GetArrayLength(arr);
|
2025-02-27 16:50:51 -06:00
|
|
|
ret.resize(count);
|
|
|
|
env->GetFloatArrayRegion((jfloatArray)arr, 0, count, ret.ptrw());
|
2014-09-02 23:13:40 -03:00
|
|
|
|
|
|
|
var = ret;
|
|
|
|
return true;
|
|
|
|
} break;
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_DOUBLE: {
|
2025-02-27 16:50:51 -06:00
|
|
|
PackedFloat64Array ret;
|
2014-09-02 23:13:40 -03:00
|
|
|
jobjectArray arr = (jobjectArray)obj;
|
|
|
|
|
|
|
|
int count = env->GetArrayLength(arr);
|
2025-02-27 16:50:51 -06:00
|
|
|
ret.resize(count);
|
|
|
|
env->GetDoubleArrayRegion((jdoubleArray)arr, 0, count, ret.ptrw());
|
2014-09-02 23:13:40 -03:00
|
|
|
|
|
|
|
var = ret;
|
|
|
|
return true;
|
|
|
|
} break;
|
|
|
|
case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_BOOLEAN: {
|
|
|
|
Array ret;
|
|
|
|
jobjectArray arr = (jobjectArray)obj;
|
|
|
|
|
|
|
|
int count = env->GetArrayLength(arr);
|
2025-02-27 16:50:51 -06:00
|
|
|
ret.resize(count);
|
2014-09-02 23:13:40 -03:00
|
|
|
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
jobject o = env->GetObjectArrayElement(arr, i);
|
2025-02-27 16:50:51 -06:00
|
|
|
if (o) {
|
2014-09-02 23:13:40 -03:00
|
|
|
bool val = env->CallBooleanMethod(o, JavaClassWrapper::singleton->Boolean_booleanValue);
|
2025-02-27 16:50:51 -06:00
|
|
|
ret[i] = val;
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
|
|
|
env->DeleteLocalRef(o);
|
|
|
|
}
|
|
|
|
|
|
|
|
var = ret;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
} break;
|
|
|
|
case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_BYTE: {
|
2025-02-27 16:50:51 -06:00
|
|
|
PackedByteArray ret;
|
2014-09-02 23:13:40 -03:00
|
|
|
jobjectArray arr = (jobjectArray)obj;
|
|
|
|
|
|
|
|
int count = env->GetArrayLength(arr);
|
2025-02-27 16:50:51 -06:00
|
|
|
ret.resize(count);
|
2014-09-02 23:13:40 -03:00
|
|
|
|
2025-02-27 16:50:51 -06:00
|
|
|
uint8_t *ptr = ret.ptrw();
|
2014-09-02 23:13:40 -03:00
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
jobject o = env->GetObjectArrayElement(arr, i);
|
2022-02-16 13:56:32 +01:00
|
|
|
if (!o) {
|
2025-02-27 16:50:51 -06:00
|
|
|
ptr[i] = 0;
|
2022-02-16 13:56:32 +01:00
|
|
|
} else {
|
2014-09-02 23:13:40 -03:00
|
|
|
int val = env->CallByteMethod(o, JavaClassWrapper::singleton->Byte_byteValue);
|
2025-02-27 16:50:51 -06:00
|
|
|
ptr[i] = (uint8_t)val;
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
|
|
|
env->DeleteLocalRef(o);
|
|
|
|
}
|
|
|
|
|
|
|
|
var = ret;
|
|
|
|
return true;
|
|
|
|
} break;
|
|
|
|
case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_CHAR: {
|
2025-02-27 16:50:51 -06:00
|
|
|
PackedByteArray ret;
|
2014-09-02 23:13:40 -03:00
|
|
|
jobjectArray arr = (jobjectArray)obj;
|
|
|
|
|
|
|
|
int count = env->GetArrayLength(arr);
|
2025-02-27 16:50:51 -06:00
|
|
|
// Char arrays are UTF-16 encoded, so it's double the length.
|
|
|
|
ret.resize(count * 2);
|
2014-09-02 23:13:40 -03:00
|
|
|
|
2025-02-27 16:50:51 -06:00
|
|
|
jchar *ptr = (jchar *)ret.ptrw();
|
2014-09-02 23:13:40 -03:00
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
jobject o = env->GetObjectArrayElement(arr, i);
|
2022-02-16 13:56:32 +01:00
|
|
|
if (!o) {
|
2025-02-27 16:50:51 -06:00
|
|
|
count = i;
|
|
|
|
break;
|
2022-02-16 13:56:32 +01:00
|
|
|
} else {
|
2014-09-02 23:13:40 -03:00
|
|
|
int val = env->CallCharMethod(o, JavaClassWrapper::singleton->Character_characterValue);
|
2025-02-27 16:50:51 -06:00
|
|
|
ptr[i] = (jchar)val;
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
|
|
|
env->DeleteLocalRef(o);
|
|
|
|
}
|
|
|
|
|
|
|
|
var = ret;
|
|
|
|
return true;
|
|
|
|
} break;
|
|
|
|
case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_SHORT: {
|
2025-02-27 16:50:51 -06:00
|
|
|
PackedInt32Array ret;
|
2014-09-02 23:13:40 -03:00
|
|
|
jobjectArray arr = (jobjectArray)obj;
|
|
|
|
|
|
|
|
int count = env->GetArrayLength(arr);
|
2025-02-27 16:50:51 -06:00
|
|
|
ret.resize(count);
|
2014-09-02 23:13:40 -03:00
|
|
|
|
2025-02-27 16:50:51 -06:00
|
|
|
int32_t *ptr = ret.ptrw();
|
2014-09-02 23:13:40 -03:00
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
jobject o = env->GetObjectArrayElement(arr, i);
|
2022-02-16 13:56:32 +01:00
|
|
|
if (!o) {
|
2025-02-27 16:50:51 -06:00
|
|
|
ptr[i] = 0;
|
2022-02-16 13:56:32 +01:00
|
|
|
} else {
|
2014-09-02 23:13:40 -03:00
|
|
|
int val = env->CallShortMethod(o, JavaClassWrapper::singleton->Short_shortValue);
|
2025-02-27 16:50:51 -06:00
|
|
|
ptr[i] = val;
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
|
|
|
env->DeleteLocalRef(o);
|
|
|
|
}
|
|
|
|
|
|
|
|
var = ret;
|
|
|
|
return true;
|
|
|
|
} break;
|
|
|
|
case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_INT: {
|
2025-02-27 16:50:51 -06:00
|
|
|
PackedInt32Array ret;
|
2014-09-02 23:13:40 -03:00
|
|
|
jobjectArray arr = (jobjectArray)obj;
|
|
|
|
|
|
|
|
int count = env->GetArrayLength(arr);
|
2025-02-27 16:50:51 -06:00
|
|
|
ret.resize(count);
|
2014-09-02 23:13:40 -03:00
|
|
|
|
2025-02-27 16:50:51 -06:00
|
|
|
int32_t *ptr = ret.ptrw();
|
2014-09-02 23:13:40 -03:00
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
jobject o = env->GetObjectArrayElement(arr, i);
|
2022-02-16 13:56:32 +01:00
|
|
|
if (!o) {
|
2025-02-27 16:50:51 -06:00
|
|
|
ptr[i] = 0;
|
2022-02-16 13:56:32 +01:00
|
|
|
} else {
|
2014-09-02 23:13:40 -03:00
|
|
|
int val = env->CallIntMethod(o, JavaClassWrapper::singleton->Integer_integerValue);
|
2025-02-27 16:50:51 -06:00
|
|
|
ptr[i] = val;
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
|
|
|
env->DeleteLocalRef(o);
|
|
|
|
}
|
|
|
|
|
|
|
|
var = ret;
|
|
|
|
return true;
|
|
|
|
} break;
|
|
|
|
case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_LONG: {
|
2025-02-27 16:50:51 -06:00
|
|
|
PackedInt64Array ret;
|
2014-09-02 23:13:40 -03:00
|
|
|
jobjectArray arr = (jobjectArray)obj;
|
|
|
|
|
|
|
|
int count = env->GetArrayLength(arr);
|
2025-02-27 16:50:51 -06:00
|
|
|
ret.resize(count);
|
2014-09-02 23:13:40 -03:00
|
|
|
|
2025-02-27 16:50:51 -06:00
|
|
|
int64_t *ptr = ret.ptrw();
|
2014-09-02 23:13:40 -03:00
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
jobject o = env->GetObjectArrayElement(arr, i);
|
2022-02-16 13:56:32 +01:00
|
|
|
if (!o) {
|
2025-02-27 16:50:51 -06:00
|
|
|
ptr[i] = 0;
|
2022-02-16 13:56:32 +01:00
|
|
|
} else {
|
2014-09-02 23:13:40 -03:00
|
|
|
int64_t val = env->CallLongMethod(o, JavaClassWrapper::singleton->Long_longValue);
|
2025-02-27 16:50:51 -06:00
|
|
|
ptr[i] = val;
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
|
|
|
env->DeleteLocalRef(o);
|
|
|
|
}
|
|
|
|
|
|
|
|
var = ret;
|
|
|
|
return true;
|
|
|
|
} break;
|
|
|
|
case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_FLOAT: {
|
2025-02-27 16:50:51 -06:00
|
|
|
PackedFloat32Array ret;
|
2014-09-02 23:13:40 -03:00
|
|
|
jobjectArray arr = (jobjectArray)obj;
|
|
|
|
|
|
|
|
int count = env->GetArrayLength(arr);
|
2025-02-27 16:50:51 -06:00
|
|
|
ret.resize(count);
|
2014-09-02 23:13:40 -03:00
|
|
|
|
2025-02-27 16:50:51 -06:00
|
|
|
float *ptr = ret.ptrw();
|
2014-09-02 23:13:40 -03:00
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
jobject o = env->GetObjectArrayElement(arr, i);
|
2022-02-16 13:56:32 +01:00
|
|
|
if (!o) {
|
2025-02-27 16:50:51 -06:00
|
|
|
ptr[i] = 0.0;
|
2022-02-16 13:56:32 +01:00
|
|
|
} else {
|
2014-09-02 23:13:40 -03:00
|
|
|
float val = env->CallFloatMethod(o, JavaClassWrapper::singleton->Float_floatValue);
|
2025-02-27 16:50:51 -06:00
|
|
|
ptr[i] = val;
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
|
|
|
env->DeleteLocalRef(o);
|
|
|
|
}
|
|
|
|
|
|
|
|
var = ret;
|
|
|
|
return true;
|
|
|
|
} break;
|
|
|
|
case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_DOUBLE: {
|
2025-02-27 16:50:51 -06:00
|
|
|
PackedFloat64Array ret;
|
2014-09-02 23:13:40 -03:00
|
|
|
jobjectArray arr = (jobjectArray)obj;
|
|
|
|
|
|
|
|
int count = env->GetArrayLength(arr);
|
2025-02-27 16:50:51 -06:00
|
|
|
ret.resize(count);
|
2014-09-02 23:13:40 -03:00
|
|
|
|
2025-02-27 16:50:51 -06:00
|
|
|
double *ptr = ret.ptrw();
|
2014-09-02 23:13:40 -03:00
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
jobject o = env->GetObjectArrayElement(arr, i);
|
2022-02-16 13:56:32 +01:00
|
|
|
if (!o) {
|
2025-02-27 16:50:51 -06:00
|
|
|
ptr[i] = 0.0;
|
2022-02-16 13:56:32 +01:00
|
|
|
} else {
|
2014-09-02 23:13:40 -03:00
|
|
|
double val = env->CallDoubleMethod(o, JavaClassWrapper::singleton->Double_doubleValue);
|
2025-02-27 16:50:51 -06:00
|
|
|
ptr[i] = val;
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
|
|
|
env->DeleteLocalRef(o);
|
|
|
|
}
|
|
|
|
|
|
|
|
var = ret;
|
|
|
|
return true;
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_STRING: {
|
2025-02-27 16:50:51 -06:00
|
|
|
PackedStringArray ret;
|
2014-09-02 23:13:40 -03:00
|
|
|
jobjectArray arr = (jobjectArray)obj;
|
|
|
|
|
|
|
|
int count = env->GetArrayLength(arr);
|
2025-02-27 16:50:51 -06:00
|
|
|
ret.resize(count);
|
2014-09-02 23:13:40 -03:00
|
|
|
|
2025-02-27 16:50:51 -06:00
|
|
|
String *ptr = ret.ptrw();
|
2014-09-02 23:13:40 -03:00
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
jobject o = env->GetObjectArrayElement(arr, i);
|
2025-02-27 16:50:51 -06:00
|
|
|
if (o) {
|
2019-02-26 23:54:45 +03:00
|
|
|
String val = jstring_to_string((jstring)o, env);
|
2025-02-27 16:50:51 -06:00
|
|
|
ptr[i] = val;
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
|
|
|
env->DeleteLocalRef(o);
|
|
|
|
}
|
|
|
|
|
|
|
|
var = ret;
|
|
|
|
return true;
|
|
|
|
} break;
|
2024-11-20 10:10:17 -08:00
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_CHARSEQUENCE: {
|
2025-02-27 16:50:51 -06:00
|
|
|
PackedStringArray ret;
|
2024-11-20 10:10:17 -08:00
|
|
|
jobjectArray arr = (jobjectArray)obj;
|
|
|
|
|
|
|
|
int count = env->GetArrayLength(arr);
|
2025-02-27 16:50:51 -06:00
|
|
|
ret.resize(count);
|
2024-11-20 10:10:17 -08:00
|
|
|
|
2025-02-27 16:50:51 -06:00
|
|
|
String *ptr = ret.ptrw();
|
2024-11-20 10:10:17 -08:00
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
jobject o = env->GetObjectArrayElement(arr, i);
|
2025-02-27 16:50:51 -06:00
|
|
|
if (o) {
|
2024-11-20 10:10:17 -08:00
|
|
|
String val = charsequence_to_string(env, o);
|
2025-02-27 16:50:51 -06:00
|
|
|
ptr[i] = val;
|
2024-11-20 10:10:17 -08:00
|
|
|
}
|
|
|
|
env->DeleteLocalRef(o);
|
|
|
|
}
|
|
|
|
|
|
|
|
var = ret;
|
|
|
|
return true;
|
|
|
|
} break;
|
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_CALLABLE: {
|
|
|
|
Array ret;
|
|
|
|
jobjectArray jarr = (jobjectArray)obj;
|
|
|
|
int count = env->GetArrayLength(jarr);
|
2025-02-27 16:50:51 -06:00
|
|
|
ret.resize(count);
|
2024-11-20 10:10:17 -08:00
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
jobject o = env->GetObjectArrayElement(jarr, i);
|
2025-02-27 16:50:51 -06:00
|
|
|
if (o) {
|
2024-11-20 10:10:17 -08:00
|
|
|
Callable callable = jcallable_to_callable(env, o);
|
2025-02-27 16:50:51 -06:00
|
|
|
ret[i] = callable;
|
2024-11-20 10:10:17 -08:00
|
|
|
}
|
|
|
|
env->DeleteLocalRef(o);
|
|
|
|
}
|
|
|
|
|
|
|
|
var = ret;
|
|
|
|
return true;
|
|
|
|
} break;
|
2014-09-02 23:13:40 -03:00
|
|
|
case ARG_ARRAY_BIT | ARG_TYPE_CLASS: {
|
2025-02-27 16:50:51 -06:00
|
|
|
Array ret;
|
|
|
|
jobjectArray jarr = (jobjectArray)obj;
|
|
|
|
int count = env->GetArrayLength(jarr);
|
|
|
|
ret.resize(count);
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
jobject obj = env->GetObjectArrayElement(jarr, i);
|
|
|
|
if (obj) {
|
|
|
|
jclass java_class = env->GetObjectClass(obj);
|
|
|
|
Ref<JavaClass> java_class_wrapped = JavaClassWrapper::singleton->wrap_jclass(java_class);
|
|
|
|
env->DeleteLocalRef(java_class);
|
|
|
|
|
|
|
|
if (java_class_wrapped.is_valid()) {
|
2025-03-06 16:52:40 -06:00
|
|
|
String cn = java_class_wrapped->get_java_class_name();
|
2025-03-14 18:01:12 -05:00
|
|
|
if (cn == "org.godotengine.godot.Dictionary" || cn == "java.util.HashMap") {
|
2025-03-06 16:52:40 -06:00
|
|
|
ret[i] = _jobject_to_variant(env, obj);
|
|
|
|
} else {
|
|
|
|
Ref<JavaObject> java_obj_wrapped = Ref<JavaObject>(memnew(JavaObject(java_class_wrapped, obj)));
|
|
|
|
ret[i] = java_obj_wrapped;
|
|
|
|
}
|
2025-02-27 16:50:51 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
env->DeleteLocalRef(obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
var = ret;
|
|
|
|
return true;
|
2014-09-02 23:13:40 -03:00
|
|
|
} break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-09-26 08:41:46 -07:00
|
|
|
Ref<JavaClass> JavaClassWrapper::_wrap(const String &p_class, bool p_allow_private_methods_access) {
|
2024-05-28 12:15:00 +02:00
|
|
|
String class_name_dots = p_class.replace_char('/', '.');
|
2024-08-20 16:53:57 -05:00
|
|
|
if (class_cache.has(class_name_dots)) {
|
|
|
|
return class_cache[class_name_dots];
|
2022-02-16 13:56:32 +01:00
|
|
|
}
|
2014-09-02 23:13:40 -03:00
|
|
|
|
2021-01-19 13:29:41 +01:00
|
|
|
JNIEnv *env = get_jni_env();
|
2022-05-30 22:13:49 +01:00
|
|
|
ERR_FAIL_NULL_V(env, Ref<JavaClass>());
|
2014-09-02 23:13:40 -03:00
|
|
|
|
2024-05-28 12:15:00 +02:00
|
|
|
jclass bclass = env->FindClass(class_name_dots.replace_char('.', '/').utf8().get_data());
|
2025-03-04 21:14:05 +08:00
|
|
|
ERR_FAIL_NULL_V_MSG(bclass, Ref<JavaClass>(), vformat("Java class '%s' not found.", p_class));
|
2014-09-02 23:13:40 -03:00
|
|
|
|
2024-08-20 16:53:57 -05:00
|
|
|
jobjectArray constructors = (jobjectArray)env->CallObjectMethod(bclass, Class_getDeclaredConstructors);
|
|
|
|
ERR_FAIL_NULL_V(constructors, Ref<JavaClass>());
|
2014-09-02 23:13:40 -03:00
|
|
|
|
2024-08-20 16:53:57 -05:00
|
|
|
jobjectArray methods = (jobjectArray)env->CallObjectMethod(bclass, Class_getDeclaredMethods);
|
2022-05-30 22:13:49 +01:00
|
|
|
ERR_FAIL_NULL_V(methods, Ref<JavaClass>());
|
2014-09-02 23:13:40 -03:00
|
|
|
|
|
|
|
Ref<JavaClass> java_class = memnew(JavaClass);
|
2024-08-20 16:53:57 -05:00
|
|
|
java_class->java_class_name = class_name_dots;
|
|
|
|
Vector<String> class_name_parts = class_name_dots.split(".");
|
|
|
|
java_class->java_constructor_name = class_name_parts[class_name_parts.size() - 1];
|
|
|
|
java_class->_class = (jclass)env->NewGlobalRef(bclass);
|
|
|
|
class_cache[class_name_dots] = java_class;
|
|
|
|
|
|
|
|
LocalVector<jobject> methods_and_constructors;
|
|
|
|
int constructor_count = env->GetArrayLength(constructors);
|
|
|
|
int method_count = env->GetArrayLength(methods);
|
|
|
|
methods_and_constructors.resize(method_count + constructor_count);
|
|
|
|
for (int i = 0; i < constructor_count; i++) {
|
|
|
|
methods_and_constructors[i] = env->GetObjectArrayElement(constructors, i);
|
|
|
|
}
|
|
|
|
for (int i = 0; i < method_count; i++) {
|
|
|
|
methods_and_constructors[constructor_count + i] = env->GetObjectArrayElement(methods, i);
|
|
|
|
}
|
2014-09-02 23:13:40 -03:00
|
|
|
|
2024-08-20 16:53:57 -05:00
|
|
|
for (int i = 0; i < (int)methods_and_constructors.size(); i++) {
|
|
|
|
jobject obj = methods_and_constructors[i];
|
2014-09-02 23:13:40 -03:00
|
|
|
ERR_CONTINUE(!obj);
|
|
|
|
|
2024-08-20 16:53:57 -05:00
|
|
|
bool is_constructor = i < constructor_count;
|
|
|
|
|
|
|
|
String str_method;
|
|
|
|
if (is_constructor) {
|
|
|
|
str_method = "<init>";
|
|
|
|
} else {
|
|
|
|
jstring name = (jstring)env->CallObjectMethod(obj, Method_getName);
|
|
|
|
str_method = jstring_to_string(name, env);
|
|
|
|
env->DeleteLocalRef(name);
|
|
|
|
}
|
2014-09-02 23:13:40 -03:00
|
|
|
|
|
|
|
Vector<String> params;
|
|
|
|
|
2024-08-20 16:53:57 -05:00
|
|
|
jint mods = env->CallIntMethod(obj, is_constructor ? Constructor_getModifiers : Method_getModifiers);
|
2014-09-02 23:13:40 -03:00
|
|
|
|
2024-09-26 08:41:46 -07:00
|
|
|
if (!(mods & 0x0001) && (is_constructor || !p_allow_private_methods_access)) {
|
2014-09-02 23:13:40 -03:00
|
|
|
env->DeleteLocalRef(obj);
|
|
|
|
continue; //not public bye
|
|
|
|
}
|
|
|
|
|
2024-08-20 16:53:57 -05:00
|
|
|
jobjectArray param_types = (jobjectArray)env->CallObjectMethod(obj, is_constructor ? Constructor_getParameterTypes : Method_getParameterTypes);
|
|
|
|
int count = env->GetArrayLength(param_types);
|
2014-09-02 23:13:40 -03:00
|
|
|
|
|
|
|
if (!java_class->methods.has(str_method)) {
|
|
|
|
java_class->methods[str_method] = List<JavaClass::MethodInfo>();
|
|
|
|
}
|
|
|
|
|
|
|
|
JavaClass::MethodInfo mi;
|
|
|
|
mi._static = (mods & 0x8) != 0;
|
2024-08-20 16:53:57 -05:00
|
|
|
mi._constructor = is_constructor;
|
2014-09-02 23:13:40 -03:00
|
|
|
bool valid = true;
|
|
|
|
String signature = "(";
|
|
|
|
|
2024-08-20 16:53:57 -05:00
|
|
|
for (int j = 0; j < count; j++) {
|
2014-09-02 23:13:40 -03:00
|
|
|
jobject obj2 = env->GetObjectArrayElement(param_types, j);
|
|
|
|
String strsig;
|
|
|
|
uint32_t sig = 0;
|
|
|
|
if (!_get_type_sig(env, obj2, sig, strsig)) {
|
|
|
|
valid = false;
|
|
|
|
env->DeleteLocalRef(obj2);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
signature += strsig;
|
|
|
|
mi.param_types.push_back(sig);
|
|
|
|
mi.param_sigs.push_back(strsig);
|
|
|
|
env->DeleteLocalRef(obj2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!valid) {
|
2024-08-20 16:53:57 -05:00
|
|
|
print_line("Method can't be bound (unsupported arguments): " + class_name_dots + "::" + str_method);
|
2014-09-02 23:13:40 -03:00
|
|
|
env->DeleteLocalRef(obj);
|
|
|
|
env->DeleteLocalRef(param_types);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
signature += ")";
|
|
|
|
|
2024-08-20 16:53:57 -05:00
|
|
|
if (is_constructor) {
|
|
|
|
signature += "V";
|
|
|
|
mi.return_type = JavaClass::ARG_TYPE_CLASS;
|
|
|
|
} else {
|
|
|
|
jobject return_type = (jobject)env->CallObjectMethod(obj, Method_getReturnType);
|
|
|
|
|
|
|
|
String strsig;
|
|
|
|
uint32_t sig = 0;
|
|
|
|
if (!_get_type_sig(env, return_type, sig, strsig)) {
|
|
|
|
print_line("Method can't be bound (unsupported return type): " + class_name_dots + "::" + str_method);
|
|
|
|
env->DeleteLocalRef(obj);
|
|
|
|
env->DeleteLocalRef(param_types);
|
|
|
|
env->DeleteLocalRef(return_type);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
signature += strsig;
|
|
|
|
mi.return_type = sig;
|
2014-09-02 23:13:40 -03:00
|
|
|
|
|
|
|
env->DeleteLocalRef(return_type);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool discard = false;
|
|
|
|
|
|
|
|
for (List<JavaClass::MethodInfo>::Element *E = java_class->methods[str_method].front(); E; E = E->next()) {
|
|
|
|
float new_likeliness = 0;
|
|
|
|
float existing_likeliness = 0;
|
|
|
|
|
2022-02-16 13:56:32 +01:00
|
|
|
if (E->get().param_types.size() != mi.param_types.size()) {
|
2014-09-02 23:13:40 -03:00
|
|
|
continue;
|
2022-02-16 13:56:32 +01:00
|
|
|
}
|
2022-05-30 22:13:49 +01:00
|
|
|
bool this_valid = true;
|
2014-09-02 23:13:40 -03:00
|
|
|
for (int j = 0; j < E->get().param_types.size(); j++) {
|
|
|
|
Variant::Type _new;
|
|
|
|
float new_l;
|
|
|
|
Variant::Type existing;
|
|
|
|
float existing_l;
|
|
|
|
JavaClass::_convert_to_variant_type(E->get().param_types[j], existing, existing_l);
|
|
|
|
JavaClass::_convert_to_variant_type(mi.param_types[j], _new, new_l);
|
|
|
|
if (_new != existing) {
|
2022-05-30 22:13:49 +01:00
|
|
|
this_valid = false;
|
2014-09-02 23:13:40 -03:00
|
|
|
break;
|
2025-05-28 11:21:22 -05:00
|
|
|
} else if ((_new == Variant::OBJECT || _new == Variant::ARRAY) && E->get().param_sigs[j] != mi.param_sigs[j]) {
|
|
|
|
this_valid = false;
|
|
|
|
break;
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
|
|
|
new_likeliness += new_l;
|
2025-05-28 11:21:22 -05:00
|
|
|
existing_likeliness += existing_l;
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
|
|
|
|
2022-05-30 22:13:49 +01:00
|
|
|
if (!this_valid) {
|
2014-09-02 23:13:40 -03:00
|
|
|
continue;
|
2022-02-16 13:56:32 +01:00
|
|
|
}
|
2014-09-02 23:13:40 -03:00
|
|
|
|
|
|
|
if (new_likeliness > existing_likeliness) {
|
|
|
|
java_class->methods[str_method].erase(E);
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
discard = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!discard) {
|
2022-02-16 13:56:32 +01:00
|
|
|
if (mi._static) {
|
2014-09-02 23:13:40 -03:00
|
|
|
mi.method = env->GetStaticMethodID(bclass, str_method.utf8().get_data(), signature.utf8().get_data());
|
2022-02-16 13:56:32 +01:00
|
|
|
} else {
|
2014-09-02 23:13:40 -03:00
|
|
|
mi.method = env->GetMethodID(bclass, str_method.utf8().get_data(), signature.utf8().get_data());
|
2022-02-16 13:56:32 +01:00
|
|
|
}
|
2014-09-02 23:13:40 -03:00
|
|
|
|
|
|
|
ERR_CONTINUE(!mi.method);
|
|
|
|
|
|
|
|
java_class->methods[str_method].push_back(mi);
|
|
|
|
}
|
|
|
|
|
|
|
|
env->DeleteLocalRef(obj);
|
|
|
|
env->DeleteLocalRef(param_types);
|
2022-02-16 13:56:32 +01:00
|
|
|
}
|
2014-09-02 23:13:40 -03:00
|
|
|
|
2024-08-20 16:53:57 -05:00
|
|
|
env->DeleteLocalRef(constructors);
|
2014-09-02 23:13:40 -03:00
|
|
|
env->DeleteLocalRef(methods);
|
|
|
|
|
2024-08-20 16:53:57 -05:00
|
|
|
jobjectArray fields = (jobjectArray)env->CallObjectMethod(bclass, Class_getFields);
|
2014-09-02 23:13:40 -03:00
|
|
|
|
2024-08-20 16:53:57 -05:00
|
|
|
int count = env->GetArrayLength(fields);
|
2014-09-02 23:13:40 -03:00
|
|
|
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
jobject obj = env->GetObjectArrayElement(fields, i);
|
|
|
|
ERR_CONTINUE(!obj);
|
|
|
|
|
|
|
|
jstring name = (jstring)env->CallObjectMethod(obj, Field_getName);
|
2019-02-26 23:54:45 +03:00
|
|
|
String str_field = jstring_to_string(name, env);
|
2014-09-02 23:13:40 -03:00
|
|
|
env->DeleteLocalRef(name);
|
|
|
|
int mods = env->CallIntMethod(obj, Field_getModifiers);
|
|
|
|
if ((mods & 0x8) && (mods & 0x10) && (mods & 0x1)) { //static final public!
|
|
|
|
|
2020-04-02 01:20:12 +02:00
|
|
|
jobject objc = env->CallObjectMethod(obj, Field_get, nullptr);
|
2014-09-02 23:13:40 -03:00
|
|
|
if (objc) {
|
|
|
|
uint32_t sig;
|
|
|
|
String strsig;
|
|
|
|
jclass cl = env->GetObjectClass(objc);
|
|
|
|
if (JavaClassWrapper::_get_type_sig(env, cl, sig, strsig)) {
|
|
|
|
if ((sig & JavaClass::ARG_TYPE_MASK) <= JavaClass::ARG_TYPE_STRING) {
|
|
|
|
Variant value;
|
|
|
|
if (JavaClass::_convert_object_to_variant(env, objc, value, sig)) {
|
|
|
|
java_class->constant_map[str_field] = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
env->DeleteLocalRef(cl);
|
|
|
|
}
|
|
|
|
|
|
|
|
env->DeleteLocalRef(objc);
|
|
|
|
}
|
|
|
|
env->DeleteLocalRef(obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
env->DeleteLocalRef(fields);
|
|
|
|
|
2024-08-20 16:53:57 -05:00
|
|
|
return java_class;
|
|
|
|
}
|
|
|
|
|
2024-09-26 08:41:46 -07:00
|
|
|
Ref<JavaClass> JavaClassWrapper::wrap_jclass(jclass p_class, bool p_allow_private_methods_access) {
|
2024-08-20 16:53:57 -05:00
|
|
|
JNIEnv *env = get_jni_env();
|
|
|
|
ERR_FAIL_NULL_V(env, Ref<JavaClass>());
|
|
|
|
|
|
|
|
jstring class_name = (jstring)env->CallObjectMethod(p_class, Class_getName);
|
|
|
|
String class_name_string = jstring_to_string(class_name, env);
|
|
|
|
env->DeleteLocalRef(class_name);
|
|
|
|
|
2024-09-26 08:41:46 -07:00
|
|
|
return _wrap(class_name_string, p_allow_private_methods_access);
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|
|
|
|
|
2020-04-02 01:20:12 +02:00
|
|
|
JavaClassWrapper *JavaClassWrapper::singleton = nullptr;
|
2014-09-02 23:13:40 -03:00
|
|
|
|
2024-09-26 08:41:46 -07:00
|
|
|
JavaClassWrapper::JavaClassWrapper() {
|
2014-09-02 23:13:40 -03:00
|
|
|
singleton = this;
|
|
|
|
|
2021-01-19 13:29:41 +01:00
|
|
|
JNIEnv *env = get_jni_env();
|
2022-05-30 22:13:49 +01:00
|
|
|
ERR_FAIL_NULL(env);
|
2014-09-02 23:13:40 -03:00
|
|
|
|
|
|
|
jclass bclass = env->FindClass("java/lang/Class");
|
2024-08-20 16:53:57 -05:00
|
|
|
Class_getDeclaredConstructors = env->GetMethodID(bclass, "getDeclaredConstructors", "()[Ljava/lang/reflect/Constructor;");
|
|
|
|
Class_getDeclaredMethods = env->GetMethodID(bclass, "getDeclaredMethods", "()[Ljava/lang/reflect/Method;");
|
|
|
|
Class_getFields = env->GetMethodID(bclass, "getFields", "()[Ljava/lang/reflect/Field;");
|
2014-09-02 23:13:40 -03:00
|
|
|
Class_getName = env->GetMethodID(bclass, "getName", "()Ljava/lang/String;");
|
2024-08-20 16:53:57 -05:00
|
|
|
Class_getSuperclass = env->GetMethodID(bclass, "getSuperclass", "()Ljava/lang/Class;");
|
|
|
|
env->DeleteLocalRef(bclass);
|
|
|
|
|
|
|
|
bclass = env->FindClass("java/lang/reflect/Constructor");
|
|
|
|
Constructor_getParameterTypes = env->GetMethodID(bclass, "getParameterTypes", "()[Ljava/lang/Class;");
|
|
|
|
Constructor_getModifiers = env->GetMethodID(bclass, "getModifiers", "()I");
|
2024-04-15 10:30:18 -07:00
|
|
|
env->DeleteLocalRef(bclass);
|
2022-05-30 22:13:49 +01:00
|
|
|
|
2014-09-02 23:13:40 -03:00
|
|
|
bclass = env->FindClass("java/lang/reflect/Method");
|
2024-08-20 16:53:57 -05:00
|
|
|
Method_getParameterTypes = env->GetMethodID(bclass, "getParameterTypes", "()[Ljava/lang/Class;");
|
|
|
|
Method_getReturnType = env->GetMethodID(bclass, "getReturnType", "()Ljava/lang/Class;");
|
|
|
|
Method_getName = env->GetMethodID(bclass, "getName", "()Ljava/lang/String;");
|
|
|
|
Method_getModifiers = env->GetMethodID(bclass, "getModifiers", "()I");
|
2024-04-15 10:30:18 -07:00
|
|
|
env->DeleteLocalRef(bclass);
|
2022-05-30 22:13:49 +01:00
|
|
|
|
2014-09-02 23:13:40 -03:00
|
|
|
bclass = env->FindClass("java/lang/reflect/Field");
|
|
|
|
Field_getName = env->GetMethodID(bclass, "getName", "()Ljava/lang/String;");
|
|
|
|
Field_getModifiers = env->GetMethodID(bclass, "getModifiers", "()I");
|
|
|
|
Field_get = env->GetMethodID(bclass, "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
|
2024-04-15 10:30:18 -07:00
|
|
|
env->DeleteLocalRef(bclass);
|
2022-05-30 22:13:49 +01:00
|
|
|
|
2014-09-02 23:13:40 -03:00
|
|
|
bclass = env->FindClass("java/lang/Boolean");
|
|
|
|
Boolean_booleanValue = env->GetMethodID(bclass, "booleanValue", "()Z");
|
2024-04-15 10:30:18 -07:00
|
|
|
env->DeleteLocalRef(bclass);
|
2014-09-02 23:13:40 -03:00
|
|
|
|
|
|
|
bclass = env->FindClass("java/lang/Byte");
|
|
|
|
Byte_byteValue = env->GetMethodID(bclass, "byteValue", "()B");
|
2024-04-15 10:30:18 -07:00
|
|
|
env->DeleteLocalRef(bclass);
|
2014-09-02 23:13:40 -03:00
|
|
|
|
|
|
|
bclass = env->FindClass("java/lang/Character");
|
|
|
|
Character_characterValue = env->GetMethodID(bclass, "charValue", "()C");
|
2024-04-15 10:30:18 -07:00
|
|
|
env->DeleteLocalRef(bclass);
|
2014-09-02 23:13:40 -03:00
|
|
|
|
|
|
|
bclass = env->FindClass("java/lang/Short");
|
|
|
|
Short_shortValue = env->GetMethodID(bclass, "shortValue", "()S");
|
2024-04-15 10:30:18 -07:00
|
|
|
env->DeleteLocalRef(bclass);
|
2014-09-02 23:13:40 -03:00
|
|
|
|
|
|
|
bclass = env->FindClass("java/lang/Integer");
|
|
|
|
Integer_integerValue = env->GetMethodID(bclass, "intValue", "()I");
|
2024-04-15 10:30:18 -07:00
|
|
|
env->DeleteLocalRef(bclass);
|
2014-09-02 23:13:40 -03:00
|
|
|
|
|
|
|
bclass = env->FindClass("java/lang/Long");
|
|
|
|
Long_longValue = env->GetMethodID(bclass, "longValue", "()J");
|
2024-04-15 10:30:18 -07:00
|
|
|
env->DeleteLocalRef(bclass);
|
2014-09-02 23:13:40 -03:00
|
|
|
|
|
|
|
bclass = env->FindClass("java/lang/Float");
|
|
|
|
Float_floatValue = env->GetMethodID(bclass, "floatValue", "()F");
|
2024-04-15 10:30:18 -07:00
|
|
|
env->DeleteLocalRef(bclass);
|
2014-09-02 23:13:40 -03:00
|
|
|
|
|
|
|
bclass = env->FindClass("java/lang/Double");
|
|
|
|
Double_doubleValue = env->GetMethodID(bclass, "doubleValue", "()D");
|
2024-04-15 10:30:18 -07:00
|
|
|
env->DeleteLocalRef(bclass);
|
2014-09-02 23:13:40 -03:00
|
|
|
}
|