summaryrefslogtreecommitdiff
path: root/modules/mono/mono_gd
diff options
context:
space:
mode:
authorIgnacio Roldán Etcheverry <ignalfonsore@gmail.com>2022-05-28 04:56:46 +0200
committerIgnacio Roldán Etcheverry <ignalfonsore@gmail.com>2022-08-22 03:36:51 +0200
commite235cef09f71d0cd752ba4931640be24dcb551ab (patch)
treebb347c5defc17beb54490d48a91edef9da2b0d1d /modules/mono/mono_gd
parentd78e0a842638df9c98a8f7637b125d36e488a367 (diff)
C#: Re-implement assembly reloading with ALCs
Diffstat (limited to 'modules/mono/mono_gd')
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp88
-rw-r--r--modules/mono/mono_gd/gd_mono.h36
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.cpp5
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.h15
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