gd_mono_utils.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. /*************************************************************************/
  2. /* gd_mono_utils.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2021 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. #include "gd_mono_utils.h"
  31. #include <mono/metadata/debug-helpers.h>
  32. #include <mono/metadata/exception.h>
  33. #include "core/debugger/engine_debugger.h"
  34. #include "core/debugger/script_debugger.h"
  35. #include "core/io/dir_access.h"
  36. #include "core/object/ref_counted.h"
  37. #include "core/os/mutex.h"
  38. #include "core/os/os.h"
  39. #ifdef TOOLS_ENABLED
  40. #include "editor/debugger/editor_debugger_node.h"
  41. #endif
  42. #include "../csharp_script.h"
  43. #include "../utils/macros.h"
  44. #include "gd_mono.h"
  45. #include "gd_mono_cache.h"
  46. #include "gd_mono_class.h"
  47. #include "gd_mono_marshal.h"
  48. namespace GDMonoUtils {
  49. MonoObject *unmanaged_get_managed(Object *unmanaged) {
  50. if (!unmanaged) {
  51. return nullptr;
  52. }
  53. if (unmanaged->get_script_instance()) {
  54. CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(unmanaged->get_script_instance());
  55. if (cs_instance) {
  56. return cs_instance->get_mono_object();
  57. }
  58. }
  59. // If the owner does not have a CSharpInstance...
  60. void *data = CSharpLanguage::get_instance_binding(unmanaged);
  61. ERR_FAIL_NULL_V(data, nullptr);
  62. CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->value();
  63. ERR_FAIL_COND_V(!script_binding.inited, nullptr);
  64. MonoGCHandleData &gchandle = script_binding.gchandle;
  65. MonoObject *target = gchandle.get_target();
  66. if (target) {
  67. return target;
  68. }
  69. CSharpLanguage::get_singleton()->release_script_gchandle(gchandle);
  70. // Create a new one
  71. #ifdef DEBUG_ENABLED
  72. CRASH_COND(script_binding.type_name == StringName());
  73. CRASH_COND(script_binding.wrapper_class == nullptr);
  74. #endif
  75. MonoObject *mono_object = GDMonoUtils::create_managed_for_godot_object(script_binding.wrapper_class, script_binding.type_name, unmanaged);
  76. ERR_FAIL_NULL_V(mono_object, nullptr);
  77. gchandle = MonoGCHandleData::new_strong_handle(mono_object);
  78. // Tie managed to unmanaged
  79. RefCounted *rc = Object::cast_to<RefCounted>(unmanaged);
  80. if (rc) {
  81. // Unsafe refcount increment. The managed instance also counts as a reference.
  82. // This way if the unmanaged world has no references to our owner
  83. // but the managed instance is alive, the refcount will be 1 instead of 0.
  84. // See: godot_icall_RefCounted_Dtor(MonoObject *p_obj, Object *p_ptr)
  85. rc->reference();
  86. CSharpLanguage::get_singleton()->post_unsafe_reference(rc);
  87. }
  88. return mono_object;
  89. }
  90. void set_main_thread(MonoThread *p_thread) {
  91. mono_thread_set_main(p_thread);
  92. }
  93. MonoThread *attach_current_thread() {
  94. ERR_FAIL_COND_V(!GDMono::get_singleton()->is_runtime_initialized(), nullptr);
  95. MonoDomain *scripts_domain = GDMono::get_singleton()->get_scripts_domain();
  96. #ifndef GD_MONO_SINGLE_APPDOMAIN
  97. MonoThread *mono_thread = mono_thread_attach(scripts_domain ? scripts_domain : mono_get_root_domain());
  98. #else
  99. // The scripts domain is the root domain
  100. MonoThread *mono_thread = mono_thread_attach(scripts_domain);
  101. #endif
  102. ERR_FAIL_NULL_V(mono_thread, nullptr);
  103. return mono_thread;
  104. }
  105. void detach_current_thread() {
  106. ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized());
  107. MonoThread *mono_thread = mono_thread_current();
  108. ERR_FAIL_NULL(mono_thread);
  109. mono_thread_detach(mono_thread);
  110. }
  111. void detach_current_thread(MonoThread *p_mono_thread) {
  112. ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized());
  113. ERR_FAIL_NULL(p_mono_thread);
  114. mono_thread_detach(p_mono_thread);
  115. }
  116. MonoThread *get_current_thread() {
  117. return mono_thread_current();
  118. }
  119. bool is_thread_attached() {
  120. return mono_domain_get() != nullptr;
  121. }
  122. uint32_t new_strong_gchandle(MonoObject *p_object) {
  123. return mono_gchandle_new(p_object, /* pinned: */ false);
  124. }
  125. uint32_t new_strong_gchandle_pinned(MonoObject *p_object) {
  126. return mono_gchandle_new(p_object, /* pinned: */ true);
  127. }
  128. uint32_t new_weak_gchandle(MonoObject *p_object) {
  129. return mono_gchandle_new_weakref(p_object, /* track_resurrection: */ false);
  130. }
  131. void free_gchandle(uint32_t p_gchandle) {
  132. mono_gchandle_free(p_gchandle);
  133. }
  134. void runtime_object_init(MonoObject *p_this_obj, GDMonoClass *p_class, MonoException **r_exc) {
  135. GDMonoMethod *ctor = p_class->get_method(".ctor", 0);
  136. ERR_FAIL_NULL(ctor);
  137. ctor->invoke_raw(p_this_obj, nullptr, r_exc);
  138. }
  139. GDMonoClass *get_object_class(MonoObject *p_object) {
  140. return GDMono::get_singleton()->get_class(mono_object_get_class(p_object));
  141. }
  142. GDMonoClass *type_get_proxy_class(const StringName &p_type) {
  143. String class_name = p_type;
  144. if (class_name[0] == '_') {
  145. class_name = class_name.substr(1, class_name.length());
  146. }
  147. GDMonoClass *klass = GDMono::get_singleton()->get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name);
  148. if (klass && klass->is_static()) {
  149. // A static class means this is a Godot singleton class. If an instance is needed we use Godot.Object.
  150. return GDMonoCache::cached_data.class_GodotObject;
  151. }
  152. #ifdef TOOLS_ENABLED
  153. if (!klass) {
  154. return GDMono::get_singleton()->get_editor_api_assembly()->get_class(BINDINGS_NAMESPACE, class_name);
  155. }
  156. #endif
  157. return klass;
  158. }
  159. GDMonoClass *get_class_native_base(GDMonoClass *p_class) {
  160. GDMonoClass *klass = p_class;
  161. do {
  162. const GDMonoAssembly *assembly = klass->get_assembly();
  163. if (assembly == GDMono::get_singleton()->get_core_api_assembly()) {
  164. return klass;
  165. }
  166. #ifdef TOOLS_ENABLED
  167. if (assembly == GDMono::get_singleton()->get_editor_api_assembly()) {
  168. return klass;
  169. }
  170. #endif
  171. } while ((klass = klass->get_parent_class()) != nullptr);
  172. return nullptr;
  173. }
  174. MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringName &p_native, Object *p_object) {
  175. bool parent_is_object_class = ClassDB::is_parent_class(p_object->get_class_name(), p_native);
  176. ERR_FAIL_COND_V_MSG(!parent_is_object_class, nullptr,
  177. "Type inherits from native type '" + p_native + "', so it can't be instantiated in object of type: '" + p_object->get_class() + "'.");
  178. MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr());
  179. ERR_FAIL_NULL_V(mono_object, nullptr);
  180. CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, p_object);
  181. // Construct
  182. GDMonoUtils::runtime_object_init(mono_object, p_class);
  183. return mono_object;
  184. }
  185. MonoDomain *create_domain(const String &p_friendly_name) {
  186. print_verbose("Mono: Creating domain '" + p_friendly_name + "'...");
  187. MonoDomain *domain = mono_domain_create_appdomain((char *)p_friendly_name.utf8().get_data(), nullptr);
  188. if (domain) {
  189. // Workaround to avoid this exception:
  190. // System.Configuration.ConfigurationErrorsException: Error Initializing the configuration system.
  191. // ---> System.ArgumentException: The 'ExeConfigFilename' argument cannot be null.
  192. mono_domain_set_config(domain, ".", "");
  193. }
  194. return domain;
  195. }
  196. String get_type_desc(MonoType *p_type) {
  197. return mono_type_full_name(p_type);
  198. }
  199. String get_type_desc(MonoReflectionType *p_reftype) {
  200. return get_type_desc(mono_reflection_type_get_type(p_reftype));
  201. }
  202. String get_exception_name_and_message(MonoException *p_exc) {
  203. String res;
  204. MonoClass *klass = mono_object_get_class((MonoObject *)p_exc);
  205. MonoType *type = mono_class_get_type(klass);
  206. char *full_name = mono_type_full_name(type);
  207. res += full_name;
  208. mono_free(full_name);
  209. res += ": ";
  210. MonoProperty *prop = mono_class_get_property_from_name(klass, "Message");
  211. MonoString *msg = (MonoString *)property_get_value(prop, (MonoObject *)p_exc, nullptr, nullptr);
  212. res += GDMonoMarshal::mono_string_to_godot(msg);
  213. return res;
  214. }
  215. void debug_print_unhandled_exception(MonoException *p_exc) {
  216. print_unhandled_exception(p_exc);
  217. debug_send_unhandled_exception_error(p_exc);
  218. }
  219. void debug_send_unhandled_exception_error(MonoException *p_exc) {
  220. #ifdef DEBUG_ENABLED
  221. if (!EngineDebugger::is_active()) {
  222. #ifdef TOOLS_ENABLED
  223. if (Engine::get_singleton()->is_editor_hint()) {
  224. ERR_PRINT(GDMonoUtils::get_exception_name_and_message(p_exc));
  225. }
  226. #endif
  227. return;
  228. }
  229. static thread_local bool _recursion_flag_ = false;
  230. if (_recursion_flag_) {
  231. return;
  232. }
  233. _recursion_flag_ = true;
  234. SCOPE_EXIT { _recursion_flag_ = false; };
  235. ScriptLanguage::StackInfo separator;
  236. separator.file = String();
  237. separator.func = "--- " + RTR("End of inner exception stack trace") + " ---";
  238. separator.line = 0;
  239. Vector<ScriptLanguage::StackInfo> si;
  240. String exc_msg;
  241. while (p_exc != nullptr) {
  242. GDMonoClass *st_klass = CACHED_CLASS(System_Diagnostics_StackTrace);
  243. MonoObject *stack_trace = mono_object_new(mono_domain_get(), st_klass->get_mono_ptr());
  244. MonoBoolean need_file_info = true;
  245. void *ctor_args[2] = { p_exc, &need_file_info };
  246. MonoException *unexpected_exc = nullptr;
  247. CACHED_METHOD(System_Diagnostics_StackTrace, ctor_Exception_bool)->invoke_raw(stack_trace, ctor_args, &unexpected_exc);
  248. if (unexpected_exc) {
  249. GDMonoInternals::unhandled_exception(unexpected_exc);
  250. return;
  251. }
  252. Vector<ScriptLanguage::StackInfo> _si;
  253. if (stack_trace != nullptr) {
  254. _si = CSharpLanguage::get_singleton()->stack_trace_get_info(stack_trace);
  255. for (int i = _si.size() - 1; i >= 0; i--) {
  256. si.insert(0, _si[i]);
  257. }
  258. }
  259. exc_msg += (exc_msg.length() > 0 ? " ---> " : "") + GDMonoUtils::get_exception_name_and_message(p_exc);
  260. GDMonoClass *exc_class = GDMono::get_singleton()->get_class(mono_get_exception_class());
  261. GDMonoProperty *inner_exc_prop = exc_class->get_property("InnerException");
  262. CRASH_COND(inner_exc_prop == nullptr);
  263. MonoObject *inner_exc = inner_exc_prop->get_value((MonoObject *)p_exc);
  264. if (inner_exc != nullptr) {
  265. si.insert(0, separator);
  266. }
  267. p_exc = (MonoException *)inner_exc;
  268. }
  269. String file = si.size() ? si[0].file : __FILE__;
  270. String func = si.size() ? si[0].func : FUNCTION_STR;
  271. int line = si.size() ? si[0].line : __LINE__;
  272. String error_msg = "Unhandled exception";
  273. EngineDebugger::get_script_debugger()->send_error(func, file, line, error_msg, exc_msg, true, ERR_HANDLER_ERROR, si);
  274. #endif
  275. }
  276. void debug_unhandled_exception(MonoException *p_exc) {
  277. GDMonoInternals::unhandled_exception(p_exc); // prints the exception as well
  278. }
  279. void print_unhandled_exception(MonoException *p_exc) {
  280. mono_print_unhandled_exception((MonoObject *)p_exc);
  281. }
  282. void set_pending_exception(MonoException *p_exc) {
  283. #ifdef NO_PENDING_EXCEPTIONS
  284. debug_unhandled_exception(p_exc);
  285. #else
  286. if (get_runtime_invoke_count() == 0) {
  287. debug_unhandled_exception(p_exc);
  288. return;
  289. }
  290. if (!mono_runtime_set_pending_exception(p_exc, false)) {
  291. ERR_PRINT("Exception thrown from managed code, but it could not be set as pending:");
  292. GDMonoUtils::debug_print_unhandled_exception(p_exc);
  293. }
  294. #endif
  295. }
  296. thread_local int current_invoke_count = 0;
  297. MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **r_exc) {
  298. GD_MONO_BEGIN_RUNTIME_INVOKE;
  299. MonoObject *ret = mono_runtime_invoke(p_method, p_obj, p_params, (MonoObject **)r_exc);
  300. GD_MONO_END_RUNTIME_INVOKE;
  301. return ret;
  302. }
  303. MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **r_exc) {
  304. GD_MONO_BEGIN_RUNTIME_INVOKE;
  305. MonoObject *ret = mono_runtime_invoke_array(p_method, p_obj, p_params, (MonoObject **)r_exc);
  306. GD_MONO_END_RUNTIME_INVOKE;
  307. return ret;
  308. }
  309. MonoString *object_to_string(MonoObject *p_obj, MonoException **r_exc) {
  310. GD_MONO_BEGIN_RUNTIME_INVOKE;
  311. MonoString *ret = mono_object_to_string(p_obj, (MonoObject **)r_exc);
  312. GD_MONO_END_RUNTIME_INVOKE;
  313. return ret;
  314. }
  315. void property_set_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc) {
  316. GD_MONO_BEGIN_RUNTIME_INVOKE;
  317. mono_property_set_value(p_prop, p_obj, p_params, (MonoObject **)r_exc);
  318. GD_MONO_END_RUNTIME_INVOKE;
  319. }
  320. MonoObject *property_get_value(MonoProperty *p_prop, void *p_obj, void **p_params, MonoException **r_exc) {
  321. GD_MONO_BEGIN_RUNTIME_INVOKE;
  322. MonoObject *ret = mono_property_get_value(p_prop, p_obj, p_params, (MonoObject **)r_exc);
  323. GD_MONO_END_RUNTIME_INVOKE;
  324. return ret;
  325. }
  326. uint64_t unbox_enum_value(MonoObject *p_boxed, MonoType *p_enum_basetype, bool &r_error) {
  327. r_error = false;
  328. switch (mono_type_get_type(p_enum_basetype)) {
  329. case MONO_TYPE_BOOLEAN:
  330. return (bool)GDMonoMarshal::unbox<MonoBoolean>(p_boxed) ? 1 : 0;
  331. case MONO_TYPE_CHAR:
  332. return GDMonoMarshal::unbox<uint16_t>(p_boxed);
  333. case MONO_TYPE_U1:
  334. return GDMonoMarshal::unbox<uint8_t>(p_boxed);
  335. case MONO_TYPE_U2:
  336. return GDMonoMarshal::unbox<uint16_t>(p_boxed);
  337. case MONO_TYPE_U4:
  338. return GDMonoMarshal::unbox<uint32_t>(p_boxed);
  339. case MONO_TYPE_U8:
  340. return GDMonoMarshal::unbox<uint64_t>(p_boxed);
  341. case MONO_TYPE_I1:
  342. return GDMonoMarshal::unbox<int8_t>(p_boxed);
  343. case MONO_TYPE_I2:
  344. return GDMonoMarshal::unbox<int16_t>(p_boxed);
  345. case MONO_TYPE_I4:
  346. return GDMonoMarshal::unbox<int32_t>(p_boxed);
  347. case MONO_TYPE_I8:
  348. return GDMonoMarshal::unbox<int64_t>(p_boxed);
  349. default:
  350. r_error = true;
  351. return 0;
  352. }
  353. }
  354. void dispose(MonoObject *p_mono_object, MonoException **r_exc) {
  355. CACHED_METHOD_THUNK(GodotObject, Dispose).invoke(p_mono_object, r_exc);
  356. }
  357. namespace Marshal {
  358. #ifdef MONO_GLUE_ENABLED
  359. #ifdef TOOLS_ENABLED
  360. #define NO_GLUE_RET(m_ret) \
  361. { \
  362. if (!GDMonoCache::cached_data.godot_api_cache_updated) \
  363. return m_ret; \
  364. }
  365. #else
  366. #define NO_GLUE_RET(m_ret) \
  367. {}
  368. #endif
  369. #else
  370. #define NO_GLUE_RET(m_ret) \
  371. { return m_ret; }
  372. #endif
  373. bool type_has_flags_attribute(MonoReflectionType *p_reftype) {
  374. NO_GLUE_RET(false);
  375. MonoException *exc = nullptr;
  376. MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeHasFlagsAttribute).invoke(p_reftype, &exc);
  377. UNHANDLED_EXCEPTION(exc);
  378. return (bool)res;
  379. }
  380. } // namespace Marshal
  381. ScopeThreadAttach::ScopeThreadAttach() {
  382. if (likely(GDMono::get_singleton()->is_runtime_initialized()) && unlikely(!mono_domain_get())) {
  383. mono_thread = GDMonoUtils::attach_current_thread();
  384. }
  385. }
  386. ScopeThreadAttach::~ScopeThreadAttach() {
  387. if (unlikely(mono_thread)) {
  388. GDMonoUtils::detach_current_thread(mono_thread);
  389. }
  390. }
  391. StringName get_native_godot_class_name(GDMonoClass *p_class) {
  392. MonoObject *native_name_obj = p_class->get_field(BINDINGS_NATIVE_NAME_FIELD)->get_value(nullptr);
  393. return (StringName)GDMonoMarshal::mono_object_to_variant(native_name_obj);
  394. }
  395. } // namespace GDMonoUtils