13 changed files with 341 additions and 40 deletions
@ -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)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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() |
||||||
|
{ |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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
|
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue