Browse Source

Split assembly loading logic out of AssemblyTreeNode.

pull/10/head
Daniel Grunwald 14 years ago
parent
commit
381df74fa6
  1. 130
      ILSpy/AssemblyList.cs
  2. 1
      ILSpy/ILSpy.csproj
  3. 147
      ILSpy/LoadedAssembly.cs
  4. 15
      ILSpy/MainWindow.xaml.cs
  5. 14
      ILSpy/TextView/DecompilerTextView.cs
  6. 168
      ILSpy/TreeNodes/AssemblyListTreeNode.cs
  7. 17
      ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs
  8. 166
      ILSpy/TreeNodes/AssemblyTreeNode.cs
  9. 2
      ILSpy/TreeNodes/BaseTypesTreeNode.cs
  10. 25
      SharpTreeView/SharpTreeNodeCollection.cs

130
ILSpy/AssemblyList.cs

@ -45,15 +45,10 @@ namespace ICSharpCode.ILSpy @@ -45,15 +45,10 @@ namespace ICSharpCode.ILSpy
/// Write accesses are allowed on the GUI thread only (but still need locking!)
/// </summary>
/// <remarks>
/// 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 <see cref="GetAssemblies()"/> method.
/// </remarks>
internal readonly ObservableCollection<AssemblyTreeNode> assemblies = new ObservableCollection<AssemblyTreeNode>();
/// <summary>
/// Dictionary for quickly finding types (used in hyperlink navigation)
/// </summary>
readonly ConcurrentDictionary<TypeDefinition, TypeTreeNode> typeDict = new ConcurrentDictionary<TypeDefinition, TypeTreeNode>();
internal readonly ObservableCollection<LoadedAssembly> assemblies = new ObservableCollection<LoadedAssembly>();
public AssemblyList(string listName)
{
@ -76,7 +71,7 @@ namespace ICSharpCode.ILSpy @@ -76,7 +71,7 @@ namespace ICSharpCode.ILSpy
/// <summary>
/// Gets the loaded assemblies. This method is thread-safe.
/// </summary>
public AssemblyTreeNode[] GetAssemblies()
public LoadedAssembly[] GetAssemblies()
{
lock (assemblies) {
return assemblies.ToArray();
@ -119,129 +114,24 @@ namespace ICSharpCode.ILSpy @@ -119,129 +114,24 @@ namespace ICSharpCode.ILSpy
}
}
/// <summary>
/// Registers a type node in the dictionary for quick type lookup.
/// </summary>
/// <remarks>This method is called by the assembly loading code (on a background thread)</remarks>
public void RegisterTypeNode(TypeTreeNode node)
{
// called on background loading thread, so we need to use a ConcurrentDictionary
typeDict[node.TypeDefinition] = node;
}
#region Find*Node
/// <summary>
/// Looks up the type node corresponding to the type definition.
/// Returns null if no matching node is found.
/// </summary>
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<TypeTreeNode>().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<AssemblyListTreeNode>().Any(n => n.AssemblyList == this))
return node;
}
}
return null;
}
/// <summary>
/// Looks up the method node corresponding to the method definition.
/// Returns null if no matching node is found.
/// </summary>
public MethodTreeNode FindMethodNode(MethodDefinition def)
{
if (def == null)
return null;
TypeTreeNode typeNode = FindTypeNode(def.DeclaringType);
typeNode.EnsureLazyChildren();
MethodTreeNode methodNode = typeNode.Children.OfType<MethodTreeNode>().FirstOrDefault(m => m.MethodDefinition == def && !m.IsHidden);
if (methodNode != null)
return methodNode;
foreach (var p in typeNode.Children.OfType<ILSpyTreeNode>()) {
if (p.IsHidden)
continue;
// method might be a child or a property or events
p.EnsureLazyChildren();
methodNode = p.Children.OfType<MethodTreeNode>().FirstOrDefault(m => m.MethodDefinition == def && !m.IsHidden);
if (methodNode != null)
return methodNode;
}
return null;
}
/// <summary>
/// Looks up the field node corresponding to the field definition.
/// Returns null if no matching node is found.
/// </summary>
public FieldTreeNode FindFieldNode(FieldDefinition def)
{
if (def == null)
return null;
TypeTreeNode typeNode = FindTypeNode(def.DeclaringType);
typeNode.EnsureLazyChildren();
return typeNode.Children.OfType<FieldTreeNode>().FirstOrDefault(m => m.FieldDefinition == def && !m.IsHidden);
}
/// <summary>
/// Looks up the property node corresponding to the property definition.
/// Returns null if no matching node is found.
/// </summary>
public PropertyTreeNode FindPropertyNode(PropertyDefinition def)
{
if (def == null)
return null;
TypeTreeNode typeNode = FindTypeNode(def.DeclaringType);
typeNode.EnsureLazyChildren();
return typeNode.Children.OfType<PropertyTreeNode>().FirstOrDefault(m => m.PropertyDefinition == def && !m.IsHidden);
}
/// <summary>
/// Looks up the event node corresponding to the event definition.
/// Returns null if no matching node is found.
/// </summary>
public EventTreeNode FindEventNode(EventDefinition def)
{
if (def == null)
return null;
TypeTreeNode typeNode = FindTypeNode(def.DeclaringType);
typeNode.EnsureLazyChildren();
return typeNode.Children.OfType<EventTreeNode>().FirstOrDefault(m => m.EventDefinition == def && !m.IsHidden);
}
#endregion
/// <summary>
/// Opens an assembly from disk.
/// Returns the existing assembly node if it is already loaded.
/// </summary>
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;
}
}
}

1
ILSpy/ILSpy.csproj

@ -93,6 +93,7 @@ @@ -93,6 +93,7 @@
<Compile Include="ISmartTextOutput.cs" />
<Compile Include="Language.cs" />
<Compile Include="Images\Images.cs" />
<Compile Include="LoadedAssembly.cs" />
<Compile Include="Mono.Cecil.Rocks\MethodBodyRocks.cs" />
<Compile Include="NavigationHistory.cs" />
<Compile Include="OpenFromGacDialog.xaml.cs">

147
ILSpy/LoadedAssembly.cs

@ -0,0 +1,147 @@ @@ -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
{
/// <summary>
/// Represents an assembly loaded into ILSpy.
/// </summary>
sealed class LoadedAssembly
{
readonly Task<AssemblyDefinition> 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<AssemblyDefinition>(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<string, LoadedAssembly>(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<Task<AssemblyDefinition>> onAssemblyLoaded, TaskScheduler taskScheduler)
{
return this.assemblyTask.ContinueWith(onAssemblyLoaded, taskScheduler);
}
public void WaitUntilLoaded()
{
assemblyTask.Wait();
}
}
}

15
ILSpy/MainWindow.xaml.cs

@ -155,8 +155,8 @@ namespace ICSharpCode.ILSpy @@ -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<AssemblyTreeNode>().Any(a => a.LoadedAssembly == asm));
}
void LoadInitialAssemblies()
@ -198,6 +198,10 @@ namespace ICSharpCode.ILSpy @@ -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 @@ -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)

14
ILSpy/TextView/DecompilerTextView.cs

@ -348,19 +348,19 @@ namespace ICSharpCode.ILSpy.TextView @@ -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

168
ILSpy/TreeNodes/AssemblyListTreeNode.cs

@ -17,11 +17,13 @@ @@ -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 @@ -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<LoadedAssembly> 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<LoadedAssembly>().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 @@ -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 @@ -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;
}
/// <summary>
/// Looks up the type node corresponding to the type definition.
/// Returns null if no matching node is found.
/// </summary>
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<TypeTreeNode>().FirstOrDefault(t => t.TypeDefinition == def && !t.IsHidden);
}
} else {
AssemblyTreeNode asm = FindAssemblyNode(def.Module.Assembly);
if (asm != null) {
return asm.FindTypeNode(def);
}
}
return null;
}
/// <summary>
/// Looks up the method node corresponding to the method definition.
/// Returns null if no matching node is found.
/// </summary>
public MethodTreeNode FindMethodNode(MethodDefinition def)
{
if (def == null)
return null;
TypeTreeNode typeNode = FindTypeNode(def.DeclaringType);
typeNode.EnsureLazyChildren();
MethodTreeNode methodNode = typeNode.Children.OfType<MethodTreeNode>().FirstOrDefault(m => m.MethodDefinition == def && !m.IsHidden);
if (methodNode != null)
return methodNode;
foreach (var p in typeNode.Children.OfType<ILSpyTreeNode>()) {
if (p.IsHidden)
continue;
// method might be a child or a property or events
p.EnsureLazyChildren();
methodNode = p.Children.OfType<MethodTreeNode>().FirstOrDefault(m => m.MethodDefinition == def && !m.IsHidden);
if (methodNode != null)
return methodNode;
}
return null;
}
/// <summary>
/// Looks up the field node corresponding to the field definition.
/// Returns null if no matching node is found.
/// </summary>
public FieldTreeNode FindFieldNode(FieldDefinition def)
{
if (def == null)
return null;
TypeTreeNode typeNode = FindTypeNode(def.DeclaringType);
typeNode.EnsureLazyChildren();
return typeNode.Children.OfType<FieldTreeNode>().FirstOrDefault(m => m.FieldDefinition == def && !m.IsHidden);
}
/// <summary>
/// Looks up the property node corresponding to the property definition.
/// Returns null if no matching node is found.
/// </summary>
public PropertyTreeNode FindPropertyNode(PropertyDefinition def)
{
if (def == null)
return null;
TypeTreeNode typeNode = FindTypeNode(def.DeclaringType);
typeNode.EnsureLazyChildren();
return typeNode.Children.OfType<PropertyTreeNode>().FirstOrDefault(m => m.PropertyDefinition == def && !m.IsHidden);
}
/// <summary>
/// Looks up the event node corresponding to the event definition.
/// Returns null if no matching node is found.
/// </summary>
public EventTreeNode FindEventNode(EventDefinition def)
{
if (def == null)
return null;
TypeTreeNode typeNode = FindTypeNode(def.DeclaringType);
typeNode.EnsureLazyChildren();
return typeNode.Children.OfType<EventTreeNode>().FirstOrDefault(m => m.EventDefinition == def && !m.IsHidden);
}
#endregion
}
}

17
ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs

@ -63,19 +63,22 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -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));
}
}
}
}

166
ILSpy/TreeNodes/AssemblyTreeNode.cs

@ -38,55 +38,38 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -38,55 +38,38 @@ namespace ICSharpCode.ILSpy.TreeNodes
/// </summary>
sealed class AssemblyTreeNode : ILSpyTreeNode
{
readonly AssemblyList assemblyList;
readonly string fileName;
string shortName;
readonly Task<AssemblyDefinition> assemblyTask;
readonly LoadedAssembly assembly;
readonly List<TypeTreeNode> classes = new List<TypeTreeNode>();
readonly Dictionary<string, NamespaceTreeNode> namespaces = new Dictionary<string, NamespaceTreeNode>();
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<AssemblyDefinition>(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 @@ -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<AssemblyDefinition> assemblyTask)
@ -120,45 +88,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -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 @@ -177,28 +107,32 @@ namespace ICSharpCode.ILSpy.TreeNodes
return menu;
}
Dictionary<TypeDefinition, TypeTreeNode> typeDict = new Dictionary<TypeDefinition, TypeTreeNode>();
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 @@ -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 @@ -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 @@ -238,41 +184,13 @@ namespace ICSharpCode.ILSpy.TreeNodes
public override IDataObject Copy(SharpTreeNode[] nodes)
{
DataObject dataObject = new DataObject();
dataObject.SetData(DataFormat, nodes.OfType<AssemblyTreeNode>().Select(n => n.fileName).ToArray());
dataObject.SetData(DataFormat, nodes.OfType<AssemblyTreeNode>().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<string, AssemblyTreeNode>(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 @@ -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);
}
}
}

2
ILSpy/TreeNodes/BaseTypesTreeNode.cs

@ -128,7 +128,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -128,7 +128,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
if (def != null) {
var assemblyListNode = node.Ancestors().OfType<AssemblyListTreeNode>().FirstOrDefault();
if (assemblyListNode != null) {
assemblyListNode.Select(assemblyListNode.AssemblyList.FindTypeNode(def));
assemblyListNode.Select(assemblyListNode.FindTypeNode(def));
return true;
}
}

25
SharpTreeView/SharpTreeNodeCollection.cs

@ -173,30 +173,5 @@ namespace ICSharpCode.TreeView @@ -173,30 +173,5 @@ namespace ICSharpCode.TreeView
{
return list.GetEnumerator();
}
public void BindToObservableCollection<T>(ObservableCollection<T> 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<SharpTreeNode>());
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");
}
};
}
}
}

Loading…
Cancel
Save