From 5ad3cf9143c9e4e84421fc61d701c5f494b88ea7 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 11 Jul 2018 00:31:40 +0200 Subject: [PATCH] Add support for module references in ILSpy. --- ILSpy/AssemblyList.cs | 5 +- ILSpy/LoadedAssembly.cs | 69 +++++++++++++++++++++- ILSpy/TreeNodes/ModuleReferenceTreeNode.cs | 37 +++++++++--- ILSpy/TreeNodes/ReferenceFolderTreeNode.cs | 2 +- 4 files changed, 101 insertions(+), 12 deletions(-) diff --git a/ILSpy/AssemblyList.cs b/ILSpy/AssemblyList.cs index 8a7fc5add..caa7f8ae3 100644 --- a/ILSpy/AssemblyList.cs +++ b/ILSpy/AssemblyList.cs @@ -38,9 +38,10 @@ namespace ICSharpCode.ILSpy /// Dirty flag, used to mark modifications so that the list is saved later bool dirty; - + internal readonly ConcurrentDictionary<(string assemblyName, bool isWinRT), LoadedAssembly> assemblyLookupCache = new ConcurrentDictionary<(string assemblyName, bool isWinRT), LoadedAssembly>(); - + internal readonly ConcurrentDictionary moduleLookupCache = new ConcurrentDictionary(); + /// /// The assemblies in this list. /// Needs locking for multi-threaded access! diff --git a/ILSpy/LoadedAssembly.cs b/ILSpy/LoadedAssembly.cs index 02503c124..2420dfa82 100644 --- a/ILSpy/LoadedAssembly.cs +++ b/ILSpy/LoadedAssembly.cs @@ -220,7 +220,7 @@ namespace ICSharpCode.ILSpy } } - sealed class MyAssemblyResolver : Decompiler.Metadata.IAssemblyResolver + sealed class MyAssemblyResolver : IAssemblyResolver { readonly LoadedAssembly parent; @@ -233,6 +233,11 @@ namespace ICSharpCode.ILSpy { return parent.LookupReferencedAssembly(reference)?.GetPEFileOrNull(); } + + public PEFile ResolveModule(PEFile mainModule, string moduleName) + { + return parent.LookupReferencedModule(mainModule, moduleName)?.GetPEFileOrNull(); + } } public IAssemblyResolver GetAssemblyResolver() @@ -261,6 +266,15 @@ namespace ICSharpCode.ILSpy } } + public LoadedAssembly LookupReferencedModule(PEFile mainModule, string moduleName) + { + if (mainModule == null) + throw new ArgumentNullException(nameof(mainModule)); + if (moduleName == null) + throw new ArgumentNullException(nameof(moduleName)); + return assemblyList.moduleLookupCache.GetOrAdd(mainModule.FileName + ";" + moduleName, _ => LookupReferencedModuleInternal(mainModule, moduleName)); + } + class MyUniversalResolver : UniversalAssemblyResolver { public MyUniversalResolver(LoadedAssembly assembly) @@ -326,7 +340,58 @@ namespace ICSharpCode.ILSpy }); return asm; } - + + LoadedAssembly LookupReferencedModuleInternal(PEFile mainModule, string moduleName) + { + string file; + LoadedAssembly asm; + lock (loadingAssemblies) { + foreach (LoadedAssembly loaded in assemblyList.GetAssemblies()) { + var reader = loaded.GetPEFileOrNull()?.Metadata; + if (reader == null || reader.IsAssembly) continue; + var moduleDef = reader.GetModuleDefinition(); + if (moduleName.Equals(reader.GetString(moduleDef.Name), StringComparison.OrdinalIgnoreCase)) { + LoadedAssemblyReferencesInfo.AddMessageOnce(moduleName, MessageKind.Info, "Success - Found in Assembly List"); + return loaded; + } + } + + file = Path.Combine(Path.GetDirectoryName(mainModule.FileName), moduleName); + if (!File.Exists(file)) + return null; + + foreach (LoadedAssembly loaded in assemblyList.GetAssemblies()) { + if (loaded.FileName.Equals(file, StringComparison.OrdinalIgnoreCase)) { + return loaded; + } + } + + if (file != null && loadingAssemblies.TryGetValue(file, out asm)) + return asm; + + if (assemblyLoadDisableCount > 0) + return null; + + if (file != null) { + LoadedAssemblyReferencesInfo.AddMessage(moduleName, MessageKind.Info, "Success - Loading from: " + file); + asm = new LoadedAssembly(assemblyList, file) { IsAutoLoaded = true }; + } else { + LoadedAssemblyReferencesInfo.AddMessageOnce(moduleName, MessageKind.Error, "Could not find reference: " + moduleName); + return null; + } + loadingAssemblies.Add(file, asm); + } + App.Current.Dispatcher.BeginInvoke((Action)delegate () { + lock (assemblyList.assemblies) { + assemblyList.assemblies.Add(asm); + } + lock (loadingAssemblies) { + loadingAssemblies.Remove(file); + } + }); + return asm; + } + public Task ContinueWhenLoaded(Action> onAssemblyLoaded, TaskScheduler taskScheduler) { return this.assemblyTask.ContinueWith(onAssemblyLoaded, default(CancellationToken), TaskContinuationOptions.RunContinuationsAsynchronously, taskScheduler); diff --git a/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs b/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs index f834d9fe3..2c9164184 100644 --- a/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs +++ b/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs @@ -27,30 +27,53 @@ namespace ICSharpCode.ILSpy.TreeNodes /// sealed class ModuleReferenceTreeNode : ILSpyTreeNode { + readonly AssemblyTreeNode parentAssembly; readonly MetadataReader metadata; readonly ModuleReferenceHandle handle; readonly ModuleReference reference; + readonly AssemblyFileHandle fileHandle; + readonly AssemblyFile file; + readonly string moduleName; - public ModuleReferenceTreeNode(ModuleReferenceHandle r, MetadataReader module) + public ModuleReferenceTreeNode(AssemblyTreeNode parentAssembly, ModuleReferenceHandle r, MetadataReader module) { + this.parentAssembly = parentAssembly ?? throw new ArgumentNullException(nameof(parentAssembly)); if (r.IsNil) throw new ArgumentNullException(nameof(r)); this.metadata = module; this.handle = r; this.reference = module.GetModuleReference(r); + this.moduleName = metadata.GetString(reference.Name); + + foreach (var h in module.AssemblyFiles) { + var file = module.GetAssemblyFile(h); + if (module.StringComparer.Equals(file.Name, moduleName)) { + this.file = file; + this.fileHandle = h; + break; + } + } } public override object Text { - get { return metadata.GetString(reference.Name) + ((EntityHandle)handle).ToSuffixString(); } + get { return moduleName + ((EntityHandle)handle).ToSuffixString(); } } - - public override object Icon { - get { return Images.Library; } + + public override object Icon => Images.Library; + + public override void ActivateItem(System.Windows.RoutedEventArgs e) + { + var assemblyListNode = parentAssembly.Parent as AssemblyListTreeNode; + if (assemblyListNode != null && file.ContainsMetadata) { + assemblyListNode.Select(assemblyListNode.FindAssemblyNode(parentAssembly.LoadedAssembly.LookupReferencedModule(parentAssembly.LoadedAssembly.GetPEFileOrNull(), metadata.GetString(reference.Name)))); + e.Handled = true; + } } - + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { - language.WriteCommentLine(output, metadata.GetString(reference.Name)); + language.WriteCommentLine(output, moduleName); + language.WriteCommentLine(output, file.ContainsMetadata ? "contains metadata" : "contains no metadata"); } } } diff --git a/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs b/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs index 4490cbcf8..13c0f07e7 100644 --- a/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs +++ b/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs @@ -58,7 +58,7 @@ namespace ICSharpCode.ILSpy.TreeNodes foreach (var r in module.AssemblyReferences.OrderBy(r => r.Name)) this.Children.Add(new AssemblyReferenceTreeNode(r, parentAssembly)); foreach (var r in metadata.GetModuleReferences().OrderBy(r => metadata.GetString(metadata.GetModuleReference(r).Name))) - this.Children.Add(new ModuleReferenceTreeNode(r, metadata)); + this.Children.Add(new ModuleReferenceTreeNode(parentAssembly, r, metadata)); } public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)