diff --git a/ILSpy/AssemblyList.cs b/ILSpy/AssemblyList.cs index 82a3efdaf..7fa82293b 100644 --- a/ILSpy/AssemblyList.cs +++ b/ILSpy/AssemblyList.cs @@ -45,15 +45,10 @@ namespace ICSharpCode.ILSpy /// Write accesses are allowed on the GUI thread only (but still need locking!) /// /// - /// Technically read accesses need locking on when done on non-GUI threads... but whenever possible, use the + /// Technically read accesses need locking when done on non-GUI threads... but whenever possible, use the /// thread-safe method. /// - internal readonly ObservableCollection assemblies = new ObservableCollection(); - - /// - /// Dictionary for quickly finding types (used in hyperlink navigation) - /// - readonly ConcurrentDictionary typeDict = new ConcurrentDictionary(); + internal readonly ObservableCollection assemblies = new ObservableCollection(); public AssemblyList(string listName) { @@ -76,7 +71,7 @@ namespace ICSharpCode.ILSpy /// /// Gets the loaded assemblies. This method is thread-safe. /// - public AssemblyTreeNode[] GetAssemblies() + public LoadedAssembly[] GetAssemblies() { lock (assemblies) { return assemblies.ToArray(); @@ -119,129 +114,24 @@ namespace ICSharpCode.ILSpy } } - /// - /// Registers a type node in the dictionary for quick type lookup. - /// - /// This method is called by the assembly loading code (on a background thread) - public void RegisterTypeNode(TypeTreeNode node) - { - // called on background loading thread, so we need to use a ConcurrentDictionary - typeDict[node.TypeDefinition] = node; - } - - #region Find*Node - /// - /// Looks up the type node corresponding to the type definition. - /// Returns null if no matching node is found. - /// - public TypeTreeNode FindTypeNode(TypeDefinition def) - { - if (def == null) - return null; - App.Current.Dispatcher.VerifyAccess(); - if (def.DeclaringType != null) { - TypeTreeNode decl = FindTypeNode(def.DeclaringType); - if (decl != null) { - decl.EnsureLazyChildren(); - return decl.Children.OfType().FirstOrDefault(t => t.TypeDefinition == def && !t.IsHidden); - } - } else { - TypeTreeNode node; - if (typeDict.TryGetValue(def, out node)) { - // Ensure that the node is connected to the tree - node.ParentAssemblyNode.EnsureLazyChildren(); - // Validate that the node wasn't removed due to visibility settings: - if (node.Ancestors().OfType().Any(n => n.AssemblyList == this)) - return node; - } - } - return null; - } - - /// - /// Looks up the method node corresponding to the method definition. - /// Returns null if no matching node is found. - /// - public MethodTreeNode FindMethodNode(MethodDefinition def) - { - if (def == null) - return null; - TypeTreeNode typeNode = FindTypeNode(def.DeclaringType); - typeNode.EnsureLazyChildren(); - MethodTreeNode methodNode = typeNode.Children.OfType().FirstOrDefault(m => m.MethodDefinition == def && !m.IsHidden); - if (methodNode != null) - return methodNode; - foreach (var p in typeNode.Children.OfType()) { - if (p.IsHidden) - continue; - // method might be a child or a property or events - p.EnsureLazyChildren(); - methodNode = p.Children.OfType().FirstOrDefault(m => m.MethodDefinition == def && !m.IsHidden); - if (methodNode != null) - return methodNode; - } - - return null; - } - - /// - /// Looks up the field node corresponding to the field definition. - /// Returns null if no matching node is found. - /// - public FieldTreeNode FindFieldNode(FieldDefinition def) - { - if (def == null) - return null; - TypeTreeNode typeNode = FindTypeNode(def.DeclaringType); - typeNode.EnsureLazyChildren(); - return typeNode.Children.OfType().FirstOrDefault(m => m.FieldDefinition == def && !m.IsHidden); - } - - /// - /// Looks up the property node corresponding to the property definition. - /// Returns null if no matching node is found. - /// - public PropertyTreeNode FindPropertyNode(PropertyDefinition def) - { - if (def == null) - return null; - TypeTreeNode typeNode = FindTypeNode(def.DeclaringType); - typeNode.EnsureLazyChildren(); - return typeNode.Children.OfType().FirstOrDefault(m => m.PropertyDefinition == def && !m.IsHidden); - } - - /// - /// Looks up the event node corresponding to the event definition. - /// Returns null if no matching node is found. - /// - public EventTreeNode FindEventNode(EventDefinition def) - { - if (def == null) - return null; - TypeTreeNode typeNode = FindTypeNode(def.DeclaringType); - typeNode.EnsureLazyChildren(); - return typeNode.Children.OfType().FirstOrDefault(m => m.EventDefinition == def && !m.IsHidden); - } - #endregion - /// /// Opens an assembly from disk. /// Returns the existing assembly node if it is already loaded. /// - public AssemblyTreeNode OpenAssembly(string file) + public LoadedAssembly OpenAssembly(string file) { App.Current.Dispatcher.VerifyAccess(); file = Path.GetFullPath(file); - foreach (AssemblyTreeNode node in this.assemblies) { - if (file.Equals(node.FileName, StringComparison.OrdinalIgnoreCase)) - return node; + foreach (LoadedAssembly asm in this.assemblies) { + if (file.Equals(asm.FileName, StringComparison.OrdinalIgnoreCase)) + return asm; } - var newNode = new AssemblyTreeNode(file, this); - this.assemblies.Add(newNode); - return newNode; + var newAsm = new LoadedAssembly(this, file); + this.assemblies.Add(newAsm); + return newAsm; } } } diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 8eed57475..db01ba487 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -93,6 +93,7 @@ + diff --git a/ILSpy/LoadedAssembly.cs b/ILSpy/LoadedAssembly.cs new file mode 100644 index 000000000..6e9752b1b --- /dev/null +++ b/ILSpy/LoadedAssembly.cs @@ -0,0 +1,147 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Threading; + +using Mono.Cecil; + +namespace ICSharpCode.ILSpy +{ + /// + /// Represents an assembly loaded into ILSpy. + /// + sealed class LoadedAssembly + { + readonly Task assemblyTask; + readonly AssemblyList assemblyList; + readonly string fileName; + string shortName; + + public LoadedAssembly(AssemblyList assemblyList, string fileName) + { + if (assemblyList == null) + throw new ArgumentNullException("assemblyList"); + if (fileName == null) + throw new ArgumentNullException("fileName"); + this.assemblyList = assemblyList; + this.fileName = fileName; + + this.assemblyTask = Task.Factory.StartNew(LoadAssembly); // requires that this.fileName is set + this.shortName = Path.GetFileNameWithoutExtension(fileName); + } + + public AssemblyDefinition AssemblyDefinition { + get { + try { + return assemblyTask.Result; + } catch (AggregateException) { + return null; + } + } + } + + public AssemblyList AssemblyList { + get { return assemblyList; } + } + + public string FileName { + get { return fileName; } + } + + public string ShortName { + get { return shortName; } + } + + public bool IsLoaded { + get { return assemblyTask.IsCompleted; } + } + + public bool HasLoadError { + get { return assemblyTask.IsFaulted; } + } + + AssemblyDefinition LoadAssembly() + { + // runs on background thread + ReaderParameters p = new ReaderParameters(); + p.AssemblyResolver = new MyAssemblyResolver(this); + return AssemblyDefinition.ReadAssembly(fileName, p); + } + + sealed class MyAssemblyResolver : IAssemblyResolver + { + readonly LoadedAssembly parent; + + public MyAssemblyResolver(LoadedAssembly 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; + } + } + + public LoadedAssembly LookupReferencedAssembly(string fullName) + { + foreach (LoadedAssembly asm in assemblyList.GetAssemblies()) { + if (asm.AssemblyDefinition != null && fullName.Equals(asm.AssemblyDefinition.FullName, StringComparison.OrdinalIgnoreCase)) + return asm; + } + + if (!App.Current.Dispatcher.CheckAccess()) { + // Call this method on the GUI thread. + return (LoadedAssembly)App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Func(LookupReferencedAssembly), fullName); + } + + 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; + } + } + + public Task ContinueWhenLoaded(Action> onAssemblyLoaded, TaskScheduler taskScheduler) + { + return this.assemblyTask.ContinueWith(onAssemblyLoaded, taskScheduler); + } + + public void WaitUntilLoaded() + { + assemblyTask.Wait(); + } + } +} diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 333dffb09..9abe7a2a0 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -155,8 +155,8 @@ namespace ICSharpCode.ILSpy void assemblyList_Assemblies_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.OldItems != null) - foreach (AssemblyTreeNode node in e.OldItems) - history.RemoveAll(n => n.AncestorsAndSelf().Contains(node)); + foreach (LoadedAssembly asm in e.OldItems) + history.RemoveAll(n => n.AncestorsAndSelf().OfType().Any(a => a.LoadedAssembly == asm)); } void LoadInitialAssemblies() @@ -198,6 +198,10 @@ namespace ICSharpCode.ILSpy get { return assemblyList; } } + internal AssemblyListTreeNode AssemblyListTreeNode { + get { return assemblyListTreeNode; } + } + #region Node Selection internal void SelectNode(SharpTreeNode obj, bool recordNavigationInHistory = true) { @@ -330,8 +334,11 @@ namespace ICSharpCode.ILSpy foreach (string file in fileNames) { var asm = assemblyList.OpenAssembly(file); if (asm != null) { - treeView.SelectedItems.Add(asm); - lastNode = asm; + var node = assemblyListTreeNode.FindAssemblyNode(asm); + if (node != null) { + treeView.SelectedItems.Add(node); + lastNode = node; + } } } if (lastNode != null) diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 9621bcec4..a652c618c 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -348,19 +348,19 @@ namespace ICSharpCode.ILSpy.TextView return; } } - var assemblyList = mainWindow.AssemblyList; + var assemblyListTreeNode = mainWindow.AssemblyListTreeNode; if (reference is TypeReference) { - mainWindow.SelectNode(assemblyList.FindTypeNode(((TypeReference)reference).Resolve())); + mainWindow.SelectNode(assemblyListTreeNode.FindTypeNode(((TypeReference)reference).Resolve())); } else if (reference is MethodReference) { - mainWindow.SelectNode(assemblyList.FindMethodNode(((MethodReference)reference).Resolve())); + mainWindow.SelectNode(assemblyListTreeNode.FindMethodNode(((MethodReference)reference).Resolve())); } else if (reference is FieldReference) { - mainWindow.SelectNode(assemblyList.FindFieldNode(((FieldReference)reference).Resolve())); + mainWindow.SelectNode(assemblyListTreeNode.FindFieldNode(((FieldReference)reference).Resolve())); } else if (reference is PropertyReference) { - mainWindow.SelectNode(assemblyList.FindPropertyNode(((PropertyReference)reference).Resolve())); + mainWindow.SelectNode(assemblyListTreeNode.FindPropertyNode(((PropertyReference)reference).Resolve())); } else if (reference is EventReference) { - mainWindow.SelectNode(assemblyList.FindEventNode(((EventReference)reference).Resolve())); + mainWindow.SelectNode(assemblyListTreeNode.FindEventNode(((EventReference)reference).Resolve())); } else if (reference is AssemblyDefinition) { - mainWindow.SelectNode(assemblyList.GetAssemblies().FirstOrDefault(node => node.AssemblyDefinition == reference)); + mainWindow.SelectNode(assemblyListTreeNode.FindAssemblyNode((AssemblyDefinition)reference)); } } #endregion diff --git a/ILSpy/TreeNodes/AssemblyListTreeNode.cs b/ILSpy/TreeNodes/AssemblyListTreeNode.cs index 594e4f1ae..0530055c3 100644 --- a/ILSpy/TreeNodes/AssemblyListTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyListTreeNode.cs @@ -17,11 +17,13 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Collections.ObjectModel; +using System.Collections.Specialized; using System.Linq; using System.Windows; - using ICSharpCode.Decompiler; using ICSharpCode.TreeView; +using Mono.Cecil; namespace ICSharpCode.ILSpy.TreeNodes { @@ -42,13 +44,38 @@ namespace ICSharpCode.ILSpy.TreeNodes if (assemblyList == null) throw new ArgumentNullException("assemblyList"); this.assemblyList = assemblyList; - this.Children.BindToObservableCollection(assemblyList.assemblies); + BindToObservableCollection(assemblyList.assemblies); } public override object Text { get { return assemblyList.ListName; } } + void BindToObservableCollection(ObservableCollection collection) + { + this.Children.Clear(); + this.Children.AddRange(collection.Select(a => new AssemblyTreeNode(a))); + collection.CollectionChanged += delegate(object sender, NotifyCollectionChangedEventArgs e) { + switch (e.Action) { + case NotifyCollectionChangedAction.Add: + this.Children.InsertRange(e.NewStartingIndex, e.NewItems.Cast().Select(a => new AssemblyTreeNode(a))); + break; + case NotifyCollectionChangedAction.Remove: + this.Children.RemoveRange(e.OldStartingIndex, e.OldItems.Count); + break; + case NotifyCollectionChangedAction.Replace: + case NotifyCollectionChangedAction.Move: + throw new NotImplementedException(); + case NotifyCollectionChangedAction.Reset: + this.Children.Clear(); + this.Children.AddRange(collection.Select(a => new AssemblyTreeNode(a))); + break; + default: + throw new NotSupportedException("Invalid value for NotifyCollectionChangedAction"); + } + }; + } + public override bool CanDrop(DragEventArgs e, int index) { e.Effects = DragDropEffects.Move; @@ -69,20 +96,20 @@ namespace ICSharpCode.ILSpy.TreeNodes files = e.Data.GetData(DataFormats.FileDrop) as string[]; if (files != null) { lock (assemblyList.assemblies) { - var nodes = (from file in files - where file != null - select assemblyList.OpenAssembly(file) into node - where node != null - select node).Distinct().ToList(); - foreach (AssemblyTreeNode node in nodes) { - int nodeIndex = assemblyList.assemblies.IndexOf(node); + var assemblies = (from file in files + where file != null + select assemblyList.OpenAssembly(file) into node + where node != null + select node).Distinct().ToList(); + foreach (LoadedAssembly asm in assemblies) { + int nodeIndex = assemblyList.assemblies.IndexOf(asm); if (nodeIndex < index) index--; assemblyList.assemblies.RemoveAt(nodeIndex); } - nodes.Reverse(); - foreach (AssemblyTreeNode node in nodes) { - assemblyList.assemblies.Insert(index, node); + assemblies.Reverse(); + foreach (LoadedAssembly asm in assemblies) { + assemblyList.assemblies.Insert(index, asm); } } } @@ -94,11 +121,126 @@ namespace ICSharpCode.ILSpy.TreeNodes { language.WriteCommentLine(output, "List: " + assemblyList.ListName); output.WriteLine(); - foreach (AssemblyTreeNode asm in assemblyList.GetAssemblies()) { + foreach (AssemblyTreeNode asm in this.Children) { language.WriteCommentLine(output, new string('-', 60)); output.WriteLine(); asm.Decompile(language, output, options); } } + + #region Find*Node + + public AssemblyTreeNode FindAssemblyNode(AssemblyDefinition asm) + { + if (asm == null) + return null; + App.Current.Dispatcher.VerifyAccess(); + foreach (AssemblyTreeNode node in this.Children) { + if (node.LoadedAssembly.IsLoaded && node.LoadedAssembly.AssemblyDefinition == asm) + return node; + } + return null; + } + + public AssemblyTreeNode FindAssemblyNode(LoadedAssembly asm) + { + if (asm == null) + return null; + App.Current.Dispatcher.VerifyAccess(); + foreach (AssemblyTreeNode node in this.Children) { + if (node.LoadedAssembly == asm) + return node; + } + return null; + } + + /// + /// Looks up the type node corresponding to the type definition. + /// Returns null if no matching node is found. + /// + public TypeTreeNode FindTypeNode(TypeDefinition def) + { + if (def == null) + return null; + if (def.DeclaringType != null) { + TypeTreeNode decl = FindTypeNode(def.DeclaringType); + if (decl != null) { + decl.EnsureLazyChildren(); + return decl.Children.OfType().FirstOrDefault(t => t.TypeDefinition == def && !t.IsHidden); + } + } else { + AssemblyTreeNode asm = FindAssemblyNode(def.Module.Assembly); + if (asm != null) { + return asm.FindTypeNode(def); + } + } + return null; + } + + /// + /// Looks up the method node corresponding to the method definition. + /// Returns null if no matching node is found. + /// + public MethodTreeNode FindMethodNode(MethodDefinition def) + { + if (def == null) + return null; + TypeTreeNode typeNode = FindTypeNode(def.DeclaringType); + typeNode.EnsureLazyChildren(); + MethodTreeNode methodNode = typeNode.Children.OfType().FirstOrDefault(m => m.MethodDefinition == def && !m.IsHidden); + if (methodNode != null) + return methodNode; + foreach (var p in typeNode.Children.OfType()) { + if (p.IsHidden) + continue; + // method might be a child or a property or events + p.EnsureLazyChildren(); + methodNode = p.Children.OfType().FirstOrDefault(m => m.MethodDefinition == def && !m.IsHidden); + if (methodNode != null) + return methodNode; + } + + return null; + } + + /// + /// Looks up the field node corresponding to the field definition. + /// Returns null if no matching node is found. + /// + public FieldTreeNode FindFieldNode(FieldDefinition def) + { + if (def == null) + return null; + TypeTreeNode typeNode = FindTypeNode(def.DeclaringType); + typeNode.EnsureLazyChildren(); + return typeNode.Children.OfType().FirstOrDefault(m => m.FieldDefinition == def && !m.IsHidden); + } + + /// + /// Looks up the property node corresponding to the property definition. + /// Returns null if no matching node is found. + /// + public PropertyTreeNode FindPropertyNode(PropertyDefinition def) + { + if (def == null) + return null; + TypeTreeNode typeNode = FindTypeNode(def.DeclaringType); + typeNode.EnsureLazyChildren(); + return typeNode.Children.OfType().FirstOrDefault(m => m.PropertyDefinition == def && !m.IsHidden); + } + + /// + /// Looks up the event node corresponding to the event definition. + /// Returns null if no matching node is found. + /// + public EventTreeNode FindEventNode(EventDefinition def) + { + if (def == null) + return null; + TypeTreeNode typeNode = FindTypeNode(def.DeclaringType); + typeNode.EnsureLazyChildren(); + return typeNode.Children.OfType().FirstOrDefault(m => m.EventDefinition == def && !m.IsHidden); + } + #endregion } } diff --git a/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs b/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs index c0af4ae79..13704127d 100644 --- a/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs @@ -63,19 +63,22 @@ namespace ICSharpCode.ILSpy.TreeNodes { var assemblyListNode = parentAssembly.Parent as AssemblyListTreeNode; if (assemblyListNode != null) { - assemblyListNode.Select(parentAssembly.LookupReferencedAssembly(r.FullName)); + assemblyListNode.Select(assemblyListNode.FindAssemblyNode(parentAssembly.LoadedAssembly.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)); + var assemblyListNode = parentAssembly.Parent as AssemblyListTreeNode; + if (assemblyListNode != null) { + var refNode = assemblyListNode.FindAssemblyNode(parentAssembly.LoadedAssembly.LookupReferencedAssembly(r.FullName)); + if (refNode != null) { + AssemblyDefinition asm = refNode.LoadedAssembly.AssemblyDefinition; + if (asm != null) { + foreach (var childRef in asm.MainModule.AssemblyReferences) + this.Children.Add(new AssemblyReferenceTreeNode(childRef, refNode)); + } } } } diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs index 78006ec04..7b9136717 100644 --- a/ILSpy/TreeNodes/AssemblyTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs @@ -38,55 +38,38 @@ namespace ICSharpCode.ILSpy.TreeNodes /// sealed class AssemblyTreeNode : ILSpyTreeNode { - - readonly AssemblyList assemblyList; - readonly string fileName; - string shortName; - readonly Task assemblyTask; + readonly LoadedAssembly assembly; readonly List classes = new List(); readonly Dictionary namespaces = new Dictionary(); - public AssemblyTreeNode(string fileName, AssemblyList assemblyList) + public AssemblyTreeNode(LoadedAssembly assembly) { - if (fileName == null) - throw new ArgumentNullException("fileName"); + if (assembly == null) + throw new ArgumentNullException("assembly"); - this.fileName = fileName; - this.assemblyList = assemblyList; - this.assemblyTask = Task.Factory.StartNew(LoadAssembly); // requires that this.fileName is set - this.shortName = Path.GetFileNameWithoutExtension(fileName); + this.assembly = assembly; - assemblyTask.ContinueWith(OnAssemblyLoaded, TaskScheduler.FromCurrentSynchronizationContext()); + assembly.ContinueWhenLoaded(OnAssemblyLoaded, TaskScheduler.FromCurrentSynchronizationContext()); this.LazyLoading = true; } - public string FileName { - get { return fileName; } - } - public AssemblyList AssemblyList { - get { return assemblyList; } + get { return assembly.AssemblyList; } } - public AssemblyDefinition AssemblyDefinition { - get { - try { - return assemblyTask.Result; - } catch { - return null; - } - } + public LoadedAssembly LoadedAssembly { + get { return assembly; } } public override object Text { - get { return HighlightSearchMatch(shortName); } + get { return HighlightSearchMatch(assembly.ShortName); } } public override object Icon { get { - if (assemblyTask.IsCompleted) { - return assemblyTask.IsFaulted ? Images.AssemblyWarning : Images.Assembly; + if (assembly.IsLoaded) { + return assembly.HasLoadError ? Images.AssemblyWarning : Images.Assembly; } else { return Images.AssemblyLoading; } @@ -94,22 +77,7 @@ namespace ICSharpCode.ILSpy.TreeNodes } public override bool ShowExpander { - get { return !assemblyTask.IsFaulted; } - } - - AssemblyDefinition LoadAssembly() - { - // runs on background thread - ReaderParameters p = new ReaderParameters(); - p.AssemblyResolver = new MyAssemblyResolver(this); - AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(fileName, p); - foreach (TypeDefinition type in assembly.MainModule.Types.OrderBy(t => t.FullName)) { - TypeTreeNode node = new TypeTreeNode(type, this); - classes.Add(node); - assemblyList.RegisterTypeNode(node); - } - - return assembly; + get { return !assembly.HasLoadError; } } void OnAssemblyLoaded(Task assemblyTask) @@ -120,45 +88,7 @@ namespace ICSharpCode.ILSpy.TreeNodes if (assemblyTask.IsFaulted) { RaisePropertyChanged("ShowExpander"); // cannot expand assemblies with load error } else { - AssemblyDefinition assembly = assemblyTask.Result; - if (shortName != assembly.Name.Name) { - shortName = assembly.Name.Name; - RaisePropertyChanged("Text"); - } - } - } - - 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; + RaisePropertyChanged("Text"); // shortname might have changed } } @@ -177,28 +107,32 @@ namespace ICSharpCode.ILSpy.TreeNodes return menu; } + Dictionary typeDict = new Dictionary(); + protected override void LoadChildren() { - try { - assemblyTask.Wait(); - } catch (AggregateException) { + AssemblyDefinition assemblyDefinition = assembly.AssemblyDefinition; + if (assemblyDefinition == null) { // if we crashed on loading, then we don't have any children return; } - ModuleDefinition mainModule = assemblyTask.Result.MainModule; + ModuleDefinition mainModule = assemblyDefinition.MainModule; + this.Children.Add(new ReferenceFolderTreeNode(mainModule, this)); if (mainModule.HasResources) this.Children.Add(new ResourceListTreeNode(mainModule)); foreach (NamespaceTreeNode ns in namespaces.Values) { ns.Children.Clear(); } - foreach (TypeTreeNode type in classes) { + foreach (TypeDefinition type in mainModule.Types.OrderBy(t => t.FullName)) { NamespaceTreeNode ns; if (!namespaces.TryGetValue(type.Namespace, out ns)) { ns = new NamespaceTreeNode(type.Namespace); namespaces[type.Namespace] = ns; } - ns.Children.Add(type); + TypeTreeNode node = new TypeTreeNode(type, this); + typeDict[type] = node; + ns.Children.Add(node); } foreach (NamespaceTreeNode ns in namespaces.Values.OrderBy(n => n.Name)) { if (ns.Children.Count > 0) @@ -206,6 +140,18 @@ namespace ICSharpCode.ILSpy.TreeNodes } } + public TypeTreeNode FindTypeNode(TypeDefinition def) + { + if (def == null) + return null; + EnsureLazyChildren(); + TypeTreeNode node; + if (typeDict.TryGetValue(def, out node)) + return node; + else + return null; + } + public override bool CanDrag(SharpTreeNode[] nodes) { return nodes.All(n => n is AssemblyTreeNode); @@ -228,8 +174,8 @@ namespace ICSharpCode.ILSpy.TreeNodes public override void DeleteCore() { - lock (assemblyList.assemblies) { - assemblyList.assemblies.Remove(this); + lock (assembly.AssemblyList.assemblies) { + assembly.AssemblyList.assemblies.Remove(assembly); } } @@ -238,41 +184,13 @@ namespace ICSharpCode.ILSpy.TreeNodes public override IDataObject Copy(SharpTreeNode[] nodes) { DataObject dataObject = new DataObject(); - dataObject.SetData(DataFormat, nodes.OfType().Select(n => n.fileName).ToArray()); + dataObject.SetData(DataFormat, nodes.OfType().Select(n => n.LoadedAssembly.FileName).ToArray()); return dataObject; } - public AssemblyTreeNode LookupReferencedAssembly(string fullName) - { - foreach (AssemblyTreeNode node in assemblyList.GetAssemblies()) { - if (node.AssemblyDefinition != null && fullName.Equals(node.AssemblyDefinition.FullName, StringComparison.OrdinalIgnoreCase)) - return node; - } - - if (!App.Current.Dispatcher.CheckAccess()) { - // Call this method on the GUI thread. - return (AssemblyTreeNode)App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Func(LookupReferencedAssembly), fullName); - } - - 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; - } - } - public override FilterResult Filter(FilterSettings settings) { - if (settings.SearchTermMatches(shortName)) + if (settings.SearchTermMatches(assembly.ShortName)) return FilterResult.Match; else return FilterResult.Recurse; @@ -280,8 +198,8 @@ namespace ICSharpCode.ILSpy.TreeNodes public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { - // use assemblyTask.Result instead of this.AssemblyDefinition so that load errors are passed on to the caller - language.DecompileAssembly(assemblyTask.Result, fileName, output, options); + assembly.WaitUntilLoaded(); // necessary so that load errors are passed on to the caller + language.DecompileAssembly(assembly.AssemblyDefinition, assembly.FileName, output, options); } } } diff --git a/ILSpy/TreeNodes/BaseTypesTreeNode.cs b/ILSpy/TreeNodes/BaseTypesTreeNode.cs index 447b782a5..ff3f82c43 100644 --- a/ILSpy/TreeNodes/BaseTypesTreeNode.cs +++ b/ILSpy/TreeNodes/BaseTypesTreeNode.cs @@ -128,7 +128,7 @@ namespace ICSharpCode.ILSpy.TreeNodes if (def != null) { var assemblyListNode = node.Ancestors().OfType().FirstOrDefault(); if (assemblyListNode != null) { - assemblyListNode.Select(assemblyListNode.AssemblyList.FindTypeNode(def)); + assemblyListNode.Select(assemblyListNode.FindTypeNode(def)); return true; } } diff --git a/SharpTreeView/SharpTreeNodeCollection.cs b/SharpTreeView/SharpTreeNodeCollection.cs index f598a0f2b..57d13124e 100644 --- a/SharpTreeView/SharpTreeNodeCollection.cs +++ b/SharpTreeView/SharpTreeNodeCollection.cs @@ -173,30 +173,5 @@ namespace ICSharpCode.TreeView { return list.GetEnumerator(); } - - public void BindToObservableCollection(ObservableCollection collection) where T : SharpTreeNode - { - Clear(); - AddRange(collection); - collection.CollectionChanged += delegate(object sender, NotifyCollectionChangedEventArgs e) { - switch (e.Action) { - case NotifyCollectionChangedAction.Add: - InsertRange(e.NewStartingIndex, e.NewItems.Cast()); - break; - case NotifyCollectionChangedAction.Remove: - RemoveRange(e.OldStartingIndex, e.OldItems.Count); - break; - case NotifyCollectionChangedAction.Replace: - case NotifyCollectionChangedAction.Move: - throw new NotImplementedException(); - case NotifyCollectionChangedAction.Reset: - Clear(); - AddRange(collection); - break; - default: - throw new NotSupportedException("Invalid value for NotifyCollectionChangedAction"); - } - }; - } } }