From 8bd82689fb9422318025e588ea72618b9ec95441 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Mon, 13 Apr 2026 15:07:45 -0500 Subject: [PATCH] [Java.Interop.Tools.Cecil] Fix retry logic in ReadAssembly to skip symbol loading The retry in ReadAssembly sets reader_parameters.ReadSymbols = false, but LoadFromMemoryMappedFile checks the instance field loadDebugSymbols instead, so the retry re-opens the same locked PDB file and throws the same IOException. Fix by passing an explicit loadSymbols parameter to LoadFromMemoryMappedFile so the retry can actually skip symbol loading. When loadDebugSymbols is false, rethrow immediately since there is nothing to retry. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../DirectoryAssemblyResolver.cs | 14 ++++++++------ .../DirectoryAssemblyResolverTests.cs | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/Java.Interop.Tools.Cecil/Java.Interop.Tools.Cecil/DirectoryAssemblyResolver.cs b/src/Java.Interop.Tools.Cecil/Java.Interop.Tools.Cecil/DirectoryAssemblyResolver.cs index aeaaa6113..0fd3c37e5 100644 --- a/src/Java.Interop.Tools.Cecil/Java.Interop.Tools.Cecil/DirectoryAssemblyResolver.cs +++ b/src/Java.Interop.Tools.Cecil/Java.Interop.Tools.Cecil/DirectoryAssemblyResolver.cs @@ -162,24 +162,26 @@ protected virtual AssemblyDefinition ReadAssembly (string file) SymbolReaderProvider = loadReaderParameters.SymbolReaderProvider, }; try { - return LoadFromMemoryMappedFile (file, reader_parameters); + return LoadFromMemoryMappedFile (file, reader_parameters, loadDebugSymbols); } catch (Exception ex) { + if (!loadDebugSymbols) + throw; 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 ()); reader_parameters.ReadSymbols = false; - return LoadFromMemoryMappedFile (file, reader_parameters); + return LoadFromMemoryMappedFile (file, reader_parameters, loadSymbols: false); } finally { reader_parameters.SymbolStream?.Dispose (); } } - AssemblyDefinition LoadFromMemoryMappedFile (string file, ReaderParameters options) + AssemblyDefinition LoadFromMemoryMappedFile (string file, ReaderParameters options, bool loadSymbols) { // We can't use MemoryMappedFile when ReadWrite is true if (options.ReadWrite) { - if (loadDebugSymbols) { + if (loadSymbols) { LoadSymbols (file, options, File.OpenRead); } return AssemblyDefinition.ReadAssembly (file, options); @@ -187,9 +189,9 @@ AssemblyDefinition LoadFromMemoryMappedFile (string file, ReaderParameters optio // We know the capacity for disposables var disposables = new List ( - (1 + (loadDebugSymbols ? 1 : 0)) * OpenMemoryMappedViewStream_disposables_Add_calls); + (1 + (loadSymbols ? 1 : 0)) * OpenMemoryMappedViewStream_disposables_Add_calls); try { - if (loadDebugSymbols) { + if (loadSymbols) { LoadSymbols (file, options, f => OpenMemoryMappedViewStream (f, disposables)); } diff --git a/tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/DirectoryAssemblyResolverTests.cs b/tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/DirectoryAssemblyResolverTests.cs index 7053476d7..c2d48837f 100644 --- a/tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/DirectoryAssemblyResolverTests.cs +++ b/tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/DirectoryAssemblyResolverTests.cs @@ -49,5 +49,21 @@ public void LoadSymbols ([Values (true, false)] bool loadDebugSymbols, [Values ( Assert.IsNotNull (assembly); Assert.AreEqual (loadDebugSymbols, assembly.MainModule.HasSymbols); } + + [Test] + public void LoadSymbols_LockedPdb ([Values (true, false)] bool readWrite) + { + // Lock the PDB file exclusively so LoadSymbols will fail with IOException + using var lockStream = new FileStream (symbol_path, FileMode.Open, FileAccess.ReadWrite, FileShare.None); + + using var resolver = new DirectoryAssemblyResolver (Log, loadDebugSymbols: true, new ReaderParameters { + ReadWrite = readWrite + }); + + // Should succeed by retrying without symbols instead of throwing + var assembly = resolver.Load (assembly_path); + Assert.IsNotNull (assembly); + Assert.IsFalse (assembly.MainModule.HasSymbols); + } } }