mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
323 lines
11 KiB
323 lines
11 KiB
// 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.Collections.ObjectModel; |
|
using System.Collections.Specialized; |
|
using System.IO; |
|
using System.Linq; |
|
using System.Reflection.PortableExecutable; |
|
using System.Windows; |
|
using ICSharpCode.Decompiler; |
|
using ICSharpCode.Decompiler.Metadata; |
|
using ICSharpCode.Decompiler.TypeSystem; |
|
using ICSharpCode.TreeView; |
|
|
|
namespace ICSharpCode.ILSpy.TreeNodes |
|
{ |
|
/// <summary> |
|
/// Represents a list of assemblies. |
|
/// This is used as (invisible) root node of the tree view. |
|
/// </summary> |
|
sealed class AssemblyListTreeNode : ILSpyTreeNode |
|
{ |
|
readonly AssemblyList assemblyList; |
|
|
|
public AssemblyList AssemblyList |
|
{ |
|
get { return assemblyList; } |
|
} |
|
|
|
public AssemblyListTreeNode(AssemblyList assemblyList) |
|
{ |
|
this.assemblyList = assemblyList ?? throw new ArgumentNullException(nameof(assemblyList)); |
|
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; |
|
if (e.Data.GetDataPresent(AssemblyTreeNode.DataFormat)) |
|
return true; |
|
else if (e.Data.GetDataPresent(DataFormats.FileDrop)) |
|
return true; |
|
else { |
|
e.Effects = DragDropEffects.None; |
|
return false; |
|
} |
|
} |
|
|
|
public override void Drop(DragEventArgs e, int index) |
|
{ |
|
string[] files = e.Data.GetData(AssemblyTreeNode.DataFormat) as string[]; |
|
if (files == null) |
|
files = e.Data.GetData(DataFormats.FileDrop) as string[]; |
|
if (files != null) { |
|
lock (assemblyList.assemblies) { |
|
var assemblies = files |
|
.Where(file => file != null) |
|
.SelectMany(file => OpenAssembly(assemblyList, file)) |
|
.Where(asm => asm != null) |
|
.Distinct() |
|
.ToArray(); |
|
foreach (LoadedAssembly asm in assemblies) { |
|
int nodeIndex = assemblyList.assemblies.IndexOf(asm); |
|
if (nodeIndex < index) |
|
index--; |
|
assemblyList.assemblies.RemoveAt(nodeIndex); |
|
} |
|
Array.Reverse(assemblies); |
|
foreach (LoadedAssembly asm in assemblies) { |
|
assemblyList.assemblies.Insert(index, asm); |
|
} |
|
} |
|
} |
|
} |
|
|
|
private IEnumerable<LoadedAssembly> OpenAssembly(AssemblyList assemblyList, string file) |
|
{ |
|
if (file.EndsWith(".nupkg")) { |
|
LoadedNugetPackage package = new LoadedNugetPackage(file); |
|
var selectionDialog = new NugetPackageBrowserDialog(package); |
|
selectionDialog.Owner = Application.Current.MainWindow; |
|
if (selectionDialog.ShowDialog() != true) |
|
yield break; |
|
foreach (var entry in selectionDialog.SelectedItems) { |
|
var nugetAsm = assemblyList.OpenAssembly("nupkg://" + file + ";" + entry.Name, entry.Stream, true); |
|
if (nugetAsm != null) { |
|
yield return nugetAsm; |
|
} |
|
} |
|
yield break; |
|
} |
|
yield return assemblyList.OpenAssembly(file); |
|
} |
|
|
|
public Action<SharpTreeNode> Select = delegate { }; |
|
|
|
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) |
|
{ |
|
language.WriteCommentLine(output, "List: " + assemblyList.ListName); |
|
output.WriteLine(); |
|
foreach (AssemblyTreeNode asm in this.Children) { |
|
language.WriteCommentLine(output, new string('-', 60)); |
|
output.WriteLine(); |
|
asm.Decompile(language, output, options); |
|
} |
|
} |
|
|
|
#region Find*Node |
|
public ILSpyTreeNode FindResourceNode(Resource resource) |
|
{ |
|
if (resource == null || resource.IsNil) |
|
return null; |
|
foreach (AssemblyTreeNode node in this.Children) |
|
{ |
|
if (node.LoadedAssembly.IsLoaded) |
|
{ |
|
node.EnsureLazyChildren(); |
|
foreach (var item in node.Children.OfType<ResourceListTreeNode>()) |
|
{ |
|
var founded = item.Children.OfType<ResourceTreeNode>().Where(x => x.Resource == resource).FirstOrDefault(); |
|
if (founded != null) |
|
return founded; |
|
|
|
var foundedResEntry = item.Children.OfType<ResourceEntryNode>().Where(x => resource.Name.Equals(x.Text)).FirstOrDefault(); |
|
if (foundedResEntry != null) |
|
return foundedResEntry; |
|
} |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
public ILSpyTreeNode FindResourceNode(Resource resource, string name) |
|
{ |
|
var resourceNode = FindResourceNode(resource); |
|
if (resourceNode == null || name == null || name.Equals(resourceNode.Text)) |
|
return resourceNode; |
|
|
|
resourceNode.EnsureLazyChildren(); |
|
return resourceNode.Children.OfType<ILSpyTreeNode>().Where(x => name.Equals(x.Text)).FirstOrDefault() ?? resourceNode; |
|
} |
|
|
|
public AssemblyTreeNode FindAssemblyNode(IModule module) |
|
{ |
|
return FindAssemblyNode(module.PEFile); |
|
} |
|
|
|
public AssemblyTreeNode FindAssemblyNode(PEFile module) |
|
{ |
|
if (module == null) |
|
return null; |
|
App.Current.Dispatcher.VerifyAccess(); |
|
foreach (AssemblyTreeNode node in this.Children) { |
|
if (node.LoadedAssembly.IsLoaded && node.LoadedAssembly.GetPEFileOrNull() == module) |
|
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(ITypeDefinition def) |
|
{ |
|
if (def == null) |
|
return null; |
|
var declaringType = def.DeclaringTypeDefinition; |
|
if (declaringType != null) { |
|
TypeTreeNode decl = FindTypeNode(declaringType); |
|
if (decl != null) { |
|
decl.EnsureLazyChildren(); |
|
return decl.Children.OfType<TypeTreeNode>().FirstOrDefault(t => t.TypeDefinition.MetadataToken == def.MetadataToken && !t.IsHidden); |
|
} |
|
} else { |
|
AssemblyTreeNode asm = FindAssemblyNode(def.ParentModule); |
|
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 ILSpyTreeNode FindMethodNode(IMethod def) |
|
{ |
|
TypeTreeNode typeNode = FindTypeNode(def.DeclaringTypeDefinition); |
|
if (typeNode == null) |
|
return null; |
|
// method might be an accessor, must look for parent node |
|
ILSpyTreeNode parentNode = typeNode; |
|
MethodTreeNode methodNode; |
|
parentNode.EnsureLazyChildren(); |
|
switch (def.AccessorOwner) { |
|
case IProperty p: |
|
parentNode = parentNode.Children.OfType<PropertyTreeNode>().FirstOrDefault(m => m.PropertyDefinition.MetadataToken == p.MetadataToken && !m.IsHidden); |
|
if (parentNode == null) |
|
return null; |
|
parentNode.EnsureLazyChildren(); |
|
methodNode = parentNode.Children.OfType<MethodTreeNode>().FirstOrDefault(m => m.MethodDefinition.MetadataToken == def.MetadataToken && !m.IsHidden); |
|
if (methodNode == null || methodNode.IsHidden) |
|
return parentNode; |
|
return methodNode; |
|
case IEvent e: |
|
parentNode = parentNode.Children.OfType<EventTreeNode>().FirstOrDefault(m => m.EventDefinition.MetadataToken == e.MetadataToken && !m.IsHidden); |
|
if (parentNode == null) |
|
return null; |
|
parentNode.EnsureLazyChildren(); |
|
methodNode = parentNode.Children.OfType<MethodTreeNode>().FirstOrDefault(m => m.MethodDefinition.MetadataToken == def.MetadataToken && !m.IsHidden); |
|
if (methodNode == null || methodNode.IsHidden) |
|
return parentNode; |
|
return methodNode; |
|
default: |
|
methodNode = typeNode.Children.OfType<MethodTreeNode>().FirstOrDefault(m => m.MethodDefinition.MetadataToken == def.MetadataToken && !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(IField def) |
|
{ |
|
TypeTreeNode typeNode = FindTypeNode(def.DeclaringTypeDefinition); |
|
if (typeNode == null) |
|
return null; |
|
typeNode.EnsureLazyChildren(); |
|
return typeNode.Children.OfType<FieldTreeNode>().FirstOrDefault(m => m.FieldDefinition.MetadataToken == def.MetadataToken && !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(IProperty def) |
|
{ |
|
TypeTreeNode typeNode = FindTypeNode(def.DeclaringTypeDefinition); |
|
if (typeNode == null) |
|
return null; |
|
typeNode.EnsureLazyChildren(); |
|
return typeNode.Children.OfType<PropertyTreeNode>().FirstOrDefault(m => m.PropertyDefinition.MetadataToken == def.MetadataToken && !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(IEvent def) |
|
{ |
|
TypeTreeNode typeNode = FindTypeNode(def.DeclaringTypeDefinition); |
|
if (typeNode == null) |
|
return null; |
|
typeNode.EnsureLazyChildren(); |
|
return typeNode.Children.OfType<EventTreeNode>().FirstOrDefault(m => m.EventDefinition.MetadataToken == def.MetadataToken && !m.IsHidden); |
|
} |
|
#endregion |
|
} |
|
}
|
|
|