Skip to content

Commit 57de175

Browse files
[Java.Interop.Tools.Cecil] use MemoryMappedFile in DirectoryAssemblyResolver
Context: dotnet/linker#1130 Context: https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1783420 In dotnet/linker#1130, their version of `DirectoryAssemblyResolver` was reworked to use `MemoryMappedFile` in the .NET 6 timeframe. This commit ports these changes here, as we have MSBuild tasks in xamarin/xamarin-android that use `DirectoryAssemblyResolver`. Primarily, the `<GenerateJavaStubs />` MSBuild task uses `DirectoryAssemblyResolver` to iterate over types in assembly to emit Java source code and generate `AndroidManifest.xml`. The results of these changes in a `dotnet new maui` project, an initial clean build: GenerateJavaStubs = 1.318 s, 1 calls. GenerateJavaStubs = 1.254 s, 1 calls. Saving ~64ms or about ~5% in this example. The current version of the linker's resolver in .NET 8+ has moved to the dotnet/runtime repo at: https://github.com/dotnet/runtime/blob/cd7d006030a7feace9076fa275fb5bffc1bf4a90/src/tools/illink/src/linker/Linker/AssemblyResolver.cs
1 parent 07d5595 commit 57de175

File tree

1 file changed

+30
-2
lines changed

1 file changed

+30
-2
lines changed

src/Java.Interop.Tools.Cecil/Java.Interop.Tools.Cecil/DirectoryAssemblyResolver.cs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
using System.Collections;
3333
using System.Collections.Generic;
3434
using System.IO;
35+
using System.IO.MemoryMappedFiles;
3536
using System.Linq;
3637
using System.Reflection;
3738

@@ -61,6 +62,7 @@ public class DirectoryAssemblyResolver : IAssemblyResolver {
6162

6263
public ICollection<string> SearchDirectories {get; private set;}
6364

65+
readonly List<MemoryMappedViewStream> viewStreams = new List<MemoryMappedViewStream> ();
6466
Dictionary<string, AssemblyDefinition?> cache;
6567
bool loadDebugSymbols;
6668
Action<TraceLevel, string> logger;
@@ -103,6 +105,10 @@ protected virtual void Dispose (bool disposing)
103105
e.Value?.Dispose ();
104106
}
105107
cache.Clear ();
108+
foreach (var viewStream in viewStreams) {
109+
viewStream.Dispose ();
110+
}
111+
viewStreams.Clear ();
106112
}
107113

108114
public Dictionary<string, AssemblyDefinition?> ToResolverCache ()
@@ -160,14 +166,36 @@ protected virtual AssemblyDefinition ReadAssembly (string file)
160166
SymbolStream = loadReaderParameters.SymbolStream,
161167
};
162168
try {
163-
return AssemblyDefinition.ReadAssembly (file, reader_parameters);
169+
return LoadFromMemoryMappedFile (file, reader_parameters);
164170
} catch (Exception ex) {
165171
logger (
166172
TraceLevel.Verbose,
167173
$"Failed to read '{file}' with debugging symbols. Retrying to load it without it. Error details are logged below.");
168174
logger (TraceLevel.Verbose, $"{ex.ToString ()}");
169175
reader_parameters.ReadSymbols = false;
170-
return AssemblyDefinition.ReadAssembly (file, reader_parameters);
176+
return LoadFromMemoryMappedFile (file, reader_parameters);
177+
}
178+
}
179+
180+
AssemblyDefinition LoadFromMemoryMappedFile(string file, ReaderParameters options)
181+
{
182+
MemoryMappedViewStream? viewStream = null;
183+
try {
184+
// Create stream because CreateFromFile(string, ...) uses FileShare.None which is too strict
185+
using var fileStream = new FileStream (file, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, false);
186+
using var mappedFile = MemoryMappedFile.CreateFromFile (
187+
fileStream, null, fileStream.Length, MemoryMappedFileAccess.Read, HandleInheritability.None, true);
188+
viewStream = mappedFile.CreateViewStream (0, 0, MemoryMappedFileAccess.Read);
189+
190+
AssemblyDefinition result = ModuleDefinition.ReadModule (viewStream, options).Assembly;
191+
viewStreams.Add (viewStream);
192+
193+
// We transferred the ownership of the viewStream to the collection.
194+
viewStream = null;
195+
196+
return result;
197+
} finally {
198+
viewStream?.Dispose ();
171199
}
172200
}
173201

0 commit comments

Comments
 (0)