From d121d6e57c4773063a388007918651c5ed340b76 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 4 Feb 2011 03:17:22 +0100 Subject: [PATCH] Implemented drilling down into references. --- ILSpy/AssemblyListTreeNode.cs | 38 +---------------- ILSpy/AssemblyReferenceTreeNode.cs | 31 ++++++++++++-- ILSpy/AssemblyTreeNode.cs | 67 +++++++++++++++++++++++++++--- ILSpy/GacInterop.cs | 4 ++ ILSpy/MainWindow.xaml.cs | 31 +++++++++----- ILSpy/OpenFromGacDialog.xaml | 5 ++- ILSpy/ReferenceFolderTreeNode.cs | 10 +++-- 7 files changed, 126 insertions(+), 60 deletions(-) diff --git a/ILSpy/AssemblyListTreeNode.cs b/ILSpy/AssemblyListTreeNode.cs index 400a5ce43..9f82e1104 100644 --- a/ILSpy/AssemblyListTreeNode.cs +++ b/ILSpy/AssemblyListTreeNode.cs @@ -17,7 +17,7 @@ namespace ICSharpCode.ILSpy /// /// Represents a list of assemblies. /// - sealed class AssemblyListTreeNode : SharpTreeNode, IAssemblyResolver + sealed class AssemblyListTreeNode : SharpTreeNode { public override bool CanDelete(SharpTreeNode[] nodes) { @@ -80,40 +80,6 @@ namespace ICSharpCode.ILSpy return newNode; } - AssemblyTreeNode OpenGacAssembly(string fullName) - { - App.Current.Dispatcher.VerifyAccess(); - - string file = GacInterop.FindAssemblyInNetGac(AssemblyNameReference.Parse(fullName)); - if (file != null) { - return OpenAssembly(file); - } else { - return null; - } - } - - AssemblyDefinition IAssemblyResolver.Resolve(AssemblyNameReference name) - { - var node = OpenGacAssembly(name.FullName); - return node != null ? node.AssemblyDefinition : null; - } - - AssemblyDefinition IAssemblyResolver.Resolve(AssemblyNameReference name, ReaderParameters parameters) - { - var node = OpenGacAssembly(name.FullName); - return node != null ? node.AssemblyDefinition : null; - } - - AssemblyDefinition IAssemblyResolver.Resolve(string fullName) - { - var node = OpenGacAssembly(fullName); - return node != null ? node.AssemblyDefinition : null; - } - - AssemblyDefinition IAssemblyResolver.Resolve(string fullName, ReaderParameters parameters) - { - var node = OpenGacAssembly(fullName); - return node != null ? node.AssemblyDefinition : null; - } + public Action Select = delegate {}; } } diff --git a/ILSpy/AssemblyReferenceTreeNode.cs b/ILSpy/AssemblyReferenceTreeNode.cs index 7f9f45558..147b75652 100644 --- a/ILSpy/AssemblyReferenceTreeNode.cs +++ b/ILSpy/AssemblyReferenceTreeNode.cs @@ -2,6 +2,7 @@ // This code is distributed under MIT X11 license (for details please see \doc\license.txt) using System; +using System.Linq; using ICSharpCode.TreeView; using Mono.Cecil; @@ -10,15 +11,20 @@ namespace ICSharpCode.ILSpy /// /// Node within assembly reference list. /// - public class AssemblyReferenceTreeNode : SharpTreeNode + sealed class AssemblyReferenceTreeNode : SharpTreeNode { readonly AssemblyNameReference r; + readonly AssemblyTreeNode parentAssembly; - public AssemblyReferenceTreeNode(AssemblyNameReference r) + public AssemblyReferenceTreeNode(AssemblyNameReference r, AssemblyTreeNode parentAssembly) { + if (parentAssembly == null) + throw new ArgumentNullException("parentAssembly"); if (r == null) throw new ArgumentNullException("r"); this.r = r; + this.parentAssembly = parentAssembly; + this.LazyLoading = true; } public override object Text { @@ -29,6 +35,25 @@ namespace ICSharpCode.ILSpy get { return Images.Assembly; } } - // TODO: allow drilling down into references used by this reference + public override void ActivateItem(System.Windows.RoutedEventArgs e) + { + var assemblyListNode = parentAssembly.Parent as AssemblyListTreeNode; + if (assemblyListNode != null) { + assemblyListNode.Select(parentAssembly.LookupReferencedAssembly(r.FullName)); + e.Handled = true; + } + } + + protected override void LoadChildren() + { + var refNode = parentAssembly.LookupReferencedAssembly(r.FullName); + if (refNode != null) { + AssemblyDefinition asm = refNode.AssemblyDefinition; + if (asm != null) { + foreach (var childRef in asm.MainModule.AssemblyReferences) + this.Children.Add(new AssemblyReferenceTreeNode(childRef, refNode)); + } + } + } } } diff --git a/ILSpy/AssemblyTreeNode.cs b/ILSpy/AssemblyTreeNode.cs index 6f6766ccf..9a1603533 100644 --- a/ILSpy/AssemblyTreeNode.cs +++ b/ILSpy/AssemblyTreeNode.cs @@ -16,7 +16,7 @@ namespace ICSharpCode.ILSpy { sealed class AssemblyTreeNode : SharpTreeNode { - readonly IAssemblyResolver assemblyResolver; + readonly AssemblyListTreeNode assemblyList; readonly string fileName; string shortName; readonly Task assemblyTask; @@ -24,13 +24,13 @@ namespace ICSharpCode.ILSpy readonly Dictionary namespaces = new Dictionary(); readonly SynchronizationContext syncContext; - public AssemblyTreeNode(string fileName, IAssemblyResolver assemblyResolver) + public AssemblyTreeNode(string fileName, AssemblyListTreeNode assemblyList) { if (fileName == null) throw new ArgumentNullException("fileName"); this.fileName = fileName; - this.assemblyResolver = assemblyResolver; + this.assemblyList = assemblyList; this.assemblyTask = Task.Factory.StartNew(LoadAssembly); // requires that this.fileName is set this.shortName = Path.GetFileNameWithoutExtension(fileName); this.syncContext = SynchronizationContext.Current; @@ -58,7 +58,7 @@ namespace ICSharpCode.ILSpy { // runs on background thread ReaderParameters p = new ReaderParameters(); - p.AssemblyResolver = assemblyResolver; + p.AssemblyResolver = new MyAssemblyResolver(this); AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(fileName, p); foreach (TypeDefinition type in assembly.MainModule.Types.OrderBy(t => t.FullName)) { classes.Add(new TypeTreeNode(type)); @@ -74,10 +74,44 @@ namespace ICSharpCode.ILSpy return assembly; } + sealed class MyAssemblyResolver : IAssemblyResolver + { + readonly AssemblyTreeNode parent; + + public MyAssemblyResolver(AssemblyTreeNode parent) + { + this.parent = parent; + } + + public AssemblyDefinition Resolve(AssemblyNameReference name) + { + var node = parent.LookupReferencedAssembly(name.FullName); + return node != null ? node.AssemblyDefinition : null; + } + + public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters) + { + var node = parent.LookupReferencedAssembly(name.FullName); + return node != null ? node.AssemblyDefinition : null; + } + + public AssemblyDefinition Resolve(string fullName) + { + var node = parent.LookupReferencedAssembly(fullName); + return node != null ? node.AssemblyDefinition : null; + } + + public AssemblyDefinition Resolve(string fullName, ReaderParameters parameters) + { + var node = parent.LookupReferencedAssembly(fullName); + return node != null ? node.AssemblyDefinition : null; + } + } + protected override void LoadChildren() { assemblyTask.Wait(); - this.Children.Add(new ReferenceFolderTreeNode(assemblyTask.Result.MainModule)); + this.Children.Add(new ReferenceFolderTreeNode(assemblyTask.Result.MainModule, this)); foreach (NamespaceTreeNode ns in namespaces.Values) { ns.Children.Clear(); } @@ -150,5 +184,28 @@ namespace ICSharpCode.ILSpy dataObject.SetData(DataFormat, nodes.OfType().Select(n => n.fileName).ToArray()); return dataObject; } + + public AssemblyTreeNode LookupReferencedAssembly(string fullName) + { + foreach (AssemblyTreeNode node in assemblyList.Children) { + if (fullName.Equals(node.AssemblyDefinition.FullName, StringComparison.OrdinalIgnoreCase)) + return node; + } + + var name = AssemblyNameReference.Parse(fullName); + string file = GacInterop.FindAssemblyInNetGac(name); + if (file == null) { + string dir = Path.GetDirectoryName(this.fileName); + if (File.Exists(Path.Combine(dir, name.Name + ".dll"))) + file = Path.Combine(dir, name.Name + ".dll"); + else if (File.Exists(Path.Combine(dir, name.Name + ".exe"))) + file = Path.Combine(dir, name.Name + ".exe"); + } + if (file != null) { + return assemblyList.OpenAssembly(file); + } else { + return null; + } + } } } diff --git a/ILSpy/GacInterop.cs b/ILSpy/GacInterop.cs index 6ec8071f4..1f11ab83f 100644 --- a/ILSpy/GacInterop.cs +++ b/ILSpy/GacInterop.cs @@ -75,6 +75,10 @@ namespace ICSharpCode.ILSpy public static string FindAssemblyInNetGac (AssemblyNameReference reference) { + // without public key, it can't be in the GAC + if (reference.PublicKeyToken == null) + return null; + for (int i = 0; i < 2; i++) { for (int j = 0; j < gacs.Length; j++) { var gac = Path.Combine (gac_paths [i], gacs [j]); diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index c784f3579..4d776a882 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -40,6 +40,10 @@ namespace ICSharpCode.ILSpy textEditor.Text = "// Welcome to ILSpy!"; treeView.Root = assemblyList; + assemblyList.Select = delegate(SharpTreeNode obj) { + treeView.SelectedItem = obj; + treeView.ScrollIntoView(obj); + }; foreach (Assembly asm in initialAssemblies) assemblyList.OpenAssembly(asm.Location); @@ -53,13 +57,23 @@ namespace ICSharpCode.ILSpy dlg.Multiselect = true; dlg.RestoreDirectory = true; if (dlg.ShowDialog() == true) { - treeView.UnselectAll(); - foreach (string file in dlg.FileNames) { - var asm = assemblyList.OpenAssembly(file); - if (asm != null) - treeView.SelectedItems.Add(asm); + OpenFiles(dlg.FileNames); + } + } + + void OpenFiles(string[] fileNames) + { + treeView.UnselectAll(); + SharpTreeNode lastNode = null; + foreach (string file in fileNames) { + var asm = assemblyList.OpenAssembly(file); + if (asm != null) { + treeView.SelectedItems.Add(asm); + lastNode = asm; } } + if (lastNode != null) + treeView.ScrollIntoView(lastNode); } void ExitClick(object sender, RoutedEventArgs e) @@ -79,12 +93,7 @@ namespace ICSharpCode.ILSpy OpenFromGacDialog dlg = new OpenFromGacDialog(); dlg.Owner = this; if (dlg.ShowDialog() == true) { - treeView.UnselectAll(); - foreach (string file in dlg.SelectedFileNames) { - var asm = assemblyList.OpenAssembly(file); - if (asm != null) - treeView.SelectedItems.Add(asm); - } + OpenFiles(dlg.SelectedFileNames); } } } diff --git a/ILSpy/OpenFromGacDialog.xaml b/ILSpy/OpenFromGacDialog.xaml index bc79661a6..f9aba3c31 100644 --- a/ILSpy/OpenFromGacDialog.xaml +++ b/ILSpy/OpenFromGacDialog.xaml @@ -20,7 +20,10 @@ - + + + + diff --git a/ILSpy/ReferenceFolderTreeNode.cs b/ILSpy/ReferenceFolderTreeNode.cs index b22683113..7fdcf4881 100644 --- a/ILSpy/ReferenceFolderTreeNode.cs +++ b/ILSpy/ReferenceFolderTreeNode.cs @@ -10,13 +10,15 @@ namespace ICSharpCode.ILSpy /// /// References folder. /// - public sealed class ReferenceFolderTreeNode : SharpTreeNode + sealed class ReferenceFolderTreeNode : SharpTreeNode { - ModuleDefinition module; + readonly ModuleDefinition module; + readonly AssemblyTreeNode parentAssembly; - public ReferenceFolderTreeNode(ModuleDefinition module) + public ReferenceFolderTreeNode(ModuleDefinition module, AssemblyTreeNode parentAssembly) { this.module = module; + this.parentAssembly = parentAssembly; this.LazyLoading = true; } @@ -35,7 +37,7 @@ namespace ICSharpCode.ILSpy protected override void LoadChildren() { foreach (var r in module.AssemblyReferences) - this.Children.Add(new AssemblyReferenceTreeNode(r)); + this.Children.Add(new AssemblyReferenceTreeNode(r, parentAssembly)); foreach (var r in module.ModuleReferences) this.Children.Add(new ModuleReferenceTreeNode(r)); }