Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/linker/Linker.Steps/OutputStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,9 @@ static string GetConfigFile (string assembly)
return assembly + ".config";
}

static FileInfo GetOriginalAssemblyFileInfo (AssemblyDefinition assembly)
FileInfo GetOriginalAssemblyFileInfo (AssemblyDefinition assembly)
{
return new FileInfo (assembly.MainModule.FileName);
return new FileInfo (Context.Resolver.GetAssemblyFileName (assembly));
}

protected virtual void CopyAssembly (AssemblyDefinition assembly, string directory)
Expand Down
23 changes: 22 additions & 1 deletion src/linker/Linker/AssemblyResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,20 @@ AssemblyDefinition GetAssembly (string file, ReaderParameters parameters)
}
#endif

public string GetAssemblyFileName(AssemblyDefinition assembly)
{
#if FEATURE_ILLINK
if (assemblyToPath.TryGetValue(assembly, out string path)) {
return path;
}
else
#endif
{
// Must be an assembly that we didn't open through the resolver
return assembly.MainModule.FileName;
}
}

AssemblyDefinition ResolveFromReferences (AssemblyNameReference name, Collection<string> references, ReaderParameters parameters)
{
var fileName = name.Name + ".dll";
Expand All @@ -99,6 +113,11 @@ AssemblyDefinition ResolveFromReferences (AssemblyNameReference name, Collection
return null;
}

public AssemblyDefinition ResolveFromPath(string path, ReaderParameters parameters)
{
return CacheAssembly (GetAssembly (path, parameters));
}

public override AssemblyDefinition Resolve (AssemblyNameReference name, ReaderParameters parameters)
{
// Validate arguments, similarly to how the base class does it.
Expand Down Expand Up @@ -133,7 +152,7 @@ public override AssemblyDefinition Resolve (AssemblyNameReference name, ReaderPa
public virtual AssemblyDefinition CacheAssembly (AssemblyDefinition assembly)
{
_assemblies [assembly.Name.Name] = assembly;
base.AddSearchDirectory (Path.GetDirectoryName (assembly.MainModule.FileName));
base.AddSearchDirectory (Path.GetDirectoryName (GetAssemblyFileName(assembly)));
return assembly;
}

Expand All @@ -151,6 +170,8 @@ protected override void Dispose (bool disposing)
_assemblies.Clear ();
if (_unresolvedAssemblies != null)
_unresolvedAssemblies.Clear ();

base.Dispose (disposing);
}
}
}
35 changes: 34 additions & 1 deletion src/linker/Linker/DirectoryAssemblyResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.MemoryMappedFiles;
using Mono.Collections.Generic;
using Mono.Cecil;

Expand All @@ -15,6 +16,10 @@ public abstract class DirectoryAssemblyResolver : IAssemblyResolver {

readonly Collection<string> directories;

protected readonly Dictionary<AssemblyDefinition, string> assemblyToPath = new Dictionary<AssemblyDefinition, string> ();

readonly List<MemoryMappedViewStream> viewStreams = new List<MemoryMappedViewStream> ();

public void AddSearchDirectory (string directory)
{
directories.Add (directory);
Expand All @@ -40,7 +45,28 @@ protected AssemblyDefinition GetAssembly (string file, ReaderParameters paramete
if (parameters.AssemblyResolver == null)
parameters.AssemblyResolver = this;

return ModuleDefinition.ReadModule (file, parameters).Assembly;
MemoryMappedViewStream viewStream = null;
try {
// Create stream because CreateFromFile(string, ...) uses FileShare.None which is too strict
using var fileStream = new FileStream (file, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, false);
using var mappedFile = MemoryMappedFile.CreateFromFile (
fileStream, null, fileStream.Length, MemoryMappedFileAccess.Read, HandleInheritability.None, true);
viewStream = mappedFile.CreateViewStream (0, 0, MemoryMappedFileAccess.Read);

AssemblyDefinition result = ModuleDefinition.ReadModule (viewStream, parameters).Assembly;

assemblyToPath.Add (result, file);

viewStreams.Add (viewStream);

// We transferred the ownership of the viewStream to the collection.
viewStream = null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this needed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's needed because we want to Dispose the viewStream if ModuleDefinition.ReadModule above failed for any reason - such assembly is unusable and we might as well throw away the mapping now. If accessing the assembly from the module succeeded, we do not want to dispose it because it's used by Cecil and the viewStreams collection now manages the lifetime.

It's also a reason why I'm not using a using - the viewStream handling is odd. If you see a better way to do this, I can change it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might consider using using for fileStream and mappedFile and keep the only viewStream in custom finally to make the logic/flow more obvious


return result;
} finally {
if (viewStream != null)
viewStream.Dispose ();
}
}

public virtual AssemblyDefinition Resolve (AssemblyNameReference name)
Expand Down Expand Up @@ -89,6 +115,13 @@ public void Dispose ()

protected virtual void Dispose (bool disposing)
{
if (disposing) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should there be also finalizer?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The class is not holding unmanaged resources so I don't think we need one.

foreach (var viewStream in viewStreams) {
viewStream.Dispose ();
}

viewStreams.Clear ();
}
}
}
}
Expand Down
5 changes: 2 additions & 3 deletions src/linker/Linker/LinkContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,7 @@ public AssemblyDefinition Resolve (string name)
{
if (File.Exists (name)) {
try {
AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly (name, _readerParameters);
return _resolver.CacheAssembly (assembly);
return _resolver.ResolveFromPath (name, _readerParameters);
} catch (Exception e) {
throw new AssemblyResolutionException (new AssemblyNameReference (name, new Version ()), e);
}
Expand Down Expand Up @@ -314,7 +313,7 @@ public virtual void SafeReadSymbols (AssemblyDefinition assembly)
try {
var symbolReader = _symbolReaderProvider.GetSymbolReader (
assembly.MainModule,
assembly.MainModule.FileName);
_resolver.GetAssemblyFileName(assembly));

if (symbolReader == null)
return;
Expand Down