refactor assembly resolver to avoid repeatedly copying search directory list

This commit is contained in:
Jesse Plamondon-Willard 2022-07-08 19:02:33 -04:00
parent 0c0f7898f4
commit 1b3a1a48d0
No known key found for this signature in database
GPG Key ID: CF8B1456B3E29F49
3 changed files with 67 additions and 12 deletions

View File

@ -244,8 +244,8 @@ namespace StardewModdingAPI
internal static void ConfigureAssemblyResolver(AssemblyDefinitionResolver resolver) internal static void ConfigureAssemblyResolver(AssemblyDefinitionResolver resolver)
{ {
// add search paths // add search paths
resolver.AddSearchDirectory(Constants.GamePath); resolver.TryAddSearchDirectory(Constants.GamePath);
resolver.AddSearchDirectory(Constants.InternalFilesPath); resolver.TryAddSearchDirectory(Constants.InternalFilesPath);
// add SMAPI explicitly // add SMAPI explicitly
// Normally this would be handled automatically by the search paths, but for some reason there's a specific // Normally this would be handled automatically by the search paths, but for some reason there's a specific

View File

@ -4,18 +4,31 @@ using Mono.Cecil;
namespace StardewModdingAPI.Framework.ModLoading namespace StardewModdingAPI.Framework.ModLoading
{ {
/// <summary>A minimal assembly definition resolver which resolves references to known assemblies.</summary> /// <summary>A minimal assembly definition resolver which resolves references to known assemblies.</summary>
internal class AssemblyDefinitionResolver : DefaultAssemblyResolver internal class AssemblyDefinitionResolver : IAssemblyResolver
{ {
/********* /*********
** Fields ** Fields
*********/ *********/
/// <summary>The underlying assembly resolver.</summary>
private readonly DefaultAssemblyResolverWrapper Resolver = new();
/// <summary>The known assemblies.</summary> /// <summary>The known assemblies.</summary>
private readonly IDictionary<string, AssemblyDefinition> Lookup = new Dictionary<string, AssemblyDefinition>(); private readonly IDictionary<string, AssemblyDefinition> Lookup = new Dictionary<string, AssemblyDefinition>();
/// <summary>The directory paths to search for assemblies.</summary>
private readonly HashSet<string> SearchPaths = new();
/********* /*********
** Public methods ** Public methods
*********/ *********/
/// <summary>Construct an instance.</summary>
public AssemblyDefinitionResolver()
{
foreach (string path in this.Resolver.GetSearchDirectories())
this.SearchPaths.Add(path);
}
/// <summary>Add known assemblies to the resolver.</summary> /// <summary>Add known assemblies to the resolver.</summary>
/// <param name="assemblies">The known assemblies.</param> /// <param name="assemblies">The known assemblies.</param>
public void Add(params AssemblyDefinition[] assemblies) public void Add(params AssemblyDefinition[] assemblies)
@ -29,7 +42,7 @@ namespace StardewModdingAPI.Framework.ModLoading
/// <param name="names">The assembly names for which it should be returned.</param> /// <param name="names">The assembly names for which it should be returned.</param>
public void AddWithExplicitNames(AssemblyDefinition assembly, params string[] names) public void AddWithExplicitNames(AssemblyDefinition assembly, params string[] names)
{ {
this.RegisterAssembly(assembly); this.Resolver.AddAssembly(assembly);
foreach (string name in names) foreach (string name in names)
this.Lookup[name] = assembly; this.Lookup[name] = assembly;
} }
@ -37,18 +50,52 @@ namespace StardewModdingAPI.Framework.ModLoading
/// <summary>Resolve an assembly reference.</summary> /// <summary>Resolve an assembly reference.</summary>
/// <param name="name">The assembly name.</param> /// <param name="name">The assembly name.</param>
/// <exception cref="AssemblyResolutionException">The assembly can't be resolved.</exception> /// <exception cref="AssemblyResolutionException">The assembly can't be resolved.</exception>
public override AssemblyDefinition Resolve(AssemblyNameReference name) public AssemblyDefinition Resolve(AssemblyNameReference name)
{ {
return this.ResolveName(name.Name) ?? base.Resolve(name); return this.ResolveName(name.Name) ?? this.Resolver.Resolve(name);
} }
/// <summary>Resolve an assembly reference.</summary> /// <summary>Resolve an assembly reference.</summary>
/// <param name="name">The assembly name.</param> /// <param name="name">The assembly name.</param>
/// <param name="parameters">The assembly reader parameters.</param> /// <param name="parameters">The assembly reader parameters.</param>
/// <exception cref="AssemblyResolutionException">The assembly can't be resolved.</exception> /// <exception cref="AssemblyResolutionException">The assembly can't be resolved.</exception>
public override AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters) public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters)
{ {
return this.ResolveName(name.Name) ?? base.Resolve(name, parameters); return this.ResolveName(name.Name) ?? this.Resolver.Resolve(name, parameters);
}
/// <summary>Add a directory path to search for assemblies, if it's non-null and not already added.</summary>
/// <param name="path">The path to search.</param>
/// <returns>Returns whether the path was successfully added.</returns>
public bool TryAddSearchDirectory(string? path)
{
if (path is not null && this.SearchPaths.Add(path))
{
this.Resolver.AddSearchDirectory(path);
return true;
}
return false;
}
/// <summary>Remove a directory path to search for assemblies, if it's non-null.</summary>
/// <param name="path">The path to remove.</param>
/// <returns>Returns whether the path was in the list and removed.</returns>
public bool RemoveSearchDirectory(string? path)
{
if (path is not null && this.SearchPaths.Remove(path))
{
this.Resolver.RemoveSearchDirectory(path);
return true;
}
return false;
}
/// <inheritdoc />
public void Dispose()
{
this.Resolver.Dispose();
} }
@ -63,5 +110,16 @@ namespace StardewModdingAPI.Framework.ModLoading
? match ? match
: null; : null;
} }
/// <summary>An internal wrapper around <see cref="DefaultAssemblyResolver"/> to allow access to its protected methods.</summary>
private class DefaultAssemblyResolverWrapper : DefaultAssemblyResolver
{
/// <summary>Add an assembly to the resolver.</summary>
/// <param name="assembly">The assembly to add.</param>
public void AddAssembly(AssemblyDefinition assembly)
{
this.RegisterAssembly(assembly);
}
}
} }
} }

View File

@ -267,11 +267,8 @@ namespace StardewModdingAPI.Framework.ModLoading
// add the assembly's directory temporarily if needed // add the assembly's directory temporarily if needed
// this is needed by F# mods which bundle FSharp.Core.dll, for example // this is needed by F# mods which bundle FSharp.Core.dll, for example
string? temporarySearchDir = null; string? temporarySearchDir = null;
if (file.DirectoryName is not null && !this.AssemblyDefinitionResolver.GetSearchDirectories().Contains(file.DirectoryName)) if (this.AssemblyDefinitionResolver.TryAddSearchDirectory(file.DirectoryName))
{
this.AssemblyDefinitionResolver.AddSearchDirectory(file.DirectoryName);
temporarySearchDir = file.DirectoryName; temporarySearchDir = file.DirectoryName;
}
// read assembly // read assembly
AssemblyDefinition assembly; AssemblyDefinition assembly;