Browse Source

Look for derived types on a background thread.

pull/10/head
Daniel Grunwald 15 years ago
parent
commit
bf3f6cfb59
  1. 1
      ILSpy/ILSpy.csproj
  2. 46
      ILSpy/TreeNodes/DerivedTypesTreeNode.cs
  3. 105
      ILSpy/TreeNodes/ThreadedTreeNode.cs

1
ILSpy/ILSpy.csproj

@ -138,6 +138,7 @@ @@ -138,6 +138,7 @@
<Compile Include="TreeNodes\PropertyTreeNode.cs" />
<Compile Include="TreeNodes\ReferenceFolderTreeNode.cs" />
<Compile Include="TreeNodes\ResourceListTreeNode.cs" />
<Compile Include="TreeNodes\ThreadedTreeNode.cs" />
<Compile Include="TreeNodes\TypeTreeNode.cs" />
<EmbeddedResource Include="TextView\ILAsm-Mode.xshd" />
</ItemGroup>

46
ILSpy/TreeNodes/DerivedTypesTreeNode.cs

@ -2,9 +2,10 @@ @@ -2,9 +2,10 @@
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using ICSharpCode.Decompiler;
using ICSharpCode.NRefactory.Utils;
using Mono.Cecil;
@ -14,7 +15,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -14,7 +15,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
/// <summary>
/// Lists the super types of a class.
/// </summary>
sealed class DerivedTypesTreeNode : ILSpyTreeNode<DerivedTypesEntryNode>
sealed class DerivedTypesTreeNode : ThreadedTreeNode
{
readonly AssemblyList list;
readonly TypeDefinition type;
@ -23,7 +24,6 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -23,7 +24,6 @@ namespace ICSharpCode.ILSpy.TreeNodes
{
this.list = list;
this.type = type;
this.LazyLoading = true;
}
public override object Text {
@ -34,25 +34,25 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -34,25 +34,25 @@ namespace ICSharpCode.ILSpy.TreeNodes
get { return Images.SubTypes; }
}
protected override void LoadChildren()
protected override IEnumerable<ILSpyTreeNodeBase> FetchChildren(CancellationToken cancellationToken)
{
AddDerivedTypes(this.Children, type, list);
// FetchChildren() runs on the main thread; but the enumerator will be consumed on a background thread
var assemblies = list.Assemblies.Select(node => node.AssemblyDefinition).Where(asm => asm != null).ToArray();
return FindDerivedTypes(type, assemblies, cancellationToken);
}
internal static void AddDerivedTypes(ObservableCollection<DerivedTypesEntryNode> children, TypeDefinition type, AssemblyList list)
internal static IEnumerable<DerivedTypesEntryNode> FindDerivedTypes(TypeDefinition type, AssemblyDefinition[] assemblies, CancellationToken cancellationToken)
{
foreach (var asmNode in list.Assemblies) {
AssemblyDefinition asm = asmNode.AssemblyDefinition;
if (asm == null)
continue;
foreach (AssemblyDefinition asm in assemblies) {
foreach (TypeDefinition td in TreeTraversal.PreOrder(asm.MainModule.Types, t => t.NestedTypes)) {
cancellationToken.ThrowIfCancellationRequested();
if (type.IsInterface && td.HasInterfaces) {
foreach (TypeReference typeRef in td.Interfaces) {
if (IsSameType(typeRef, type))
children.Add(new DerivedTypesEntryNode(td, list));
yield return new DerivedTypesEntryNode(td, assemblies);
}
} else if (!type.IsInterface && td.BaseType != null && IsSameType(td.BaseType, type)) {
children.Add(new DerivedTypesEntryNode(td, list));
yield return new DerivedTypesEntryNode(td, assemblies);
}
}
}
@ -62,26 +62,17 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -62,26 +62,17 @@ namespace ICSharpCode.ILSpy.TreeNodes
{
return typeRef.FullName == type.FullName;
}
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
EnsureLazyChildren();
foreach (var child in this.Children) {
child.Decompile(language, output, options);
}
}
}
class DerivedTypesEntryNode : ILSpyTreeNode<DerivedTypesEntryNode>
class DerivedTypesEntryNode : ThreadedTreeNode
{
TypeDefinition def;
AssemblyList list;
AssemblyDefinition[] assemblies;
public DerivedTypesEntryNode(TypeDefinition def, AssemblyList list)
public DerivedTypesEntryNode(TypeDefinition def, AssemblyDefinition[] assemblies)
{
this.def = def;
this.list = list;
this.LazyLoading = true;
this.assemblies = assemblies;
}
public override bool ShowExpander {
@ -100,9 +91,10 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -100,9 +91,10 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
}
protected override void LoadChildren()
protected override IEnumerable<ILSpyTreeNodeBase> FetchChildren(CancellationToken ct)
{
DerivedTypesTreeNode.AddDerivedTypes(this.Children, def, list);
// FetchChildren() runs on the main thread; but the enumerator will be consumed on a background thread
return DerivedTypesTreeNode.FindDerivedTypes(def, assemblies, ct);
}
public override void ActivateItem(System.Windows.RoutedEventArgs e)

105
ILSpy/TreeNodes/ThreadedTreeNode.cs

@ -0,0 +1,105 @@ @@ -0,0 +1,105 @@
// 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.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;
using ICSharpCode.Decompiler;
namespace ICSharpCode.ILSpy.TreeNodes
{
/// <summary>
/// Node that is lazy-loaded and loads its children on a background thread.
/// </summary>
abstract class ThreadedTreeNode : ILSpyTreeNode<ILSpyTreeNodeBase>
{
Task<List<ILSpyTreeNodeBase>> loadChildrenTask;
public ThreadedTreeNode()
{
this.LazyLoading = true;
}
public void Invalidate()
{
this.LazyLoading = true;
this.Children.Clear();
loadChildrenTask = null;
}
/// <summary>
/// FetchChildren() runs on the main thread; but the enumerator is consumed on a background thread
/// </summary>
protected abstract IEnumerable<ILSpyTreeNodeBase> FetchChildren(CancellationToken ct);
protected override sealed void LoadChildren()
{
this.Children.Add(new LoadingTreeNode());
CancellationToken ct = CancellationToken.None;
var fetchChildrenEnumerable = FetchChildren(ct);
Task<List<ILSpyTreeNodeBase>> thisTask = null;
thisTask = new Task<List<ILSpyTreeNodeBase>>(
delegate {
List<ILSpyTreeNodeBase> result = new List<ILSpyTreeNodeBase>();
foreach (ILSpyTreeNodeBase child in fetchChildrenEnumerable) {
ct.ThrowIfCancellationRequested();
result.Add(child);
App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action<ILSpyTreeNodeBase>(
delegate (ILSpyTreeNodeBase newChild) {
// 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);
}
}), child);
}
App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(
delegate {
if (loadChildrenTask == thisTask) {
this.Children.RemoveAt(this.Children.Count - 1); // remove 'Loading...'
}
}));
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));
}
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
var loadChildrenTask = this.loadChildrenTask;
if (loadChildrenTask == null) {
App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnsureLazyChildren));
loadChildrenTask = this.loadChildrenTask;
}
if (loadChildrenTask != null) {
foreach (var child in loadChildrenTask.Result) {
child.Decompile(language, output, options);
}
}
}
sealed class LoadingTreeNode : ILSpyTreeNode<ILSpyTreeNodeBase>
{
public override object Text {
get { return "Loading..."; }
}
public override FilterResult Filter(FilterSettings settings)
{
return FilterResult.Match;
}
public override void Decompile(Language language, ICSharpCode.Decompiler.ITextOutput output, DecompilationOptions options)
{
}
}
}
}
Loading…
Cancel
Save