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
/// Write accesses are allowed on the GUI thread only (but still need locking!) /// Write accesses are allowed on the GUI thread only (but still need locking!)
/// </summary> /// </summary>
/// <remarks> /// <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. /// thread-safe <see cref="GetAssemblies()"/> method.
/// </remarks> /// </remarks>
internal readonly ObservableCollection<AssemblyTreeNode> assemblies = new ObservableCollection<AssemblyTreeNode>(); internal readonly ObservableCollection<LoadedAssembly> assemblies = new ObservableCollection<LoadedAssembly>();
/// <summary>
/// Dictionary for quickly finding types (used in hyperlink navigation)
/// </summary>
readonly ConcurrentDictionary<TypeDefinition, TypeTreeNode> typeDict = new ConcurrentDictionary<TypeDefinition, TypeTreeNode>();
public AssemblyList(string listName) public AssemblyList(string listName)
{ {
@ -76,7 +71,7 @@ namespace ICSharpCode.ILSpy
/// <summary> /// <summary>
/// Gets the loaded assemblies. This method is thread-safe. /// Gets the loaded assemblies. This method is thread-safe.
/// </summary> /// </summary>
public AssemblyTreeNode[] GetAssemblies() public LoadedAssembly[] GetAssemblies()
{ {
lock (assemblies) { lock (assemblies) {
return assemblies.ToArray(); return assemblies.ToArray();
@ -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> /// <summary>
/// Opens an assembly from disk. /// Opens an assembly from disk.
/// Returns the existing assembly node if it is already loaded. /// Returns the existing assembly node if it is already loaded.
/// </summary> /// </summary>
public AssemblyTreeNode OpenAssembly(string file) public LoadedAssembly OpenAssembly(string file)
{ {
App.Current.Dispatcher.VerifyAccess(); App.Current.Dispatcher.VerifyAccess();
file = Path.GetFullPath(file); file = Path.GetFullPath(file);
foreach (AssemblyTreeNode node in this.assemblies) { foreach (LoadedAssembly asm in this.assemblies) {
if (file.Equals(node.FileName, StringComparison.OrdinalIgnoreCase)) if (file.Equals(asm.FileName, StringComparison.OrdinalIgnoreCase))
return node; return asm;
} }
var newNode = new AssemblyTreeNode(file, this); var newAsm = new LoadedAssembly(this, file);
this.assemblies.Add(newNode); this.assemblies.Add(newAsm);
return newNode; return newAsm;
} }
} }
} }

1
ILSpy/ILSpy.csproj

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

147
ILSpy/LoadedAssembly.cs

@ -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
void assemblyList_Assemblies_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) void assemblyList_Assemblies_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{ {
if (e.OldItems != null) if (e.OldItems != null)
foreach (AssemblyTreeNode node in e.OldItems) foreach (LoadedAssembly asm in e.OldItems)
history.RemoveAll(n => n.AncestorsAndSelf().Contains(node)); history.RemoveAll(n => n.AncestorsAndSelf().OfType<AssemblyTreeNode>().Any(a => a.LoadedAssembly == asm));
} }
void LoadInitialAssemblies() void LoadInitialAssemblies()
@ -198,6 +198,10 @@ namespace ICSharpCode.ILSpy
get { return assemblyList; } get { return assemblyList; }
} }
internal AssemblyListTreeNode AssemblyListTreeNode {
get { return assemblyListTreeNode; }
}
#region Node Selection #region Node Selection
internal void SelectNode(SharpTreeNode obj, bool recordNavigationInHistory = true) internal void SelectNode(SharpTreeNode obj, bool recordNavigationInHistory = true)
{ {
@ -330,8 +334,11 @@ namespace ICSharpCode.ILSpy
foreach (string file in fileNames) { foreach (string file in fileNames) {
var asm = assemblyList.OpenAssembly(file); var asm = assemblyList.OpenAssembly(file);
if (asm != null) { if (asm != null) {
treeView.SelectedItems.Add(asm); var node = assemblyListTreeNode.FindAssemblyNode(asm);
lastNode = asm; if (node != null) {
treeView.SelectedItems.Add(node);
lastNode = node;
}
} }
} }
if (lastNode != null) if (lastNode != null)

14
ILSpy/TextView/DecompilerTextView.cs

@ -348,19 +348,19 @@ namespace ICSharpCode.ILSpy.TextView
return; return;
} }
} }
var assemblyList = mainWindow.AssemblyList; var assemblyListTreeNode = mainWindow.AssemblyListTreeNode;
if (reference is TypeReference) { if (reference is TypeReference) {
mainWindow.SelectNode(assemblyList.FindTypeNode(((TypeReference)reference).Resolve())); mainWindow.SelectNode(assemblyListTreeNode.FindTypeNode(((TypeReference)reference).Resolve()));
} else if (reference is MethodReference) { } else if (reference is MethodReference) {
mainWindow.SelectNode(assemblyList.FindMethodNode(((MethodReference)reference).Resolve())); mainWindow.SelectNode(assemblyListTreeNode.FindMethodNode(((MethodReference)reference).Resolve()));
} else if (reference is FieldReference) { } else if (reference is FieldReference) {
mainWindow.SelectNode(assemblyList.FindFieldNode(((FieldReference)reference).Resolve())); mainWindow.SelectNode(assemblyListTreeNode.FindFieldNode(((FieldReference)reference).Resolve()));
} else if (reference is PropertyReference) { } else if (reference is PropertyReference) {
mainWindow.SelectNode(assemblyList.FindPropertyNode(((PropertyReference)reference).Resolve())); mainWindow.SelectNode(assemblyListTreeNode.FindPropertyNode(((PropertyReference)reference).Resolve()));
} else if (reference is EventReference) { } else if (reference is EventReference) {
mainWindow.SelectNode(assemblyList.FindEventNode(((EventReference)reference).Resolve())); mainWindow.SelectNode(assemblyListTreeNode.FindEventNode(((EventReference)reference).Resolve()));
} else if (reference is AssemblyDefinition) { } else if (reference is AssemblyDefinition) {
mainWindow.SelectNode(assemblyList.GetAssemblies().FirstOrDefault(node => node.AssemblyDefinition == reference)); mainWindow.SelectNode(assemblyListTreeNode.FindAssemblyNode((AssemblyDefinition)reference));
} }
} }
#endregion #endregion

168
ILSpy/TreeNodes/AssemblyListTreeNode.cs

@ -17,11 +17,13 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq; using System.Linq;
using System.Windows; using System.Windows;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.TreeView; using ICSharpCode.TreeView;
using Mono.Cecil;
namespace ICSharpCode.ILSpy.TreeNodes namespace ICSharpCode.ILSpy.TreeNodes
{ {
@ -42,13 +44,38 @@ namespace ICSharpCode.ILSpy.TreeNodes
if (assemblyList == null) if (assemblyList == null)
throw new ArgumentNullException("assemblyList"); throw new ArgumentNullException("assemblyList");
this.assemblyList = assemblyList; this.assemblyList = assemblyList;
this.Children.BindToObservableCollection(assemblyList.assemblies); BindToObservableCollection(assemblyList.assemblies);
} }
public override object Text { public override object Text {
get { return assemblyList.ListName; } 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) public override bool CanDrop(DragEventArgs e, int index)
{ {
e.Effects = DragDropEffects.Move; e.Effects = DragDropEffects.Move;
@ -69,20 +96,20 @@ namespace ICSharpCode.ILSpy.TreeNodes
files = e.Data.GetData(DataFormats.FileDrop) as string[]; files = e.Data.GetData(DataFormats.FileDrop) as string[];
if (files != null) { if (files != null) {
lock (assemblyList.assemblies) { lock (assemblyList.assemblies) {
var nodes = (from file in files var assemblies = (from file in files
where file != null where file != null
select assemblyList.OpenAssembly(file) into node select assemblyList.OpenAssembly(file) into node
where node != null where node != null
select node).Distinct().ToList(); select node).Distinct().ToList();
foreach (AssemblyTreeNode node in nodes) { foreach (LoadedAssembly asm in assemblies) {
int nodeIndex = assemblyList.assemblies.IndexOf(node); int nodeIndex = assemblyList.assemblies.IndexOf(asm);
if (nodeIndex < index) if (nodeIndex < index)
index--; index--;
assemblyList.assemblies.RemoveAt(nodeIndex); assemblyList.assemblies.RemoveAt(nodeIndex);
} }
nodes.Reverse(); assemblies.Reverse();
foreach (AssemblyTreeNode node in nodes) { foreach (LoadedAssembly asm in assemblies) {
assemblyList.assemblies.Insert(index, node); assemblyList.assemblies.Insert(index, asm);
} }
} }
} }
@ -94,11 +121,126 @@ namespace ICSharpCode.ILSpy.TreeNodes
{ {
language.WriteCommentLine(output, "List: " + assemblyList.ListName); language.WriteCommentLine(output, "List: " + assemblyList.ListName);
output.WriteLine(); output.WriteLine();
foreach (AssemblyTreeNode asm in assemblyList.GetAssemblies()) { foreach (AssemblyTreeNode asm in this.Children) {
language.WriteCommentLine(output, new string('-', 60)); language.WriteCommentLine(output, new string('-', 60));
output.WriteLine(); output.WriteLine();
asm.Decompile(language, output, options); 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
{ {
var assemblyListNode = parentAssembly.Parent as AssemblyListTreeNode; var assemblyListNode = parentAssembly.Parent as AssemblyListTreeNode;
if (assemblyListNode != null) { if (assemblyListNode != null) {
assemblyListNode.Select(parentAssembly.LookupReferencedAssembly(r.FullName)); assemblyListNode.Select(assemblyListNode.FindAssemblyNode(parentAssembly.LoadedAssembly.LookupReferencedAssembly(r.FullName)));
e.Handled = true; e.Handled = true;
} }
} }
protected override void LoadChildren() protected override void LoadChildren()
{ {
var refNode = parentAssembly.LookupReferencedAssembly(r.FullName); var assemblyListNode = parentAssembly.Parent as AssemblyListTreeNode;
if (refNode != null) { if (assemblyListNode != null) {
AssemblyDefinition asm = refNode.AssemblyDefinition; var refNode = assemblyListNode.FindAssemblyNode(parentAssembly.LoadedAssembly.LookupReferencedAssembly(r.FullName));
if (asm != null) { if (refNode != null) {
foreach (var childRef in asm.MainModule.AssemblyReferences) AssemblyDefinition asm = refNode.LoadedAssembly.AssemblyDefinition;
this.Children.Add(new AssemblyReferenceTreeNode(childRef, refNode)); 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
/// </summary> /// </summary>
sealed class AssemblyTreeNode : ILSpyTreeNode sealed class AssemblyTreeNode : ILSpyTreeNode
{ {
readonly LoadedAssembly assembly;
readonly AssemblyList assemblyList;
readonly string fileName;
string shortName;
readonly Task<AssemblyDefinition> assemblyTask;
readonly List<TypeTreeNode> classes = new List<TypeTreeNode>(); readonly List<TypeTreeNode> classes = new List<TypeTreeNode>();
readonly Dictionary<string, NamespaceTreeNode> namespaces = new Dictionary<string, NamespaceTreeNode>(); readonly Dictionary<string, NamespaceTreeNode> namespaces = new Dictionary<string, NamespaceTreeNode>();
public AssemblyTreeNode(string fileName, AssemblyList assemblyList) public AssemblyTreeNode(LoadedAssembly assembly)
{ {
if (fileName == null) if (assembly == null)
throw new ArgumentNullException("fileName"); throw new ArgumentNullException("assembly");
this.fileName = fileName; this.assembly = assembly;
this.assemblyList = assemblyList;
this.assemblyTask = Task.Factory.StartNew<AssemblyDefinition>(LoadAssembly); // requires that this.fileName is set
this.shortName = Path.GetFileNameWithoutExtension(fileName);
assemblyTask.ContinueWith(OnAssemblyLoaded, TaskScheduler.FromCurrentSynchronizationContext()); assembly.ContinueWhenLoaded(OnAssemblyLoaded, TaskScheduler.FromCurrentSynchronizationContext());
this.LazyLoading = true; this.LazyLoading = true;
} }
public string FileName {
get { return fileName; }
}
public AssemblyList AssemblyList { public AssemblyList AssemblyList {
get { return assemblyList; } get { return assembly.AssemblyList; }
} }
public AssemblyDefinition AssemblyDefinition { public LoadedAssembly LoadedAssembly {
get { get { return assembly; }
try {
return assemblyTask.Result;
} catch {
return null;
}
}
} }
public override object Text { public override object Text {
get { return HighlightSearchMatch(shortName); } get { return HighlightSearchMatch(assembly.ShortName); }
} }
public override object Icon { public override object Icon {
get { get {
if (assemblyTask.IsCompleted) { if (assembly.IsLoaded) {
return assemblyTask.IsFaulted ? Images.AssemblyWarning : Images.Assembly; return assembly.HasLoadError ? Images.AssemblyWarning : Images.Assembly;
} else { } else {
return Images.AssemblyLoading; return Images.AssemblyLoading;
} }
@ -94,22 +77,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
} }
public override bool ShowExpander { public override bool ShowExpander {
get { return !assemblyTask.IsFaulted; } get { return !assembly.HasLoadError; }
}
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;
} }
void OnAssemblyLoaded(Task<AssemblyDefinition> assemblyTask) void OnAssemblyLoaded(Task<AssemblyDefinition> assemblyTask)
@ -120,45 +88,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
if (assemblyTask.IsFaulted) { if (assemblyTask.IsFaulted) {
RaisePropertyChanged("ShowExpander"); // cannot expand assemblies with load error RaisePropertyChanged("ShowExpander"); // cannot expand assemblies with load error
} else { } else {
AssemblyDefinition assembly = assemblyTask.Result; RaisePropertyChanged("Text"); // shortname might have changed
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;
} }
} }
@ -177,28 +107,32 @@ namespace ICSharpCode.ILSpy.TreeNodes
return menu; return menu;
} }
Dictionary<TypeDefinition, TypeTreeNode> typeDict = new Dictionary<TypeDefinition, TypeTreeNode>();
protected override void LoadChildren() protected override void LoadChildren()
{ {
try { AssemblyDefinition assemblyDefinition = assembly.AssemblyDefinition;
assemblyTask.Wait(); if (assemblyDefinition == null) {
} catch (AggregateException) {
// if we crashed on loading, then we don't have any children // if we crashed on loading, then we don't have any children
return; return;
} }
ModuleDefinition mainModule = assemblyTask.Result.MainModule; ModuleDefinition mainModule = assemblyDefinition.MainModule;
this.Children.Add(new ReferenceFolderTreeNode(mainModule, this)); this.Children.Add(new ReferenceFolderTreeNode(mainModule, this));
if (mainModule.HasResources) if (mainModule.HasResources)
this.Children.Add(new ResourceListTreeNode(mainModule)); this.Children.Add(new ResourceListTreeNode(mainModule));
foreach (NamespaceTreeNode ns in namespaces.Values) { foreach (NamespaceTreeNode ns in namespaces.Values) {
ns.Children.Clear(); ns.Children.Clear();
} }
foreach (TypeTreeNode type in classes) { foreach (TypeDefinition type in mainModule.Types.OrderBy(t => t.FullName)) {
NamespaceTreeNode ns; NamespaceTreeNode ns;
if (!namespaces.TryGetValue(type.Namespace, out ns)) { if (!namespaces.TryGetValue(type.Namespace, out ns)) {
ns = new NamespaceTreeNode(type.Namespace); ns = new NamespaceTreeNode(type.Namespace);
namespaces[type.Namespace] = ns; 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)) { foreach (NamespaceTreeNode ns in namespaces.Values.OrderBy(n => n.Name)) {
if (ns.Children.Count > 0) if (ns.Children.Count > 0)
@ -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) public override bool CanDrag(SharpTreeNode[] nodes)
{ {
return nodes.All(n => n is AssemblyTreeNode); return nodes.All(n => n is AssemblyTreeNode);
@ -228,8 +174,8 @@ namespace ICSharpCode.ILSpy.TreeNodes
public override void DeleteCore() public override void DeleteCore()
{ {
lock (assemblyList.assemblies) { lock (assembly.AssemblyList.assemblies) {
assemblyList.assemblies.Remove(this); assembly.AssemblyList.assemblies.Remove(assembly);
} }
} }
@ -238,41 +184,13 @@ namespace ICSharpCode.ILSpy.TreeNodes
public override IDataObject Copy(SharpTreeNode[] nodes) public override IDataObject Copy(SharpTreeNode[] nodes)
{ {
DataObject dataObject = new DataObject(); 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; 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) public override FilterResult Filter(FilterSettings settings)
{ {
if (settings.SearchTermMatches(shortName)) if (settings.SearchTermMatches(assembly.ShortName))
return FilterResult.Match; return FilterResult.Match;
else else
return FilterResult.Recurse; return FilterResult.Recurse;
@ -280,8 +198,8 @@ namespace ICSharpCode.ILSpy.TreeNodes
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) 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 assembly.WaitUntilLoaded(); // necessary so that load errors are passed on to the caller
language.DecompileAssembly(assemblyTask.Result, fileName, output, options); language.DecompileAssembly(assembly.AssemblyDefinition, assembly.FileName, output, options);
} }
} }
} }

2
ILSpy/TreeNodes/BaseTypesTreeNode.cs

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

25
SharpTreeView/SharpTreeNodeCollection.cs

@ -173,30 +173,5 @@ namespace ICSharpCode.TreeView
{ {
return list.GetEnumerator(); 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