未验证 提交 e1a6db99 编写于 作者: M Mike Voorhees 提交者: GitHub

Improve duplicate assembly resolving (#89958)

`GetAssembly` opens a new assembly and stream every time it is called and keeps them open for the duration of the run.

`RootAssemblyFile.LoadAssemblyFile` sort of handled an already loaded assembly, but it left a new `AssemblyDefinition` instance and `MemoryStream` open every time it happened.

`CacheAssembly` was easy to make a mistake with.   Forget to do the delicate dance that `RootAssemblyFile.LoadAssemblyFile` goes to already loaded assemblies and you can easily overwrite an already cached assembly.  Guard against mistakes rather than quitely overwriting the assembly that was already cached.

Why force callers of `CacheAssembly` to check if something is cached already?  Handle that case internally.

With these changes the logic in `RootAssemblyFile.LoadAssemblyFile` is simplified and an extra copy of the assembly is no longer left open in the scenario where an assembly is specified multiple times.
上级 38166fba
......@@ -98,12 +98,6 @@ protected override void Process ()
if (File.Exists (fileName)) {
assembly = Context.Resolver.GetAssembly (fileName);
AssemblyDefinition? loaded = Context.GetLoadedAssembly (assembly.Name.Name);
// The same assembly could be already loaded if there are multiple inputs pointing to same file
if (loaded != null)
return loaded;
Context.Resolver.CacheAssembly (assembly);
return assembly;
}
......
......@@ -44,6 +44,7 @@ public class AssemblyResolver : IAssemblyResolver
readonly LinkContext _context;
readonly List<string> _directories = new ();
readonly Dictionary<AssemblyDefinition, string> _assemblyToPath = new ();
readonly Dictionary<string, AssemblyDefinition> _pathToAssembly = new ();
readonly List<MemoryMappedViewStream> _viewStreams = new ();
readonly ReaderParameters _defaultReaderParameters;
......@@ -136,6 +137,12 @@ public void AddSearchDirectory (string directory)
public AssemblyDefinition GetAssembly (string file)
{
// Sanitize the path for caching purposes
file = Path.GetFullPath (file);
if (_pathToAssembly.TryGetValue (file, out var loadedAssembly))
return loadedAssembly;
MemoryMappedViewStream? viewStream = null;
try {
// Create stream because CreateFromFile(string, ...) uses FileShare.None which is too strict
......@@ -147,6 +154,7 @@ public AssemblyDefinition GetAssembly (string file)
AssemblyDefinition result = ModuleDefinition.ReadModule (viewStream, _defaultReaderParameters).Assembly;
_assemblyToPath.Add (result, file);
_pathToAssembly.Add (file, result);
_viewStreams.Add (viewStream);
......@@ -192,7 +200,14 @@ AssemblyDefinition IAssemblyResolver.Resolve (AssemblyNameReference name, Reader
public void CacheAssembly (AssemblyDefinition assembly)
{
AssemblyCache[assembly.Name.Name] = assembly;
if (AssemblyCache.TryGetValue (assembly.Name.Name, out var existing)) {
if (existing != assembly)
throw new ArgumentException ("Cannot overwrite an existing assembly with a different assembly");
return;
}
AssemblyCache.Add (assembly.Name.Name, assembly);
_context.RegisterAssembly (assembly);
}
......
......@@ -21,6 +21,12 @@ public Task CustomStepData ()
return RunTest (allowMissingWarnings: true);
}
[Fact]
public Task DuplicateRootAssembly ()
{
return RunTest (allowMissingWarnings: true);
}
[Fact]
public Task InvalidArguments ()
{
......
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Mono.Linker.Tests.Cases.Expectations.Metadata;
namespace Mono.Linker.Tests.Cases.CommandLine;
[SetupLinkerArgument ("-a", "test.exe", "entrypoint")]
[SetupLinkerArgument ("-a", "../input/test.exe", "entrypoint")]
public class DuplicateRootAssembly
{
public static void Main ()
{
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册