Browse Source

Improvements for the analyzer.

pull/70/head
Daniel Grunwald 15 years ago
parent
commit
e9ad53a607
  1. 41
      ILSpy/MainWindow.xaml
  2. 54
      ILSpy/MainWindow.xaml.cs
  3. 3
      ILSpy/SessionSettings.cs
  4. 15
      ILSpy/TextView/DecompilerTextView.cs
  5. 6
      ILSpy/TreeNodes/Analyzer/AnalyzedMethodTreeNode.cs
  6. 57
      ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs
  7. 38
      ILSpy/TreeNodes/Analyzer/AnalyzerTreeNode.cs
  8. 3
      ILSpy/TreeNodes/MethodTreeNode.cs
  9. 78
      ILSpy/TreeNodes/ThreadingSupport.cs

41
ILSpy/MainWindow.xaml

@ -61,7 +61,7 @@ @@ -61,7 +61,7 @@
<Image Width="16" Height="16" Source="Images/PrivateInternal.png" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Show _analyzer" Name="showAnalyzer" IsCheckable="True" />
<MenuItem Header="Show _analyzer" Name="showAnalyzer" IsCheckable="True" Checked="ShowAnalyzer_Checked" Unchecked="ShowAnalyzer_Unchecked" />
</MenuItem>
<MenuItem Header="_Help">
<MenuItem Header="_About" Click="AboutClick" />
@ -139,16 +139,22 @@ @@ -139,16 +139,22 @@
<GridSplitter
Grid.ZIndex="1"
Grid.Column="1"
Margin="-2,0"
BorderThickness="2,0"
BorderBrush="Transparent"
HorizontalAlignment="Stretch" />
Margin="-5,0"
BorderThickness="5,0"
HorizontalAlignment="Center"
VerticalAlignment="Stretch"
BorderBrush="Transparent" />
<!-- Right pane: Text Editor -->
<Grid Grid.Column="2">
<Grid.ColumnDefinitions>
<ColumnDefinition
Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="0.7*" MinHeight="100" Name="textViewRow" />
<RowDefinition Height="1" />
<RowDefinition Height="0" Name="analyzerRow" />
</Grid.RowDefinitions>
<Border BorderBrush="Black" BorderThickness="1" Name="updateAvailablePanel" Visibility="Collapsed">
<DockPanel>
@ -163,21 +169,20 @@ @@ -163,21 +169,20 @@
<textView:DecompilerTextView x:Name="decompilerTextView" Grid.Row="1" />
<GridSplitter
Grid.ZIndex="2"
Grid.Row="1"
Margin="0,-2"
BorderThickness="0,2"
Grid.ZIndex="1"
Grid.Row="2"
Margin="0,-2,0,-5"
BorderThickness="0,2,0,5"
BorderBrush="Transparent"
HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
VerticalAlignment="Center"
Visibility="{Binding IsChecked, ElementName=showAnalyzer, Converter={StaticResource BooleanToVisibilityConverter}}" />
<Border BorderBrush="Gray" BorderThickness="1" Grid.Row="2" Name="analyzerPanel" Visibility="{Binding IsChecked, ElementName=showAnalyzer, Converter={StaticResource BooleanToVisibilityConverter}}" MinHeight="100">
<DockPanel>
<Label DockPanel.Dock="Top">Analyzer</Label>
<tv:SharpTreeView Name="analyzerTree" ShowRoot="False" />
</DockPanel>
</Border>
<DockPanel Grid.Row="3"
Visibility="{Binding IsChecked, ElementName=showAnalyzer, Converter={StaticResource BooleanToVisibilityConverter}}">
<Label DockPanel.Dock="Top">Analyzer</Label>
<tv:SharpTreeView Name="analyzerTree" ShowRoot="False" />
</DockPanel>
</Grid>
</Grid>
</DockPanel>

54
ILSpy/MainWindow.xaml.cs

@ -33,6 +33,7 @@ using ICSharpCode.ILSpy.TreeNodes; @@ -33,6 +33,7 @@ using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpy.TreeNodes.Analyzer;
using ICSharpCode.TreeView;
using Microsoft.Win32;
using Mono.Cecil;
namespace ICSharpCode.ILSpy
{
@ -260,6 +261,23 @@ namespace ICSharpCode.ILSpy @@ -260,6 +261,23 @@ namespace ICSharpCode.ILSpy
path.Reverse();
return path.ToArray();
}
public void JumpToReference(object reference)
{
if (reference is TypeReference) {
SelectNode(assemblyListTreeNode.FindTypeNode(((TypeReference)reference).Resolve()));
} else if (reference is MethodReference) {
SelectNode(assemblyListTreeNode.FindMethodNode(((MethodReference)reference).Resolve()));
} else if (reference is FieldReference) {
SelectNode(assemblyListTreeNode.FindFieldNode(((FieldReference)reference).Resolve()));
} else if (reference is PropertyReference) {
SelectNode(assemblyListTreeNode.FindPropertyNode(((PropertyReference)reference).Resolve()));
} else if (reference is EventReference) {
SelectNode(assemblyListTreeNode.FindEventNode(((EventReference)reference).Resolve()));
} else if (reference is AssemblyDefinition) {
SelectNode(assemblyListTreeNode.FindAssemblyNode((AssemblyDefinition)reference));
}
}
#endregion
#region Open/Refresh
@ -380,20 +398,18 @@ namespace ICSharpCode.ILSpy @@ -380,20 +398,18 @@ namespace ICSharpCode.ILSpy
#endregion
#region Analyzer
public void Analyze(MethodTreeNode method)
public void AddToAnalyzer(AnalyzerTreeNode node)
{
if (analyzerTree.Root == null)
analyzerTree.Root = new SharpTreeNode();
analyzerTree.Root = new AnalyzerTreeNode { Language = sessionSettings.FilterSettings.Language };
if (analyzerPanel.Visibility != Visibility.Visible)
analyzerPanel.Visibility = Visibility.Visible;
if (!showAnalyzer.IsChecked)
showAnalyzer.IsChecked = true;
var newNode = new AnalyzedMethodTreeNode(method.MethodDefinition) {
Language = sessionSettings.FilterSettings.Language,
IsExpanded = true
};
analyzerTree.Root.Children.Add(newNode);
node.IsExpanded = true;
analyzerTree.Root.Children.Add(node);
analyzerTree.SelectedItem = node;
analyzerTree.FocusNode(node);
}
#endregion
@ -412,7 +428,25 @@ namespace ICSharpCode.ILSpy @@ -412,7 +428,25 @@ namespace ICSharpCode.ILSpy
sessionSettings.ActiveTreeViewPath = GetPathForNode(treeView.SelectedItem as SharpTreeNode);
sessionSettings.WindowBounds = this.RestoreBounds;
sessionSettings.SplitterPosition = leftColumn.Width.Value / (leftColumn.Width.Value + rightColumn.Width.Value);
if (showAnalyzer.IsChecked)
sessionSettings.AnalyzerSplitterPosition = analyzerRow.Height.Value / (analyzerRow.Height.Value + textViewRow.Height.Value);
sessionSettings.Save();
}
void ShowAnalyzer_Checked(object sender, RoutedEventArgs e)
{
analyzerRow.MinHeight = 100;
if (sessionSettings.AnalyzerSplitterPosition > 0 && sessionSettings.AnalyzerSplitterPosition < 1) {
textViewRow.Height = new GridLength(1 - sessionSettings.AnalyzerSplitterPosition, GridUnitType.Star);
analyzerRow.Height = new GridLength(sessionSettings.AnalyzerSplitterPosition, GridUnitType.Star);
}
}
void ShowAnalyzer_Unchecked(object sender, RoutedEventArgs e)
{
sessionSettings.AnalyzerSplitterPosition = analyzerRow.Height.Value / (analyzerRow.Height.Value + textViewRow.Height.Value);
analyzerRow.MinHeight = 0;
analyzerRow.Height = new GridLength(0);
}
}
}

3
ILSpy/SessionSettings.cs

@ -49,6 +49,7 @@ namespace ICSharpCode.ILSpy @@ -49,6 +49,7 @@ namespace ICSharpCode.ILSpy
this.WindowState = FromString((string)doc.Element("WindowState"), WindowState.Normal);
this.WindowBounds = FromString((string)doc.Element("WindowBounds"), new Rect(10, 10, 750, 550));
this.SplitterPosition = FromString((string)doc.Element("SplitterPosition"), 0.4);
this.AnalyzerSplitterPosition = FromString((string)doc.Element("AnalyzerSplitterPosition"), 0.3);
}
public event PropertyChangedEventHandler PropertyChanged;
@ -68,6 +69,7 @@ namespace ICSharpCode.ILSpy @@ -68,6 +69,7 @@ namespace ICSharpCode.ILSpy
public WindowState WindowState = WindowState.Normal;
public Rect WindowBounds;
public double SplitterPosition;
public double AnalyzerSplitterPosition;
public void Save()
{
@ -82,6 +84,7 @@ namespace ICSharpCode.ILSpy @@ -82,6 +84,7 @@ namespace ICSharpCode.ILSpy
doc.Add(new XElement("WindowState", ToString(this.WindowState)));
doc.Add(new XElement("WindowBounds", ToString(this.WindowBounds)));
doc.Add(new XElement("SplitterPosition", ToString(this.SplitterPosition)));
doc.Add(new XElement("AnalyzerSplitterPosition", ToString(this.AnalyzerSplitterPosition)));
ILSpySettings.SaveSettings(doc);
}

15
ILSpy/TextView/DecompilerTextView.cs

@ -359,20 +359,7 @@ namespace ICSharpCode.ILSpy.TextView @@ -359,20 +359,7 @@ namespace ICSharpCode.ILSpy.TextView
return;
}
}
var assemblyListTreeNode = mainWindow.AssemblyListTreeNode;
if (reference is TypeReference) {
mainWindow.SelectNode(assemblyListTreeNode.FindTypeNode(((TypeReference)reference).Resolve()));
} else if (reference is MethodReference) {
mainWindow.SelectNode(assemblyListTreeNode.FindMethodNode(((MethodReference)reference).Resolve()));
} else if (reference is FieldReference) {
mainWindow.SelectNode(assemblyListTreeNode.FindFieldNode(((FieldReference)reference).Resolve()));
} else if (reference is PropertyReference) {
mainWindow.SelectNode(assemblyListTreeNode.FindPropertyNode(((PropertyReference)reference).Resolve()));
} else if (reference is EventReference) {
mainWindow.SelectNode(assemblyListTreeNode.FindEventNode(((EventReference)reference).Resolve()));
} else if (reference is AssemblyDefinition) {
mainWindow.SelectNode(assemblyListTreeNode.FindAssemblyNode((AssemblyDefinition)reference));
}
mainWindow.JumpToReference(reference);
}
#endregion

6
ILSpy/TreeNodes/Analyzer/AnalyzedMethodTreeNode.cs

@ -41,6 +41,12 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -41,6 +41,12 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
get { return MethodTreeNode.GetText(analyzedMethod, Language); }
}
public override void ActivateItem(System.Windows.RoutedEventArgs e)
{
e.Handled = true;
MainWindow.Instance.JumpToReference(analyzedMethod);
}
protected override void LoadChildren()
{
this.Children.Add(new AnalyzedMethodUsedByTreeNode(analyzedMethod));

57
ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs

@ -18,15 +18,17 @@ @@ -18,15 +18,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ICSharpCode.NRefactory.Utils;
using ICSharpCode.TreeView;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
class AnalyzedMethodUsedByTreeNode : ILSpyTreeNode
class AnalyzedMethodUsedByTreeNode : AnalyzerTreeNode
{
MethodDefinition analyzedMethod;
ThreadingSupport threading;
@ -54,39 +56,46 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -54,39 +56,46 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
threading.LoadChildren(this, FetchChildren);
}
IEnumerable<ILSpyTreeNode> FetchChildren(CancellationToken ct)
protected override void OnCollapsing()
{
if (threading.IsRunning) {
this.LazyLoading = true;
threading.Cancel();
this.Children.Clear();
}
}
IEnumerable<SharpTreeNode> FetchChildren(CancellationToken ct)
{
return FindReferences(MainWindow.Instance.AssemblyList.GetAssemblies(), ct);
}
IEnumerable<ILSpyTreeNode> FindReferences(LoadedAssembly[] assemblies, CancellationToken ct)
IEnumerable<SharpTreeNode> FindReferences(LoadedAssembly[] assemblies, CancellationToken ct)
{
foreach (LoadedAssembly asm in assemblies) {
// use parallelism only on the assembly level (avoid locks within Cecil)
return assemblies.AsParallel().WithCancellation(ct).SelectMany((LoadedAssembly asm) => FindReferences(asm, ct));
}
IEnumerable<SharpTreeNode> FindReferences(LoadedAssembly asm, CancellationToken ct)
{
foreach (TypeDefinition type in TreeTraversal.PreOrder(asm.AssemblyDefinition.MainModule.Types, t => t.NestedTypes)) {
ct.ThrowIfCancellationRequested();
foreach (TypeDefinition type in TreeTraversal.PreOrder(asm.AssemblyDefinition.MainModule.Types, t => t.NestedTypes)) {
foreach (MethodDefinition method in type.Methods) {
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;
}
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);
}
if (found)
yield return new AnalyzedMethodTreeNode(method);
}
}
}
public override void Decompile(Language language, ICSharpCode.Decompiler.ITextOutput output, DecompilationOptions options)
{
throw new NotImplementedException();
}
}
}

38
ILSpy/TreeNodes/Analyzer/AnalyzerTreeNode.cs

@ -17,12 +17,48 @@ @@ -17,12 +17,48 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Linq;
using ICSharpCode.TreeView;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
class AnalyzerTreeNode : SharpTreeNode
{
public Language Language { get; set; }
Language language;
public Language Language {
get { return language; }
set {
if (language != value) {
language = value;
foreach (var child in this.Children.OfType<AnalyzerTreeNode>())
child.Language = value;
}
}
}
public override bool CanDelete()
{
return Parent != null && Parent.IsRoot;
}
public override void DeleteCore()
{
Parent.Children.Remove(this);
}
public override void Delete()
{
DeleteCore();
}
protected override void OnChildrenChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null) {
foreach (AnalyzerTreeNode a in e.NewItems.OfType<AnalyzerTreeNode>())
a.Language = this.Language;
}
base.OnChildrenChanged(e);
}
}
}

3
ILSpy/TreeNodes/MethodTreeNode.cs

@ -21,6 +21,7 @@ using System.Text; @@ -21,6 +21,7 @@ using System.Text;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy.TreeNodes.Analyzer;
using Mono.Cecil;
namespace ICSharpCode.ILSpy.TreeNodes
@ -99,7 +100,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -99,7 +100,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
{
ContextMenu menu = new ContextMenu();
MenuItem item = new MenuItem() { Header = "Analyze", Icon = new Image() { Source = Images.Search } };
item.Click += delegate { MainWindow.Instance.Analyze(this); };
item.Click += delegate { MainWindow.Instance.AddToAnalyzer(new AnalyzedMethodTreeNode(method)); };
menu.Items.Add(item);

78
ILSpy/TreeNodes/ThreadingSupport.cs

@ -3,10 +3,12 @@ @@ -3,10 +3,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;
using ICSharpCode.Decompiler;
using ICSharpCode.TreeView;
namespace ICSharpCode.ILSpy.TreeNodes
{
@ -15,27 +17,39 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -15,27 +17,39 @@ namespace ICSharpCode.ILSpy.TreeNodes
/// </summary>
class ThreadingSupport
{
Task<List<ILSpyTreeNode>> loadChildrenTask;
Task<List<SharpTreeNode>> loadChildrenTask;
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
public bool IsRunning {
get { return !loadChildrenTask.IsCompleted; }
}
public void Cancel()
{
cancellationTokenSource.Cancel();
loadChildrenTask = null;
cancellationTokenSource = new CancellationTokenSource();
}
/// <summary>
///
/// Starts loading the children of the specified node.
/// </summary>
public void LoadChildren(ILSpyTreeNode node, Func<CancellationToken, IEnumerable<ILSpyTreeNode>> fetchChildren)
public void LoadChildren(SharpTreeNode node, Func<CancellationToken, IEnumerable<SharpTreeNode>> fetchChildren)
{
node.Children.Add(new LoadingTreeNode());
CancellationToken ct = CancellationToken.None;
CancellationToken ct = cancellationTokenSource.Token;
var fetchChildrenEnumerable = fetchChildren(ct);
Task<List<ILSpyTreeNode>> thisTask = null;
thisTask = new Task<List<ILSpyTreeNode>>(
Task<List<SharpTreeNode>> thisTask = null;
thisTask = new Task<List<SharpTreeNode>>(
delegate {
List<ILSpyTreeNode> result = new List<ILSpyTreeNode>();
foreach (ILSpyTreeNode child in fetchChildrenEnumerable) {
List<SharpTreeNode> result = new List<SharpTreeNode>();
foreach (SharpTreeNode child in fetchChildrenEnumerable) {
ct.ThrowIfCancellationRequested();
result.Add(child);
App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action<ILSpyTreeNode>(
delegate (ILSpyTreeNode newChild) {
App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action<SharpTreeNode>(
delegate (SharpTreeNode newChild) {
// don't access "child" here the
// background thread might already be running the next loop iteration
if (loadChildrenTask == thisTask) {
@ -43,16 +57,27 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -43,16 +57,27 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
}), child);
}
return result;
}, ct);
loadChildrenTask = thisTask;
thisTask.Start();
thisTask.ContinueWith(
delegate (Task continuation) {
App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(
delegate {
if (loadChildrenTask == thisTask) {
node.Children.RemoveAt(node.Children.Count - 1); // remove 'Loading...'
}
if (continuation.Exception != null) { // observe exception even when task isn't current
if (loadChildrenTask == thisTask) {
foreach (Exception ex in continuation.Exception.InnerExceptions) {
node.Children.Add(new ErrorTreeNode(ex.ToString()));
}
}
}
}));
return result;
}, ct);
loadChildrenTask = thisTask;
thisTask.Start();
});
// Give the task a bit time to complete before we return to WPF - this keeps "Loading..."
// from showing up for very short waits.
thisTask.Wait(TimeSpan.FromMilliseconds(200));
@ -66,7 +91,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -66,7 +91,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
loadChildrenTask = this.loadChildrenTask;
}
if (loadChildrenTask != null) {
foreach (var child in loadChildrenTask.Result) {
foreach (ILSpyTreeNode child in loadChildrenTask.Result.Cast<ILSpyTreeNode>()) {
child.Decompile(language, output, options);
}
}
@ -87,5 +112,28 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -87,5 +112,28 @@ namespace ICSharpCode.ILSpy.TreeNodes
{
}
}
sealed class ErrorTreeNode : ILSpyTreeNode
{
string text;
public override object Text {
get { return text; }
}
public ErrorTreeNode(string text)
{
this.text = text;
}
public override FilterResult Filter(FilterSettings settings)
{
return FilterResult.Match;
}
public override void Decompile(Language language, ICSharpCode.Decompiler.ITextOutput output, DecompilationOptions options)
{
}
}
}
}

Loading…
Cancel
Save