// 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.Collections.Generic;
using System.ComponentModel.Design;
using System.IO;
using System.Linq;
using System.Threading;
using ICSharpCode.Core;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Ast;
using ICSharpCode.ILSpyAddIn.LaunchILSpy;
using ICSharpCode.ILSpyAddIn.ViewContent;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Bookmarks;
using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Editor.AvalonEdit;
using ICSharpCode.SharpDevelop.Gui;
using Mono.Cecil;
namespace ICSharpCode.ILSpyAddIn
{
///
/// Hosts a decompiled type.
///
class DecompiledViewContent : AbstractViewContentWithoutFile
{
readonly FileName assemblyFile;
readonly string fullTypeName;
readonly FileName virtualFileName;
///
/// Entity to jump to once decompilation has finished.
///
string jumpToEntityIdStringWhenDecompilationFinished;
bool decompilationFinished;
readonly CodeView codeView;
readonly CancellationTokenSource cancellation = new CancellationTokenSource();
Dictionary memberLocations;
#region Constructor
public DecompiledViewContent(FileName assemblyFile, string fullTypeName, string entityTag)
{
this.virtualFileName = FileName.Create("ilspy://" + assemblyFile + "/" + fullTypeName);
this.codeView = new CodeView();
this.assemblyFile = assemblyFile;
this.fullTypeName = fullTypeName;
this.jumpToEntityIdStringWhenDecompilationFinished = entityTag;
string shortTypeName = fullTypeName.Substring(fullTypeName.LastIndexOf('.') + 1);
this.TitleName = "[" + ReflectionHelper.SplitTypeParameterCountFromReflectionName(shortTypeName) + "]";
Thread thread = new Thread(DecompilationThread);
thread.Name = "Decompiler (" + shortTypeName + ")";
thread.Start();
BookmarkManager.Removed += BookmarkManager_Removed;
BookmarkManager.Added += BookmarkManager_Added;
}
#endregion
#region Properties
public FileName AssemblyFile {
get { return assemblyFile; }
}
///
/// The reflection name of the top-level type displayed in this view content.
///
public string FullTypeName {
get { return fullTypeName; }
}
public override object Control {
get { return codeView; }
}
public override bool IsReadOnly {
get { return true; }
}
#endregion
#region Dispose
public override void Dispose()
{
cancellation.Cancel();
codeView.Dispose();
BookmarkManager.Added -= BookmarkManager_Added;
BookmarkManager.Removed -= BookmarkManager_Removed;
// DecompileInformation data;
// DebuggerDecompilerService.DebugInformation.TryRemove(decompiledType.MetadataToken.ToInt32(), out data);
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 entityIdString)
{
if (!decompilationFinished) {
this.jumpToEntityIdStringWhenDecompilationFinished = entityIdString;
return;
}
TextLocation location;
if (memberLocations != null && memberLocations.TryGetValue(entityIdString, out location))
codeView.JumpTo(location.Line, location.Column);
}
#endregion
#region Decompilation
void DecompilationThread()
{
try {
StringWriter writer = new StringWriter();
RunDecompiler(assemblyFile, fullTypeName, new DebuggerTextOutput(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;
}
SD.AnalyticsMonitor.TrackException(ex);
StringWriter writer = new StringWriter();
writer.WriteLine(string.Format("Exception while decompiling {0} ({1})", fullTypeName, assemblyFile));
writer.WriteLine();
writer.WriteLine(ex.ToString());
WorkbenchSingleton.SafeThreadAsyncCall(OnDecompilationFinished, writer);
}
}
void RunDecompiler(string assemblyFile, string fullTypeName, DebuggerTextOutput 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 ILSpyAssemblyResolver(Path.GetDirectoryName(assemblyFile));
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);
// save decompilation data
memberLocations = textOutput.MemberLocations;
}
void OnDecompilationFinished(StringWriter output)
{
if (cancellation.IsCancellationRequested)
return;
codeView.Document.Text = output.ToString();
codeView.Document.UndoStack.ClearAll();
this.decompilationFinished = true;
JumpToEntity(this.jumpToEntityIdStringWhenDecompilationFinished);
// update UI
//UpdateIconMargin();
// fire events
OnDecompilationFinished(EventArgs.Empty);
}
#endregion
#region Update UI
/*
void UpdateIconMargin()
{
codeView.IconBarManager.UpdateClassMemberBookmarks(
ParserService.ParseFile(tempFileName, new AvalonEditDocumentAdapter(codeView.Document, null)),
null);
// load bookmarks
foreach (SDBookmark bookmark in BookmarkManager.GetBookmarks(this.codeView.TextEditor.FileName)) {
bookmark.Document = this.codeView.TextEditor.Document;
codeView.IconBarManager.Bookmarks.Add(bookmark);
}
}
public void UpdateDebuggingUI()
{
if (!DebuggerService.IsDebuggerStarted)
return;
if (decompiledType == null || decompiledType.MetadataToken == null)
return;
int typeToken = decompiledType.MetadataToken.ToInt32();
if (!DebuggerDecompilerService.DebugInformation.ContainsKey(typeToken))
return;
var decompilerService = DebuggerDecompilerService.Instance;
if (decompilerService == null || decompilerService.DebugStepInformation == null)
return;
// get debugging information
DecompileInformation debugInformation = (DecompileInformation)DebuggerDecompilerService.DebugInformation[typeToken];
int methodToken = decompilerService.DebugStepInformation.Item1;
int ilOffset = decompilerService.DebugStepInformation.Item2;
int line;
MemberReference member;
if (debugInformation.CodeMappings == null || !debugInformation.CodeMappings.ContainsKey(methodToken))
return;
debugInformation.CodeMappings[methodToken].GetInstructionByTokenAndOffset(methodToken, ilOffset, out member, out line);
// if the codemappings are not built
if (line <= 0) {
DebuggerService.CurrentDebugger.StepOver();
return;
}
// jump to line - scoll and unfold
this.UpdateCurrentLineBookmark(line);
this.JumpToLineNumber(line);
}
void UpdateCurrentLineBookmark(int lineNumber)
{
if (lineNumber <= 0)
return;
CurrentLineBookmark.SetPosition(uri, codeView.Document, lineNumber, 0, lineNumber, 0);
var currentLineBookmark = BookmarkManager.Bookmarks.OfType().FirstOrDefault();
if (currentLineBookmark != null) {
// update bookmark & marker
codeView.IconBarManager.Bookmarks.Add(currentLineBookmark);
currentLineBookmark.Document = this.codeView.TextEditor.Document;
}
}*/
#endregion
#region Bookmarks
void BookmarkManager_Removed(object sender, BookmarkEventArgs e)
{
var mark = e.Bookmark;
if (mark != null && codeView.IconBarManager.Bookmarks.Contains(mark)) {
codeView.IconBarManager.Bookmarks.Remove(mark);
mark.Document = null;
}
}
void BookmarkManager_Added(object sender, BookmarkEventArgs e)
{
var mark = e.Bookmark;
if (mark != null && mark is BreakpointBookmark && mark.FileName == virtualFileName) {
codeView.IconBarManager.Bookmarks.Add(mark);
mark.Document = this.codeView.Document;
}
}
#endregion
#region Events
public event EventHandler DecompilationFinished;
protected virtual void OnDecompilationFinished(EventArgs e)
{
if (DecompilationFinished != null) {
DecompilationFinished(this, e);
}
}
#endregion
}
}