1 /*************************************************************************/
2 /*  gd_mono_method_thunk.h                                               */
3 /*************************************************************************/
4 /*                       This file is part of:                           */
5 /*                           GODOT ENGINE                                */
6 /*                      https://godotengine.org                          */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
9 /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
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 GD_MONO_METHOD_THUNK_H
32 #define GD_MONO_METHOD_THUNK_H
33 
34 #include <type_traits>
35 
36 #include "gd_mono_class.h"
37 #include "gd_mono_header.h"
38 #include "gd_mono_marshal.h"
39 #include "gd_mono_method.h"
40 #include "gd_mono_utils.h"
41 
42 #if !defined(JAVASCRIPT_ENABLED) && !defined(IPHONE_ENABLED)
43 #define HAVE_METHOD_THUNKS
44 #endif
45 
46 #ifdef HAVE_METHOD_THUNKS
47 
48 template <class... ParamTypes>
49 struct GDMonoMethodThunk {
50 
51 	typedef void(GD_MONO_STDCALL *M)(ParamTypes... p_args, MonoException **);
52 
53 	M mono_method_thunk;
54 
55 public:
invokeGDMonoMethodThunk56 	_FORCE_INLINE_ void invoke(ParamTypes... p_args, MonoException **r_exc) {
57 		GD_MONO_BEGIN_RUNTIME_INVOKE;
58 		mono_method_thunk(p_args..., r_exc);
59 		GD_MONO_END_RUNTIME_INVOKE;
60 	}
61 
is_nullGDMonoMethodThunk62 	_FORCE_INLINE_ bool is_null() {
63 		return mono_method_thunk == NULL;
64 	}
65 
nullifyGDMonoMethodThunk66 	_FORCE_INLINE_ void nullify() {
67 		mono_method_thunk = NULL;
68 	}
69 
set_from_methodGDMonoMethodThunk70 	_FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) {
71 #ifdef DEBUG_ENABLED
72 		CRASH_COND(p_mono_method == NULL);
73 		CRASH_COND(p_mono_method->get_return_type().type_encoding != MONO_TYPE_VOID);
74 
75 		if (p_mono_method->is_static()) {
76 			CRASH_COND(p_mono_method->get_parameters_count() != sizeof...(ParamTypes));
77 		} else {
78 			CRASH_COND(p_mono_method->get_parameters_count() != (sizeof...(ParamTypes) - 1));
79 		}
80 #endif
81 		mono_method_thunk = (M)mono_method_get_unmanaged_thunk(p_mono_method->get_mono_ptr());
82 	}
83 
GDMonoMethodThunkGDMonoMethodThunk84 	GDMonoMethodThunk() :
85 			mono_method_thunk(NULL) {
86 	}
87 
GDMonoMethodThunkGDMonoMethodThunk88 	explicit GDMonoMethodThunk(GDMonoMethod *p_mono_method) {
89 		set_from_method(p_mono_method);
90 	}
91 };
92 
93 template <class R, class... ParamTypes>
94 struct GDMonoMethodThunkR {
95 
96 	typedef R(GD_MONO_STDCALL *M)(ParamTypes... p_args, MonoException **);
97 
98 	M mono_method_thunk;
99 
100 public:
invokeGDMonoMethodThunkR101 	_FORCE_INLINE_ R invoke(ParamTypes... p_args, MonoException **r_exc) {
102 		GD_MONO_BEGIN_RUNTIME_INVOKE;
103 		R r = mono_method_thunk(p_args..., r_exc);
104 		GD_MONO_END_RUNTIME_INVOKE;
105 		return r;
106 	}
107 
is_nullGDMonoMethodThunkR108 	_FORCE_INLINE_ bool is_null() {
109 		return mono_method_thunk == NULL;
110 	}
111 
nullifyGDMonoMethodThunkR112 	_FORCE_INLINE_ void nullify() {
113 		mono_method_thunk = NULL;
114 	}
115 
set_from_methodGDMonoMethodThunkR116 	_FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) {
117 #ifdef DEBUG_ENABLED
118 		CRASH_COND(p_mono_method == NULL);
119 		CRASH_COND(p_mono_method->get_return_type().type_encoding == MONO_TYPE_VOID);
120 
121 		if (p_mono_method->is_static()) {
122 			CRASH_COND(p_mono_method->get_parameters_count() != sizeof...(ParamTypes));
123 		} else {
124 			CRASH_COND(p_mono_method->get_parameters_count() != (sizeof...(ParamTypes) - 1));
125 		}
126 #endif
127 		mono_method_thunk = (M)mono_method_get_unmanaged_thunk(p_mono_method->get_mono_ptr());
128 	}
129 
GDMonoMethodThunkRGDMonoMethodThunkR130 	GDMonoMethodThunkR() :
131 			mono_method_thunk(NULL) {
132 	}
133 
GDMonoMethodThunkRGDMonoMethodThunkR134 	explicit GDMonoMethodThunkR(GDMonoMethod *p_mono_method) {
135 #ifdef DEBUG_ENABLED
136 		CRASH_COND(p_mono_method == NULL);
137 #endif
138 		mono_method_thunk = (M)mono_method_get_unmanaged_thunk(p_mono_method->get_mono_ptr());
139 	}
140 };
141 
142 #else
143 
144 template <unsigned int ThunkParamCount, class P1, class... ParamTypes>
145 struct VariadicInvokeMonoMethodImpl {
invokeVariadicInvokeMonoMethodImpl146 	static void invoke(GDMonoMethod *p_mono_method, P1 p_arg1, ParamTypes... p_args, MonoException **r_exc) {
147 		if (p_mono_method->is_static()) {
148 			void *args[ThunkParamCount] = { p_arg1, p_args... };
149 			p_mono_method->invoke_raw(NULL, args, r_exc);
150 		} else {
151 			void *args[ThunkParamCount] = { p_args... };
152 			p_mono_method->invoke_raw((MonoObject *)p_arg1, args, r_exc);
153 		}
154 	}
155 };
156 
157 template <unsigned int ThunkParamCount, class... ParamTypes>
158 struct VariadicInvokeMonoMethod {
invokeVariadicInvokeMonoMethod159 	static void invoke(GDMonoMethod *p_mono_method, ParamTypes... p_args, MonoException **r_exc) {
160 		VariadicInvokeMonoMethodImpl<ThunkParamCount, ParamTypes...>::invoke(p_mono_method, p_args..., r_exc);
161 	}
162 };
163 
164 template <>
165 struct VariadicInvokeMonoMethod<0> {
166 	static void invoke(GDMonoMethod *p_mono_method, MonoException **r_exc) {
167 #ifdef DEBUG_ENABLED
168 		CRASH_COND(!p_mono_method->is_static());
169 #endif
170 		p_mono_method->invoke_raw(NULL, NULL, r_exc);
171 	}
172 };
173 
174 template <class P1>
175 struct VariadicInvokeMonoMethod<1, P1> {
176 	static void invoke(GDMonoMethod *p_mono_method, P1 p_arg1, MonoException **r_exc) {
177 		if (p_mono_method->is_static()) {
178 			void *args[1] = { p_arg1 };
179 			p_mono_method->invoke_raw(NULL, args, r_exc);
180 		} else {
181 			p_mono_method->invoke_raw((MonoObject *)p_arg1, NULL, r_exc);
182 		}
183 	}
184 };
185 
186 template <class R>
187 R unbox_if_needed(MonoObject *p_val, const ManagedType &, typename std::enable_if<!std::is_pointer<R>::value>::type * = 0) {
188 	return GDMonoMarshal::unbox<R>(p_val);
189 }
190 
191 template <class R>
192 R unbox_if_needed(MonoObject *p_val, const ManagedType &p_type, typename std::enable_if<std::is_pointer<R>::value>::type * = 0) {
193 	if (mono_class_is_valuetype(p_type.type_class->get_mono_ptr())) {
194 		return GDMonoMarshal::unbox<R>(p_val);
195 	} else {
196 		// If it's not a value type, we assume 'R' is a pointer to 'MonoObject' or a compatible type, like 'MonoException'.
197 		return (R)p_val;
198 	}
199 }
200 
201 template <unsigned int ThunkParamCount, class R, class P1, class... ParamTypes>
202 struct VariadicInvokeMonoMethodRImpl {
203 	static R invoke(GDMonoMethod *p_mono_method, P1 p_arg1, ParamTypes... p_args, MonoException **r_exc) {
204 		if (p_mono_method->is_static()) {
205 			void *args[ThunkParamCount] = { p_arg1, p_args... };
206 			MonoObject *r = p_mono_method->invoke_raw(NULL, args, r_exc);
207 			return unbox_if_needed<R>(r, p_mono_method->get_return_type());
208 		} else {
209 			void *args[ThunkParamCount] = { p_args... };
210 			MonoObject *r = p_mono_method->invoke_raw((MonoObject *)p_arg1, args, r_exc);
211 			return unbox_if_needed<R>(r, p_mono_method->get_return_type());
212 		}
213 	}
214 };
215 
216 template <unsigned int ThunkParamCount, class R, class... ParamTypes>
217 struct VariadicInvokeMonoMethodR {
218 	static R invoke(GDMonoMethod *p_mono_method, ParamTypes... p_args, MonoException **r_exc) {
219 		return VariadicInvokeMonoMethodRImpl<ThunkParamCount, R, ParamTypes...>::invoke(p_mono_method, p_args..., r_exc);
220 	}
221 };
222 
223 template <class R>
224 struct VariadicInvokeMonoMethodR<0, R> {
225 	static R invoke(GDMonoMethod *p_mono_method, MonoException **r_exc) {
226 #ifdef DEBUG_ENABLED
227 		CRASH_COND(!p_mono_method->is_static());
228 #endif
229 		MonoObject *r = p_mono_method->invoke_raw(NULL, NULL, r_exc);
230 		return unbox_if_needed<R>(r, p_mono_method->get_return_type());
231 	}
232 };
233 
234 template <class R, class P1>
235 struct VariadicInvokeMonoMethodR<1, R, P1> {
236 	static R invoke(GDMonoMethod *p_mono_method, P1 p_arg1, MonoException **r_exc) {
237 		if (p_mono_method->is_static()) {
238 			void *args[1] = { p_arg1 };
239 			MonoObject *r = p_mono_method->invoke_raw(NULL, args, r_exc);
240 			return unbox_if_needed<R>(r, p_mono_method->get_return_type());
241 		} else {
242 			MonoObject *r = p_mono_method->invoke_raw((MonoObject *)p_arg1, NULL, r_exc);
243 			return unbox_if_needed<R>(r, p_mono_method->get_return_type());
244 		}
245 	}
246 };
247 
248 template <class... ParamTypes>
249 struct GDMonoMethodThunk {
250 
251 	GDMonoMethod *mono_method;
252 
253 public:
254 	_FORCE_INLINE_ void invoke(ParamTypes... p_args, MonoException **r_exc) {
255 		VariadicInvokeMonoMethod<sizeof...(ParamTypes), ParamTypes...>::invoke(mono_method, p_args..., r_exc);
256 	}
257 
258 	_FORCE_INLINE_ bool is_null() {
259 		return mono_method == NULL;
260 	}
261 
262 	_FORCE_INLINE_ void nullify() {
263 		mono_method = NULL;
264 	}
265 
266 	_FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) {
267 #ifdef DEBUG_ENABLED
268 		CRASH_COND(p_mono_method == NULL);
269 		CRASH_COND(p_mono_method->get_return_type().type_encoding != MONO_TYPE_VOID);
270 
271 		if (p_mono_method->is_static()) {
272 			CRASH_COND(p_mono_method->get_parameters_count() != sizeof...(ParamTypes));
273 		} else {
274 			CRASH_COND(p_mono_method->get_parameters_count() != (sizeof...(ParamTypes) - 1));
275 		}
276 #endif
277 		mono_method = p_mono_method;
278 	}
279 
280 	GDMonoMethodThunk() :
281 			mono_method(NULL) {
282 	}
283 
284 	explicit GDMonoMethodThunk(GDMonoMethod *p_mono_method) {
285 		set_from_method(p_mono_method);
286 	}
287 };
288 
289 template <class R, class... ParamTypes>
290 struct GDMonoMethodThunkR {
291 
292 	GDMonoMethod *mono_method;
293 
294 public:
295 	_FORCE_INLINE_ R invoke(ParamTypes... p_args, MonoException **r_exc) {
296 		return VariadicInvokeMonoMethodR<sizeof...(ParamTypes), R, ParamTypes...>::invoke(mono_method, p_args..., r_exc);
297 	}
298 
299 	_FORCE_INLINE_ bool is_null() {
300 		return mono_method == NULL;
301 	}
302 
303 	_FORCE_INLINE_ void nullify() {
304 		mono_method = NULL;
305 	}
306 
307 	_FORCE_INLINE_ void set_from_method(GDMonoMethod *p_mono_method) {
308 #ifdef DEBUG_ENABLED
309 		CRASH_COND(p_mono_method == NULL);
310 		CRASH_COND(p_mono_method->get_return_type().type_encoding == MONO_TYPE_VOID);
311 
312 		if (p_mono_method->is_static()) {
313 			CRASH_COND(p_mono_method->get_parameters_count() != sizeof...(ParamTypes));
314 		} else {
315 			CRASH_COND(p_mono_method->get_parameters_count() != (sizeof...(ParamTypes) - 1));
316 		}
317 #endif
318 		mono_method = p_mono_method;
319 	}
320 
321 	GDMonoMethodThunkR() :
322 			mono_method(NULL) {
323 	}
324 
325 	explicit GDMonoMethodThunkR(GDMonoMethod *p_mono_method) {
326 		set_from_method(p_mono_method);
327 	}
328 };
329 
330 #endif
331 
332 #endif // GD_MONO_METHOD_THUNK_H
333