diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index df6244777..0060256bd 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -133,6 +133,9 @@ + + + @@ -148,7 +151,7 @@ - + @@ -233,6 +236,7 @@ + \ No newline at end of file diff --git a/ILSpy/Images/Images.cs b/ILSpy/Images/Images.cs index 701ca051f..b144b6ef9 100644 --- a/ILSpy/Images/Images.cs +++ b/ILSpy/Images/Images.cs @@ -67,5 +67,6 @@ namespace ICSharpCode.ILSpy public static readonly BitmapImage ProtectedStruct = LoadBitmap("ProtectedStruct"); public static readonly BitmapImage Delete = LoadBitmap("Delete"); + public static readonly BitmapImage Search = LoadBitmap("Search"); } } diff --git a/ILSpy/MainWindow.xaml b/ILSpy/MainWindow.xaml index bc4db2307..adaffe58e 100644 --- a/ILSpy/MainWindow.xaml +++ b/ILSpy/MainWindow.xaml @@ -13,11 +13,7 @@ FocusManager.FocusedElement="{Binding ElementName=treeView}" > - - - - - + + @@ -160,6 +157,13 @@ + + + + + + + diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 4d5de03c7..e7e766f64 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -27,7 +27,10 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media.Imaging; +using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.FlowAnalysis; using ICSharpCode.ILSpy.TreeNodes; +using ICSharpCode.ILSpy.TreeNodes.Analyzer; using ICSharpCode.TreeView; using Microsoft.Win32; @@ -45,8 +48,15 @@ namespace ICSharpCode.ILSpy AssemblyList assemblyList; AssemblyListTreeNode assemblyListTreeNode; + static MainWindow instance; + + public static MainWindow Instance { + get { return instance; } + } + public MainWindow() { + instance = this; spySettings = ILSpySettings.Load(); this.sessionSettings = new SessionSettings(spySettings); this.assemblyListManager = new AssemblyListManager(spySettings); @@ -369,6 +379,26 @@ namespace ICSharpCode.ILSpy } #endregion + #region Analyzer + public void Analyze(MethodTreeNode method) + { + if (analyzerTree.Root == null) + analyzerTree.Root = new SharpTreeNode(); + + if (analyzerPanel.Visibility != Visibility.Visible) + analyzerPanel.Visibility = Visibility.Visible; + + var newNode = new AnalyzedMethodTreeNode(method.MethodDefinition) { + Language = sessionSettings.FilterSettings.Language + }; + + if (analyzerTree.Root.Children.Contains(newNode)) + analyzerTree.Root.Children.Remove(newNode); + + analyzerTree.Root.Children.Add(newNode); + } + #endregion + protected override void OnStateChanged(EventArgs e) { base.OnStateChanged(e); diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodTreeNode.cs new file mode 100644 index 000000000..809698ac5 --- /dev/null +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodTreeNode.cs @@ -0,0 +1,69 @@ +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using Mono.Cecil; + +namespace ICSharpCode.ILSpy.TreeNodes.Analyzer +{ + class AnalyzedMethodTreeNode : AnalyzerTreeNode + { + MethodDefinition analyzedMethod; + + public AnalyzedMethodTreeNode(MethodDefinition analyzedMethod) + { + if (analyzedMethod == null) + throw new ArgumentNullException("analyzedMethod"); + this.analyzedMethod = analyzedMethod; + this.LazyLoading = true; + } + + public override object Icon { + get { return MethodTreeNode.GetIcon(analyzedMethod); } + } + + public override object Text { + get { return MethodTreeNode.GetText(analyzedMethod, Language); } + } + + protected override void LoadChildren() + { + this.Children.Add(new AnalyzedMethodUsedByTreeNode(analyzedMethod)); + } + + #region Equals and GetHashCode implementation + public override bool Equals(object obj) + { + AnalyzedMethodTreeNode other = obj as AnalyzedMethodTreeNode; + if (other == null) + return false; + return object.Equals(this.analyzedMethod, other.analyzedMethod); + } + + public override int GetHashCode() + { + int hashCode = 0; + unchecked { + if (analyzedMethod != null) + hashCode += 1000000007 * analyzedMethod.GetHashCode(); + } + return hashCode; + } + #endregion + } +} diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs new file mode 100644 index 000000000..41cf720ba --- /dev/null +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs @@ -0,0 +1,92 @@ +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Threading; + +using ICSharpCode.NRefactory.Utils; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace ICSharpCode.ILSpy.TreeNodes.Analyzer +{ + class AnalyzedMethodUsedByTreeNode : ILSpyTreeNode + { + MethodDefinition analyzedMethod; + ThreadingSupport threading; + + public AnalyzedMethodUsedByTreeNode(MethodDefinition analyzedMethod) + { + if (analyzedMethod == null) + throw new ArgumentNullException("analyzedMethod"); + + this.analyzedMethod = analyzedMethod; + this.threading = new ThreadingSupport(); + this.LazyLoading = true; + } + + public override object Text { + get { return "Used By"; } + } + + public override object Icon { + get { return Images.Search; } + } + + protected override void LoadChildren() + { + threading.LoadChildren(this, FetchChildren); + } + + IEnumerable FetchChildren(CancellationToken ct) + { + return FindReferences(MainWindow.Instance.AssemblyList.GetAssemblies(), ct); + } + + IEnumerable FindReferences(LoadedAssembly[] assemblies, CancellationToken ct) + { + foreach (LoadedAssembly asm in assemblies) { + ct.ThrowIfCancellationRequested(); + foreach (TypeDefinition type in TreeTraversal.PreOrder(asm.AssemblyDefinition.MainModule.Types, t => t.NestedTypes)) { + ct.ThrowIfCancellationRequested(); + foreach (MethodDefinition method in type.Methods) { + ct.ThrowIfCancellationRequested(); + bool found = false; + if (!method.HasBody) + continue; + foreach (Instruction instr in method.Body.Instructions) { + if (instr.Operand is MethodReference + && ((MethodReference)instr.Operand).Resolve() == analyzedMethod) { + found = true; + break; + } + } + if (found) + yield return new MethodTreeNode(method); + } + } + } + } + + public override void Decompile(Language language, ICSharpCode.Decompiler.ITextOutput output, DecompilationOptions options) + { + throw new NotImplementedException(); + } + } +} diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzerTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzerTreeNode.cs new file mode 100644 index 000000000..0a91760a4 --- /dev/null +++ b/ILSpy/TreeNodes/Analyzer/AnalyzerTreeNode.cs @@ -0,0 +1,28 @@ +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using ICSharpCode.TreeView; + +namespace ICSharpCode.ILSpy.TreeNodes.Analyzer +{ + class AnalyzerTreeNode : SharpTreeNode + { + public Language Language { get; set; } + } +} diff --git a/ILSpy/TreeNodes/DerivedTypesTreeNode.cs b/ILSpy/TreeNodes/DerivedTypesTreeNode.cs index 079b3ceae..74344a4d0 100644 --- a/ILSpy/TreeNodes/DerivedTypesTreeNode.cs +++ b/ILSpy/TreeNodes/DerivedTypesTreeNode.cs @@ -15,15 +15,18 @@ namespace ICSharpCode.ILSpy.TreeNodes /// /// Lists the super types of a class. /// - sealed class DerivedTypesTreeNode : ThreadedTreeNode + sealed class DerivedTypesTreeNode : ILSpyTreeNode { readonly AssemblyList list; readonly TypeDefinition type; + ThreadingSupport threading; public DerivedTypesTreeNode(AssemblyList list, TypeDefinition type) { this.list = list; this.type = type; + this.LazyLoading = true; + this.threading = new ThreadingSupport(); } public override object Text { @@ -34,7 +37,12 @@ namespace ICSharpCode.ILSpy.TreeNodes get { return Images.SubTypes; } } - protected override IEnumerable FetchChildren(CancellationToken cancellationToken) + protected override void LoadChildren() + { + threading.LoadChildren(this, FetchChildren); + } + + 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(); @@ -62,17 +70,24 @@ namespace ICSharpCode.ILSpy.TreeNodes { return typeRef.FullName == type.FullName; } + + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + { + threading.Decompile(language, output, options, EnsureLazyChildren); + } } - class DerivedTypesEntryNode : ThreadedTreeNode + class DerivedTypesEntryNode : ILSpyTreeNode { TypeDefinition def; AssemblyDefinition[] assemblies; + ThreadingSupport threading; public DerivedTypesEntryNode(TypeDefinition def, AssemblyDefinition[] assemblies) { this.def = def; this.assemblies = assemblies; + threading = new ThreadingSupport(); } public override bool ShowExpander { @@ -91,7 +106,12 @@ namespace ICSharpCode.ILSpy.TreeNodes } } - protected override IEnumerable FetchChildren(CancellationToken ct) + protected override void LoadChildren() + { + threading.LoadChildren(this, FetchChildren); + } + + 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/ILSpyTreeNode.cs b/ILSpy/TreeNodes/ILSpyTreeNode.cs index 3aef3bfc5..960e32203 100644 --- a/ILSpy/TreeNodes/ILSpyTreeNode.cs +++ b/ILSpy/TreeNodes/ILSpyTreeNode.cs @@ -56,7 +56,7 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Hidden; } - protected object HighlightSearchMatch(string text, string suffix = null) + protected static object HighlightSearchMatch(string text, string suffix = null) { // TODO: implement highlighting the search match return text + suffix; diff --git a/ILSpy/TreeNodes/MethodTreeNode.cs b/ILSpy/TreeNodes/MethodTreeNode.cs index fd1802957..3e28884b9 100644 --- a/ILSpy/TreeNodes/MethodTreeNode.cs +++ b/ILSpy/TreeNodes/MethodTreeNode.cs @@ -18,6 +18,8 @@ using System; using System.Text; +using System.Windows.Controls; +using System.Windows.Media.Imaging; using ICSharpCode.Decompiler; using Mono.Cecil; @@ -43,30 +45,41 @@ namespace ICSharpCode.ILSpy.TreeNodes public override object Text { get { - StringBuilder b = new StringBuilder(); - b.Append('('); - for (int i = 0; i < method.Parameters.Count; i++) { - if (i > 0) b.Append(", "); - b.Append(this.Language.TypeToString(method.Parameters[i].ParameterType, false, method.Parameters[i])); - } - b.Append(") : "); - b.Append(this.Language.TypeToString(method.ReturnType, false, method.MethodReturnType)); - return HighlightSearchMatch(method.Name, b.ToString()); + return GetText(method, Language); + } + } + + public static object GetText(MethodDefinition method, Language language) + { + StringBuilder b = new StringBuilder(); + b.Append('('); + for (int i = 0; i < method.Parameters.Count; i++) { + if (i > 0) + b.Append(", "); + b.Append(language.TypeToString(method.Parameters[i].ParameterType, false, method.Parameters[i])); } + b.Append(") : "); + b.Append(language.TypeToString(method.ReturnType, false, method.MethodReturnType)); + return HighlightSearchMatch(method.Name, b.ToString()); } public override object Icon { get { - if (method.IsSpecialName && method.Name.StartsWith("op_", StringComparison.Ordinal)) - return Images.Operator; - if (method.IsStatic && method.HasCustomAttributes) { - foreach (var ca in method.CustomAttributes) { - if (ca.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute") - return Images.ExtensionMethod; - } + return GetIcon(method); + } + } + + public static BitmapImage GetIcon(MethodDefinition method) + { + if (method.IsSpecialName && method.Name.StartsWith("op_", StringComparison.Ordinal)) + return Images.Operator; + if (method.IsStatic && method.HasCustomAttributes) { + foreach (var ca in method.CustomAttributes) { + if (ca.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute") + return Images.ExtensionMethod; } - return Images.Method; } + return Images.Method; } public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) @@ -81,5 +94,16 @@ namespace ICSharpCode.ILSpy.TreeNodes else return FilterResult.Hidden; } + + public override System.Windows.Controls.ContextMenu GetContextMenu() + { + ContextMenu menu = new ContextMenu(); + MenuItem item = new MenuItem() { Header = "Analyze", Icon = new Image() { Source = Images.Search } }; + item.Click += delegate { MainWindow.Instance.Analyze(this); }; + + menu.Items.Add(item); + + return menu; + } } } diff --git a/ILSpy/TreeNodes/ThreadedTreeNode.cs b/ILSpy/TreeNodes/ThreadingSupport.cs similarity index 70% rename from ILSpy/TreeNodes/ThreadedTreeNode.cs rename to ILSpy/TreeNodes/ThreadingSupport.cs index fca99f5af..1d0dd1f71 100644 --- a/ILSpy/TreeNodes/ThreadedTreeNode.cs +++ b/ILSpy/TreeNodes/ThreadingSupport.cs @@ -11,36 +11,22 @@ using ICSharpCode.Decompiler; namespace ICSharpCode.ILSpy.TreeNodes { /// - /// Node that is lazy-loaded and loads its children on a background thread. + /// Adds threading support to nodes /// - abstract class ThreadedTreeNode : ILSpyTreeNode + class ThreadingSupport { Task> loadChildrenTask; - public ThreadedTreeNode() - { - this.LazyLoading = true; - } - - public void Invalidate() - { - this.LazyLoading = true; - this.Children.Clear(); - loadChildrenTask = null; - } - /// - /// FetchChildren() runs on the main thread; but the enumerator is consumed on a background thread + /// /// - protected abstract IEnumerable FetchChildren(CancellationToken ct); - - protected override sealed void LoadChildren() + public void LoadChildren(ILSpyTreeNode node, Func> fetchChildren) { - this.Children.Add(new LoadingTreeNode()); + node.Children.Add(new LoadingTreeNode()); CancellationToken ct = CancellationToken.None; - var fetchChildrenEnumerable = FetchChildren(ct); + var fetchChildrenEnumerable = fetchChildren(ct); Task> thisTask = null; thisTask = new Task>( delegate { @@ -53,14 +39,14 @@ namespace ICSharpCode.ILSpy.TreeNodes // don't access "child" here the // background thread might already be running the next loop iteration if (loadChildrenTask == thisTask) { - this.Children.Insert(this.Children.Count - 1, newChild); + node.Children.Insert(node.Children.Count - 1, newChild); } }), child); } App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action( delegate { if (loadChildrenTask == thisTask) { - this.Children.RemoveAt(this.Children.Count - 1); // remove 'Loading...' + node.Children.RemoveAt(node.Children.Count - 1); // remove 'Loading...' } })); return result; @@ -72,11 +58,11 @@ namespace ICSharpCode.ILSpy.TreeNodes thisTask.Wait(TimeSpan.FromMilliseconds(200)); } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public void Decompile(Language language, ITextOutput output, DecompilationOptions options, Action ensureLazyChildren) { var loadChildrenTask = this.loadChildrenTask; if (loadChildrenTask == null) { - App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnsureLazyChildren)); + App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, ensureLazyChildren); loadChildrenTask = this.loadChildrenTask; } if (loadChildrenTask != null) { diff --git a/ILSpy/themes/generic.xaml b/ILSpy/themes/generic.xaml index 7d6202224..5af30034d 100644 --- a/ILSpy/themes/generic.xaml +++ b/ILSpy/themes/generic.xaml @@ -22,4 +22,8 @@ Data = "M 5,5 L 10,10 L 15,5 L 5,5"/> + + + + \ No newline at end of file