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