summaryrefslogtreecommitdiffstats
path: root/modules/mono/mono_gd/gd_mono.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/mono/mono_gd/gd_mono.cpp')
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp779
1 files changed, 152 insertions, 627 deletions
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index ce4fc0c5a0..dcda799f32 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -30,13 +30,6 @@
#include "gd_mono.h"
-#include <mono/metadata/environment.h>
-#include <mono/metadata/exception.h>
-#include <mono/metadata/mono-config.h>
-#include <mono/metadata/mono-debug.h>
-#include <mono/metadata/mono-gc.h>
-#include <mono/metadata/profiler.h>
-
#include "core/config/project_settings.h"
#include "core/debugger/engine_debugger.h"
#include "core/io/dir_access.h"
@@ -48,61 +41,28 @@
#include "../godotsharp_dirs.h"
#include "../utils/path_utils.h"
#include "gd_mono_cache.h"
-#include "gd_mono_utils.h"
+#include <nethost.h>
+
+#include <coreclr_delegates.h>
+#include <hostfxr.h>
+
+#warning TODO mobile
+#if 0
#ifdef ANDROID_ENABLED
#include "android_mono_config.h"
#include "support/android_support.h"
#elif defined(IOS_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 turned 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 = nullptr;
namespace {
-#if defined(JAVASCRIPT_ENABLED)
-extern "C" {
-void mono_wasm_load_runtime(const char *managed_path, int enable_debugging);
-}
-#endif
-
-#if !defined(JAVASCRIPT_ENABLED)
-
-void gd_mono_setup_runtime_main_args() {
- CharString execpath = OS::get_singleton()->get_executable_path().utf8();
-
- List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
-
- List<CharString> cmdline_args_utf8;
- Vector<char *> main_args;
- main_args.resize(cmdline_args.size() + 1);
-
- main_args.write[0] = execpath.ptrw();
-
- int i = 1;
- for (const String &E : cmdline_args) {
- CharString &stored = cmdline_args_utf8.push_back(E.utf8())->get();
- main_args.write[i] = stored.ptrw();
- i++;
- }
-
- mono_runtime_set_main_args(main_args.size(), main_args.ptrw());
-}
-
+#warning "TODO .NET debugging and profiling. What's needed?"
+#if 0
void gd_mono_profiler_init() {
String profiler_args = GLOBAL_DEF("mono/profiler/args", "log:calls,alloc,sample,output=output.mlpd");
bool profiler_enabled = GLOBAL_DEF("mono/profiler/enabled", false);
@@ -165,258 +125,193 @@ void gd_mono_debug_init() {
};
mono_jit_parse_options(2, (char **)options);
}
-
-#endif // !defined(JAVASCRIPT_ENABLED)
-
-#if defined(JAVASCRIPT_ENABLED)
-MonoDomain *gd_initialize_mono_runtime() {
- const char *vfs_prefix = "managed";
- int enable_debugging = 0;
-
- // TODO: Provide a way to enable debugging on WASM release builds.
-#ifdef DEBUG_ENABLED
- enable_debugging = 1;
#endif
+} // namespace
- mono_wasm_load_runtime(vfs_prefix, enable_debugging);
-
- return mono_get_root_domain();
-}
+namespace {
+hostfxr_initialize_for_runtime_config_fn hostfxr_initialize_for_runtime_config = nullptr;
+hostfxr_get_runtime_delegate_fn hostfxr_get_runtime_delegate = nullptr;
+hostfxr_close_fn hostfxr_close = nullptr;
+
+#ifdef _WIN32
+static_assert(sizeof(char_t) == sizeof(char16_t));
+using HostFxrCharString = Char16String;
+#define HOSTFXR_STR(m_str) L##m_str
#else
-MonoDomain *gd_initialize_mono_runtime() {
- gd_mono_debug_init();
+static_assert(sizeof(char_t) == sizeof(char));
+using HostFxrCharString = CharString;
+#define HOSTFXR_STR(m_str) m_str
+#endif
-#if defined(IOS_ENABLED) || defined(ANDROID_ENABLED)
- // I don't know whether this actually matters or not
- const char *runtime_version = "mobile";
+HostFxrCharString str_to_hostfxr(const String &p_str) {
+#ifdef _WIN32
+ return p_str.utf16();
#else
- const char *runtime_version = "v4.0.30319";
+ return p_str.utf8();
#endif
-
- return mono_jit_init_version("GodotEngine.RootDomain", runtime_version);
}
-#endif
-} // namespace
-void GDMono::add_mono_shared_libs_dir_to_path() {
- // TODO: Replace this with a mono_dl_fallback
+String str_from_hostfxr(const char_t *p_buffer) {
+#ifdef _WIN32
+ return String::utf16((const char16_t *)p_buffer);
+#else
+ return String::utf8((const char *)p_buffer);
+#endif
+}
- // By default Mono seems to search shared libraries in the following directories:
- // Current working directory, @executable_path@ and PATH
- // The parent directory of the image file (assembly where the dllimport method is declared)
- // @executable_path@/../lib
- // @executable_path@/../Libraries (__MACH__ only)
+const char_t *get_data(const HostFxrCharString &p_char_str) {
+ return (const char_t *)p_char_str.get_data();
+}
- // This does not work when embedding Mono unless we use the same directory structure.
- // To fix this we append the directory containing our shared libraries to PATH.
+String find_hostfxr() {
+ const int HostApiBufferTooSmall = 0x80008098;
-#if defined(WINDOWS_ENABLED) || defined(UNIX_ENABLED)
- String path_var("PATH");
- String path_value = OS::get_singleton()->get_environment(path_var);
+ size_t buffer_size = 0;
+ int rc = get_hostfxr_path(nullptr, &buffer_size, nullptr);
-#ifdef WINDOWS_ENABLED
- path_value += ';';
+ if (rc == HostApiBufferTooSmall) {
+ // Pre-allocate a large buffer for the path to hostfxr
+ Vector<char_t> buffer;
+ buffer.resize(buffer_size);
- String bundled_bin_dir = GodotSharpDirs::get_data_mono_bin_dir();
-#ifdef TOOLS_ENABLED
- if (DirAccess::exists(bundled_bin_dir)) {
- path_value += bundled_bin_dir;
- } else {
- path_value += mono_reg_info.bin_dir;
- }
-#else
- if (DirAccess::exists(bundled_bin_dir)) {
- path_value += bundled_bin_dir;
- }
-#endif // TOOLS_ENABLED
+ rc = get_hostfxr_path(buffer.ptrw(), &buffer_size, nullptr);
-#else
- path_value += ':';
+ if (rc != 0) {
+ return String();
+ }
- String bundled_lib_dir = GodotSharpDirs::get_data_mono_lib_dir();
- if (DirAccess::exists(bundled_lib_dir)) {
- path_value += bundled_lib_dir;
- } else {
- // TODO: Do we need to add the lib dir when using the system installed Mono on Unix platforms?
+ return str_from_hostfxr(buffer.ptr());
}
-#endif // WINDOWS_ENABLED
- OS::get_singleton()->set_environment(path_var, path_value);
-#endif // WINDOWS_ENABLED || UNIX_ENABLED
+ return String();
}
-void GDMono::determine_mono_dirs(String &r_assembly_rootdir, String &r_config_dir) {
- String bundled_assembly_rootdir = GodotSharpDirs::get_data_mono_lib_dir();
- String bundled_config_dir = GodotSharpDirs::get_data_mono_etc_dir();
-
-#ifdef TOOLS_ENABLED
-
-#if defined(WINDOWS_ENABLED)
- mono_reg_info = MonoRegUtils::find_mono();
+// Forward declarations
+bool load_hostfxr() {
+ String hostfxr_path = find_hostfxr();
- if (mono_reg_info.assembly_dir.length() && DirAccess::exists(mono_reg_info.assembly_dir)) {
- r_assembly_rootdir = mono_reg_info.assembly_dir;
+ if (hostfxr_path.is_empty()) {
+ return false;
}
- if (mono_reg_info.config_dir.length() && DirAccess::exists(mono_reg_info.config_dir)) {
- r_config_dir = mono_reg_info.config_dir;
- }
-#elif defined(MACOS_ENABLED)
- const char *c_assembly_rootdir = mono_assembly_getrootdir();
- const char *c_config_dir = mono_get_config_dir();
-
- if (!c_assembly_rootdir || !c_config_dir || !DirAccess::exists(c_assembly_rootdir) || !DirAccess::exists(c_config_dir)) {
- Vector<const char *> locations;
- locations.push_back("/Library/Frameworks/Mono.framework/Versions/Current/");
- locations.push_back("/usr/local/var/homebrew/linked/mono/");
-
- for (int i = 0; i < locations.size(); i++) {
- String hint_assembly_rootdir = path::join(locations[i], "lib");
- String hint_mscorlib_path = path::join(hint_assembly_rootdir, "mono", "4.5", "mscorlib.dll");
- String hint_config_dir = path::join(locations[i], "etc");
-
- if (FileAccess::exists(hint_mscorlib_path) && DirAccess::exists(hint_config_dir)) {
- r_assembly_rootdir = hint_assembly_rootdir;
- r_config_dir = hint_config_dir;
- break;
- }
- }
- }
-#endif
+ print_verbose("Found hostfxr: " + hostfxr_path);
- if (DirAccess::exists(bundled_assembly_rootdir)) {
- r_assembly_rootdir = bundled_assembly_rootdir;
- }
+ void *lib = nullptr;
+ Error err = OS::get_singleton()->open_dynamic_library(hostfxr_path, lib);
+ // TODO: Clean up lib handle when shutting down
- if (DirAccess::exists(bundled_config_dir)) {
- r_config_dir = bundled_config_dir;
+ if (err != OK) {
+ return false;
}
-#ifdef WINDOWS_ENABLED
- if (r_assembly_rootdir.is_empty() || r_config_dir.is_empty()) {
- ERR_PRINT("Cannot find Mono in the registry.");
- // Assertion: if they are not set, then they weren't found in the registry
- CRASH_COND(mono_reg_info.assembly_dir.length() > 0 || mono_reg_info.config_dir.length() > 0);
- }
-#endif // WINDOWS_ENABLED
+ void *symbol = nullptr;
-#else
- // Export templates always use the bundled directories
- r_assembly_rootdir = bundled_assembly_rootdir;
- r_config_dir = bundled_config_dir;
-#endif
-}
+ err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "hostfxr_initialize_for_runtime_config", symbol);
+ ERR_FAIL_COND_V(err != OK, false);
+ hostfxr_initialize_for_runtime_config = (hostfxr_initialize_for_runtime_config_fn)symbol;
-void GDMono::initialize() {
- ERR_FAIL_NULL(Engine::get_singleton());
+ err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "hostfxr_get_runtime_delegate", symbol);
+ ERR_FAIL_COND_V(err != OK, false);
+ hostfxr_get_runtime_delegate = (hostfxr_get_runtime_delegate_fn)symbol;
- print_verbose("Mono: Initializing module...");
+ err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "hostfxr_close", symbol);
+ ERR_FAIL_COND_V(err != OK, false);
+ hostfxr_close = (hostfxr_close_fn)symbol;
- char *runtime_build_info = mono_get_runtime_build_info();
- print_verbose("Mono JIT compiler version " + String(runtime_build_info));
- mono_free(runtime_build_info);
+ return (hostfxr_initialize_for_runtime_config &&
+ hostfxr_get_runtime_delegate &&
+ hostfxr_close);
+}
- _init_godot_api_hashes();
- _init_exception_policy();
+load_assembly_and_get_function_pointer_fn initialize_hostfxr(const char_t *p_config_path) {
+ hostfxr_handle cxt = nullptr;
+ int rc = hostfxr_initialize_for_runtime_config(p_config_path, nullptr, &cxt);
+ if (rc != 0 || cxt == nullptr) {
+ hostfxr_close(cxt);
+ ERR_FAIL_V_MSG(nullptr, "hostfxr_initialize_for_runtime_config failed");
+ }
- GDMonoLog::get_singleton()->initialize();
+ void *load_assembly_and_get_function_pointer = nullptr;
-#if !defined(JAVASCRIPT_ENABLED)
- String assembly_rootdir;
- String config_dir;
- determine_mono_dirs(assembly_rootdir, config_dir);
+ rc = hostfxr_get_runtime_delegate(cxt,
+ hdt_load_assembly_and_get_function_pointer, &load_assembly_and_get_function_pointer);
+ if (rc != 0 || load_assembly_and_get_function_pointer == nullptr) {
+ ERR_FAIL_V_MSG(nullptr, "hostfxr_get_runtime_delegate failed");
+ }
- // Leak if we call mono_set_dirs more than once
- mono_set_dirs(assembly_rootdir.length() ? assembly_rootdir.utf8().get_data() : nullptr,
- config_dir.length() ? config_dir.utf8().get_data() : nullptr);
+ hostfxr_close(cxt);
- add_mono_shared_libs_dir_to_path();
-#endif
+ return (load_assembly_and_get_function_pointer_fn)load_assembly_and_get_function_pointer;
+}
+} // namespace
-#ifdef ANDROID_ENABLED
- mono_config_parse_memory(get_godot_android_mono_config().utf8().get_data());
-#else
- mono_config_parse(nullptr);
-#endif
+static bool _on_core_api_assembly_loaded() {
+ if (!GDMonoCache::godot_api_cache_updated) {
+ return false;
+ }
-#if defined(ANDROID_ENABLED)
- gdmono::android::support::initialize();
-#elif defined(IOS_ENABLED)
- gdmono::ios::support::initialize();
+ GDMonoCache::managed_callbacks.Dispatcher_InitializeDefaultGodotTaskScheduler();
+
+#ifdef DEBUG_ENABLED
+ // Install the trace listener now before the project assembly is loaded
+ GDMonoCache::managed_callbacks.DebuggingUtils_InstallTraceListener();
#endif
- GDMonoAssembly::initialize();
+ return true;
+}
-#if !defined(JAVASCRIPT_ENABLED)
- gd_mono_profiler_init();
-#endif
+void GDMono::initialize() {
+ ERR_FAIL_NULL(Engine::get_singleton());
- mono_install_unhandled_exception_hook(&unhandled_exception_hook, nullptr);
+ print_verbose(".NET: Initializing module...");
-#ifndef TOOLS_ENABLED
- // Exported games that don't use C# must still work. They likely don't ship with mscorlib.
- // We only initialize the Mono runtime if we can find mscorlib. Otherwise it would crash.
- if (GDMonoAssembly::find_assembly("mscorlib.dll").is_empty()) {
- print_verbose("Mono: Skipping runtime initialization because 'mscorlib.dll' could not be found");
- return;
- }
-#endif
+ _init_godot_api_hashes();
-#if !defined(NO_MONO_THREADS_SUSPEND_WORKAROUND)
- // FIXME: Temporary workaround. See: https://github.com/godotengine/godot/issues/29812
- if (!OS::get_singleton()->has_environment("MONO_THREADS_SUSPEND")) {
- OS::get_singleton()->set_environment("MONO_THREADS_SUSPEND", "preemptive");
+ if (!load_hostfxr()) {
+ ERR_FAIL_MSG(".NET: Failed to load hostfxr");
}
-#endif
- // NOTE: Internal calls must be registered after the Mono runtime initialization.
- // Otherwise registration fails with the error: 'assertion 'hash != nullptr' failed'.
+ auto config_path = str_to_hostfxr(GodotSharpDirs::get_api_assemblies_dir()
+ .plus_file("GodotPlugins.runtimeconfig.json"));
- root_domain = gd_initialize_mono_runtime();
- ERR_FAIL_NULL_MSG(root_domain, "Mono: Failed to initialize runtime.");
+ load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer =
+ initialize_hostfxr(get_data(config_path));
+ ERR_FAIL_NULL(load_assembly_and_get_function_pointer);
- GDMonoUtils::set_main_thread(GDMonoUtils::get_current_thread());
+ runtime_initialized = true;
-#if !defined(JAVASCRIPT_ENABLED)
- gd_mono_setup_runtime_main_args(); // Required for System.Environment.GetCommandLineArgs
-#endif
+ print_verbose(".NET: hostfxr initialized");
- runtime_initialized = true;
+ auto godot_plugins_path = str_to_hostfxr(GodotSharpDirs::get_api_assemblies_dir()
+ .plus_file("GodotPlugins.dll"));
- print_verbose("Mono: Runtime initialized");
+ using godot_plugins_initialize_fn = bool (*)(bool, PluginCallbacks *, GDMonoCache::ManagedCallbacks *);
+ godot_plugins_initialize_fn godot_plugins_initialize = nullptr;
-#if defined(ANDROID_ENABLED)
- gdmono::android::support::register_internal_calls();
-#endif
+ int rc = load_assembly_and_get_function_pointer(get_data(godot_plugins_path),
+ HOSTFXR_STR("GodotPlugins.Main, GodotPlugins"),
+ HOSTFXR_STR("Initialize"),
+ UNMANAGEDCALLERSONLY_METHOD,
+ nullptr,
+ (void **)&godot_plugins_initialize);
+ ERR_FAIL_COND_MSG(rc != 0, ".NET: Failed to get Godot.Plugins Initialize function pointer");
- // 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.");
+ PluginCallbacks aux_plugin_callbacks;
+ GDMonoCache::ManagedCallbacks managed_callbacks;
+ bool init_ok = godot_plugins_initialize(Engine::get_singleton()->is_editor_hint(),
+ &aux_plugin_callbacks, &managed_callbacks);
+ ERR_FAIL_COND_MSG(!init_ok, ".NET: Call to Godot.Plugins Initialize failed");
-#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
+ GDMonoCache::update_godot_api_cache(managed_callbacks);
+ plugin_callbacks = aux_plugin_callbacks;
- _register_internal_calls();
+ print_verbose(".NET: GodotPlugins initialized");
- print_verbose("Mono: INITIALIZED");
+ _on_core_api_assembly_loaded();
}
void GDMono::initialize_load_assemblies() {
- // Load assemblies. The API and tools assemblies are required,
- // the application is aborted if these assemblies cannot be loaded.
-
- if (!_try_load_api_assemblies()) {
- CRASH_NOW_MSG("Failed to load one of the API assemblies.");
- }
-
#if defined(TOOLS_ENABLED)
- bool tool_assemblies_loaded = _load_tools_assemblies();
- CRASH_COND_MSG(!tool_assemblies_loaded, "Mono: Failed to load '" TOOLS_ASM_NAME "' assemblies.");
-
if (Engine::get_singleton()->is_project_manager_hint()) {
return;
}
@@ -427,20 +322,11 @@ void GDMono::initialize_load_assemblies() {
// we're running in the editor, it may just happen to be it wasn't built yet.
if (!_load_project_assembly()) {
if (OS::get_singleton()->is_stdout_verbose()) {
- print_error("Mono: Failed to load project assembly");
+ print_error(".NET: Failed to load project assembly");
}
}
}
-void godot_register_object_icalls();
-void godot_register_placeholder_icalls();
-
-void GDMono::_register_internal_calls() {
- // Registers internal calls that were not generated.
- godot_register_object_icalls();
- godot_register_placeholder_icalls();
-}
-
void GDMono::_init_godot_api_hashes() {
#ifdef DEBUG_METHODS_ENABLED
get_api_core_hash();
@@ -451,216 +337,22 @@ void GDMono::_init_godot_api_hashes() {
#endif // DEBUG_METHODS_ENABLED
}
-void GDMono::_init_exception_policy() {
- PropertyInfo exc_policy_prop = PropertyInfo(Variant::INT, "mono/runtime/unhandled_exception_policy", PROPERTY_HINT_ENUM,
- vformat("Terminate Application:%s,Log Error:%s", (int)POLICY_TERMINATE_APP, (int)POLICY_LOG_ERROR));
- unhandled_exception_policy = (UnhandledExceptionPolicy)(int)GLOBAL_DEF(exc_policy_prop.name, (int)POLICY_TERMINATE_APP);
- ProjectSettings::get_singleton()->set_custom_property_info(exc_policy_prop.name, exc_policy_prop);
-
- if (Engine::get_singleton()->is_editor_hint()) {
- // Unhandled exceptions should not terminate the editor
- unhandled_exception_policy = POLICY_LOG_ERROR;
- }
-}
-
-void GDMono::add_assembly(int32_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) {
- if (p_name == "mscorlib" && corlib_assembly) {
- return corlib_assembly;
- }
-
- MonoDomain *domain = mono_domain_get();
- int32_t domain_id = domain ? mono_domain_get_id(domain) : 0;
- GDMonoAssembly **result = assemblies[domain_id].getptr(p_name);
- return result ? *result : nullptr;
-}
-
-bool GDMono::load_assembly(const String &p_name, GDMonoAssembly **r_assembly) {
-#ifdef DEBUG_ENABLED
- CRASH_COND(!r_assembly);
-#endif
-
- MonoAssemblyName *aname = mono_assembly_name_new(p_name.utf8());
- bool result = load_assembly(p_name, aname, r_assembly);
- mono_assembly_name_free(aname);
- mono_free(aname);
-
- return result;
-}
-
-bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly) {
-#ifdef DEBUG_ENABLED
- CRASH_COND(!r_assembly);
-#endif
-
- return load_assembly(p_name, p_aname, r_assembly, GDMonoAssembly::get_default_search_dirs());
-}
-
-bool GDMono::load_assembly(const String &p_name, MonoAssemblyName *p_aname, GDMonoAssembly **r_assembly, const Vector<String> &p_search_dirs) {
-#ifdef DEBUG_ENABLED
- CRASH_COND(!r_assembly);
-#endif
-
- print_verbose("Mono: Loading assembly " + p_name + "...");
-
- GDMonoAssembly *assembly = GDMonoAssembly::load(p_name, p_aname, /* refonly: */ false, p_search_dirs);
-
- if (!assembly) {
- return false;
- }
-
- *r_assembly = assembly;
-
- print_verbose("Mono: Assembly " + p_name + " loaded from path: " + (*r_assembly)->get_path());
-
- return true;
-}
-
-bool GDMono::load_assembly_from(const String &p_name, const String &p_path, GDMonoAssembly **r_assembly) {
- CRASH_COND(!r_assembly);
-
- print_verbose("Mono: Loading assembly " + p_name + "...");
-
- GDMonoAssembly *assembly = GDMonoAssembly::load_from(p_name, p_path, /* refonly: */ false);
-
- if (!assembly) {
- return false;
- }
-
- *r_assembly = assembly;
-
- print_verbose("Mono: Assembly " + p_name + " loaded from path: " + (*r_assembly)->get_path());
-
- return true;
-}
-
-bool GDMono::_load_corlib_assembly() {
- if (corlib_assembly) {
- return true;
- }
-
- return load_assembly("mscorlib", &corlib_assembly);
-}
-
-bool GDMono::_load_core_api_assembly(GDMonoAssembly **r_loaded_api_assembly, const String &p_config) {
- if (*r_loaded_api_assembly) {
- return true;
- }
-
- return load_assembly(CORE_API_ASSEMBLY_NAME, r_loaded_api_assembly);
-}
-
-#ifdef TOOLS_ENABLED
-bool GDMono::_load_editor_api_assembly(GDMonoAssembly **r_loaded_api_assembly, const String &p_config) {
- if (*r_loaded_api_assembly) {
- return true;
- }
-
- return load_assembly(EDITOR_API_ASSEMBLY_NAME, r_loaded_api_assembly);
-}
-#endif
-
-bool GDMono::_try_load_api_assemblies() {
- String config = get_expected_api_build_config();
-
- if (!_load_core_api_assembly(&core_api_assembly, config)) {
- if (OS::get_singleton()->is_stdout_verbose()) {
- print_error("Mono: Failed to load Core API assembly");
- }
- return false;
- }
-
-#ifdef TOOLS_ENABLED
- if (!_load_editor_api_assembly(&editor_api_assembly, config)) {
- if (OS::get_singleton()->is_stdout_verbose()) {
- print_error("Mono: Failed to load Editor API assembly");
- }
- return false;
- }
-#endif
-
- return _on_core_api_assembly_loaded();
-}
-
-bool GDMono::_on_core_api_assembly_loaded() {
- GDMonoCache::update_godot_api_cache();
-
- if (!GDMonoCache::cached_data.godot_api_cache_updated) {
- return false;
- }
-
- get_singleton()->_install_trace_listener();
-
- return true;
-}
-
-#ifdef TOOLS_ENABLED
-bool GDMono::_load_tools_assemblies() {
- if (tools_assembly && tools_project_editor_assembly) {
- return true;
- }
-
- return load_assembly(TOOLS_ASM_NAME, &tools_assembly) &&
- load_assembly(TOOLS_PROJECT_EDITOR_ASM_NAME, &tools_project_editor_assembly);
-}
-#endif
-
bool GDMono::_load_project_assembly() {
- if (project_assembly) {
- return true;
- }
-
String appname = ProjectSettings::get_singleton()->get("application/config/name");
String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
if (appname_safe.is_empty()) {
appname_safe = "UnnamedProject";
}
- bool success = load_assembly(appname_safe, &project_assembly);
+ String assembly_path = GodotSharpDirs::get_res_temp_assemblies_dir()
+ .plus_file(appname_safe + ".dll");
+ assembly_path = ProjectSettings::get_singleton()->globalize_path(assembly_path);
- if (success) {
- mono_assembly_set_main(project_assembly->get_assembly());
- MonoException *exc = nullptr;
- GDMonoCache::cached_data.methodthunk_ScriptManagerBridge_LookupScriptsInAssembly.invoke(
- mono_assembly_get_object(mono_domain_get(), project_assembly->get_assembly()), &exc);
- if (exc) {
- GDMonoUtils::debug_print_unhandled_exception(exc);
- }
- }
-
- return success;
-}
-
-void GDMono::_install_trace_listener() {
-#ifdef DEBUG_ENABLED
- // Install the trace listener now before the project assembly is loaded
- MonoException *exc = nullptr;
- GDMonoCache::cached_data.methodthunk_DebuggingUtils_InstallTraceListener.invoke(&exc);
- if (exc) {
- GDMonoUtils::debug_print_unhandled_exception(exc);
- ERR_PRINT("Failed to install 'System.Diagnostics.Trace' listener.");
- }
-#endif
-}
-
-#ifndef GD_MONO_SINGLE_APPDOMAIN
-Error GDMono::_load_scripts_domain() {
- ERR_FAIL_COND_V(scripts_domain != nullptr, ERR_BUG);
-
- print_verbose("Mono: Loading scripts domain...");
-
- scripts_domain = GDMonoUtils::create_domain("GodotEngine.Domain.Scripts");
-
- ERR_FAIL_NULL_V_MSG(scripts_domain, ERR_CANT_CREATE, "Mono: Could not create scripts app domain.");
-
- mono_domain_set(scripts_domain, true);
-
- return OK;
+ return plugin_callbacks.LoadProjectAssemblyCallback(assembly_path.utf16());
}
+#warning TODO hot-reload
+#if 0
Error GDMono::_unload_scripts_domain() {
ERR_FAIL_NULL_V(scripts_domain, ERR_BUG);
@@ -682,10 +374,6 @@ Error GDMono::_unload_scripts_domain() {
mono_gc_collect(mono_gc_max_generation());
- GDMonoCache::clear_godot_api_cache();
-
- _domain_assemblies_cleanup(mono_domain_get_id(scripts_domain));
-
core_api_assembly = nullptr;
#ifdef TOOLS_ENABLED
editor_api_assembly = nullptr;
@@ -694,7 +382,6 @@ Error GDMono::_unload_scripts_domain() {
project_assembly = nullptr;
#ifdef TOOLS_ENABLED
tools_assembly = nullptr;
- tools_project_editor_assembly = nullptr;
#endif
MonoDomain *domain = scripts_domain;
@@ -713,7 +400,6 @@ Error GDMono::_unload_scripts_domain() {
return OK;
}
-#endif
#ifdef GD_MONO_HOT_RELOAD
Error GDMono::reload_scripts_domain() {
@@ -750,51 +436,10 @@ Error GDMono::reload_scripts_domain() {
return OK;
}
#endif
-
-#ifndef GD_MONO_SINGLE_APPDOMAIN
-Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
- 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);
-
- print_verbose("Mono: Unloading domain '" + domain_name + "'...");
-
- if (mono_domain_get() == p_domain) {
- mono_domain_set(root_domain, true);
- }
-
- if (!mono_domain_finalize(p_domain, 2000)) {
- ERR_PRINT("Mono: Domain finalization timeout.");
- }
-
- mono_gc_collect(mono_gc_max_generation());
-
- _domain_assemblies_cleanup(mono_domain_get_id(p_domain));
-
- MonoException *exc = nullptr;
- mono_domain_try_unload(p_domain, (MonoObject **)&exc);
-
- if (exc) {
- ERR_PRINT("Exception thrown when unloading domain '" + domain_name + "'.");
- GDMonoUtils::debug_print_unhandled_exception(exc);
- return FAILED;
- }
-
- return OK;
-}
#endif
-void GDMono::_domain_assemblies_cleanup(int32_t p_domain_id) {
- HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[p_domain_id];
-
- for (const KeyValue<String, GDMonoAssembly *> &E : domain_assemblies) {
- memdelete(E.value);
- }
-
- assemblies.erase(p_domain_id);
-}
-
+#warning TODO Reimplement in C#
+#if 0
void GDMono::unhandled_exception_hook(MonoObject *p_exc, void *) {
// This method will be called by the runtime when a thrown exception is not handled.
// It won't be called when we manually treat a thrown exception as unhandled.
@@ -811,89 +456,37 @@ void GDMono::unhandled_exception_hook(MonoObject *p_exc, void *) {
GD_UNREACHABLE();
}
+#endif
GDMono::GDMono() {
singleton = this;
- gdmono_log = memnew(GDMonoLog);
-
runtime_initialized = false;
finalizing_scripts_domain = false;
- root_domain = nullptr;
- scripts_domain = nullptr;
-
- corlib_assembly = nullptr;
- project_assembly = nullptr;
-#ifdef TOOLS_ENABLED
- tools_assembly = nullptr;
- tools_project_editor_assembly = nullptr;
-#endif
-
api_core_hash = 0;
#ifdef TOOLS_ENABLED
api_editor_hash = 0;
#endif
-
- unhandled_exception_policy = POLICY_TERMINATE_APP;
}
GDMono::~GDMono() {
if (is_runtime_initialized()) {
-#ifndef GD_MONO_SINGLE_APPDOMAIN
+#warning "TODO assembly unloading for cleanup of disposables (including managed RefCounteds)"
+#if 0
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;
-
- 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
-
- for (const KeyValue<int32_t, HashMap<String, GDMonoAssembly *>> &E : assemblies) {
- const HashMap<String, GDMonoAssembly *> &domain_assemblies = E.value;
-
- for (const KeyValue<String, GDMonoAssembly *> &F : domain_assemblies) {
- memdelete(F.value);
- }
- }
- assemblies.clear();
print_verbose("Mono: Runtime cleanup...");
mono_jit_cleanup(root_domain);
print_verbose("Mono: Finalized");
+#endif
runtime_initialized = false;
}
@@ -902,10 +495,6 @@ GDMono::~GDMono() {
gdmono::android::support::cleanup();
#endif
- if (gdmono_log) {
- memdelete(gdmono_log);
- }
-
singleton = nullptr;
}
@@ -913,62 +502,7 @@ namespace mono_bind {
GodotSharp *GodotSharp::singleton = nullptr;
-void GodotSharp::attach_thread() {
- GDMonoUtils::attach_current_thread();
-}
-
-void GodotSharp::detach_thread() {
- GDMonoUtils::detach_current_thread();
-}
-
-int32_t GodotSharp::get_domain_id() {
- MonoDomain *domain = mono_domain_get();
- ERR_FAIL_NULL_V(domain, -1);
- return mono_domain_get_id(domain);
-}
-
-int32_t GodotSharp::get_scripts_domain_id() {
- ERR_FAIL_NULL_V_MSG(GDMono::get_singleton(),
- -1, "The Mono runtime is not initialized");
- MonoDomain *domain = GDMono::get_singleton()->get_scripts_domain();
- ERR_FAIL_NULL_V(domain, -1);
- return mono_domain_get_id(domain);
-}
-
-bool GodotSharp::is_scripts_domain_loaded() {
- return GDMono::get_singleton() != nullptr &&
- 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) {
- return is_domain_finalizing_for_unload(p_domain_id);
-}
-
-bool GodotSharp::is_domain_finalizing_for_unload(int32_t p_domain_id) {
- return is_domain_finalizing_for_unload(mono_domain_get_by_id(p_domain_id));
-}
-
-bool GodotSharp::is_domain_finalizing_for_unload(MonoDomain *p_domain) {
- GDMono *gd_mono = GDMono::get_singleton();
-
- ERR_FAIL_COND_V_MSG(!gd_mono || !gd_mono->is_runtime_initialized(),
- false, "The Mono runtime is not initialized");
-
- ERR_FAIL_NULL_V(p_domain, true);
-
- if (p_domain == gd_mono->get_scripts_domain() && gd_mono->is_finalizing_scripts_domain()) {
- return true;
- }
-
- return mono_domain_is_unloading(p_domain);
-}
-
-bool GodotSharp::is_runtime_shutting_down() {
- return mono_runtime_is_shutting_down();
-}
-
-bool GodotSharp::is_runtime_initialized() {
+bool GodotSharp::_is_runtime_initialized() {
return GDMono::get_singleton() != nullptr && GDMono::get_singleton()->is_runtime_initialized();
}
@@ -984,16 +518,7 @@ void GodotSharp::_reload_assemblies(bool p_soft_reload) {
}
void GodotSharp::_bind_methods() {
- ClassDB::bind_method(D_METHOD("attach_thread"), &GodotSharp::attach_thread);
- ClassDB::bind_method(D_METHOD("detach_thread"), &GodotSharp::detach_thread);
-
- ClassDB::bind_method(D_METHOD("get_domain_id"), &GodotSharp::get_domain_id);
- ClassDB::bind_method(D_METHOD("get_scripts_domain_id"), &GodotSharp::get_scripts_domain_id);
- ClassDB::bind_method(D_METHOD("is_scripts_domain_loaded"), &GodotSharp::is_scripts_domain_loaded);
- ClassDB::bind_method(D_METHOD("is_domain_finalizing_for_unload", "domain_id"), &GodotSharp::_is_domain_finalizing_for_unload);
-
- 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("is_runtime_initialized"), &GodotSharp::_is_runtime_initialized);
ClassDB::bind_method(D_METHOD("_reload_assemblies"), &GodotSharp::_reload_assemblies);
}