Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
using Java.Interop.Tools.Diagnostics;

using Mono.Cecil;
using Mono.Cecil.Cil;

namespace Java.Interop.Tools.Cecil {

Expand Down Expand Up @@ -149,61 +150,96 @@ public bool AddToCache (AssemblyDefinition assembly)

protected virtual AssemblyDefinition ReadAssembly (string file)
{
bool haveDebugSymbols = loadDebugSymbols &&
(File.Exists (Path.ChangeExtension (file, ".pdb")) ||
File.Exists (file + ".mdb"));
Copy link
Member Author

Choose a reason for hiding this comment

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

I removed .mdb support, which should be fine as it was removed for a while now: dotnet/android#7950

var reader_parameters = new ReaderParameters () {
ApplyWindowsRuntimeProjections = loadReaderParameters.ApplyWindowsRuntimeProjections,
AssemblyResolver = this,
MetadataImporterProvider = loadReaderParameters.MetadataImporterProvider,
InMemory = loadReaderParameters.InMemory,
MetadataResolver = loadReaderParameters.MetadataResolver,
ReadingMode = loadReaderParameters.ReadingMode,
ReadSymbols = haveDebugSymbols,
ReadWrite = loadReaderParameters.ReadWrite,
ReflectionImporterProvider = loadReaderParameters.ReflectionImporterProvider,
SymbolReaderProvider = loadReaderParameters.SymbolReaderProvider,
SymbolStream = loadReaderParameters.SymbolStream,
};
try {
return LoadFromMemoryMappedFile (file, reader_parameters);
} catch (Exception ex) {
logger (
TraceLevel.Verbose,
$"Failed to read '{file}' with debugging symbols. Retrying to load it without it. Error details are logged below.");
logger (TraceLevel.Verbose, $"{ex.ToString ()}");
logger (TraceLevel.Verbose, ex.ToString ());
reader_parameters.ReadSymbols = false;
return LoadFromMemoryMappedFile (file, reader_parameters);
} finally {
reader_parameters.SymbolStream?.Dispose ();
}
}

AssemblyDefinition LoadFromMemoryMappedFile (string file, ReaderParameters options)
{
// We can't use MemoryMappedFile when ReadWrite is true
if (options.ReadWrite) {
if (loadDebugSymbols) {
LoadSymbols (file, options, File.OpenRead);
}
return AssemblyDefinition.ReadAssembly (file, options);
}

MemoryMappedViewStream? viewStream = null;
// Likely 6 entries when symbols exist
Copy link
Contributor

Choose a reason for hiding this comment

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

Where does "6" come from? Why is that likely?

Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like OpenMemoryMappedViewStream() has 3 disposables.Add() calls, so a multiple of 3 makes sense, and there are potentially 2 OpenMemoryMappedViewStream() invocations here.

That said, the feels "distant"; if (when) OpenMemoryMappedViewStream() changes, this value will be "wrong". (Not necessarily a bad thing, but…)

I think I'd prefer:

AssemblyDefinition LoadFromMemoryMappedFile ()
{
    // …
    var disposables = new List<IDisposable>(
        (1 + (loadDebugSymbols ? 1 : 0)) * OpenMemoryMappedViewStream_disposables_Add_calls);
    // …
}

// Number of times OpenMemoryMappedViewStream() calls disposables.Add()
const int OpenMemoryMappedViewStream_disposables_Add_calls = 3;
static MemoryMappedViewStream OpenMemoryMappedViewStream (string file, List<IDisposable> disposables)
{
    // …
}

Copy link
Member Author

Choose a reason for hiding this comment

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

I made these changes.

var disposables = new List<IDisposable> (capacity: 6);
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);
if (loadDebugSymbols) {
LoadSymbols (file, options, f => OpenMemoryMappedViewStream (f, disposables));
}

var viewStream = OpenMemoryMappedViewStream (file, disposables);
AssemblyDefinition result = ModuleDefinition.ReadModule (viewStream, options).Assembly;
viewStreams.Add (viewStream);

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

// Transfer ownership to `viewStreams` collection
viewStreams.Add (viewStream);
disposables.Remove (viewStream);
if (options.SymbolStream is MemoryMappedViewStream m) {
viewStreams.Add (m);
disposables.Remove (m);
options.SymbolStream = null; // Prevents caller from disposing
}
return result;
} finally {
viewStream?.Dispose ();
for (int i = disposables.Count - 1; i >= 0; i--) {
disposables [i].Dispose ();
}
}
}

static void LoadSymbols (string assemblyPath, ReaderParameters options, Func<string, Stream> getStream)
{
var symbolStream = options.SymbolStream;
if (symbolStream == null) {
var symbolPath = Path.ChangeExtension (assemblyPath, ".pdb");
if (File.Exists (symbolPath)) {
symbolStream = getStream (symbolPath);
}
}
options.ReadSymbols = symbolStream != null;
options.SymbolStream = symbolStream;
}

static MemoryMappedViewStream OpenMemoryMappedViewStream (string file, List<IDisposable> disposables)
{
// Create stream because CreateFromFile(string, ...) uses FileShare.None which is too strict
var fileStream = new FileStream (file, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, useAsync: false);
disposables.Add (fileStream);

var mappedFile = MemoryMappedFile.CreateFromFile (
fileStream, null, fileStream.Length, MemoryMappedFileAccess.Read, HandleInheritability.None, leaveOpen: true);
disposables.Add (mappedFile);

var viewStream = mappedFile.CreateViewStream (0, 0, MemoryMappedFileAccess.Read);
disposables.Add (viewStream);

return viewStream;
}

public AssemblyDefinition GetAssembly (string fileName)
{
return Resolve (Path.GetFileNameWithoutExtension (fileName));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System.Diagnostics;
using System.IO;
using Java.Interop.Tools.Cecil;
using Mono.Cecil;
using NUnit.Framework;

namespace Java.Interop.Tools.JavaCallableWrappersTests
{
[TestFixture]
public class DirectoryAssemblyResolverTests
{
static void Log (TraceLevel level, string message)
{
TestContext.Out.WriteLine ($"{level}: {message}");

if (level == TraceLevel.Error)
Assert.Fail (message);
}

static string assembly_path;
static string symbol_path;

[OneTimeSetUp]
public static void SetUp()
{
var assembly = typeof (DirectoryAssemblyResolverTests).Assembly;
assembly_path = Path.Combine (Path.GetTempPath (), Path.GetFileName (assembly.Location));
symbol_path = Path.ChangeExtension (assembly_path, ".pdb");

File.Copy (assembly.Location, assembly_path, overwrite: true);
File.Copy (Path.ChangeExtension (assembly.Location, ".pdb"), symbol_path, overwrite: true);
}

[OneTimeTearDown]
public static void TearDown ()
{
File.Delete (assembly_path);
File.Delete (symbol_path);
}

[Test]
public void LoadSymbols ([Values (true, false)] bool loadDebugSymbols, [Values (true, false)] bool readWrite)
{
using var resolver = new DirectoryAssemblyResolver (Log, loadDebugSymbols: loadDebugSymbols, new ReaderParameters {
ReadWrite = readWrite
});

var assembly = resolver.Load (assembly_path);
Assert.IsNotNull (assembly);
Assert.AreEqual (loadDebugSymbols, assembly.MainModule.HasSymbols);
}
}
}