diff options
Diffstat (limited to 'modules/mono/mono_gd/gd_mono.cpp')
-rw-r--r-- | modules/mono/mono_gd/gd_mono.cpp | 198 |
1 files changed, 130 insertions, 68 deletions
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index eb4c263745..306a1997ab 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -58,14 +58,25 @@ #ifdef ANDROID_ENABLED #include "android_mono_config.h" -#include "gd_mono_android.h" +#include "support/android_support.h" +#elif defined(IPHONE_ENABLED) +#include "support/ios_support.h" +#endif + +#if defined(TOOL_ENABLED) && defined(GD_MONO_SINGLE_APPDOMAIN) +// This will no longer be the case if we replace appdomains with AssemblyLoadContext +#error "Editor build requires support for multiple appdomains" +#endif + +#if defined(GD_MONO_HOT_RELOAD) && defined(GD_MONO_SINGLE_APPDOMAIN) +#error "Hot reloading requires multiple appdomains" #endif // TODO: // This has turn into a gigantic mess. There's too much going on here. Too much #ifdef as well. // It's just painful to read... It needs to be re-structured. Please, clean this up, future me. -GDMono *GDMono::singleton = NULL; +GDMono *GDMono::singleton = nullptr; namespace { @@ -178,7 +189,14 @@ MonoDomain *gd_initialize_mono_runtime() { gd_mono_debug_init(); #endif - return mono_jit_init_version("GodotEngine.RootDomain", "v4.0.30319"); +#if defined(IPHONE_ENABLED) || defined(ANDROID_ENABLED) + // I don't know whether this actually matters or not + const char *runtime_version = "mobile"; +#else + const char *runtime_version = "v4.0.30319"; +#endif + + return mono_jit_init_version("GodotEngine.RootDomain", runtime_version); } #endif @@ -314,14 +332,22 @@ void GDMono::initialize() { determine_mono_dirs(assembly_rootdir, config_dir); // Leak if we call mono_set_dirs more than once - mono_set_dirs(assembly_rootdir.length() ? assembly_rootdir.utf8().get_data() : NULL, - config_dir.length() ? config_dir.utf8().get_data() : NULL); + mono_set_dirs(assembly_rootdir.length() ? assembly_rootdir.utf8().get_data() : nullptr, + config_dir.length() ? config_dir.utf8().get_data() : nullptr); add_mono_shared_libs_dir_to_path(); #endif +#ifdef ANDROID_ENABLED + mono_config_parse_memory(get_godot_android_mono_config().utf8().get_data()); +#else + mono_config_parse(nullptr); +#endif + #if defined(ANDROID_ENABLED) - GDMonoAndroid::initialize(); + gdmono::android::support::initialize(); +#elif defined(IPHONE_ENABLED) + gdmono::ios::support::initialize(); #endif GDMonoAssembly::initialize(); @@ -330,13 +356,7 @@ void GDMono::initialize() { gd_mono_profiler_init(); #endif -#ifdef ANDROID_ENABLED - mono_config_parse_memory(get_godot_android_mono_config().utf8().get_data()); -#else - mono_config_parse(NULL); -#endif - - mono_install_unhandled_exception_hook(&unhandled_exception_hook, NULL); + mono_install_unhandled_exception_hook(&unhandled_exception_hook, nullptr); #ifndef TOOLS_ENABLED // Exported games that don't use C# must still work. They likely don't ship with mscorlib. @@ -355,7 +375,7 @@ void GDMono::initialize() { #endif // NOTE: Internal calls must be registered after the Mono runtime initialization. - // Otherwise registration fails with the error: 'assertion 'hash != NULL' failed'. + // Otherwise registration fails with the error: 'assertion 'hash != nullptr' failed'. root_domain = gd_initialize_mono_runtime(); ERR_FAIL_NULL_MSG(root_domain, "Mono: Failed to initialize runtime."); @@ -371,15 +391,19 @@ void GDMono::initialize() { print_verbose("Mono: Runtime initialized"); #if defined(ANDROID_ENABLED) - GDMonoAndroid::register_internal_calls(); + gdmono::android::support::register_internal_calls(); #endif // mscorlib assembly MUST be present at initialization bool corlib_loaded = _load_corlib_assembly(); ERR_FAIL_COND_MSG(!corlib_loaded, "Mono: Failed to load mscorlib assembly."); +#ifndef GD_MONO_SINGLE_APPDOMAIN Error domain_load_err = _load_scripts_domain(); ERR_FAIL_COND_MSG(domain_load_err != OK, "Mono: Failed to load scripts domain."); +#else + scripts_domain = root_domain; +#endif _register_internal_calls(); @@ -491,11 +515,15 @@ void GDMono::add_assembly(uint32_t p_domain_id, GDMonoAssembly *p_assembly) { assemblies[p_domain_id][p_assembly->get_name()] = p_assembly; } -GDMonoAssembly **GDMono::get_loaded_assembly(const String &p_name) { +GDMonoAssembly *GDMono::get_loaded_assembly(const String &p_name) { + + if (p_name == "mscorlib") + return get_corlib_assembly(); MonoDomain *domain = mono_domain_get(); uint32_t domain_id = domain ? mono_domain_get_id(domain) : 0; - return assemblies[domain_id].getptr(p_name); + GDMonoAssembly **result = assemblies[domain_id].getptr(p_name); + return result ? *result : nullptr; } bool GDMono::load_assembly(const String &p_name, GDMonoAssembly **r_assembly, bool p_refonly) { @@ -517,7 +545,7 @@ bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMo print_verbose("Mono: Loading assembly " + p_name + (p_refonly ? " (refonly)" : "") + "..."); MonoImageOpenStatus status = MONO_IMAGE_OK; - MonoAssembly *assembly = mono_assembly_load_full(p_aname, NULL, &status, p_refonly); + MonoAssembly *assembly = mono_assembly_load_full(p_aname, nullptr, &status, p_refonly); if (!assembly) return false; @@ -528,7 +556,7 @@ bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMo GDMonoAssembly **stored_assembly = assemblies[domain_id].getptr(p_name); - ERR_FAIL_COND_V(stored_assembly == NULL, false); + ERR_FAIL_COND_V(stored_assembly == nullptr, false); ERR_FAIL_COND_V((*stored_assembly)->get_assembly() != assembly, false); *r_assembly = *stored_assembly; @@ -549,14 +577,6 @@ bool GDMono::load_assembly_from(const String &p_name, const String &p_path, GDMo if (!assembly) return false; -#ifdef DEBUG_ENABLED - uint32_t domain_id = mono_domain_get_id(mono_domain_get()); - GDMonoAssembly **stored_assembly = assemblies[domain_id].getptr(p_name); - - ERR_FAIL_COND_V(stored_assembly == NULL, false); - ERR_FAIL_COND_V(*stored_assembly != assembly, false); -#endif - *r_assembly = assembly; print_verbose("Mono: Assembly " + p_name + (p_refonly ? " (refonly)" : "") + " loaded from path: " + (*r_assembly)->get_path()); @@ -576,15 +596,15 @@ ApiAssemblyInfo::Version ApiAssemblyInfo::Version::get_from_loaded_assembly(GDMo if (nativecalls_klass) { GDMonoField *api_hash_field = nativecalls_klass->get_field("godot_api_hash"); if (api_hash_field) - api_assembly_version.godot_api_hash = GDMonoMarshal::unbox<uint64_t>(api_hash_field->get_value(NULL)); + api_assembly_version.godot_api_hash = GDMonoMarshal::unbox<uint64_t>(api_hash_field->get_value(nullptr)); GDMonoField *binds_ver_field = nativecalls_klass->get_field("bindings_version"); if (binds_ver_field) - api_assembly_version.bindings_version = GDMonoMarshal::unbox<uint32_t>(binds_ver_field->get_value(NULL)); + api_assembly_version.bindings_version = GDMonoMarshal::unbox<uint32_t>(binds_ver_field->get_value(nullptr)); GDMonoField *cs_glue_ver_field = nativecalls_klass->get_field("cs_glue_version"); if (cs_glue_ver_field) - api_assembly_version.cs_glue_version = GDMonoMarshal::unbox<uint32_t>(cs_glue_ver_field->get_value(NULL)); + api_assembly_version.cs_glue_version = GDMonoMarshal::unbox<uint32_t>(cs_glue_ver_field->get_value(nullptr)); } return api_assembly_version; @@ -715,7 +735,7 @@ bool GDMono::_temp_domain_load_are_assemblies_out_of_sync(const String &p_config GDMono::LoadedApiAssembly temp_editor_api_assembly; if (!_try_load_api_assemblies(temp_core_api_assembly, temp_editor_api_assembly, - p_config, /* refonly: */ true, /* loaded_callback: */ NULL)) { + p_config, /* refonly: */ true, /* loaded_callback: */ nullptr)) { return temp_core_api_assembly.out_of_sync || temp_editor_api_assembly.out_of_sync; } @@ -894,8 +914,8 @@ void GDMono::_load_api_assemblies() { bool api_assemblies_loaded = _try_load_api_assemblies_preset(); +#if defined(TOOLS_ENABLED) && !defined(GD_MONO_SINGLE_APPDOMAIN) if (!api_assemblies_loaded) { -#ifdef TOOLS_ENABLED // The API assemblies are out of sync or some other error happened. Fine, try one more time, but // this time update them from the prebuilt assemblies directory before trying to load them again. @@ -916,8 +936,8 @@ void GDMono::_load_api_assemblies() { // 4. Try loading the updated assemblies api_assemblies_loaded = _try_load_api_assemblies_preset(); -#endif } +#endif if (!api_assemblies_loaded) { // welp... too bad @@ -982,8 +1002,8 @@ void GDMono::_install_trace_listener() { GDMonoClass *debug_utils = get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, "DebuggingUtils"); GDMonoMethod *install_func = debug_utils->get_method("InstallTraceListener"); - MonoException *exc = NULL; - install_func->invoke_raw(NULL, NULL, &exc); + MonoException *exc = nullptr; + install_func->invoke_raw(nullptr, nullptr, &exc); if (exc) { GDMonoUtils::debug_print_unhandled_exception(exc); ERR_PRINT("Failed to install 'System.Diagnostics.Trace' listener."); @@ -991,9 +1011,10 @@ void GDMono::_install_trace_listener() { #endif } +#ifndef GD_MONO_SINGLE_APPDOMAIN Error GDMono::_load_scripts_domain() { - ERR_FAIL_COND_V(scripts_domain != NULL, ERR_BUG); + ERR_FAIL_COND_V(scripts_domain != nullptr, ERR_BUG); print_verbose("Mono: Loading scripts domain..."); @@ -1010,7 +1031,7 @@ Error GDMono::_unload_scripts_domain() { ERR_FAIL_NULL_V(scripts_domain, ERR_BUG); - print_verbose("Mono: Unloading scripts domain..."); + print_verbose("Mono: Finalizing scripts domain..."); if (mono_domain_get() != root_domain) mono_domain_set(root_domain, true); @@ -1029,21 +1050,23 @@ Error GDMono::_unload_scripts_domain() { _domain_assemblies_cleanup(mono_domain_get_id(scripts_domain)); - core_api_assembly.assembly = NULL; + core_api_assembly.assembly = nullptr; #ifdef TOOLS_ENABLED - editor_api_assembly.assembly = NULL; + editor_api_assembly.assembly = nullptr; #endif - project_assembly = NULL; + project_assembly = nullptr; #ifdef TOOLS_ENABLED - tools_assembly = NULL; - tools_project_editor_assembly = NULL; + tools_assembly = nullptr; + tools_project_editor_assembly = nullptr; #endif MonoDomain *domain = scripts_domain; - scripts_domain = NULL; + scripts_domain = nullptr; + + print_verbose("Mono: Unloading scripts domain..."); - MonoException *exc = NULL; + MonoException *exc = nullptr; mono_domain_try_unload(domain, (MonoObject **)&exc); if (exc) { @@ -1054,6 +1077,7 @@ Error GDMono::_unload_scripts_domain() { return OK; } +#endif #ifdef GD_MONO_HOT_RELOAD Error GDMono::reload_scripts_domain() { @@ -1092,9 +1116,10 @@ Error GDMono::reload_scripts_domain() { } #endif +#ifndef GD_MONO_SINGLE_APPDOMAIN Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) { - CRASH_COND(p_domain == NULL); + CRASH_COND(p_domain == nullptr); 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); @@ -1112,7 +1137,7 @@ Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) { _domain_assemblies_cleanup(mono_domain_get_id(p_domain)); - MonoException *exc = NULL; + MonoException *exc = nullptr; mono_domain_try_unload(p_domain, (MonoObject **)&exc); if (exc) { @@ -1123,6 +1148,7 @@ Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) { return OK; } +#endif GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) { @@ -1134,7 +1160,7 @@ GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) { uint32_t domain_id = mono_domain_get_id(mono_domain_get()); HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[domain_id]; - const String *k = NULL; + const String *k = nullptr; while ((k = domain_assemblies.next(k))) { GDMonoAssembly *assembly = domain_assemblies.get(*k); if (assembly->get_image() == image) { @@ -1145,30 +1171,34 @@ GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) { } } - return NULL; + return nullptr; } GDMonoClass *GDMono::get_class(const StringName &p_namespace, const StringName &p_name) { + GDMonoClass *klass = corlib_assembly->get_class(p_namespace, p_name); + if (klass) + return klass; + uint32_t domain_id = mono_domain_get_id(mono_domain_get()); HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[domain_id]; - const String *k = NULL; + const String *k = nullptr; while ((k = domain_assemblies.next(k))) { GDMonoAssembly *assembly = domain_assemblies.get(*k); - GDMonoClass *klass = assembly->get_class(p_namespace, p_name); + klass = assembly->get_class(p_namespace, p_name); if (klass) return klass; } - return NULL; + return nullptr; } void GDMono::_domain_assemblies_cleanup(uint32_t p_domain_id) { HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[p_domain_id]; - const String *k = NULL; + const String *k = nullptr; while ((k = domain_assemblies.next(k))) { memdelete(domain_assemblies.get(*k)); } @@ -1202,14 +1232,14 @@ GDMono::GDMono() { runtime_initialized = false; finalizing_scripts_domain = false; - root_domain = NULL; - scripts_domain = NULL; + root_domain = nullptr; + scripts_domain = nullptr; - corlib_assembly = NULL; - project_assembly = NULL; + corlib_assembly = nullptr; + project_assembly = nullptr; #ifdef TOOLS_ENABLED - tools_assembly = NULL; - tools_project_editor_assembly = NULL; + tools_assembly = nullptr; + tools_project_editor_assembly = nullptr; #endif api_core_hash = 0; @@ -1223,18 +1253,50 @@ GDMono::GDMono() { GDMono::~GDMono() { if (is_runtime_initialized()) { +#ifndef GD_MONO_SINGLE_APPDOMAIN if (scripts_domain) { Error err = _unload_scripts_domain(); if (err != OK) { ERR_PRINT("Mono: Failed to unload scripts domain."); } } +#else + CRASH_COND(scripts_domain != root_domain); + + print_verbose("Mono: Finalizing scripts domain..."); + + if (mono_domain_get() != root_domain) + mono_domain_set(root_domain, true); + + finalizing_scripts_domain = true; + + if (!mono_domain_finalize(root_domain, 2000)) { + ERR_PRINT("Mono: Domain finalization timeout."); + } + + finalizing_scripts_domain = false; - const uint32_t *k = NULL; + mono_gc_collect(mono_gc_max_generation()); + + GDMonoCache::clear_godot_api_cache(); + + _domain_assemblies_cleanup(mono_domain_get_id(root_domain)); + + core_api_assembly.assembly = nullptr; + + project_assembly = nullptr; + + root_domain = nullptr; + scripts_domain = nullptr; + + // Leave the rest to 'mono_jit_cleanup' +#endif + + const uint32_t *k = nullptr; while ((k = assemblies.next(k))) { HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies.get(*k); - const String *kk = NULL; + const String *kk = nullptr; while ((kk = domain_assemblies.next(kk))) { memdelete(domain_assemblies.get(*kk)); } @@ -1245,22 +1307,22 @@ GDMono::~GDMono() { mono_jit_cleanup(root_domain); -#if defined(ANDROID_ENABLED) - GDMonoAndroid::cleanup(); -#endif - print_verbose("Mono: Finalized"); runtime_initialized = false; } +#if defined(ANDROID_ENABLED) + gdmono::android::support::cleanup(); +#endif + if (gdmono_log) memdelete(gdmono_log); - singleton = NULL; + singleton = nullptr; } -_GodotSharp *_GodotSharp::singleton = NULL; +_GodotSharp *_GodotSharp::singleton = nullptr; void _GodotSharp::attach_thread() { @@ -1288,7 +1350,7 @@ int32_t _GodotSharp::get_scripts_domain_id() { bool _GodotSharp::is_scripts_domain_loaded() { - return GDMono::get_singleton()->is_runtime_initialized() && GDMono::get_singleton()->get_scripts_domain() != NULL; + return GDMono::get_singleton()->is_runtime_initialized() && GDMono::get_singleton()->get_scripts_domain() != nullptr; } bool _GodotSharp::_is_domain_finalizing_for_unload(int32_t p_domain_id) { @@ -1353,5 +1415,5 @@ _GodotSharp::_GodotSharp() { _GodotSharp::~_GodotSharp() { - singleton = NULL; + singleton = nullptr; } |