diff --git a/ILSpy/AssemblyList.cs b/ILSpy/AssemblyList.cs index f8d76fbc2..acda9915f 100644 --- a/ILSpy/AssemblyList.cs +++ b/ILSpy/AssemblyList.cs @@ -44,6 +44,10 @@ namespace ICSharpCode.ILSpy /// Needs locking for multi-threaded access! /// 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 + /// thread-safe method. + /// internal readonly ObservableCollection assemblies = new ObservableCollection(); /// @@ -167,10 +171,10 @@ namespace ICSharpCode.ILSpy MethodTreeNode methodNode = typeNode.VisibleChildren.OfType().FirstOrDefault(m => m.MethodDefinition == def); if (methodNode != null) return methodNode; - foreach (var p in typeNode.VisibleChildren.OfType>()) { + foreach (var p in typeNode.VisibleChildren.OfType()) { // method might be a child or a property or events p.EnsureLazyChildren(); - methodNode = p.Children.FirstOrDefault(m => m.MethodDefinition == def); + methodNode = p.Children.OfType().FirstOrDefault(m => m.MethodDefinition == def); if (methodNode != null) return methodNode; } diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 59cdec347..333dffb09 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -372,24 +372,24 @@ namespace ICSharpCode.ILSpy void TreeView_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (treeView.SelectedItems.Count == 1) { - ILSpyTreeNodeBase node = treeView.SelectedItem as ILSpyTreeNodeBase; + ILSpyTreeNode node = treeView.SelectedItem as ILSpyTreeNode; if (node != null && node.View(decompilerTextView)) return; } decompilerTextView.Decompile(sessionSettings.FilterSettings.Language, - treeView.GetTopLevelSelection().OfType(), + treeView.GetTopLevelSelection().OfType(), new DecompilationOptions()); } void saveCode_Click(object sender, RoutedEventArgs e) { if (treeView.SelectedItems.Count == 1) { - ILSpyTreeNodeBase node = treeView.SelectedItem as ILSpyTreeNodeBase; + ILSpyTreeNode node = treeView.SelectedItem as ILSpyTreeNode; if (node != null && node.Save()) return; } decompilerTextView.SaveToDisk(sessionSettings.FilterSettings.Language, - treeView.GetTopLevelSelection().OfType(), + treeView.GetTopLevelSelection().OfType(), new DecompilationOptions()); } #endregion diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 34a957524..b8a480155 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -188,7 +188,7 @@ namespace ICSharpCode.ILSpy.TextView /// Starts the decompilation of the given nodes. /// The result is displayed in the text view. /// - public void Decompile(ILSpy.Language language, IEnumerable treeNodes, DecompilationOptions options) + public void Decompile(ILSpy.Language language, IEnumerable treeNodes, DecompilationOptions options) { // Some actions like loading an assembly list cause several selection changes in the tree view, // and each of those will start a decompilation action. @@ -209,10 +209,10 @@ namespace ICSharpCode.ILSpy.TextView sealed class DecompilationContext { public readonly ILSpy.Language Language; - public readonly ILSpyTreeNodeBase[] TreeNodes; + public readonly ILSpyTreeNode[] TreeNodes; public readonly DecompilationOptions Options; - public DecompilationContext(ILSpy.Language language, ILSpyTreeNodeBase[] treeNodes, DecompilationOptions options) + public DecompilationContext(ILSpy.Language language, ILSpyTreeNode[] treeNodes, DecompilationOptions options) { this.Language = language; this.TreeNodes = treeNodes; @@ -367,7 +367,7 @@ namespace ICSharpCode.ILSpy.TextView /// /// Shows the 'save file dialog', prompting the user to save the decompiled nodes to disk. /// - public void SaveToDisk(ILSpy.Language language, IEnumerable treeNodes, DecompilationOptions options) + public void SaveToDisk(ILSpy.Language language, IEnumerable treeNodes, DecompilationOptions options) { if (!treeNodes.Any()) return; diff --git a/ILSpy/TreeNodes/AssemblyListTreeNode.cs b/ILSpy/TreeNodes/AssemblyListTreeNode.cs index a3d573b2b..93843fd6e 100644 --- a/ILSpy/TreeNodes/AssemblyListTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyListTreeNode.cs @@ -29,7 +29,7 @@ namespace ICSharpCode.ILSpy.TreeNodes /// Represents a list of assemblies. /// This is used as (invisible) root node of the tree view. /// - sealed class AssemblyListTreeNode : ILSpyTreeNode + sealed class AssemblyListTreeNode : ILSpyTreeNode { readonly AssemblyList assemblyList; @@ -38,11 +38,11 @@ namespace ICSharpCode.ILSpy.TreeNodes } public AssemblyListTreeNode(AssemblyList assemblyList) - : base(assemblyList.assemblies) { if (assemblyList == null) throw new ArgumentNullException("assemblyList"); this.assemblyList = assemblyList; + this.Children.BindToObservableCollection(assemblyList.assemblies); } public override object Text { diff --git a/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs b/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs index 79078e0a8..c0af4ae79 100644 --- a/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs @@ -27,7 +27,7 @@ namespace ICSharpCode.ILSpy.TreeNodes /// /// Node within assembly reference list. /// - sealed class AssemblyReferenceTreeNode : ILSpyTreeNode + sealed class AssemblyReferenceTreeNode : ILSpyTreeNode { readonly AssemblyNameReference r; readonly AssemblyTreeNode parentAssembly; diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs index 3446931fa..ff3954a9a 100644 --- a/ILSpy/TreeNodes/AssemblyTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs @@ -36,7 +36,7 @@ namespace ICSharpCode.ILSpy.TreeNodes /// Tree node representing an assembly. /// This class is responsible for loading both namespace and type nodes. /// - sealed class AssemblyTreeNode : ILSpyTreeNode + sealed class AssemblyTreeNode : ILSpyTreeNode { readonly AssemblyList assemblyList; diff --git a/ILSpy/TreeNodes/BaseTypesTreeNode.cs b/ILSpy/TreeNodes/BaseTypesTreeNode.cs index 78f661c1c..447b782a5 100644 --- a/ILSpy/TreeNodes/BaseTypesTreeNode.cs +++ b/ILSpy/TreeNodes/BaseTypesTreeNode.cs @@ -30,7 +30,7 @@ namespace ICSharpCode.ILSpy.TreeNodes /// /// Lists the base types of a class. /// - sealed class BaseTypesTreeNode : ILSpyTreeNode + sealed class BaseTypesTreeNode : ILSpyTreeNode { readonly TypeDefinition type; @@ -53,7 +53,7 @@ namespace ICSharpCode.ILSpy.TreeNodes AddBaseTypes(this.Children, type); } - internal static void AddBaseTypes(ObservableCollection children, TypeDefinition type) + internal static void AddBaseTypes(SharpTreeNodeCollection children, TypeDefinition type) { if (type.BaseType != null) children.Add(new BaseTypesEntryNode(type.BaseType, false)); @@ -65,13 +65,13 @@ namespace ICSharpCode.ILSpy.TreeNodes public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnsureLazyChildren)); - foreach (var child in this.Children) { + foreach (ILSpyTreeNode child in this.Children) { child.Decompile(language, output, options); } } } - sealed class BaseTypesEntryNode : ILSpyTreeNode + sealed class BaseTypesEntryNode : ILSpyTreeNode { TypeReference tr; TypeDefinition def; diff --git a/ILSpy/TreeNodes/DerivedTypesTreeNode.cs b/ILSpy/TreeNodes/DerivedTypesTreeNode.cs index 02a62e0cb..079b3ceae 100644 --- a/ILSpy/TreeNodes/DerivedTypesTreeNode.cs +++ b/ILSpy/TreeNodes/DerivedTypesTreeNode.cs @@ -34,7 +34,7 @@ namespace ICSharpCode.ILSpy.TreeNodes get { return Images.SubTypes; } } - protected override IEnumerable FetchChildren(CancellationToken cancellationToken) + protected override IEnumerable FetchChildren(CancellationToken cancellationToken) { // FetchChildren() runs on the main thread; but the enumerator will be consumed on a background thread var assemblies = list.GetAssemblies().Select(node => node.AssemblyDefinition).Where(asm => asm != null).ToArray(); @@ -91,7 +91,7 @@ namespace ICSharpCode.ILSpy.TreeNodes } } - protected override IEnumerable FetchChildren(CancellationToken ct) + protected override IEnumerable FetchChildren(CancellationToken ct) { // FetchChildren() runs on the main thread; but the enumerator will be consumed on a background thread return DerivedTypesTreeNode.FindDerivedTypes(def, assemblies, ct); diff --git a/ILSpy/TreeNodes/EventTreeNode.cs b/ILSpy/TreeNodes/EventTreeNode.cs index b45b7767e..9d75b5215 100644 --- a/ILSpy/TreeNodes/EventTreeNode.cs +++ b/ILSpy/TreeNodes/EventTreeNode.cs @@ -25,7 +25,7 @@ namespace ICSharpCode.ILSpy.TreeNodes /// /// Represents an event in the TreeView. /// - sealed class EventTreeNode : ILSpyTreeNode + sealed class EventTreeNode : ILSpyTreeNode { readonly EventDefinition ev; diff --git a/ILSpy/TreeNodes/FieldTreeNode.cs b/ILSpy/TreeNodes/FieldTreeNode.cs index ea7b85136..3838c0522 100644 --- a/ILSpy/TreeNodes/FieldTreeNode.cs +++ b/ILSpy/TreeNodes/FieldTreeNode.cs @@ -25,7 +25,7 @@ namespace ICSharpCode.ILSpy.TreeNodes /// /// Represents a field in the TreeView. /// - sealed class FieldTreeNode : ILSpyTreeNode + sealed class FieldTreeNode : ILSpyTreeNode { readonly FieldDefinition field; diff --git a/ILSpy/TreeNodes/ILSpyTreeNode.cs b/ILSpy/TreeNodes/ILSpyTreeNode.cs index aaf614a6c..63afc56a9 100644 --- a/ILSpy/TreeNodes/ILSpyTreeNode.cs +++ b/ILSpy/TreeNodes/ILSpyTreeNode.cs @@ -29,7 +29,7 @@ namespace ICSharpCode.ILSpy.TreeNodes /// /// Base class of all ILSpy tree nodes. /// - abstract class ILSpyTreeNodeBase : SharpTreeNode + abstract class ILSpyTreeNode : SharpTreeNode { FilterSettings filterSettings; @@ -44,15 +44,13 @@ namespace ICSharpCode.ILSpy.TreeNodes } public Language Language { - get { return filterSettings.Language; } + get { return filterSettings != null ? filterSettings.Language : Languages.AllLanguages[0]; } } public SharpTreeNodeCollection VisibleChildren { get { return base.Children; } } - protected abstract void OnFilterSettingsChanged(); - public virtual FilterResult Filter(FilterSettings settings) { if (string.IsNullOrEmpty(settings.SearchTerm)) @@ -88,85 +86,8 @@ namespace ICSharpCode.ILSpy.TreeNodes { return false; } - } - - enum FilterResult - { - /// - /// Hides the node. - /// - Hidden, - /// - /// Shows the node (and resets the search term for child nodes). - /// - Match, - /// - /// Hides the node only if all children are hidden (and resets the search term for child nodes). - /// - MatchAndRecurse, - /// - /// Hides the node only if all children are hidden (doesn't reset the search term for child nodes). - /// - Recurse - } - - /// - /// Base class for ILSpy tree nodes. - /// - abstract class ILSpyTreeNode : ILSpyTreeNodeBase where T : ILSpyTreeNodeBase - { - public ILSpyTreeNode() - : this(new ObservableCollection()) - { - } - - public ILSpyTreeNode(ObservableCollection children) - { - if (children == null) - throw new ArgumentNullException("children"); - this.allChildren = children; - children.CollectionChanged += allChildren_CollectionChanged; - } - void allChildren_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - var visibleChildren = this.VisibleChildren; - - switch (e.Action) { - case NotifyCollectionChangedAction.Add: - if (e.NewItems.Count == 1 && e.NewStartingIndex == allChildren.Count - 1) { - T newChild = (T)e.NewItems[0]; - if (FilterChild(newChild)) - visibleChildren.Add(newChild); - break; - } else { - goto default; - } - case NotifyCollectionChangedAction.Remove: - if (e.OldItems.Count == 1) { - visibleChildren.Remove((T)e.OldItems[0]); - break; - } else { - goto default; - } - default: - ResetChildren(); - break; - } - } - - void ResetChildren() - { - var visibleChildren = this.VisibleChildren; - - visibleChildren.Clear(); - foreach (T child in allChildren) { - if (FilterChild(child)) - visibleChildren.Add(child); - } - } - - bool FilterChild(T child) + bool FilterChild(ILSpyTreeNode child) { FilterResult r; if (this.FilterSettings == null) @@ -203,35 +124,29 @@ namespace ICSharpCode.ILSpy.TreeNodes return filterSettings; } - protected override void OnFilterSettingsChanged() + protected virtual void OnFilterSettingsChanged() { - var visibleChildren = this.VisibleChildren; - var allChildren = this.Children; - int j = 0; - for (int i = 0; i < allChildren.Count; i++) { - T child = allChildren[i]; - if (j < visibleChildren.Count && visibleChildren[j] == child) { - // it was visible before - if (FilterChild(child)) { - j++; // keep it visible - } else { - visibleChildren.RemoveAt(j); // hide it - } - } else { - // it wasn't visible before - if (FilterChild(child)) { - // make it visible - visibleChildren.Insert(j++, child); - } - } - } RaisePropertyChanged("Text"); } - - readonly ObservableCollection allChildren; - - public new ObservableCollection Children { - get { return allChildren; } - } + } + + enum FilterResult + { + /// + /// Hides the node. + /// + Hidden, + /// + /// Shows the node (and resets the search term for child nodes). + /// + Match, + /// + /// Hides the node only if all children are hidden (and resets the search term for child nodes). + /// + MatchAndRecurse, + /// + /// Hides the node only if all children are hidden (doesn't reset the search term for child nodes). + /// + Recurse } } diff --git a/ILSpy/TreeNodes/MethodTreeNode.cs b/ILSpy/TreeNodes/MethodTreeNode.cs index 29243798d..fd1802957 100644 --- a/ILSpy/TreeNodes/MethodTreeNode.cs +++ b/ILSpy/TreeNodes/MethodTreeNode.cs @@ -26,7 +26,7 @@ namespace ICSharpCode.ILSpy.TreeNodes /// /// Tree Node representing a field, method, property, or event. /// - sealed class MethodTreeNode : ILSpyTreeNode + sealed class MethodTreeNode : ILSpyTreeNode { MethodDefinition method; diff --git a/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs b/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs index 20cfba942..50902e805 100644 --- a/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs +++ b/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs @@ -26,7 +26,7 @@ namespace ICSharpCode.ILSpy.TreeNodes /// /// Module reference in ReferenceFolderTreeNode. /// - sealed class ModuleReferenceTreeNode : ILSpyTreeNode + sealed class ModuleReferenceTreeNode : ILSpyTreeNode { ModuleReference r; diff --git a/ILSpy/TreeNodes/NamespaceTreeNode.cs b/ILSpy/TreeNodes/NamespaceTreeNode.cs index f32421603..69810d852 100644 --- a/ILSpy/TreeNodes/NamespaceTreeNode.cs +++ b/ILSpy/TreeNodes/NamespaceTreeNode.cs @@ -25,7 +25,7 @@ namespace ICSharpCode.ILSpy.TreeNodes /// /// Namespace node. The loading of the type nodes is handled by the parent AssemblyTreeNode. /// - sealed class NamespaceTreeNode : ILSpyTreeNode + sealed class NamespaceTreeNode : ILSpyTreeNode { string name; @@ -58,7 +58,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { - language.DecompileNamespace(name, this.Children.Select(t => t.TypeDefinition), output, options); + language.DecompileNamespace(name, this.Children.OfType().Select(t => t.TypeDefinition), output, options); } } } diff --git a/ILSpy/TreeNodes/PropertyTreeNode.cs b/ILSpy/TreeNodes/PropertyTreeNode.cs index 82f23218e..b815524ca 100644 --- a/ILSpy/TreeNodes/PropertyTreeNode.cs +++ b/ILSpy/TreeNodes/PropertyTreeNode.cs @@ -25,7 +25,7 @@ namespace ICSharpCode.ILSpy.TreeNodes /// /// Represents a property in the TreeView. /// - sealed class PropertyTreeNode : ILSpyTreeNode + sealed class PropertyTreeNode : ILSpyTreeNode { readonly PropertyDefinition property; readonly bool isIndexer; diff --git a/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs b/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs index fce228391..8a013a13c 100644 --- a/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs +++ b/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs @@ -27,7 +27,7 @@ namespace ICSharpCode.ILSpy.TreeNodes /// /// References folder. /// - sealed class ReferenceFolderTreeNode : ILSpyTreeNode + sealed class ReferenceFolderTreeNode : ILSpyTreeNode { readonly ModuleDefinition module; readonly AssemblyTreeNode parentAssembly; @@ -62,7 +62,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnsureLazyChildren)); - foreach (var child in this.Children) { + foreach (ILSpyTreeNode child in this.Children) { child.Decompile(language, output, options); } } diff --git a/ILSpy/TreeNodes/ResourceListTreeNode.cs b/ILSpy/TreeNodes/ResourceListTreeNode.cs index 6c681c950..b56b2abbd 100644 --- a/ILSpy/TreeNodes/ResourceListTreeNode.cs +++ b/ILSpy/TreeNodes/ResourceListTreeNode.cs @@ -18,7 +18,7 @@ namespace ICSharpCode.ILSpy.TreeNodes /// /// Lists the embedded resources in an assembly. /// - sealed class ResourceListTreeNode : ILSpyTreeNode + sealed class ResourceListTreeNode : ILSpyTreeNode { readonly ModuleDefinition module; @@ -53,14 +53,14 @@ namespace ICSharpCode.ILSpy.TreeNodes public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnsureLazyChildren)); - foreach (var child in this.Children) { + foreach (ILSpyTreeNode child in this.Children) { child.Decompile(language, output, options); output.WriteLine(); } } } - class ResourceTreeNode : ILSpyTreeNode + class ResourceTreeNode : ILSpyTreeNode { Resource r; diff --git a/ILSpy/TreeNodes/ThreadedTreeNode.cs b/ILSpy/TreeNodes/ThreadedTreeNode.cs index 39f96c61f..243b5cddb 100644 --- a/ILSpy/TreeNodes/ThreadedTreeNode.cs +++ b/ILSpy/TreeNodes/ThreadedTreeNode.cs @@ -13,9 +13,9 @@ namespace ICSharpCode.ILSpy.TreeNodes /// /// Node that is lazy-loaded and loads its children on a background thread. /// - abstract class ThreadedTreeNode : ILSpyTreeNode + abstract class ThreadedTreeNode : ILSpyTreeNode { - Task> loadChildrenTask; + Task> loadChildrenTask; public ThreadedTreeNode() { @@ -32,7 +32,7 @@ namespace ICSharpCode.ILSpy.TreeNodes /// /// FetchChildren() runs on the main thread; but the enumerator is consumed on a background thread /// - protected abstract IEnumerable FetchChildren(CancellationToken ct); + protected abstract IEnumerable FetchChildren(CancellationToken ct); protected override sealed void LoadChildren() { @@ -41,15 +41,15 @@ namespace ICSharpCode.ILSpy.TreeNodes CancellationToken ct = CancellationToken.None; var fetchChildrenEnumerable = FetchChildren(ct); - Task> thisTask = null; - thisTask = new Task>( + Task> thisTask = null; + thisTask = new Task>( delegate { - List result = new List(); - foreach (ILSpyTreeNodeBase child in fetchChildrenEnumerable) { + List result = new List(); + foreach (ILSpyTreeNode child in fetchChildrenEnumerable) { ct.ThrowIfCancellationRequested(); result.Add(child); - App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action( - delegate (ILSpyTreeNodeBase newChild) { + App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action( + delegate (ILSpyTreeNode newChild) { // don't access "child" here the background thread might already be running // the next loop iteration if (loadChildrenTask == thisTask) { @@ -86,7 +86,7 @@ namespace ICSharpCode.ILSpy.TreeNodes } } - sealed class LoadingTreeNode : ILSpyTreeNode + sealed class LoadingTreeNode : ILSpyTreeNode { public override object Text { get { return "Loading..."; } diff --git a/ILSpy/TreeNodes/TypeTreeNode.cs b/ILSpy/TreeNodes/TypeTreeNode.cs index c30d88e76..9effcead4 100644 --- a/ILSpy/TreeNodes/TypeTreeNode.cs +++ b/ILSpy/TreeNodes/TypeTreeNode.cs @@ -26,7 +26,7 @@ using Mono.Cecil; namespace ICSharpCode.ILSpy.TreeNodes { - sealed class TypeTreeNode : ILSpyTreeNode + sealed class TypeTreeNode : ILSpyTreeNode { readonly TypeDefinition type; readonly AssemblyTreeNode parentAssemblyNode;