Skip to content
This repository was archived by the owner on Jul 6, 2026. It is now read-only.

Commit 7d42864

Browse files
[Java.Interop.Tools.Cecil] DirectoryAssemblyResolver+MemoryMappedFile (#1103)
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`][0] in the .NET 6 time frame. Port these changes to Java.Interop, as we have MSBuild tasks in xamarin/xamarin-android that use `DirectoryAssemblyResolver`. Primarily, the [`<GenerateJavaStubs/>` MSBuild task][1] uses `DirectoryAssemblyResolver` to iterate over types in assembly to emit Java source code and generate `AndroidManifest.xml`. Note: `MemoryMappedFile` can *only* be used when we are *not* opening the assembly as `.ReadWrite`. Trying to use `MemoryMappedFile` for read+write scenarios would result in an `InvalidOperationException`: error XAGJS7009: System.InvalidOperationException: Operation is not valid due to the current state of the object. error XAGJS7009: at Mono.Cecil.Cil.DefaultSymbolWriterProvider.GetSymbolWriter(ModuleDefinition module, String fileName) error XAGJS7009: at Mono.Cecil.ModuleWriter.GetSymbolWriter(ModuleDefinition module, String fq_name, ISymbolWriterProvider symbol_writer_provider, WriterParameters parameters) error XAGJS7009: at Mono.Cecil.ModuleWriter.Write(ModuleDefinition module, Disposable`1 stream, WriterParameters parameters) error XAGJS7009: at Mono.Cecil.ModuleWriter.WriteModule(ModuleDefinition module, Disposable`1 stream, WriterParameters parameters) error XAGJS7009: at Mono.Cecil.ModuleDefinition.Write(String fileName, WriterParameters parameters) error XAGJS7009: at Mono.Cecil.AssemblyDefinition.Write(String fileName, WriterParameters parameters) error XAGJS7009: at Xamarin.Android.Tasks.MarshalMethodsAssemblyRewriter.Rewrite(DirectoryAssemblyResolver resolver, List`1 targetAssemblyPaths, Boolean brokenExceptionTransitions) error XAGJS7009: at Xamarin.Android.Tasks.GenerateJavaStubs.Run(DirectoryAssemblyResolver res, Boolean useMarshalMethods) error XAGJS7009: at Xamarin.Android.Tasks.GenerateJavaStubs.RunTask() The results of these changes in a `dotnet new maui` project, an initial clean build: * Before MemoryMappedFile: GenerateJavaStubs = 1.318 s, 1 calls. * After MemoryMappedFile: GenerateJavaStubs = 1.254 s, 1 calls. Saving ~64ms or about ~5% in this example. Note: The current version of the linker's resolver in .NET 8+ has [moved to the dotnet/runtime repo][2]. [0]: https://learn.microsoft.com/dotnet/api/system.io.memorymappedfiles.memorymappedfile?view=net-8.0 [1]: https://github.com/xamarin/xamarin-android/blob/e694ba52a0cf5f45b213c8902bdbfb0ed348374a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs#L131 [2]: https://github.com/dotnet/runtime/blob/cd7d006030a7feace9076fa275fb5bffc1bf4a90/src/tools/illink/src/linker/Linker/AssemblyResolver.cs
1 parent 3c2a066 commit 7d42864

1 file changed

Lines changed: 35 additions & 2 deletions

File tree

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

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

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

6263
publicICollection<string>SearchDirectories{get;privateset;}
6364

65+
readonlyList<MemoryMappedViewStream>viewStreams=newList<MemoryMappedViewStream>();
6466
Dictionary<string,AssemblyDefinition?>cache;
6567
boolloadDebugSymbols;
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(varviewStreaminviewStreams){
109+
viewStream.Dispose();
110+
}
111+
viewStreams.Clear();
106112
}
107113

108114
publicDictionary<string,AssemblyDefinition?>ToResolverCache()
@@ -160,14 +166,41 @@ protected virtual AssemblyDefinition ReadAssembly (string file)
160166
SymbolStream=loadReaderParameters.SymbolStream,
161167
};
162168
try{
163-
returnAssemblyDefinition.ReadAssembly(file,reader_parameters);
169+
returnLoadFromMemoryMappedFile(file,reader_parameters);
164170
}catch(Exceptionex){
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-
returnAssemblyDefinition.ReadAssembly(file,reader_parameters);
176+
returnLoadFromMemoryMappedFile(file,reader_parameters);
177+
}
178+
}
179+
180+
AssemblyDefinitionLoadFromMemoryMappedFile(stringfile,ReaderParametersoptions)
181+
{
182+
// We can't use MemoryMappedFile when ReadWrite is true
183+
if(options.ReadWrite){
184+
returnAssemblyDefinition.ReadAssembly(file,options);
185+
}
186+
187+
MemoryMappedViewStream?viewStream=null;
188+
try{
189+
// Create stream because CreateFromFile(string, ...) uses FileShare.None which is too strict
190+
usingvarfileStream=newFileStream(file,FileMode.Open,FileAccess.Read,FileShare.Read,4096,false);
191+
usingvarmappedFile=MemoryMappedFile.CreateFromFile(
192+
fileStream,null,fileStream.Length,MemoryMappedFileAccess.Read,HandleInheritability.None,true);
193+
viewStream=mappedFile.CreateViewStream(0,0,MemoryMappedFileAccess.Read);
194+
195+
AssemblyDefinitionresult=ModuleDefinition.ReadModule(viewStream,options).Assembly;
196+
viewStreams.Add(viewStream);
197+
198+
// We transferred the ownership of the viewStream to the collection.
199+
viewStream=null;
200+
201+
returnresult;
202+
}finally{
203+
viewStream?.Dispose();
171204
}
172205
}
173206

0 commit comments

Comments
 (0)