diff options
author | Ignacio Roldán Etcheverry <ignalfonsore@gmail.com> | 2022-05-28 04:56:46 +0200 |
---|---|---|
committer | Ignacio Roldán Etcheverry <ignalfonsore@gmail.com> | 2022-08-22 03:36:51 +0200 |
commit | e235cef09f71d0cd752ba4931640be24dcb551ab (patch) | |
tree | bb347c5defc17beb54490d48a91edef9da2b0d1d /modules/mono/mono_gd | |
parent | d78e0a842638df9c98a8f7637b125d36e488a367 (diff) |
C#: Re-implement assembly reloading with ALCs
Diffstat (limited to 'modules/mono/mono_gd')
-rw-r--r-- | modules/mono/mono_gd/gd_mono.cpp | 88 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono.h | 36 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_cache.cpp | 5 | ||||
-rw-r--r-- | modules/mono/mono_gd/gd_mono_cache.h | 15 |
4 files changed, 57 insertions, 87 deletions
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index d57ad2831a..f5e38c2e61 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -501,106 +501,48 @@ void GDMono::_init_godot_api_hashes() { #ifdef TOOLS_ENABLED bool GDMono::_load_project_assembly() { - 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"; - } + String appname_safe = ProjectSettings::get_singleton()->get_safe_project_name(); String assembly_path = GodotSharpDirs::get_res_temp_assemblies_dir() .plus_file(appname_safe + ".dll"); assembly_path = ProjectSettings::get_singleton()->globalize_path(assembly_path); - return plugin_callbacks.LoadProjectAssemblyCallback(assembly_path.utf16()); -} -#endif - -#warning TODO hot-reload -#if 0 -Error GDMono::_unload_scripts_domain() { - ERR_FAIL_NULL_V(scripts_domain, ERR_BUG); - - CSharpLanguage::get_singleton()->_on_scripts_domain_about_to_unload(); - - print_verbose("Mono: Finalizing scripts domain..."); - - if (mono_domain_get() != root_domain) { - mono_domain_set(root_domain, true); - } - - finalizing_scripts_domain = true; + String loaded_assembly_path; + bool success = plugin_callbacks.LoadProjectAssemblyCallback(assembly_path.utf16(), &loaded_assembly_path); - if (!mono_domain_finalize(scripts_domain, 2000)) { - ERR_PRINT("Mono: Domain finalization timeout."); + if (success) { + project_assembly_path = loaded_assembly_path.simplify_path(); + project_assembly_modified_time = FileAccess::get_modified_time(loaded_assembly_path); } - finalizing_scripts_domain = false; - - mono_gc_collect(mono_gc_max_generation()); - - core_api_assembly = nullptr; -#ifdef TOOLS_ENABLED - editor_api_assembly = nullptr; -#endif - - project_assembly = nullptr; -#ifdef TOOLS_ENABLED - tools_assembly = nullptr; -#endif - - MonoDomain *domain = scripts_domain; - scripts_domain = nullptr; - - print_verbose("Mono: Unloading scripts domain..."); - - MonoException *exc = nullptr; - mono_domain_try_unload(domain, (MonoObject **)&exc); - - if (exc) { - ERR_PRINT("Exception thrown when unloading scripts domain."); - GDMonoUtils::debug_unhandled_exception(exc); - return FAILED; - } - - return OK; + return success; } +#endif #ifdef GD_MONO_HOT_RELOAD -Error GDMono::reload_scripts_domain() { +Error GDMono::reload_project_assemblies() { ERR_FAIL_COND_V(!runtime_initialized, ERR_BUG); - if (scripts_domain) { - Error domain_unload_err = _unload_scripts_domain(); - ERR_FAIL_COND_V_MSG(domain_unload_err != OK, domain_unload_err, "Mono: Failed to unload scripts domain."); - } - - Error domain_load_err = _load_scripts_domain(); - ERR_FAIL_COND_V_MSG(domain_load_err != OK, domain_load_err, "Mono: Failed to load scripts domain."); + finalizing_scripts_domain = true; - // Load assemblies. The API and tools assemblies are required, - // the application is aborted if these assemblies cannot be loaded. + CSharpLanguage::get_singleton()->_on_scripts_domain_about_to_unload(); - if (!_try_load_api_assemblies()) { - CRASH_NOW_MSG("Failed to load one of the API assemblies."); + if (!get_plugin_callbacks().UnloadProjectPluginCallback()) { + ERR_FAIL_V_MSG(Error::FAILED, ".NET: Failed to unload assemblies."); } -#if defined(TOOLS_ENABLED) - bool tools_assemblies_loaded = _load_tools_assemblies(); - CRASH_COND_MSG(!tools_assemblies_loaded, "Mono: Failed to load '" TOOLS_ASM_NAME "' assemblies."); -#endif + finalizing_scripts_domain = false; // Load the project's main assembly. Here, during hot-reloading, we do // consider failing to load the project's main assembly to be an error. - // However, unlike the API and tools assemblies, the application can continue working. if (!_load_project_assembly()) { - print_error("Mono: Failed to load project assembly"); + print_error(".NET: Failed to load project assembly."); return ERR_CANT_OPEN; } return OK; } #endif -#endif GDMono::GDMono() { singleton = this; diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h index 301782575c..776399a544 100644 --- a/modules/mono/mono_gd/gd_mono.h +++ b/modules/mono/mono_gd/gd_mono.h @@ -45,10 +45,12 @@ namespace gdmono { #ifdef TOOLS_ENABLED struct PluginCallbacks { - using FuncLoadProjectAssemblyCallback = bool(GD_CLR_STDCALL *)(const char16_t *); + using FuncLoadProjectAssemblyCallback = bool(GD_CLR_STDCALL *)(const char16_t *, String *); using FuncLoadToolsAssemblyCallback = Object *(GD_CLR_STDCALL *)(const char16_t *); + using FuncUnloadProjectPluginCallback = bool(GD_CLR_STDCALL *)(); FuncLoadProjectAssemblyCallback LoadProjectAssemblyCallback = nullptr; FuncLoadToolsAssemblyCallback LoadToolsAssemblyCallback = nullptr; + FuncUnloadProjectPluginCallback UnloadProjectPluginCallback = nullptr; }; #endif @@ -63,14 +65,13 @@ class GDMono { void *hostfxr_dll_handle = nullptr; bool is_native_aot = false; + String project_assembly_path; + uint64_t project_assembly_modified_time = 0; + #ifdef TOOLS_ENABLED bool _load_project_assembly(); #endif - bool _try_load_api_assemblies(); - - Error _unload_scripts_domain(); - uint64_t api_core_hash; #ifdef TOOLS_ENABLED uint64_t api_editor_hash; @@ -114,17 +115,32 @@ public: #endif } - static GDMono *get_singleton() { return singleton; } + static GDMono *get_singleton() { + return singleton; + } + + _FORCE_INLINE_ bool is_runtime_initialized() const { + return runtime_initialized; + } + _FORCE_INLINE_ bool is_finalizing_scripts_domain() { + return finalizing_scripts_domain; + } - _FORCE_INLINE_ bool is_runtime_initialized() const { return runtime_initialized; } - _FORCE_INLINE_ bool is_finalizing_scripts_domain() { return finalizing_scripts_domain; } + _FORCE_INLINE_ const String &get_project_assembly_path() const { + return project_assembly_path; + } + _FORCE_INLINE_ uint64_t get_project_assembly_modified_time() const { + return project_assembly_modified_time; + } #ifdef TOOLS_ENABLED - const gdmono::PluginCallbacks &get_plugin_callbacks() { return plugin_callbacks; } + const gdmono::PluginCallbacks &get_plugin_callbacks() { + return plugin_callbacks; + } #endif #ifdef GD_MONO_HOT_RELOAD - Error reload_scripts_domain(); + Error reload_project_assemblies(); #endif void initialize(); diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp index fc47a0e09b..37433c59ee 100644 --- a/modules/mono/mono_gd/gd_mono_cache.cpp +++ b/modules/mono/mono_gd/gd_mono_cache.cpp @@ -52,6 +52,8 @@ void update_godot_api_cache(const ManagedCallbacks &p_managed_callbacks) { CHECK_CALLBACK_NOT_NULL(SignalAwaiter, SignalCallback); CHECK_CALLBACK_NOT_NULL(DelegateUtils, InvokeWithVariantArgs); CHECK_CALLBACK_NOT_NULL(DelegateUtils, DelegateEquals); + CHECK_CALLBACK_NOT_NULL(DelegateUtils, TrySerializeDelegateWithGCHandle); + CHECK_CALLBACK_NOT_NULL(DelegateUtils, TryDeserializeDelegateWithGCHandle); CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, FrameCallback); CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, CreateManagedForGodotObjectBinding); CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, CreateManagedForGodotObjectScriptInstance); @@ -64,6 +66,7 @@ void update_godot_api_cache(const ManagedCallbacks &p_managed_callbacks) { CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, AddScriptBridge); CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, GetOrCreateScriptBridgeForPath); CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, RemoveScriptBridge); + CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, TryReloadRegisteredScriptWithClass); CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, UpdateScriptClassInfo); CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, SwapGCHandleForType); CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, GetPropertyInfoList); @@ -74,6 +77,8 @@ void update_godot_api_cache(const ManagedCallbacks &p_managed_callbacks) { CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, CallDispose); CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, CallToString); CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, HasMethodUnknownParams); + CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, SerializeState); + CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, DeserializeState); CHECK_CALLBACK_NOT_NULL(GCHandleBridge, FreeGCHandle); CHECK_CALLBACK_NOT_NULL(DebuggingUtils, GetCurrentStackInfo); CHECK_CALLBACK_NOT_NULL(DisposablesTracker, OnGodotShuttingDown); diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h index 763a7f3e5e..92778cc2c9 100644 --- a/modules/mono/mono_gd/gd_mono_cache.h +++ b/modules/mono/mono_gd/gd_mono_cache.h @@ -74,6 +74,8 @@ struct ManagedCallbacks { using FuncSignalAwaiter_SignalCallback = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const Variant **, int32_t, bool *); using FuncDelegateUtils_InvokeWithVariantArgs = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const Variant **, uint32_t, const Variant *); using FuncDelegateUtils_DelegateEquals = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, GCHandleIntPtr); + using FuncDelegateUtils_TrySerializeDelegateWithGCHandle = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, const Array *); + using FuncDelegateUtils_TryDeserializeDelegateWithGCHandle = bool(GD_CLR_STDCALL *)(const Array *, GCHandleIntPtr *); using FuncScriptManagerBridge_FrameCallback = void(GD_CLR_STDCALL *)(); using FuncScriptManagerBridge_CreateManagedForGodotObjectBinding = GCHandleIntPtr(GD_CLR_STDCALL *)(const StringName *, Object *); using FuncScriptManagerBridge_CreateManagedForGodotObjectScriptInstance = bool(GD_CLR_STDCALL *)(const CSharpScript *, Object *, const Variant **, int32_t); @@ -86,7 +88,8 @@ struct ManagedCallbacks { using FuncScriptManagerBridge_AddScriptBridge = bool(GD_CLR_STDCALL *)(const CSharpScript *, const String *); using FuncScriptManagerBridge_GetOrCreateScriptBridgeForPath = void(GD_CLR_STDCALL *)(const String *, Ref<CSharpScript> *); using FuncScriptManagerBridge_RemoveScriptBridge = void(GD_CLR_STDCALL *)(const CSharpScript *); - using FuncScriptManagerBridge_UpdateScriptClassInfo = void(GD_CLR_STDCALL *)(const CSharpScript *, bool *, Dictionary *); + using FuncScriptManagerBridge_TryReloadRegisteredScriptWithClass = bool(GD_CLR_STDCALL *)(const CSharpScript *); + using FuncScriptManagerBridge_UpdateScriptClassInfo = void(GD_CLR_STDCALL *)(const CSharpScript *, bool *, Dictionary *, Ref<CSharpScript> *); using FuncScriptManagerBridge_SwapGCHandleForType = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, GCHandleIntPtr *, bool); using FuncScriptManagerBridge_GetPropertyInfoList = void(GD_CLR_STDCALL *)(CSharpScript *, Callback_ScriptManagerBridge_GetPropertyInfoList_Add); using FuncScriptManagerBridge_GetPropertyDefaultValues = void(GD_CLR_STDCALL *)(CSharpScript *, Callback_ScriptManagerBridge_GetPropertyDefaultValues_Add); @@ -96,6 +99,8 @@ struct ManagedCallbacks { using FuncCSharpInstanceBridge_CallDispose = void(GD_CLR_STDCALL *)(GCHandleIntPtr, bool); using FuncCSharpInstanceBridge_CallToString = void(GD_CLR_STDCALL *)(GCHandleIntPtr, String *, bool *); using FuncCSharpInstanceBridge_HasMethodUnknownParams = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, const StringName *); + using FuncCSharpInstanceBridge_SerializeState = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const Dictionary *, const Dictionary *); + using FuncCSharpInstanceBridge_DeserializeState = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const Dictionary *, const Dictionary *); using FuncGCHandleBridge_FreeGCHandle = void(GD_CLR_STDCALL *)(GCHandleIntPtr); using FuncDebuggingUtils_GetCurrentStackInfo = void(GD_CLR_STDCALL *)(Vector<ScriptLanguage::StackInfo> *); using FuncDisposablesTracker_OnGodotShuttingDown = void(GD_CLR_STDCALL *)(); @@ -104,6 +109,8 @@ struct ManagedCallbacks { FuncSignalAwaiter_SignalCallback SignalAwaiter_SignalCallback; FuncDelegateUtils_InvokeWithVariantArgs DelegateUtils_InvokeWithVariantArgs; FuncDelegateUtils_DelegateEquals DelegateUtils_DelegateEquals; + FuncDelegateUtils_TrySerializeDelegateWithGCHandle DelegateUtils_TrySerializeDelegateWithGCHandle; + FuncDelegateUtils_TryDeserializeDelegateWithGCHandle DelegateUtils_TryDeserializeDelegateWithGCHandle; FuncScriptManagerBridge_FrameCallback ScriptManagerBridge_FrameCallback; FuncScriptManagerBridge_CreateManagedForGodotObjectBinding ScriptManagerBridge_CreateManagedForGodotObjectBinding; FuncScriptManagerBridge_CreateManagedForGodotObjectScriptInstance ScriptManagerBridge_CreateManagedForGodotObjectScriptInstance; @@ -116,6 +123,7 @@ struct ManagedCallbacks { FuncScriptManagerBridge_AddScriptBridge ScriptManagerBridge_AddScriptBridge; FuncScriptManagerBridge_GetOrCreateScriptBridgeForPath ScriptManagerBridge_GetOrCreateScriptBridgeForPath; FuncScriptManagerBridge_RemoveScriptBridge ScriptManagerBridge_RemoveScriptBridge; + FuncScriptManagerBridge_TryReloadRegisteredScriptWithClass ScriptManagerBridge_TryReloadRegisteredScriptWithClass; FuncScriptManagerBridge_UpdateScriptClassInfo ScriptManagerBridge_UpdateScriptClassInfo; FuncScriptManagerBridge_SwapGCHandleForType ScriptManagerBridge_SwapGCHandleForType; FuncScriptManagerBridge_GetPropertyInfoList ScriptManagerBridge_GetPropertyInfoList; @@ -126,6 +134,8 @@ struct ManagedCallbacks { FuncCSharpInstanceBridge_CallDispose CSharpInstanceBridge_CallDispose; FuncCSharpInstanceBridge_CallToString CSharpInstanceBridge_CallToString; FuncCSharpInstanceBridge_HasMethodUnknownParams CSharpInstanceBridge_HasMethodUnknownParams; + FuncCSharpInstanceBridge_SerializeState CSharpInstanceBridge_SerializeState; + FuncCSharpInstanceBridge_DeserializeState CSharpInstanceBridge_DeserializeState; FuncGCHandleBridge_FreeGCHandle GCHandleBridge_FreeGCHandle; FuncDebuggingUtils_GetCurrentStackInfo DebuggingUtils_GetCurrentStackInfo; FuncDisposablesTracker_OnGodotShuttingDown DisposablesTracker_OnGodotShuttingDown; @@ -137,9 +147,6 @@ extern bool godot_api_cache_updated; void update_godot_api_cache(const ManagedCallbacks &p_managed_callbacks); -inline void clear_godot_api_cache() { - managed_callbacks = ManagedCallbacks(); -} } // namespace GDMonoCache #undef GD_CLR_STDCALL |