From 77b237d5a4dc3bd9ed1725f4fc569bff9e94e96a Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 2 Jun 2024 10:46:57 +0200 Subject: [PATCH] Fix mysteriously deleted issue #3197: Skip file formats that currently can only be loaded from a file when dealing with a file from a bundle or package. --- ICSharpCode.Decompiler/Metadata/WebCilFile.cs | 21 +++++++++++++-- .../FileLoaders/ArchiveFileLoader.cs | 7 ++++- .../FileLoaders/BundleFileLoader.cs | 7 ++++- ICSharpCode.ILSpyX/FileLoaders/LoadResult.cs | 6 +++-- .../FileLoaders/MetadataFileLoader.cs | 2 +- .../FileLoaders/WebCilFileLoader.cs | 13 ++++++--- .../XamarinCompressedFileLoader.cs | 4 +-- ICSharpCode.ILSpyX/LoadedAssembly.cs | 27 +++++++++++++++---- 8 files changed, 69 insertions(+), 18 deletions(-) diff --git a/ICSharpCode.Decompiler/Metadata/WebCilFile.cs b/ICSharpCode.Decompiler/Metadata/WebCilFile.cs index 291316085..d862c261b 100644 --- a/ICSharpCode.Decompiler/Metadata/WebCilFile.cs +++ b/ICSharpCode.Decompiler/Metadata/WebCilFile.cs @@ -47,9 +47,14 @@ namespace ICSharpCode.Decompiler.Metadata this.WasmSections = wasmSections; } - public static WebCilFile? FromStream(string fileName, MetadataReaderOptions metadataOptions = MetadataReaderOptions.Default) + public static WebCilFile? FromFile(string fileName, MetadataReaderOptions metadataOptions = MetadataReaderOptions.Default) { - using var memoryMappedFile = MemoryMappedFile.CreateFromFile(fileName, FileMode.Open, null, 0, MemoryMappedFileAccess.Read); + using var memoryMappedFile = TryCreateFromFile(fileName); + if (memoryMappedFile == null) + { + return null; + } + var view = memoryMappedFile.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read); try { @@ -124,6 +129,18 @@ namespace ICSharpCode.Decompiler.Metadata { view?.Dispose(); } + + static MemoryMappedFile? TryCreateFromFile(string fileName) + { + try + { + return MemoryMappedFile.CreateFromFile(fileName, FileMode.Open, null, 0, MemoryMappedFileAccess.Read); + } + catch (IOException) + { + return null; + } + } } static unsafe bool TryReadWebCilSegment(BinaryReader reader, out WebcilHeader webcilHeader, out long metadataOffset, out long webcilOffset, [NotNullWhen(true)] out SectionHeader[]? sectionHeaders) diff --git a/ICSharpCode.ILSpyX/FileLoaders/ArchiveFileLoader.cs b/ICSharpCode.ILSpyX/FileLoaders/ArchiveFileLoader.cs index 6d6cda73a..4fb869d80 100644 --- a/ICSharpCode.ILSpyX/FileLoaders/ArchiveFileLoader.cs +++ b/ICSharpCode.ILSpyX/FileLoaders/ArchiveFileLoader.cs @@ -23,8 +23,13 @@ namespace ICSharpCode.ILSpyX.FileLoaders { public sealed class ArchiveFileLoader : IFileLoader { - public Task Load(string fileName, Stream stream, FileLoadSettings settings) + public Task Load(string fileName, Stream stream, FileLoadContext settings) { + if (settings.ParentBundle != null) + { + return Task.FromResult(null); + } + try { var zip = LoadedPackage.FromZipFile(fileName); diff --git a/ICSharpCode.ILSpyX/FileLoaders/BundleFileLoader.cs b/ICSharpCode.ILSpyX/FileLoaders/BundleFileLoader.cs index 4f0ec5bfe..9d5daaff7 100644 --- a/ICSharpCode.ILSpyX/FileLoaders/BundleFileLoader.cs +++ b/ICSharpCode.ILSpyX/FileLoaders/BundleFileLoader.cs @@ -23,8 +23,13 @@ namespace ICSharpCode.ILSpyX.FileLoaders { public sealed class BundleFileLoader : IFileLoader { - public Task Load(string fileName, Stream stream, FileLoadSettings settings) + public Task Load(string fileName, Stream stream, FileLoadContext settings) { + if (settings.ParentBundle != null) + { + return Task.FromResult(null); + } + var bundle = LoadedPackage.FromBundle(fileName); var result = bundle != null ? new LoadResult { Package = bundle } : null; return Task.FromResult(result); diff --git a/ICSharpCode.ILSpyX/FileLoaders/LoadResult.cs b/ICSharpCode.ILSpyX/FileLoaders/LoadResult.cs index 73c771a8d..ee4fea611 100644 --- a/ICSharpCode.ILSpyX/FileLoaders/LoadResult.cs +++ b/ICSharpCode.ILSpyX/FileLoaders/LoadResult.cs @@ -29,12 +29,14 @@ namespace ICSharpCode.ILSpyX.FileLoaders public MetadataFile? MetadataFile { get; init; } public Exception? FileLoadException { get; init; } public LoadedPackage? Package { get; init; } + + public bool IsSuccess => FileLoadException == null; } - public record FileLoadSettings(bool ApplyWinRTProjections); + public record FileLoadContext(bool ApplyWinRTProjections, LoadedAssembly? ParentBundle); public interface IFileLoader { - Task Load(string fileName, Stream stream, FileLoadSettings settings); + Task Load(string fileName, Stream stream, FileLoadContext context); } } diff --git a/ICSharpCode.ILSpyX/FileLoaders/MetadataFileLoader.cs b/ICSharpCode.ILSpyX/FileLoaders/MetadataFileLoader.cs index c7de1fb8d..f70d236b6 100644 --- a/ICSharpCode.ILSpyX/FileLoaders/MetadataFileLoader.cs +++ b/ICSharpCode.ILSpyX/FileLoaders/MetadataFileLoader.cs @@ -29,7 +29,7 @@ namespace ICSharpCode.ILSpyX.FileLoaders { public sealed class MetadataFileLoader : IFileLoader { - public Task Load(string fileName, Stream stream, FileLoadSettings settings) + public Task Load(string fileName, Stream stream, FileLoadContext settings) { try { diff --git a/ICSharpCode.ILSpyX/FileLoaders/WebCilFileLoader.cs b/ICSharpCode.ILSpyX/FileLoaders/WebCilFileLoader.cs index 441ff10fb..4b20b3962 100644 --- a/ICSharpCode.ILSpyX/FileLoaders/WebCilFileLoader.cs +++ b/ICSharpCode.ILSpyX/FileLoaders/WebCilFileLoader.cs @@ -26,13 +26,18 @@ namespace ICSharpCode.ILSpyX.FileLoaders { public sealed class WebCilFileLoader : IFileLoader { - public Task Load(string fileName, Stream stream, FileLoadSettings settings) + public Task Load(string fileName, Stream stream, FileLoadContext settings) { + if (settings.ParentBundle != null) + { + return Task.FromResult(null); + } + MetadataReaderOptions options = settings.ApplyWinRTProjections - ? MetadataReaderOptions.ApplyWindowsRuntimeProjections - : MetadataReaderOptions.None; + ? MetadataReaderOptions.ApplyWindowsRuntimeProjections + : MetadataReaderOptions.None; - var wasm = WebCilFile.FromStream(fileName, options); + var wasm = WebCilFile.FromFile(fileName, options); var result = wasm != null ? new LoadResult { MetadataFile = wasm } : null; return Task.FromResult(result); } diff --git a/ICSharpCode.ILSpyX/FileLoaders/XamarinCompressedFileLoader.cs b/ICSharpCode.ILSpyX/FileLoaders/XamarinCompressedFileLoader.cs index bce1c8918..349822f41 100644 --- a/ICSharpCode.ILSpyX/FileLoaders/XamarinCompressedFileLoader.cs +++ b/ICSharpCode.ILSpyX/FileLoaders/XamarinCompressedFileLoader.cs @@ -31,7 +31,7 @@ namespace ICSharpCode.ILSpyX.FileLoaders { public sealed class XamarinCompressedFileLoader : IFileLoader { - public async Task Load(string fileName, Stream stream, FileLoadSettings settings) + public async Task Load(string fileName, Stream stream, FileLoadContext context) { const uint CompressedDataMagic = 0x5A4C4158; // Magic used for Xamarin compressed module header ('XALZ', little-endian) using var fileReader = new BinaryReader(stream, Encoding.UTF8, leaveOpen: true); @@ -54,7 +54,7 @@ namespace ICSharpCode.ILSpyX.FileLoaders // Load module from decompressed data buffer using (var uncompressedStream = new MemoryStream(dst, writable: false)) { - MetadataReaderOptions options = settings.ApplyWinRTProjections + MetadataReaderOptions options = context.ApplyWinRTProjections ? MetadataReaderOptions.ApplyWindowsRuntimeProjections : MetadataReaderOptions.None; diff --git a/ICSharpCode.ILSpyX/LoadedAssembly.cs b/ICSharpCode.ILSpyX/LoadedAssembly.cs index cee856141..169012d44 100644 --- a/ICSharpCode.ILSpyX/LoadedAssembly.cs +++ b/ICSharpCode.ILSpyX/LoadedAssembly.cs @@ -314,7 +314,7 @@ namespace ICSharpCode.ILSpyX async Task LoadAsync(Task? streamTask) { using var stream = await PrepareStream(); - FileLoadSettings settings = new FileLoadSettings(applyWinRTProjections); + FileLoadContext settings = new FileLoadContext(applyWinRTProjections, ParentBundle); LoadResult? result = null; @@ -322,16 +322,33 @@ namespace ICSharpCode.ILSpyX { foreach (var loader in fileLoaders.RegisteredLoaders) { + // In each iteration any of the following things may happen: + // Load returns null because the loader is unable to handle the file, we simply continue without recording the result. + // Load returns a non-null value that is either a valid result or an exception: + // - if it's a success, we use that and end the loop, + // - if it's an error, we remember the error, discarding any previous errors. + // Load throws an exception, remember the error, discarding any previous errors. stream.Position = 0; - result = await loader.Load(fileName, stream, settings).ConfigureAwait(false); - if (result != null) + try { - break; + var nextResult = await loader.Load(fileName, stream, settings).ConfigureAwait(false); + if (nextResult != null) + { + result = nextResult; + if (result.IsSuccess) + { + break; + } + } + } + catch (Exception ex) + { + result = new LoadResult { FileLoadException = ex }; } } } - if (result == null) + if (result?.IsSuccess != true) { stream.Position = 0; try