Merge pull request #67511 from neikeq/issue-66060

C#: Load assemblies as collectible only in the Godot editor
This commit is contained in:
Rémi Verschelde 2022-11-25 10:00:33 +01:00
commit c3af45791c
No known key found for this signature in database
GPG key ID: C3336907360768E1
3 changed files with 32 additions and 10 deletions

View file

@ -710,6 +710,12 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
return;
}
if (!Engine::get_singleton()->is_editor_hint()) {
// We disable collectible assemblies in the game player, because the limitations cause
// issues with mocking libraries. As such, we can only reload assemblies in the editor.
return;
}
// TODO:
// Currently, this reloads all scripts, including those whose class is not part of the
// assembly load context being unloaded. As such, we unnecessarily reload GodotTools.

View file

@ -28,17 +28,24 @@ namespace GodotPlugins
get => _pluginLoadContext?.AssemblyLoadedPath;
}
public bool IsCollectible
{
[MethodImpl(MethodImplOptions.NoInlining)]
get => _pluginLoadContext?.IsCollectible ?? false;
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static (Assembly, PluginLoadContextWrapper) CreateAndLoadFromAssemblyName(
AssemblyName assemblyName,
string pluginPath,
ICollection<string> sharedAssemblies,
AssemblyLoadContext mainLoadContext
AssemblyLoadContext mainLoadContext,
bool isCollectible
)
{
var wrapper = new PluginLoadContextWrapper();
wrapper._pluginLoadContext = new PluginLoadContext(
pluginPath, sharedAssemblies, mainLoadContext);
pluginPath, sharedAssemblies, mainLoadContext, isCollectible);
var assembly = wrapper._pluginLoadContext.LoadFromAssemblyName(assemblyName);
return (assembly, wrapper);
}
@ -61,6 +68,7 @@ namespace GodotPlugins
private static readonly Assembly CoreApiAssembly = typeof(Godot.Object).Assembly;
private static Assembly? _editorApiAssembly;
private static PluginLoadContextWrapper? _projectLoadContext;
private static bool _editorHint = false;
private static readonly AssemblyLoadContext MainLoadContext =
AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()) ??
@ -77,15 +85,17 @@ namespace GodotPlugins
{
try
{
_editorHint = editorHint.ToBool();
_dllImportResolver = new GodotDllImportResolver(godotDllHandle).OnResolveDllImport;
SharedAssemblies.Add(CoreApiAssembly.GetName());
NativeLibrary.SetDllImportResolver(CoreApiAssembly, _dllImportResolver);
AlcReloadCfg.Configure(alcReloadEnabled: editorHint.ToBool());
AlcReloadCfg.Configure(alcReloadEnabled: _editorHint);
NativeFuncs.Initialize(unmanagedCallbacks, unmanagedCallbacksSize);
if (editorHint.ToBool())
if (_editorHint)
{
_editorApiAssembly = Assembly.Load("GodotSharpEditor");
SharedAssemblies.Add(_editorApiAssembly.GetName());
@ -128,7 +138,7 @@ namespace GodotPlugins
string assemblyPath = new(nAssemblyPath);
(var projectAssembly, _projectLoadContext) = LoadPlugin(assemblyPath);
(var projectAssembly, _projectLoadContext) = LoadPlugin(assemblyPath, isCollectible: _editorHint);
string loadedAssemblyPath = _projectLoadContext.AssemblyLoadedPath ?? assemblyPath;
*outLoadedAssemblyPath = Marshaling.ConvertStringToNative(loadedAssemblyPath);
@ -155,7 +165,7 @@ namespace GodotPlugins
if (_editorApiAssembly == null)
throw new InvalidOperationException("The Godot editor API assembly is not loaded.");
var (assembly, _) = LoadPlugin(assemblyPath);
var (assembly, _) = LoadPlugin(assemblyPath, isCollectible: _editorHint);
NativeLibrary.SetDllImportResolver(assembly, _dllImportResolver!);
@ -180,7 +190,7 @@ namespace GodotPlugins
}
}
private static (Assembly, PluginLoadContextWrapper) LoadPlugin(string assemblyPath)
private static (Assembly, PluginLoadContextWrapper) LoadPlugin(string assemblyPath, bool isCollectible)
{
string assemblyName = Path.GetFileNameWithoutExtension(assemblyPath);
@ -194,7 +204,7 @@ namespace GodotPlugins
}
return PluginLoadContextWrapper.CreateAndLoadFromAssemblyName(
new AssemblyName(assemblyName), assemblyPath, sharedAssemblies, MainLoadContext);
new AssemblyName(assemblyName), assemblyPath, sharedAssemblies, MainLoadContext, isCollectible);
}
[UnmanagedCallersOnly]
@ -218,6 +228,12 @@ namespace GodotPlugins
if (pluginLoadContext == null)
return true;
if (!pluginLoadContext.IsCollectible)
{
Console.Error.WriteLine("Cannot unload a non-collectible assembly load context.");
return false;
}
Console.WriteLine("Unloading assembly load context...");
var alcWeakReference = pluginLoadContext.CreateWeakReference();

View file

@ -15,8 +15,8 @@ namespace GodotPlugins
public string? AssemblyLoadedPath { get; private set; }
public PluginLoadContext(string pluginPath, ICollection<string> sharedAssemblies,
AssemblyLoadContext mainLoadContext)
: base(isCollectible: true)
AssemblyLoadContext mainLoadContext, bool isCollectible)
: base(isCollectible)
{
_resolver = new AssemblyDependencyResolver(pluginPath);
_sharedAssemblies = sharedAssemblies;