From 40643ffdefe2cb5e1bd36ee4a7a1a92a396ca0e5 Mon Sep 17 00:00:00 2001 From: aldelaro5 Date: Thu, 29 Aug 2024 21:12:07 -0400 Subject: [PATCH] Offload the load context caching to bepinex --- BepInEx.Core/Bootstrap/BaseChainloader.cs | 13 +++--- BepInEx.Core/Bootstrap/TypeLoader.cs | 41 +++++++++++++++++++ BepInEx.Core/Contract/IPluginLoadContext.cs | 2 +- .../BepInExPatcherLoadContext.cs | 22 ++-------- .../BepInExPluginLoadContext.cs | 24 ++--------- .../Patching/AssemblyPatcher.cs | 12 ++++-- 6 files changed, 64 insertions(+), 50 deletions(-) diff --git a/BepInEx.Core/Bootstrap/BaseChainloader.cs b/BepInEx.Core/Bootstrap/BaseChainloader.cs index c2e7c7e..c233d7e 100644 --- a/BepInEx.Core/Bootstrap/BaseChainloader.cs +++ b/BepInEx.Core/Bootstrap/BaseChainloader.cs @@ -146,7 +146,7 @@ public abstract class BaseChainloader /// Value holds info of the originating provider. These contexts are used by chainloader to /// load plugins. Plugins that are loaded successfully are added to . /// - public Dictionary LoadContexts { get; } = new(); + internal Dictionary LoadContexts { get; } = new(); /// /// Collection of error chainloader messages that occured during plugin loading. @@ -350,7 +350,7 @@ public abstract class BaseChainloader LoadPlugins(providers, provider: true); FinishedProviders?.Invoke(); - var plugins = TypeLoader.GetPluginsFromLoadContexts(LoadContexts.Keys, ToPluginInfo, HasBepinPluginType, "chainloader"); + var plugins = TypeLoader.GetPluginsFromLoadContexts(LoadContexts.Keys.Cast(), ToPluginInfo, HasBepinPluginType, "chainloader"); Logger.Log(LogLevel.Info, $"{plugins.Count} plugin{(plugins.Count == 1 ? "" : "s")} to load"); LoadPlugins(plugins, provider: false); Finished?.Invoke(); @@ -414,23 +414,24 @@ public abstract class BaseChainloader var pluginLoadContexts = ((BasePluginProvider)pluginInfo.Instance).GetPlugins(); foreach (IPluginLoadContext context in pluginLoadContexts) { - LoadContexts[context] = pluginInfo; + var cachedContext = new CachedPluginLoadContext(context); + LoadContexts[cachedContext] = pluginInfo; } ProviderLoaded?.Invoke(pluginInfo); } else { - pluginInfo.Source = LoadContexts[pluginInfo.LoadContext]; + pluginInfo.Source = LoadContexts[(CachedPluginLoadContext)pluginInfo.LoadContext]; pluginInfo.Instance = LoadPlugin(pluginInfo, ass); PluginLoaded?.Invoke(pluginInfo); - pluginInfo.LoadContext.Dispose(); + foreach (CachedPluginLoadContext context in LoadContexts.Keys) + context.Dispose(); } } catch (Exception ex) { invalidPlugins.Add(pluginInfo.Metadata.GUID); existingPluginInfos.Remove(pluginInfo.Metadata.GUID); - pluginInfo.LoadContext?.Dispose(); Logger.Log(LogLevel.Error, $"Error loading [{pluginInfo}]: {(ex is ReflectionTypeLoadException re ? TypeLoader.TypeLoadExceptionToString(re) : ex.ToString())}"); diff --git a/BepInEx.Core/Bootstrap/TypeLoader.cs b/BepInEx.Core/Bootstrap/TypeLoader.cs index b847c4e..d83e6d8 100644 --- a/BepInEx.Core/Bootstrap/TypeLoader.cs +++ b/BepInEx.Core/Bootstrap/TypeLoader.cs @@ -10,6 +10,47 @@ using Mono.Cecil; namespace BepInEx.Bootstrap; +internal class CachedPluginLoadContext : IPluginLoadContext, IDisposable +{ + public IPluginLoadContext PluginLoadContext { get; } + private byte[] assemblyData; + private byte[] assemblySymbolsData; + private readonly Dictionary files = new(); + + public CachedPluginLoadContext(IPluginLoadContext pluginLoadContext) + { + PluginLoadContext = pluginLoadContext; + } + + public string AssemblyIdentifier => PluginLoadContext.AssemblyIdentifier; + public string AssemblyHash => PluginLoadContext.AssemblyHash; + public byte[] GetAssemblyData() + { + return assemblyData ??= PluginLoadContext.GetAssemblyData(); + } + + public byte[] GetAssemblySymbolsData() + { + return assemblySymbolsData ??= PluginLoadContext.GetAssemblySymbolsData(); + } + + public byte[] GetFile(string relativePath) + { + if (!files.ContainsKey(relativePath)) + { + files[relativePath] = PluginLoadContext.GetFile(relativePath); + } + return files[relativePath]; + } + + public void Dispose() + { + assemblyData = null; + assemblySymbolsData = null; + files.Clear(); + } +} + /// /// A cacheable metadata item. Can be used with and /// to cache plugin metadata. diff --git a/BepInEx.Core/Contract/IPluginLoadContext.cs b/BepInEx.Core/Contract/IPluginLoadContext.cs index 013b973..1812ea8 100644 --- a/BepInEx.Core/Contract/IPluginLoadContext.cs +++ b/BepInEx.Core/Contract/IPluginLoadContext.cs @@ -6,7 +6,7 @@ namespace BepInEx; /// An interface that allows to dynamically load and track an assembly /// which can be obtained from a provider /// -public interface IPluginLoadContext : IDisposable +public interface IPluginLoadContext { /// /// An identifier that uniquely identifies an assembly from a provider no matter its revision diff --git a/BepInEx.PatcherProvider/BepInExPatcherLoadContext.cs b/BepInEx.PatcherProvider/BepInExPatcherLoadContext.cs index 3aca66f..be1ca0a 100644 --- a/BepInEx.PatcherProvider/BepInExPatcherLoadContext.cs +++ b/BepInEx.PatcherProvider/BepInExPatcherLoadContext.cs @@ -7,25 +7,15 @@ public class BepInExPatcherLoadContext : IPluginLoadContext { public string AssemblyIdentifier { get; internal set; } public string AssemblyHash { get; internal set; } - private byte[] assemblyData; - private byte[] assemblySymbolsData; public byte[] GetAssemblyData() { - if (assemblyData == null) - { - assemblyData = File.ReadAllBytes(AssemblyIdentifier); - } - - return assemblyData; + return File.ReadAllBytes(AssemblyIdentifier); } public byte[] GetAssemblySymbolsData() { - if (assemblySymbolsData == null) - { - if (!Utility.TryResolveDllSymbols(AssemblyIdentifier, out assemblySymbolsData)) - assemblySymbolsData = null; - } + if (Utility.TryResolveAssemblySymbols(AssemblyIdentifier, out var assemblySymbolsData)) + return assemblySymbolsData; return assemblySymbolsData; } @@ -39,10 +29,4 @@ public class BepInExPatcherLoadContext : IPluginLoadContext string filePath = Path.Combine(assemblyFolder, relativePath); return File.ReadAllBytes(filePath); } - - public void Dispose() - { - assemblyData = null; - assemblySymbolsData = null; - } } diff --git a/BepInEx.PluginProvider/BepInExPluginLoadContext.cs b/BepInEx.PluginProvider/BepInExPluginLoadContext.cs index 389e181..d7b0136 100644 --- a/BepInEx.PluginProvider/BepInExPluginLoadContext.cs +++ b/BepInEx.PluginProvider/BepInExPluginLoadContext.cs @@ -7,25 +7,15 @@ public class BepInExPluginLoadContext : IPluginLoadContext { public string AssemblyIdentifier { get; internal set; } public string AssemblyHash { get; internal set; } - private byte[] assemblyData; - private byte[] assemblySymbolsData; public byte[] GetAssemblyData() { - if (assemblyData == null) - { - assemblyData = File.ReadAllBytes(AssemblyIdentifier); - } - - return assemblyData; + return File.ReadAllBytes(AssemblyIdentifier); } - + public byte[] GetAssemblySymbolsData() { - if (assemblySymbolsData == null) - { - if (!Utility.TryResolveDllSymbols(AssemblyIdentifier, out assemblySymbolsData)) - assemblySymbolsData = null; - } + if (Utility.TryResolveAssemblySymbols(AssemblyIdentifier, out var assemblySymbolsData)) + return assemblySymbolsData; return assemblySymbolsData; } @@ -39,10 +29,4 @@ public class BepInExPluginLoadContext : IPluginLoadContext string filePath = Path.Combine(assemblyFolder, relativePath); return File.ReadAllBytes(filePath); } - - public void Dispose() - { - assemblyData = null; - assemblySymbolsData = null; - } } diff --git a/BepInEx.Preloader.Core/Patching/AssemblyPatcher.cs b/BepInEx.Preloader.Core/Patching/AssemblyPatcher.cs index a9822d9..64e04d8 100644 --- a/BepInEx.Preloader.Core/Patching/AssemblyPatcher.cs +++ b/BepInEx.Preloader.Core/Patching/AssemblyPatcher.cs @@ -25,7 +25,7 @@ public class AssemblyPatcher : IDisposable private Func assemblyLoader; - private readonly List loadContexts = new(); + private readonly List loadContexts = new(); private readonly List providers = new(); public AssemblyPatcher(Func assemblyLoader) @@ -164,7 +164,7 @@ public class AssemblyPatcher : IDisposable var instance = (BasePatcherProvider) Activator.CreateInstance(type); providers.Add(instance); - loadContexts.AddRange(instance.GetPatchers()); + loadContexts.AddRange(instance.GetPatchers().Select(x => new CachedPluginLoadContext(x))); } catch (Exception e) { @@ -174,7 +174,7 @@ public class AssemblyPatcher : IDisposable } } - var patchers = TypeLoader.GetPluginsFromLoadContexts(loadContexts, ToPatcherPlugin, HasPatcherType); + var patchers = TypeLoader.GetPluginsFromLoadContexts(loadContexts.Cast(), ToPatcherPlugin, HasPatcherType); var sortedPatchers = new List(); @@ -188,11 +188,15 @@ public class AssemblyPatcher : IDisposable var assName = ass.GetName(); Logger.Log(LogLevel.Debug, $"Loaded patcher {patcherPlugin.TypeName} from [{assName.Name} {assName.Version}]"); - patcherPlugin.LoadContext.Dispose(); } PatcherContext.PatchDefinitions.AddRange(sortedPatchers); Logger.Log(LogLevel.Info, $"Loaded {sortedPatchers.Count} patcher{(sortedPatchers.Count > 0 ? "s" : "")}"); + + foreach (CachedPluginLoadContext loadContext in loadContexts) + { + loadContext.Dispose(); + } } private Assembly PatcherProvidersAssemblyResolver(object sender, ResolveEventArgs args)