Browse Source

Add decompiler functionality to ILSpy AddIn.

pull/16/head
Daniel Grunwald 14 years ago
parent
commit
48b5e0e52f
  1. 6
      src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyAddIn.addin
  2. 26
      src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyAddIn.csproj
  3. 20
      src/AddIns/DisplayBindings/ILSpyAddIn/LaunchILSpy/ILSpyController.cs
  4. 54
      src/AddIns/DisplayBindings/ILSpyAddIn/NavigateToDecompiledEntityService.cs
  5. 2
      src/AddIns/DisplayBindings/ILSpyAddIn/Properties/AssemblyInfo.cs
  6. 34
      src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/CodeView.cs
  7. 156
      src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/DecompiledViewContent.cs
  8. 14
      src/Main/Base/Project/Src/Editor/Commands/GoToDefinition.cs
  9. 4
      src/Main/Base/Project/Src/Gui/Pads/ClassBrowser/Nodes/ClassNode.cs
  10. 16
      src/Main/Base/Project/Src/Gui/Pads/ClassBrowser/Nodes/MemberNode.cs
  11. 37
      src/Main/Base/Project/Src/Services/NavigationService/NavigationService.cs
  12. 6
      src/Main/Base/Project/Src/Services/RefactoringService/GoToClassAction.cs
  13. 6
      src/Main/Base/Project/Src/Services/RefactoringService/GoToMemberAction.cs

6
src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyAddIn.addin

@ -12,7 +12,11 @@
<Import assembly = "ILSpyAddIn.dll"/> <Import assembly = "ILSpyAddIn.dll"/>
</Runtime> </Runtime>
<!-- Text editor context menu --> <Path name="/SharpDevelop/Services/NavigateToEntityService">
<Class id="ILSpy" class="ICSharpCode.ILSpyAddIn.NavigateToDecompiledEntityService"/>
</Path>
<!-- Text editor context menu: Launch ILSpy command -->
<Path name = "/SharpDevelop/ViewContent/DefaultTextEditor/ClassMemberContextMenu"> <Path name = "/SharpDevelop/ViewContent/DefaultTextEditor/ClassMemberContextMenu">
<MenuItem id="ILSpy" icon="ILSpy" type="Item" label="${res:ILSpyAddIn.OpenILSpyCommand}" class="ICSharpCode.ILSpyAddIn.TextEditorContextMenuCommand"/> <MenuItem id="ILSpy" icon="ILSpy" type="Item" label="${res:ILSpyAddIn.OpenILSpyCommand}" class="ICSharpCode.ILSpyAddIn.TextEditorContextMenuCommand"/>

26
src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyAddIn.csproj

@ -40,17 +40,30 @@
</PropertyGroup> </PropertyGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
<ItemGroup> <ItemGroup>
<Reference Include="PresentationCore">
<RequiredTargetFramework>3.0</RequiredTargetFramework>
</Reference>
<Reference Include="PresentationFramework">
<RequiredTargetFramework>3.0</RequiredTargetFramework>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core"> <Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework> <RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference> </Reference>
<Reference Include="System.Drawing" /> <Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" /> <Reference Include="System.Windows.Forms" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase">
<RequiredTargetFramework>3.0</RequiredTargetFramework>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\..\..\Main\GlobalAssemblyInfo.cs"> <Compile Include="..\..\..\Main\GlobalAssemblyInfo.cs">
<Link>Properties\GlobalAssemblyInfo.cs</Link> <Link>Properties\GlobalAssemblyInfo.cs</Link>
</Compile> </Compile>
<Compile Include="NavigateToDecompiledEntityService.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="LaunchILSpy\ILSpyController.cs" /> <Compile Include="LaunchILSpy\ILSpyController.cs" />
<Compile Include="LaunchILSpy\SetILSpyPathDialog.cs" /> <Compile Include="LaunchILSpy\SetILSpyPathDialog.cs" />
@ -58,6 +71,8 @@
<DependentUpon>SetILSpyPathDialog.cs</DependentUpon> <DependentUpon>SetILSpyPathDialog.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="LaunchILSpy\TextEditorContextMenuCommand.cs" /> <Compile Include="LaunchILSpy\TextEditorContextMenuCommand.cs" />
<Compile Include="ViewContent\CodeView.cs" />
<Compile Include="ViewContent\DecompiledViewContent.cs" />
<EmbeddedResource Include="LaunchILSpy\SetILSpyPathDialog.resx"> <EmbeddedResource Include="LaunchILSpy\SetILSpyPathDialog.resx">
<DependentUpon>SetILSpyPathDialog.cs</DependentUpon> <DependentUpon>SetILSpyPathDialog.cs</DependentUpon>
</EmbeddedResource> </EmbeddedResource>
@ -66,6 +81,11 @@
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\Libraries\AvalonEdit\ICSharpCode.AvalonEdit\ICSharpCode.AvalonEdit.csproj">
<Project>{6C55B776-26D4-4DB3-A6AB-87E783B2F3D1}</Project>
<Name>ICSharpCode.AvalonEdit</Name>
<Private>False</Private>
</ProjectReference>
<ProjectReference Include="..\..\..\Libraries\ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj"> <ProjectReference Include="..\..\..\Libraries\ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj">
<Project>{984CC812-9470-4A13-AFF9-CC44068D666C}</Project> <Project>{984CC812-9470-4A13-AFF9-CC44068D666C}</Project>
<Name>ICSharpCode.Decompiler</Name> <Name>ICSharpCode.Decompiler</Name>
@ -94,8 +114,14 @@
<Name>ICSharpCode.SharpDevelop.Dom</Name> <Name>ICSharpCode.SharpDevelop.Dom</Name>
<Private>False</Private> <Private>False</Private>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\AvalonEdit.AddIn\AvalonEdit.AddIn.csproj">
<Project>{0162E499-42D0-409B-AA25-EED21F75336B}</Project>
<Name>AvalonEdit.AddIn</Name>
<Private>False</Private>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="LaunchILSpy" /> <Folder Include="LaunchILSpy" />
<Folder Include="ViewContent" />
</ItemGroup> </ItemGroup>
</Project> </Project>

20
src/AddIns/DisplayBindings/ILSpyAddIn/LaunchILSpy/ILSpyController.cs

@ -32,12 +32,7 @@ namespace ICSharpCode.ILSpyAddIn
ReflectionProjectContent rpc = pc as ReflectionProjectContent; ReflectionProjectContent rpc = pc as ReflectionProjectContent;
string assemblyLocation = null; string assemblyLocation = null;
if (rpc != null) { if (rpc != null) {
// prefer GAC assemblies over reference assemblies: assemblyLocation = GetAssemblyLocation(rpc);
assemblyLocation = FindAssemblyInNetGac(new DomAssemblyName(rpc.AssemblyFullName));
if (string.IsNullOrEmpty(assemblyLocation)) {
// use file only if assembly isn't in GAC:
assemblyLocation = rpc.AssemblyLocation;
}
} else { } else {
IProject project = pc.Project as IProject; IProject project = pc.Project as IProject;
if (project != null) { if (project != null) {
@ -59,6 +54,19 @@ namespace ICSharpCode.ILSpyAddIn
Process.Start(ilspyPath, commandLine); Process.Start(ilspyPath, commandLine);
} }
public static string GetAssemblyLocation(ReflectionProjectContent rpc)
{
if (rpc == null)
throw new ArgumentNullException("rpc");
// prefer GAC assemblies over reference assemblies:
string assemblyLocation = FindAssemblyInNetGac(new DomAssemblyName(rpc.AssemblyFullName));
if (string.IsNullOrEmpty(assemblyLocation)) {
// use file only if assembly isn't in GAC:
assemblyLocation = rpc.AssemblyLocation;
}
return assemblyLocation;
}
#region Find ILSpy #region Find ILSpy
internal const string ILSpyExePathPropertyName = "ILSpyAddIn.ILSpyExePath"; internal const string ILSpyExePathPropertyName = "ILSpyAddIn.ILSpyExePath";

54
src/AddIns/DisplayBindings/ILSpyAddIn/NavigateToDecompiledEntityService.cs

@ -0,0 +1,54 @@
// 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 ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Gui;
namespace ICSharpCode.ILSpyAddIn
{
public class NavigateToDecompiledEntityService : INavigateToEntityService
{
public bool NavigateTo(IEntity entity)
{
if (entity == null)
throw new ArgumentNullException("entity");
// Get the underlying entity for generic instance members
while ((entity is IMember) && ((IMember)entity).GenericMember != null)
entity = ((IMember)entity).GenericMember;
IClass declaringType = (entity as IClass) ?? entity.DeclaringType;
if (declaringType == null)
return false;
// get the top-level type
while (declaringType.DeclaringType != null)
declaringType = declaringType.DeclaringType;
ReflectionProjectContent rpc = entity.ProjectContent as ReflectionProjectContent;
if (rpc != null) {
string assemblyLocation = ILSpyController.GetAssemblyLocation(rpc);
if (!string.IsNullOrEmpty(assemblyLocation) && File.Exists(assemblyLocation)) {
NavigateTo(assemblyLocation, declaringType.FullyQualifiedName, ((AbstractEntity)entity).DocumentationTag);
return true;
}
}
return false;
}
public static void NavigateTo(string assemblyFile, string typeName, string entityTag)
{
foreach (var vc in WorkbenchSingleton.Workbench.ViewContentCollection.OfType<DecompiledViewContent>()) {
if (string.Equals(vc.AssemblyFile, assemblyFile, StringComparison.OrdinalIgnoreCase) && typeName == vc.FullTypeName) {
vc.WorkbenchWindow.SelectWindow();
vc.JumpToEntity(entityTag);
return;
}
}
WorkbenchSingleton.Workbench.ShowView(new DecompiledViewContent(assemblyFile, typeName, entityTag));
}
}
}

2
src/AddIns/DisplayBindings/ILSpyAddIn/Properties/AssemblyInfo.cs

@ -20,5 +20,3 @@ using System.Reflection;
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: System.CLSCompliant(true)]

34
src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/CodeView.cs

@ -0,0 +1,34 @@
// 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.Windows.Controls;
using ICSharpCode.AvalonEdit.AddIn;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Highlighting;
namespace ICSharpCode.ILSpyAddIn.ViewContent
{
/// <summary>
/// Equivalent to AE.AddIn CodeEditor, but without editing capabilities.
/// </summary>
public class CodeView : Grid, IDisposable
{
readonly SharpDevelopTextEditor textEditor = new SharpDevelopTextEditor();
public CodeView()
{
this.Children.Add(textEditor);
textEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("C#");
}
public TextDocument Document {
get { return textEditor.Document; }
set { textEditor.Document = value; }
}
public void Dispose()
{
}
}
}

156
src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/DecompiledViewContent.cs

@ -0,0 +1,156 @@
// 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.Threading;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.Core;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Ast;
using ICSharpCode.ILSpyAddIn.ViewContent;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Gui;
using Mono.Cecil;
namespace ICSharpCode.ILSpyAddIn
{
/// <summary>
/// Hosts a decompiled type.
/// </summary>
public class DecompiledViewContent : AbstractViewContentWithoutFile
{
readonly string assemblyFile;
readonly string fullTypeName;
/// <summary>
/// Entity to jump to once decompilation has finished.
/// </summary>
string jumpToEntityTagWhenDecompilationFinished;
bool decompilationFinished;
readonly CodeView codeView = new CodeView();
readonly CancellationTokenSource cancellation = new CancellationTokenSource();
#region Constructor
public DecompiledViewContent(string assemblyFile, string fullTypeName, string entityTag)
{
this.assemblyFile = assemblyFile;
this.fullTypeName = fullTypeName;
this.jumpToEntityTagWhenDecompilationFinished = entityTag;
string shortTypeName = fullTypeName.Substring(fullTypeName.LastIndexOf('.') + 1);
this.TitleName = "[" + ReflectionHelper.SplitTypeParameterCountFromReflectionName(shortTypeName) + "]";
Thread thread = new Thread(DecompilationThread);
thread.Name = "Decompiler (" + shortTypeName + ")";
thread.Start();
}
#endregion
#region Properties
public string AssemblyFile {
get { return assemblyFile; }
}
public string FullTypeName {
get { return fullTypeName; }
}
public override object Control {
get { return codeView; }
}
#endregion
#region Dispose
public override void Dispose()
{
cancellation.Cancel();
codeView.Dispose();
base.Dispose();
}
#endregion
#region Load/Save
public override void Load()
{
// nothing to do...
}
public override void Save()
{
if (!decompilationFinished)
return;
// TODO: show Save As dialog to allow the user to save the decompiled file
}
#endregion
#region JumpToEntity
public void JumpToEntity(string entityTag)
{
if (!decompilationFinished) {
this.jumpToEntityTagWhenDecompilationFinished = entityTag;
return;
}
// TODO: implement this
}
#endregion
#region Decompilation
void DecompilationThread()
{
try {
StringWriter writer = new StringWriter();
RunDecompiler(assemblyFile, fullTypeName, new PlainTextOutput(writer), cancellation.Token);
if (!cancellation.IsCancellationRequested) {
WorkbenchSingleton.SafeThreadAsyncCall(OnDecompilationFinished, writer);
}
} catch (OperationCanceledException) {
// ignore cancellation
} catch (Exception ex) {
if (cancellation.IsCancellationRequested) {
MessageService.ShowException(ex);
return;
}
AnalyticsMonitorService.TrackException(ex);
StringWriter writer = new StringWriter();
writer.WriteLine("Exception while decompiling " + fullTypeName);
writer.WriteLine();
writer.WriteLine(ex.ToString());
WorkbenchSingleton.SafeThreadAsyncCall(OnDecompilationFinished, writer);
}
}
static void RunDecompiler(string assemblyFile, string fullTypeName, ITextOutput textOutput, CancellationToken cancellationToken)
{
ReaderParameters readerParameters = new ReaderParameters();
// Use new assembly resolver instance so that the AssemblyDefinitions can be garbage-collected
// once the code is decompiled.
readerParameters.AssemblyResolver = new DefaultAssemblyResolver();
ModuleDefinition module = ModuleDefinition.ReadModule(assemblyFile, readerParameters);
TypeDefinition typeDefinition = module.GetType(fullTypeName);
if (typeDefinition == null)
throw new InvalidOperationException("Could not find type");
DecompilerContext context = new DecompilerContext(module);
context.CancellationToken = cancellationToken;
AstBuilder astBuilder = new AstBuilder(context);
astBuilder.AddType(typeDefinition);
astBuilder.GenerateCode(textOutput);
}
void OnDecompilationFinished(StringWriter output)
{
if (cancellation.IsCancellationRequested)
return;
codeView.Document.Text = output.ToString();
codeView.Document.UndoStack.ClearAll();
this.decompilationFinished = true;
JumpToEntity(this.jumpToEntityTagWhenDecompilationFinished);
}
#endregion
}
}

14
src/Main/Base/Project/Src/Editor/Commands/GoToDefinition.cs

@ -15,11 +15,19 @@ namespace ICSharpCode.SharpDevelop.Editor.Commands
{ {
protected override void RunImpl(ITextEditor editor, int offset, ResolveResult symbol) protected override void RunImpl(ITextEditor editor, int offset, ResolveResult symbol)
{ {
if (symbol == null)
return;
FilePosition pos = symbol.GetDefinitionPosition(); FilePosition pos = symbol.GetDefinitionPosition();
if (pos.IsEmpty) { if (pos.IsEmpty) {
//new GoToDecompiledDefinition().Run(symbol); IEntity entity;
if (symbol is MemberResolveResult) {
entity = ((MemberResolveResult)symbol).ResolvedMember;
} else if (symbol is TypeResolveResult) {
entity = ((TypeResolveResult)symbol).ResolvedClass;
} else {
entity = null;
}
if (entity != null) {
NavigationService.NavigateTo(entity);
}
} else { } else {
try { try {
if (pos.Position.IsEmpty) if (pos.Position.IsEmpty)

4
src/Main/Base/Project/Src/Gui/Pads/ClassBrowser/Nodes/ClassNode.cs

@ -55,9 +55,7 @@ namespace ICSharpCode.SharpDevelop.Gui.ClassBrowser
public override void ActivateItem() public override void ActivateItem()
{ {
if (c.CompilationUnit != null) { NavigationService.NavigateTo(c);
FileService.JumpToFilePosition(c.CompilationUnit.FileName, c.Region.BeginLine, c.Region.BeginColumn);
}
} }
protected override void Initialize() protected override void Initialize()

16
src/Main/Base/Project/Src/Gui/Pads/ClassBrowser/Nodes/MemberNode.cs

@ -8,19 +8,9 @@ namespace ICSharpCode.SharpDevelop.Gui.ClassBrowser
{ {
public class MemberNode : ExtTreeNode public class MemberNode : ExtTreeNode
{ {
int line;
int column;
ModifierEnum modifiers; ModifierEnum modifiers;
IClass declaringType; IClass declaringType;
string FileName {
get {
if (declaringType == null || declaringType.CompilationUnit == null) {
return null;
}
return declaringType.CompilationUnit.FileName;
}
}
public override bool Visible { public override bool Visible {
get { get {
ClassBrowserFilter filter = ClassBrowserPad.Instance.Filter; ClassBrowserFilter filter = ClassBrowserPad.Instance.Filter;
@ -51,8 +41,6 @@ namespace ICSharpCode.SharpDevelop.Gui.ClassBrowser
this.ContextmenuAddinTreePath = "/SharpDevelop/Pads/ClassBrowser/MemberContextMenu"; this.ContextmenuAddinTreePath = "/SharpDevelop/Pads/ClassBrowser/MemberContextMenu";
declaringType = member.DeclaringType; declaringType = member.DeclaringType;
modifiers = member.Modifiers; modifiers = member.Modifiers;
line = member.Region.BeginLine;
column = member.Region.BeginColumn;
} }
public static string GetText(IMember member) public static string GetText(IMember member)
@ -123,9 +111,7 @@ namespace ICSharpCode.SharpDevelop.Gui.ClassBrowser
public override void ActivateItem() public override void ActivateItem()
{ {
if (FileName != null) { NavigationService.NavigateTo(member);
FileService.JumpToFilePosition(FileName, line, column);
}
} }
} }
} }

37
src/Main/Base/Project/Src/Services/NavigationService/NavigationService.cs

@ -445,5 +445,42 @@ namespace ICSharpCode.SharpDevelop
} }
} }
#endregion #endregion
#region Navigate To Entity
public static bool NavigateTo(Dom.IEntity entity)
{
if (entity == null)
throw new ArgumentNullException("entity");
var cu = entity.CompilationUnit;
Dom.DomRegion region;
if (entity is Dom.IClass)
region = ((Dom.IClass)entity).Region;
else if (entity is Dom.IMember)
region = ((Dom.IMember)entity).Region;
else
region = Dom.DomRegion.Empty;
if (cu == null || string.IsNullOrEmpty(cu.FileName) || region.IsEmpty) {
foreach (var item in AddInTree.BuildItems<INavigateToEntityService>("/SharpDevelop/Services/NavigateToEntityService", null, false)) {
if (item.NavigateTo(entity))
return true;
}
return false;
} else {
return FileService.JumpToFilePosition(cu.FileName, region.BeginLine, region.BeginColumn) != null;
}
}
#endregion
}
/// <summary>
/// Called by <see cref="NavigationService.NavigateTo"/> when the entity is not defined in source code.
/// </summary>
/// <remarks>
/// Loaded from addin tree path "/SharpDevelop/Services/NavigateToEntityService"
/// </remarks>
public interface INavigateToEntityService
{
bool NavigateTo(Dom.IEntity entity);
} }
} }

6
src/Main/Base/Project/Src/Services/RefactoringService/GoToClassAction.cs

@ -26,11 +26,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring
public void Execute() public void Execute()
{ {
var cu = this.Class.CompilationUnit; NavigationService.NavigateTo(this.Class);
var region = this.Class.Region;
if (cu == null || cu.FileName == null || region == null || region.IsEmpty)
return;
FileService.JumpToFilePosition(cu.FileName, region.BeginLine, region.BeginColumn);
} }
} }
} }

6
src/Main/Base/Project/Src/Services/RefactoringService/GoToMemberAction.cs

@ -26,11 +26,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring
public void Execute() public void Execute()
{ {
var cu = this.Member.CompilationUnit; NavigationService.NavigateTo(this.Member);
var region = this.Member.Region;
if (cu == null || cu.FileName == null || region == null || region.IsEmpty)
return;
FileService.JumpToFilePosition(cu.FileName, region.BeginLine, region.BeginColumn);
} }
} }
} }

Loading…
Cancel
Save