diff options
author | Ignacio Etcheverry <ignalfonsore@gmail.com> | 2019-07-03 09:44:53 +0200 |
---|---|---|
committer | Ignacio Etcheverry <ignalfonsore@gmail.com> | 2019-07-05 09:38:23 +0200 |
commit | 270af6fa089ccfb93ace68ada8d476bd902b10fa (patch) | |
tree | fee771a4f58a8d799f70d37547391831f52b5cd2 /modules/mono/mono_gd | |
parent | 7b569e91c0c6b84965cad416b8e86dcfdacbcfc4 (diff) |
Re-write mono module editor code in C#
Make the build system automatically build the C# Api assemblies to be shipped with the editor.
Make the editor, editor player and debug export templates use Api assemblies built with debug symbols.
Always run MSBuild to build the editor tools and Api assemblies when building Godot.
Several bugs fixed related to assembly hot reloading and restoring state.
Fix StringExtensions internal calls not being registered correctly, resulting in MissingMethodException.
Diffstat (limited to 'modules/mono/mono_gd')
-rw-r--r-- | modules/mono/mono_gd/gd_mono.cpp | 233 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono.h | 27 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_assembly.cpp | 22 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_class.cpp | 17 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_class.h | 1 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_field.cpp | 28 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_internals.cpp | 7 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_marshal.cpp | 36 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_utils.cpp | 114 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_utils.h | 24 |
10 files changed, 318 insertions, 191 deletions
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index c90da31f3a..7ae991eeaa 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -52,7 +52,6 @@ #include "gd_mono_utils.h" #ifdef TOOLS_ENABLED -#include "../editor/godotsharp_editor.h" #include "main/main.h" #endif @@ -99,7 +98,7 @@ void gdmono_profiler_init() { #ifdef DEBUG_ENABLED -static bool _wait_for_debugger_msecs(uint32_t p_msecs) { +bool _wait_for_debugger_msecs(uint32_t p_msecs) { do { if (mono_is_debugger_attached()) @@ -129,16 +128,17 @@ void gdmono_debug_init() { bool da_suspend = GLOBAL_DEF("mono/debugger_agent/wait_for_debugger", false); int da_timeout = GLOBAL_DEF("mono/debugger_agent/wait_timeout", 3000); + CharString da_args = OS::get_singleton()->get_environment("GODOT_MONO_DEBUGGER_AGENT").utf8(); + #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint() || ProjectSettings::get_singleton()->get_resource_path().empty() || Main::is_project_manager()) { - return; + if (da_args.size() == 0) + return; } #endif - CharString da_args = OS::get_singleton()->get_environment("GODOT_MONO_DEBUGGER_AGENT").utf8(); - if (da_args.length() == 0) { da_args = String("--debugger-agent=transport=dt_socket,address=127.0.0.1:" + itos(da_port) + ",embedding=1,server=y,suspend=" + (da_suspend ? "y,timeout=" + itos(da_timeout) : "n")) @@ -207,6 +207,10 @@ void GDMono::initialize() { print_verbose("Mono: Initializing module..."); + char *runtime_build_info = mono_get_runtime_build_info(); + print_verbose("Mono JIT compiler version " + String(runtime_build_info)); + mono_free(runtime_build_info); + #ifdef DEBUG_METHODS_ENABLED _initialize_and_check_api_hashes(); #endif @@ -339,18 +343,6 @@ void GDMono::initialize() { ERR_EXPLAIN("Mono: Failed to load mscorlib assembly"); ERR_FAIL_COND(!_load_corlib_assembly()); -#ifdef TOOLS_ENABLED - // The tools domain must be loaded here, before the scripts domain. - // Otherwise domain unload on the scripts domain will hang indefinitely. - - ERR_EXPLAIN("Mono: Failed to load tools domain"); - ERR_FAIL_COND(_load_tools_domain() != OK); - - // TODO move to editor init callback, and do it lazily when required before editor init (e.g.: bindings generation) - ERR_EXPLAIN("Mono: Failed to load Editor Tools assembly"); - ERR_FAIL_COND(!_load_editor_tools_assembly()); -#endif - ERR_EXPLAIN("Mono: Failed to load scripts domain"); ERR_FAIL_COND(_load_scripts_domain() != OK); @@ -365,8 +357,15 @@ void GDMono::initialize() { // The following assemblies are not required at initialization #ifdef MONO_GLUE_ENABLED if (_load_api_assemblies()) { - // Everything is fine with the api assemblies, load the project assembly + // Everything is fine with the api assemblies, load the tools and project assemblies + +#if defined(TOOLS_ENABLED) + ERR_EXPLAIN("Mono: Failed to load GodotTools assemblies"); + ERR_FAIL_COND(!_load_tools_assemblies()); +#endif + _load_project_assembly(); + } else { if ((core_api_assembly && (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated)) #ifdef TOOLS_ENABLED @@ -427,10 +426,6 @@ void GDMono::_register_internal_calls() { #ifdef MONO_GLUE_ENABLED GodotSharpBindings::register_generated_icalls(); #endif - -#ifdef TOOLS_ENABLED - GodotSharpEditor::register_internal_calls(); -#endif } void GDMono::_initialize_and_check_api_hashes() { @@ -569,6 +564,50 @@ bool GDMono::_load_corlib_assembly() { return success; } +static bool copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name, APIAssembly::Type p_api_type) { + + // Create destination directory if needed + if (!DirAccess::exists(p_dst_dir)) { + DirAccess *da = DirAccess::create_for_path(p_dst_dir); + Error err = da->make_dir_recursive(p_dst_dir); + memdelete(da); + + if (err != OK) { + ERR_PRINTS("Failed to create destination directory for the API assemblies. Error: " + itos(err)); + return false; + } + } + + String assembly_file = p_assembly_name + ".dll"; + String assembly_src = p_src_dir.plus_file(assembly_file); + String assembly_dst = p_dst_dir.plus_file(assembly_file); + + if (!FileAccess::exists(assembly_dst) || + FileAccess::get_modified_time(assembly_src) > FileAccess::get_modified_time(assembly_dst) || + GDMono::get_singleton()->metadata_is_api_assembly_invalidated(p_api_type)) { + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + + String xml_file = p_assembly_name + ".xml"; + if (da->copy(p_src_dir.plus_file(xml_file), p_dst_dir.plus_file(xml_file)) != OK) + WARN_PRINTS("Failed to copy " + xml_file); + + String pdb_file = p_assembly_name + ".pdb"; + if (da->copy(p_src_dir.plus_file(pdb_file), p_dst_dir.plus_file(pdb_file)) != OK) + WARN_PRINTS("Failed to copy " + pdb_file); + + Error err = da->copy(assembly_src, assembly_dst); + + if (err != OK) { + ERR_PRINTS("Failed to copy " + assembly_file); + return false; + } + + GDMono::get_singleton()->metadata_set_api_assembly_invalidated(p_api_type, false); + } + + return true; +} + bool GDMono::_load_core_api_assembly() { if (core_api_assembly) @@ -576,19 +615,31 @@ bool GDMono::_load_core_api_assembly() { #ifdef TOOLS_ENABLED if (metadata_is_api_assembly_invalidated(APIAssembly::API_CORE)) { - print_verbose("Mono: Skipping loading of Core API assembly because it was invalidated"); - return false; + String prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug"); + String prebuilt_dll_path = prebuilt_api_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll"); + String invalidated_dll_path = get_invalidated_api_assembly_path(APIAssembly::API_CORE); + + if (!FileAccess::exists(prebuilt_dll_path) || + FileAccess::get_modified_time(invalidated_dll_path) == FileAccess::get_modified_time(prebuilt_dll_path)) { + print_verbose("Mono: Skipping loading of Core API assembly because it was invalidated"); + return false; + } else { + // Copy the prebuilt Api + String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir(); + if (!copy_api_assembly(prebuilt_api_dir, res_assemblies_dir, CORE_API_ASSEMBLY_NAME, APIAssembly::API_CORE) || + !copy_api_assembly(prebuilt_api_dir, res_assemblies_dir, EDITOR_API_ASSEMBLY_NAME, APIAssembly::API_EDITOR)) { + print_verbose("Mono: Failed to copy prebuilt API. Skipping loading of Core API assembly because it was invalidated"); + return false; + } + } } #endif String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(CORE_API_ASSEMBLY_NAME ".dll"); - if (!FileAccess::exists(assembly_path)) - return false; - - bool success = load_assembly_from(CORE_API_ASSEMBLY_NAME, - assembly_path, - &core_api_assembly); + bool success = (FileAccess::exists(assembly_path) && + load_assembly_from(CORE_API_ASSEMBLY_NAME, assembly_path, &core_api_assembly)) || + load_assembly(CORE_API_ASSEMBLY_NAME, &core_api_assembly); if (success) { #ifdef MONO_GLUE_ENABLED @@ -616,18 +667,29 @@ bool GDMono::_load_editor_api_assembly() { return true; if (metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR)) { - print_verbose("Mono: Skipping loading of Editor API assembly because it was invalidated"); - return false; + String prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug"); + String prebuilt_dll_path = prebuilt_api_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); + String invalidated_dll_path = get_invalidated_api_assembly_path(APIAssembly::API_EDITOR); + + if (!FileAccess::exists(prebuilt_dll_path) || + FileAccess::get_modified_time(invalidated_dll_path) == FileAccess::get_modified_time(prebuilt_dll_path)) { + print_verbose("Mono: Skipping loading of Editor API assembly because it was invalidated"); + return false; + } else { + // Copy the prebuilt editor Api (no need to copy the core api if we got to this point) + String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir(); + if (!copy_api_assembly(prebuilt_api_dir, res_assemblies_dir, EDITOR_API_ASSEMBLY_NAME, APIAssembly::API_EDITOR)) { + print_verbose("Mono: Failed to copy prebuilt API. Skipping loading of Editor API assembly because it was invalidated"); + return false; + } + } } String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(EDITOR_API_ASSEMBLY_NAME ".dll"); - if (!FileAccess::exists(assembly_path)) - return false; - - bool success = load_assembly_from(EDITOR_API_ASSEMBLY_NAME, - assembly_path, - &editor_api_assembly); + bool success = (FileAccess::exists(assembly_path) && + load_assembly_from(EDITOR_API_ASSEMBLY_NAME, assembly_path, &editor_api_assembly)) || + load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly); if (success) { #ifdef MONO_GLUE_ENABLED @@ -643,14 +705,15 @@ bool GDMono::_load_editor_api_assembly() { #endif #ifdef TOOLS_ENABLED -bool GDMono::_load_editor_tools_assembly() { +bool GDMono::_load_tools_assemblies() { - if (editor_tools_assembly) + if (tools_assembly && tools_project_editor_assembly) return true; - _GDMONO_SCOPE_DOMAIN_(tools_domain) + bool success = load_assembly(TOOLS_ASSEMBLY_NAME, &tools_assembly) && + load_assembly(TOOLS_PROJECT_EDITOR_ASSEMBLY_NAME, &tools_project_editor_assembly); - return load_assembly(EDITOR_TOOLS_ASSEMBLY_NAME, &editor_tools_assembly); + return success; } #endif @@ -781,6 +844,14 @@ bool GDMono::metadata_is_api_assembly_invalidated(APIAssembly::Type p_api_type) return metadata->get_value(section, "invalidated", false) && modified_time <= stored_modified_time; } + +String GDMono::get_invalidated_api_assembly_path(APIAssembly::Type p_api_type) { + + return GodotSharpDirs::get_res_assemblies_dir() + .plus_file(p_api_type == APIAssembly::API_CORE ? + CORE_API_ASSEMBLY_NAME ".dll" : + EDITOR_API_ASSEMBLY_NAME ".dll"); +} #endif Error GDMono::_load_scripts_domain() { @@ -826,6 +897,8 @@ Error GDMono::_unload_scripts_domain() { project_assembly = NULL; #ifdef TOOLS_ENABLED editor_api_assembly = NULL; + tools_assembly = NULL; + tools_project_editor_assembly = NULL; #endif core_api_assembly_out_of_sync = false; @@ -848,22 +921,6 @@ Error GDMono::_unload_scripts_domain() { return OK; } -#ifdef TOOLS_ENABLED -Error GDMono::_load_tools_domain() { - - ERR_FAIL_COND_V(tools_domain != NULL, ERR_BUG); - - print_verbose("Mono: Loading tools domain..."); - - tools_domain = GDMonoUtils::create_domain("GodotEngine.ToolsDomain"); - - ERR_EXPLAIN("Mono: Could not create tools app domain"); - ERR_FAIL_NULL_V(tools_domain, ERR_CANT_CREATE); - - return OK; -} -#endif - #ifdef GD_MONO_HOT_RELOAD Error GDMono::reload_scripts_domain() { @@ -925,6 +982,11 @@ Error GDMono::reload_scripts_domain() { } } +#ifdef TOOLS_ENABLED + ERR_EXPLAIN("Mono: Failed to load GodotTools assemblies"); + ERR_FAIL_COND_V(!_load_tools_assemblies(), ERR_CANT_OPEN); +#endif + if (!_load_project_assembly()) { return ERR_CANT_OPEN; } @@ -939,7 +1001,7 @@ Error GDMono::reload_scripts_domain() { Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) { CRASH_COND(p_domain == NULL); - CRASH_COND(p_domain == SCRIPTS_DOMAIN); // Should use _unload_scripts_domain() instead + CRASH_COND(p_domain == GDMono::get_singleton()->get_scripts_domain()); // Should use _unload_scripts_domain() instead String domain_name = mono_domain_get_friendly_name(p_domain); @@ -956,18 +1018,12 @@ Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) { _domain_assemblies_cleanup(mono_domain_get_id(p_domain)); -#ifdef TOOLS_ENABLED - if (p_domain == tools_domain) { - editor_tools_assembly = NULL; - } -#endif - MonoException *exc = NULL; mono_domain_try_unload(p_domain, (MonoObject **)&exc); if (exc) { ERR_PRINTS("Exception thrown when unloading domain `" + domain_name + "`"); - GDMonoUtils::debug_unhandled_exception(exc); + GDMonoUtils::debug_print_unhandled_exception(exc); return FAILED; } @@ -998,6 +1054,22 @@ GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) { return NULL; } +GDMonoClass *GDMono::get_class(const StringName &p_namespace, const StringName &p_name) { + + uint32_t domain_id = mono_domain_get_id(mono_domain_get()); + HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[domain_id]; + + const String *k = NULL; + while ((k = domain_assemblies.next(k))) { + GDMonoAssembly *assembly = domain_assemblies.get(*k); + GDMonoClass *klass = assembly->get_class(p_namespace, p_name); + if (klass) + return klass; + } + + return NULL; +} + void GDMono::_domain_assemblies_cleanup(uint32_t p_domain_id) { HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[p_domain_id]; @@ -1038,9 +1110,6 @@ GDMono::GDMono() { root_domain = NULL; scripts_domain = NULL; -#ifdef TOOLS_ENABLED - tools_domain = NULL; -#endif core_api_assembly_out_of_sync = false; #ifdef TOOLS_ENABLED @@ -1052,7 +1121,8 @@ GDMono::GDMono() { project_assembly = NULL; #ifdef TOOLS_ENABLED editor_api_assembly = NULL; - editor_tools_assembly = NULL; + tools_assembly = NULL; + tools_project_editor_assembly = NULL; #endif api_core_hash = 0; @@ -1064,16 +1134,6 @@ GDMono::GDMono() { GDMono::~GDMono() { if (is_runtime_initialized()) { - -#ifdef TOOLS_ENABLED - if (tools_domain) { - Error err = finalize_and_unload_domain(tools_domain); - if (err != OK) { - ERR_PRINT("Mono: Failed to unload tools domain"); - } - } -#endif - if (scripts_domain) { Error err = _unload_scripts_domain(); if (err != OK) { @@ -1128,14 +1188,14 @@ int32_t _GodotSharp::get_domain_id() { int32_t _GodotSharp::get_scripts_domain_id() { - MonoDomain *domain = SCRIPTS_DOMAIN; + MonoDomain *domain = GDMono::get_singleton()->get_scripts_domain(); CRASH_COND(!domain); // User must check if scripts domain is loaded before calling this method return mono_domain_get_id(domain); } bool _GodotSharp::is_scripts_domain_loaded() { - return GDMono::get_singleton()->is_runtime_initialized() && SCRIPTS_DOMAIN != NULL; + return GDMono::get_singleton()->is_runtime_initialized() && GDMono::get_singleton()->get_scripts_domain() != NULL; } bool _GodotSharp::_is_domain_finalizing_for_unload(int32_t p_domain_id) { @@ -1157,7 +1217,7 @@ bool _GodotSharp::is_domain_finalizing_for_unload(MonoDomain *p_domain) { if (!p_domain) return true; - if (p_domain == SCRIPTS_DOMAIN && GDMono::get_singleton()->is_finalizing_scripts_domain()) + if (p_domain == GDMono::get_singleton()->get_scripts_domain() && GDMono::get_singleton()->is_finalizing_scripts_domain()) return true; return mono_domain_is_unloading(p_domain); } @@ -1172,6 +1232,12 @@ bool _GodotSharp::is_runtime_initialized() { return GDMono::get_singleton()->is_runtime_initialized(); } +void _GodotSharp::_reload_assemblies(bool p_soft_reload) { +#ifdef GD_MONO_HOT_RELOAD + CSharpLanguage::get_singleton()->reload_assemblies(p_soft_reload); +#endif +} + void _GodotSharp::_bind_methods() { ClassDB::bind_method(D_METHOD("attach_thread"), &_GodotSharp::attach_thread); @@ -1184,6 +1250,7 @@ void _GodotSharp::_bind_methods() { ClassDB::bind_method(D_METHOD("is_runtime_shutting_down"), &_GodotSharp::is_runtime_shutting_down); ClassDB::bind_method(D_METHOD("is_runtime_initialized"), &_GodotSharp::is_runtime_initialized); + ClassDB::bind_method(D_METHOD("_reload_assemblies"), &_GodotSharp::_reload_assemblies); } _GodotSharp::_GodotSharp() { diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h index 95340edcca..a926bf4126 100644 --- a/modules/mono/mono_gd/gd_mono.h +++ b/modules/mono/mono_gd/gd_mono.h @@ -78,11 +78,6 @@ struct Version { String to_string(Type p_type); } // namespace APIAssembly -#define SCRIPTS_DOMAIN GDMono::get_singleton()->get_scripts_domain() -#ifdef TOOLS_ENABLED -#define TOOLS_DOMAIN GDMono::get_singleton()->get_tools_domain() -#endif - class GDMono { bool runtime_initialized; @@ -90,9 +85,6 @@ class GDMono { MonoDomain *root_domain; MonoDomain *scripts_domain; -#ifdef TOOLS_ENABLED - MonoDomain *tools_domain; -#endif bool core_api_assembly_out_of_sync; #ifdef TOOLS_ENABLED @@ -104,7 +96,8 @@ class GDMono { GDMonoAssembly *project_assembly; #ifdef TOOLS_ENABLED GDMonoAssembly *editor_api_assembly; - GDMonoAssembly *editor_tools_assembly; + GDMonoAssembly *tools_assembly; + GDMonoAssembly *tools_project_editor_assembly; #endif HashMap<uint32_t, HashMap<String, GDMonoAssembly *> > assemblies; @@ -115,7 +108,7 @@ class GDMono { bool _load_core_api_assembly(); #ifdef TOOLS_ENABLED bool _load_editor_api_assembly(); - bool _load_editor_tools_assembly(); + bool _load_tools_assemblies(); #endif bool _load_project_assembly(); @@ -132,10 +125,6 @@ class GDMono { Error _load_scripts_domain(); Error _unload_scripts_domain(); -#ifdef TOOLS_ENABLED - Error _load_tools_domain(); -#endif - uint64_t api_core_hash; #ifdef TOOLS_ENABLED uint64_t api_editor_hash; @@ -170,6 +159,7 @@ public: #ifdef TOOLS_ENABLED void metadata_set_api_assembly_invalidated(APIAssembly::Type p_api_type, bool p_invalidated); bool metadata_is_api_assembly_invalidated(APIAssembly::Type p_api_type); + String get_invalidated_api_assembly_path(APIAssembly::Type p_api_type); #endif static GDMono *get_singleton() { return singleton; } @@ -185,16 +175,14 @@ public: _FORCE_INLINE_ bool is_finalizing_scripts_domain() { return finalizing_scripts_domain; } _FORCE_INLINE_ MonoDomain *get_scripts_domain() { return scripts_domain; } -#ifdef TOOLS_ENABLED - _FORCE_INLINE_ MonoDomain *get_tools_domain() { return tools_domain; } -#endif _FORCE_INLINE_ GDMonoAssembly *get_corlib_assembly() const { return corlib_assembly; } _FORCE_INLINE_ GDMonoAssembly *get_core_api_assembly() const { return core_api_assembly; } _FORCE_INLINE_ GDMonoAssembly *get_project_assembly() const { return project_assembly; } #ifdef TOOLS_ENABLED _FORCE_INLINE_ GDMonoAssembly *get_editor_api_assembly() const { return editor_api_assembly; } - _FORCE_INLINE_ GDMonoAssembly *get_editor_tools_assembly() const { return editor_tools_assembly; } + _FORCE_INLINE_ GDMonoAssembly *get_tools_assembly() const { return tools_assembly; } + _FORCE_INLINE_ GDMonoAssembly *get_tools_project_editor_assembly() const { return tools_project_editor_assembly; } #endif #if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED) @@ -202,6 +190,7 @@ public: #endif GDMonoClass *get_class(MonoClass *p_raw_class); + GDMonoClass *get_class(const StringName &p_namespace, const StringName &p_name); #ifdef GD_MONO_HOT_RELOAD Error reload_scripts_domain(); @@ -276,6 +265,8 @@ class _GodotSharp : public Object { List<NodePath *> np_delete_queue; List<RID *> rid_delete_queue; + void _reload_assemblies(bool p_soft_reload); + protected: static _GodotSharp *singleton; static void _bind_methods(); diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp index 1b765a09ff..8e63ef3563 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.cpp +++ b/modules/mono/mono_gd/gd_mono_assembly.cpp @@ -46,6 +46,20 @@ bool GDMonoAssembly::in_preload = false; Vector<String> GDMonoAssembly::search_dirs; +static String _get_expected_api_build_config() { +#ifdef TOOLS_ENABLED + return "Debug"; +#else + +#ifdef DEBUG_ENABLED + return "Debug"; +#else + return "Release"; +#endif + +#endif +} + void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config, const String &p_custom_bcl_dir) { String framework_dir; @@ -67,11 +81,19 @@ void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const Strin r_search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir()); } + String api_config = p_custom_config.empty() ? _get_expected_api_build_config() : + (p_custom_config == "Release" ? "Release" : "Debug"); + r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_base_dir().plus_file(api_config)); + r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir()); r_search_dirs.push_back(OS::get_singleton()->get_resource_dir()); r_search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir()); + #ifdef TOOLS_ENABLED r_search_dirs.push_back(GodotSharpDirs::get_data_editor_tools_dir()); + + // For GodotTools to find the api assemblies + r_search_dirs.push_back(GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug")); #endif } diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp index 4342f46109..1c10d3c8eb 100644 --- a/modules/mono/mono_gd/gd_mono_class.cpp +++ b/modules/mono/mono_gd/gd_mono_class.cpp @@ -41,7 +41,7 @@ String GDMonoClass::get_full_name(MonoClass *p_mono_class) { MonoException *exc = NULL; MonoString *str = GDMonoUtils::object_to_string((MonoObject *)type_obj, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + UNHANDLED_EXCEPTION(exc); return GDMonoMarshal::mono_string_to_godot(str); } @@ -74,16 +74,13 @@ bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const { } GDMonoClass *GDMonoClass::get_parent_class() { + MonoClass *parent_mono_class = mono_class_get_parent(mono_class); + return parent_mono_class ? GDMono::get_singleton()->get_class(parent_mono_class) : NULL; +} - if (assembly) { - MonoClass *parent_mono_class = mono_class_get_parent(mono_class); - - if (parent_mono_class) { - return GDMono::get_singleton()->get_class(parent_mono_class); - } - } - - return NULL; +GDMonoClass *GDMonoClass::get_nesting_class() { + MonoClass *nesting_type = mono_class_get_nesting_type(mono_class); + return nesting_type ? GDMono::get_singleton()->get_class(nesting_type) : NULL; } #ifdef TOOLS_ENABLED diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h index 249422b844..40e1574927 100644 --- a/modules/mono/mono_gd/gd_mono_class.h +++ b/modules/mono/mono_gd/gd_mono_class.h @@ -121,6 +121,7 @@ public: _FORCE_INLINE_ const GDMonoAssembly *get_assembly() const { return assembly; } GDMonoClass *get_parent_class(); + GDMonoClass *get_nesting_class(); #ifdef TOOLS_ENABLED Vector<MonoClassField *> get_enum_fields(); diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp index 2e79f87625..3999658f93 100644 --- a/modules/mono/mono_gd/gd_mono_field.cpp +++ b/modules/mono/mono_gd/gd_mono_field.cpp @@ -315,7 +315,7 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ // The order in which we check the following interfaces is very important (dictionaries and generics first) - MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type()); + MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type_class->get_mono_type()); MonoReflectionType *key_reftype, *value_reftype; if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) { @@ -340,9 +340,15 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ } if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array)); - mono_field_set_value(p_object, mono_field, managed); - break; + if (GDMonoUtils::tools_godot_api_check()) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array)); + mono_field_set_value(p_object, mono_field, managed); + break; + } else { + MonoObject *managed = (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_value.operator Array()); + mono_field_set_value(p_object, mono_field, managed); + break; + } } ERR_EXPLAIN(String() + "Attempted to set the value of a field of unmarshallable type: " + type_class->get_name()); @@ -450,7 +456,7 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ } break; case MONO_TYPE_GENERICINST: { - MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type()); + MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type.type_class->get_mono_type()); if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), type.type_class); @@ -489,9 +495,15 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ } if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array)); - mono_field_set_value(p_object, mono_field, managed); - break; + if (GDMonoUtils::tools_godot_api_check()) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array)); + mono_field_set_value(p_object, mono_field, managed); + break; + } else { + MonoObject *managed = (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_value.operator Array()); + mono_field_set_value(p_object, mono_field, managed); + break; + } } } break; diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp index cb28efb4e5..a84332d4cd 100644 --- a/modules/mono/mono_gd/gd_mono_internals.cpp +++ b/modules/mono/mono_gd/gd_mono_internals.cpp @@ -74,15 +74,14 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) { script_binding.type_name = NATIVE_GDMONOCLASS_NAME(klass); script_binding.wrapper_class = klass; script_binding.gchandle = MonoGCHandle::create_strong(managed); + script_binding.owner = unmanaged; - Reference *kref = Object::cast_to<Reference>(unmanaged); - if (kref) { + if (ref) { // Unsafe refcount increment. The managed instance also counts as a reference. // This way if the unmanaged world has no references to our owner // but the managed instance is alive, the refcount will be 1 instead of 0. // See: godot_icall_Reference_Dtor(MonoObject *p_obj, Object *p_ptr) - - kref->reference(); + ref->reference(); } // The object was just created, no script instance binding should have been attached diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index 87157ed233..42102ed835 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -159,7 +159,7 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) { // The order in which we check the following interfaces is very important (dictionaries and generics first) - MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type()); + MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type_class->get_mono_type()); if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) { return Variant::DICTIONARY; @@ -179,7 +179,7 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) { } break; case MONO_TYPE_GENERICINST: { - MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type()); + MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type()); if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { return Variant::DICTIONARY; @@ -217,7 +217,7 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) { bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type) { switch (p_array_type.type_encoding) { case MONO_TYPE_GENERICINST: { - MonoReflectionType *array_reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_array_type.type_class->get_mono_type()); + MonoReflectionType *array_reftype = mono_type_get_object(mono_domain_get(), p_array_type.type_class->get_mono_type()); if (GDMonoUtils::Marshal::type_is_generic_array(array_reftype)) { MonoReflectionType *elem_reftype; @@ -244,7 +244,7 @@ bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_ bool try_get_dictionary_key_value_types(const ManagedType &p_dictionary_type, ManagedType &r_key_type, ManagedType &r_value_type) { switch (p_dictionary_type.type_encoding) { case MONO_TYPE_GENERICINST: { - MonoReflectionType *dict_reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_dictionary_type.type_class->get_mono_type()); + MonoReflectionType *dict_reftype = mono_type_get_object(mono_domain_get(), p_dictionary_type.type_class->get_mono_type()); if (GDMonoUtils::Marshal::type_is_generic_dictionary(dict_reftype)) { MonoReflectionType *key_reftype; @@ -539,7 +539,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty // The order in which we check the following interfaces is very important (dictionaries and generics first) - MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type()); + MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type_class->get_mono_type()); MonoReflectionType *key_reftype, *value_reftype; if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) { @@ -558,7 +558,11 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty } if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { - return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); + if (GDMonoUtils::tools_godot_api_check()) { + return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); + } else { + return (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_var->operator Array()); + } } } break; case MONO_TYPE_OBJECT: { @@ -652,7 +656,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty } break; case MONO_TYPE_GENERICINST: { - MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type()); + MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type()); if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), p_type.type_class); @@ -681,7 +685,11 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty } if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { - return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); + if (GDMonoUtils::tools_godot_api_check()) { + return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); + } else { + return (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_var->operator Array()); + } } } break; } break; @@ -831,20 +839,20 @@ Variant mono_object_to_variant(MonoObject *p_obj) { if (CACHED_CLASS(Array) == type_class) { MonoException *exc = NULL; Array *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Array, GetPtr), p_obj, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + UNHANDLED_EXCEPTION(exc); return ptr ? Variant(*ptr) : Variant(); } if (CACHED_CLASS(Dictionary) == type_class) { MonoException *exc = NULL; Dictionary *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Dictionary, GetPtr), p_obj, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + UNHANDLED_EXCEPTION(exc); return ptr ? Variant(*ptr) : Variant(); } // The order in which we check the following interfaces is very important (dictionaries and generics first) - MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type()); + MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type_class->get_mono_type()); if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) { return GDMonoUtils::Marshal::generic_idictionary_to_dictionary(p_obj); @@ -864,19 +872,19 @@ Variant mono_object_to_variant(MonoObject *p_obj) { } break; case MONO_TYPE_GENERICINST: { - MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type()); + MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type.type_class->get_mono_type()); if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { MonoException *exc = NULL; MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + UNHANDLED_EXCEPTION(exc); return *unbox<Dictionary *>(ret); } if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) { MonoException *exc = NULL; MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + UNHANDLED_EXCEPTION(exc); return *unbox<Array *>(ret); } diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index 1b32f1126e..5987fa8ebb 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -125,6 +125,7 @@ void MonoCache::clear_godot_api_cache() { class_Array = NULL; class_Dictionary = NULL; class_MarshalUtils = NULL; + class_ISerializationListener = NULL; #ifdef DEBUG_ENABLED class_DebuggingUtils = NULL; @@ -242,6 +243,7 @@ void update_godot_api_cache() { CACHE_CLASS_AND_CHECK(Array, GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Array)); CACHE_CLASS_AND_CHECK(Dictionary, GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary)); CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils)); + CACHE_CLASS_AND_CHECK(ISerializationListener, GODOT_API_CLASS(ISerializationListener)); #ifdef DEBUG_ENABLED CACHE_CLASS_AND_CHECK(DebuggingUtils, GODOT_API_CLASS(DebuggingUtils)); @@ -302,7 +304,7 @@ void update_godot_api_cache() { #endif // TODO Move to CSharpLanguage::init() and do handle disposal - MonoObject *task_scheduler = mono_object_new(SCRIPTS_DOMAIN, GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr()); + MonoObject *task_scheduler = mono_object_new(mono_domain_get(), GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr()); GDMonoUtils::runtime_object_init(task_scheduler, GODOT_API_CLASS(GodotTaskScheduler)); mono_cache.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler); @@ -371,7 +373,6 @@ MonoObject *unmanaged_get_managed(Object *unmanaged) { // This way if the unmanaged world has no references to our owner // but the managed instance is alive, the refcount will be 1 instead of 0. // See: godot_icall_Reference_Dtor(MonoObject *p_obj, Object *p_ptr) - ref->reference(); } @@ -384,7 +385,7 @@ void set_main_thread(MonoThread *p_thread) { void attach_current_thread() { ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized()); - MonoThread *mono_thread = mono_thread_attach(SCRIPTS_DOMAIN); + MonoThread *mono_thread = mono_thread_attach(mono_domain_get()); ERR_FAIL_NULL(mono_thread); } @@ -448,17 +449,12 @@ GDMonoClass *get_class_native_base(GDMonoClass *p_class) { } MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringName &p_native, Object *p_object) { - String object_type = p_object->get_class_name(); - - if (object_type[0] == '_') - object_type = object_type.substr(1, object_type.length()); - - if (!ClassDB::is_parent_class(object_type, p_native)) { + if (!ClassDB::is_parent_class(p_object->get_class_name(), p_native)) { ERR_EXPLAIN("Type inherits from native type '" + p_native + "', so it can't be instanced in object of type: '" + p_object->get_class() + "'"); ERR_FAIL_V(NULL); } - MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_mono_ptr()); + MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr()); ERR_FAIL_NULL_V(mono_object, NULL); CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, p_object); @@ -470,7 +466,7 @@ MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringNa } MonoObject *create_managed_from(const NodePath &p_from) { - MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, CACHED_CLASS_RAW(NodePath)); + MonoObject *mono_object = mono_object_new(mono_domain_get(), CACHED_CLASS_RAW(NodePath)); ERR_FAIL_NULL_V(mono_object, NULL); // Construct @@ -482,7 +478,7 @@ MonoObject *create_managed_from(const NodePath &p_from) { } MonoObject *create_managed_from(const RID &p_from) { - MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, CACHED_CLASS_RAW(RID)); + MonoObject *mono_object = mono_object_new(mono_domain_get(), CACHED_CLASS_RAW(RID)); ERR_FAIL_NULL_V(mono_object, NULL); // Construct @@ -494,7 +490,7 @@ MonoObject *create_managed_from(const RID &p_from) { } MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class) { - MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_mono_ptr()); + MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr()); ERR_FAIL_NULL_V(mono_object, NULL); // Search constructor that takes a pointer as parameter @@ -518,13 +514,13 @@ MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class) { MonoException *exc = NULL; GDMonoUtils::runtime_invoke(m, mono_object, args, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + UNHANDLED_EXCEPTION(exc); return mono_object; } MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class) { - MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_mono_ptr()); + MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr()); ERR_FAIL_NULL_V(mono_object, NULL); // Search constructor that takes a pointer as parameter @@ -548,7 +544,7 @@ MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class) MonoException *exc = NULL; GDMonoUtils::runtime_invoke(m, mono_object, args, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + UNHANDLED_EXCEPTION(exc); return mono_object; } @@ -667,7 +663,10 @@ void print_unhandled_exception(MonoException *p_exc) { } void set_pending_exception(MonoException *p_exc) { -#ifdef HAS_PENDING_EXCEPTIONS +#ifdef NO_PENDING_EXCEPTIONS + debug_unhandled_exception(p_exc); + GD_UNREACHABLE(); +#else if (get_runtime_invoke_count() == 0) { debug_unhandled_exception(p_exc); GD_UNREACHABLE(); @@ -677,9 +676,6 @@ void set_pending_exception(MonoException *p_exc) { ERR_PRINTS("Exception thrown from managed code, but it could not be set as pending:"); GDMonoUtils::debug_print_unhandled_exception(p_exc); } -#else - debug_unhandled_exception(p_exc); - GD_UNREACHABLE(); #endif } @@ -755,113 +751,137 @@ void dispose(MonoObject *p_mono_object, MonoException **r_exc) { namespace Marshal { -MonoBoolean type_is_generic_array(MonoReflectionType *p_reftype) { +#ifdef MONO_GLUE_ENABLED +#ifdef TOOLS_ENABLED +#define NO_GLUE_RET(m_ret) \ + { \ + if (!mono_cache.godot_api_cache_updated) return m_ret; \ + } +#else +#define NO_GLUE_RET(m_ret) \ + {} +#endif +#else +#define NO_GLUE_RET(m_ret) \ + { return m_ret; } +#endif + +bool type_is_generic_array(MonoReflectionType *p_reftype) { + NO_GLUE_RET(false); TypeIsGenericArray thunk = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray); MonoException *exc = NULL; MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); - return res; + UNHANDLED_EXCEPTION(exc); + return (bool)res; } -MonoBoolean type_is_generic_dictionary(MonoReflectionType *p_reftype) { +bool type_is_generic_dictionary(MonoReflectionType *p_reftype) { + NO_GLUE_RET(false); TypeIsGenericDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary); MonoException *exc = NULL; MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); - return res; + UNHANDLED_EXCEPTION(exc); + return (bool)res; } void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype) { ArrayGetElementType thunk = CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType); MonoException *exc = NULL; invoke_method_thunk(thunk, p_array_reftype, r_elem_reftype, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + UNHANDLED_EXCEPTION(exc); } void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) { DictionaryGetKeyValueTypes thunk = CACHED_METHOD_THUNK(MarshalUtils, DictionaryGetKeyValueTypes); MonoException *exc = NULL; invoke_method_thunk(thunk, p_dict_reftype, r_key_reftype, r_value_reftype, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + UNHANDLED_EXCEPTION(exc); } -MonoBoolean generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype) { +bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype) { + NO_GLUE_RET(false); GenericIEnumerableIsAssignableFromType thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType); MonoException *exc = NULL; MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); - return res; + UNHANDLED_EXCEPTION(exc); + return (bool)res; } -MonoBoolean generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype) { +bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype) { + NO_GLUE_RET(false); GenericIDictionaryIsAssignableFromType thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType); MonoException *exc = NULL; MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); - return res; + UNHANDLED_EXCEPTION(exc); + return (bool)res; } -MonoBoolean generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype) { +bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype) { + NO_GLUE_RET(false); GenericIEnumerableIsAssignableFromType_with_info thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info); MonoException *exc = NULL; MonoBoolean res = invoke_method_thunk(thunk, p_reftype, r_elem_reftype, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); - return res; + UNHANDLED_EXCEPTION(exc); + return (bool)res; } -MonoBoolean generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) { +bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) { + NO_GLUE_RET(false); GenericIDictionaryIsAssignableFromType_with_info thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType_with_info); MonoException *exc = NULL; MonoBoolean res = invoke_method_thunk(thunk, p_reftype, r_key_reftype, r_value_reftype, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); - return res; + UNHANDLED_EXCEPTION(exc); + return (bool)res; } Array enumerable_to_array(MonoObject *p_enumerable) { + NO_GLUE_RET(Array()); Array result; EnumerableToArray thunk = CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray); MonoException *exc = NULL; invoke_method_thunk(thunk, p_enumerable, &result, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + UNHANDLED_EXCEPTION(exc); return result; } Dictionary idictionary_to_dictionary(MonoObject *p_idictionary) { + NO_GLUE_RET(Dictionary()); Dictionary result; IDictionaryToDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary); MonoException *exc = NULL; invoke_method_thunk(thunk, p_idictionary, &result, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + UNHANDLED_EXCEPTION(exc); return result; } Dictionary generic_idictionary_to_dictionary(MonoObject *p_generic_idictionary) { + NO_GLUE_RET(Dictionary()); Dictionary result; GenericIDictionaryToDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryToDictionary); MonoException *exc = NULL; invoke_method_thunk(thunk, p_generic_idictionary, &result, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + UNHANDLED_EXCEPTION(exc); return result; } GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype) { + NO_GLUE_RET(NULL); MakeGenericArrayType thunk = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericArrayType); MonoException *exc = NULL; MonoReflectionType *reftype = invoke_method_thunk(thunk, p_elem_reftype, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + UNHANDLED_EXCEPTION(exc); return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype))); } GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) { + NO_GLUE_RET(NULL); MakeGenericDictionaryType thunk = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericDictionaryType); MonoException *exc = NULL; MonoReflectionType *reftype = invoke_method_thunk(thunk, p_key_reftype, p_value_reftype, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + UNHANDLED_EXCEPTION(exc); return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype))); } } // namespace Marshal -// namespace Marshal - } // namespace GDMonoUtils diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index 00e1ffdd31..f535fbb6d0 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -41,7 +41,7 @@ #include "core/object.h" #include "core/reference.h" -#define UNLIKELY_UNHANDLED_EXCEPTION(m_exc) \ +#define UNHANDLED_EXCEPTION(m_exc) \ if (unlikely(m_exc != NULL)) { \ GDMonoUtils::debug_unhandled_exception(m_exc); \ GD_UNREACHABLE(); \ @@ -78,16 +78,16 @@ typedef void (*GenericIDictionaryToDictionary)(MonoObject *, Dictionary *, MonoE namespace Marshal { -MonoBoolean type_is_generic_array(MonoReflectionType *p_reftype); -MonoBoolean type_is_generic_dictionary(MonoReflectionType *p_reftype); +bool type_is_generic_array(MonoReflectionType *p_reftype); +bool type_is_generic_dictionary(MonoReflectionType *p_reftype); void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype); void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype); -MonoBoolean generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype); -MonoBoolean generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype); -MonoBoolean generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype); -MonoBoolean generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype); +bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype); +bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype); +bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype); +bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype); GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype); GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype); @@ -157,6 +157,7 @@ struct MonoCache { GDMonoClass *class_Array; GDMonoClass *class_Dictionary; GDMonoClass *class_MarshalUtils; + GDMonoClass *class_ISerializationListener; #ifdef DEBUG_ENABLED GDMonoClass *class_DebuggingUtils; @@ -235,10 +236,19 @@ void update_godot_api_cache(); inline void clear_corlib_cache() { mono_cache.clear_corlib_cache(); } + inline void clear_godot_api_cache() { mono_cache.clear_godot_api_cache(); } +_FORCE_INLINE_ bool tools_godot_api_check() { +#ifdef TOOLS_ENABLED + return mono_cache.godot_api_cache_updated; +#else + return true; // Assume it's updated if this was called, otherwise it's a bug +#endif +} + _FORCE_INLINE_ void hash_combine(uint32_t &p_hash, const uint32_t &p_with_hash) { p_hash ^= p_with_hash + 0x9e3779b9 + (p_hash << 6) + (p_hash >> 2); } |