1/**************************************************************************/
2/* jni_singleton.h */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#ifndef JNI_SINGLETON_H
32#define JNI_SINGLETON_H
33
34#include "core/config/engine.h"
35#include "core/variant/variant.h"
36
37#ifdef ANDROID_ENABLED
38#include "jni_utils.h"
39#endif
40
41class JNISingleton : public Object {
42 GDCLASS(JNISingleton, Object);
43
44#ifdef ANDROID_ENABLED
45 struct MethodData {
46 jmethodID method;
47 Variant::Type ret_type;
48 Vector<Variant::Type> argtypes;
49 };
50
51 jobject instance;
52 RBMap<StringName, MethodData> method_map;
53#endif
54
55public:
56 virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override {
57#ifdef ANDROID_ENABLED
58 RBMap<StringName, MethodData>::Element *E = method_map.find(p_method);
59
60 // Check the method we're looking for is in the JNISingleton map and that
61 // the arguments match.
62 bool call_error = !E || E->get().argtypes.size() != p_argcount;
63 if (!call_error) {
64 for (int i = 0; i < p_argcount; i++) {
65 if (!Variant::can_convert(p_args[i]->get_type(), E->get().argtypes[i])) {
66 call_error = true;
67 break;
68 }
69 }
70 }
71
72 if (call_error) {
73 // The method is not in this map, defaulting to the regular instance calls.
74 return Object::callp(p_method, p_args, p_argcount, r_error);
75 }
76
77 ERR_FAIL_NULL_V(instance, Variant());
78
79 r_error.error = Callable::CallError::CALL_OK;
80
81 jvalue *v = nullptr;
82
83 if (p_argcount) {
84 v = (jvalue *)alloca(sizeof(jvalue) * p_argcount);
85 }
86
87 JNIEnv *env = get_jni_env();
88
89 int res = env->PushLocalFrame(16);
90
91 ERR_FAIL_COND_V(res != 0, Variant());
92
93 List<jobject> to_erase;
94 for (int i = 0; i < p_argcount; i++) {
95 jvalret vr = _variant_to_jvalue(env, E->get().argtypes[i], p_args[i]);
96 v[i] = vr.val;
97 if (vr.obj) {
98 to_erase.push_back(vr.obj);
99 }
100 }
101
102 Variant ret;
103
104 switch (E->get().ret_type) {
105 case Variant::NIL: {
106 env->CallVoidMethodA(instance, E->get().method, v);
107 } break;
108 case Variant::BOOL: {
109 ret = env->CallBooleanMethodA(instance, E->get().method, v) == JNI_TRUE;
110 } break;
111 case Variant::INT: {
112 ret = env->CallIntMethodA(instance, E->get().method, v);
113 } break;
114 case Variant::FLOAT: {
115 ret = env->CallFloatMethodA(instance, E->get().method, v);
116 } break;
117 case Variant::STRING: {
118 jobject o = env->CallObjectMethodA(instance, E->get().method, v);
119 ret = jstring_to_string((jstring)o, env);
120 env->DeleteLocalRef(o);
121 } break;
122 case Variant::PACKED_STRING_ARRAY: {
123 jobjectArray arr = (jobjectArray)env->CallObjectMethodA(instance, E->get().method, v);
124
125 ret = _jobject_to_variant(env, arr);
126
127 env->DeleteLocalRef(arr);
128 } break;
129 case Variant::PACKED_INT32_ARRAY: {
130 jintArray arr = (jintArray)env->CallObjectMethodA(instance, E->get().method, v);
131
132 int fCount = env->GetArrayLength(arr);
133 Vector<int> sarr;
134 sarr.resize(fCount);
135
136 int *w = sarr.ptrw();
137 env->GetIntArrayRegion(arr, 0, fCount, w);
138 ret = sarr;
139 env->DeleteLocalRef(arr);
140 } break;
141 case Variant::PACKED_INT64_ARRAY: {
142 jlongArray arr = (jlongArray)env->CallObjectMethodA(instance, E->get().method, v);
143
144 int fCount = env->GetArrayLength(arr);
145 Vector<int64_t> sarr;
146 sarr.resize(fCount);
147
148 int64_t *w = sarr.ptrw();
149 env->GetLongArrayRegion(arr, 0, fCount, w);
150 ret = sarr;
151 env->DeleteLocalRef(arr);
152 } break;
153 case Variant::PACKED_FLOAT32_ARRAY: {
154 jfloatArray arr = (jfloatArray)env->CallObjectMethodA(instance, E->get().method, v);
155
156 int fCount = env->GetArrayLength(arr);
157 Vector<float> sarr;
158 sarr.resize(fCount);
159
160 float *w = sarr.ptrw();
161 env->GetFloatArrayRegion(arr, 0, fCount, w);
162 ret = sarr;
163 env->DeleteLocalRef(arr);
164 } break;
165 case Variant::PACKED_FLOAT64_ARRAY: {
166 jdoubleArray arr = (jdoubleArray)env->CallObjectMethodA(instance, E->get().method, v);
167
168 int fCount = env->GetArrayLength(arr);
169 Vector<double> sarr;
170 sarr.resize(fCount);
171
172 double *w = sarr.ptrw();
173 env->GetDoubleArrayRegion(arr, 0, fCount, w);
174 ret = sarr;
175 env->DeleteLocalRef(arr);
176 } break;
177 case Variant::DICTIONARY: {
178 jobject obj = env->CallObjectMethodA(instance, E->get().method, v);
179 ret = _jobject_to_variant(env, obj);
180 env->DeleteLocalRef(obj);
181
182 } break;
183 default: {
184 env->PopLocalFrame(nullptr);
185 ERR_FAIL_V(Variant());
186 } break;
187 }
188
189 while (to_erase.size()) {
190 env->DeleteLocalRef(to_erase.front()->get());
191 to_erase.pop_front();
192 }
193
194 env->PopLocalFrame(nullptr);
195
196 return ret;
197#else // ANDROID_ENABLED
198
199 // Defaulting to the regular instance calls.
200 return Object::callp(p_method, p_args, p_argcount, r_error);
201#endif
202 }
203
204#ifdef ANDROID_ENABLED
205 jobject get_instance() const {
206 return instance;
207 }
208
209 void set_instance(jobject p_instance) {
210 instance = p_instance;
211 }
212
213 void add_method(const StringName &p_name, jmethodID p_method, const Vector<Variant::Type> &p_args, Variant::Type p_ret_type) {
214 MethodData md;
215 md.method = p_method;
216 md.argtypes = p_args;
217 md.ret_type = p_ret_type;
218 method_map[p_name] = md;
219 }
220
221 void add_signal(const StringName &p_name, const Vector<Variant::Type> &p_args) {
222 if (p_args.size() == 0) {
223 ADD_SIGNAL(MethodInfo(p_name));
224 } else if (p_args.size() == 1) {
225 ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1")));
226 } else if (p_args.size() == 2) {
227 ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"), PropertyInfo(p_args[1], "arg2")));
228 } else if (p_args.size() == 3) {
229 ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"), PropertyInfo(p_args[1], "arg2"), PropertyInfo(p_args[2], "arg3")));
230 } else if (p_args.size() == 4) {
231 ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"), PropertyInfo(p_args[1], "arg2"), PropertyInfo(p_args[2], "arg3"), PropertyInfo(p_args[3], "arg4")));
232 } else if (p_args.size() == 5) {
233 ADD_SIGNAL(MethodInfo(p_name, PropertyInfo(p_args[0], "arg1"), PropertyInfo(p_args[1], "arg2"), PropertyInfo(p_args[2], "arg3"), PropertyInfo(p_args[3], "arg4"), PropertyInfo(p_args[4], "arg5")));
234 }
235 }
236
237#endif
238
239 JNISingleton() {
240#ifdef ANDROID_ENABLED
241 instance = nullptr;
242#endif
243 }
244};
245
246#endif // JNI_SINGLETON_H
247