Browse Source

Stepping in IL debugger

pull/191/merge
Eusebiu Marcu 15 years ago
parent
commit
00d01f24ba
  1. 45
      Debugger/Debugger.Core/Breakpoint.cs
  2. 27
      Debugger/Debugger.Core/SourcecodeSegment.cs
  3. 30
      Debugger/ILSpy.Debugger/AvalonEdit/IconBarMargin.cs
  4. 53
      Debugger/ILSpy.Debugger/Bookmarks/CurrentLineBookmark.cs
  5. 8
      Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj
  6. 7
      Debugger/ILSpy.Debugger/Services/Debugger/DebuggerService.cs
  7. 24
      Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs
  8. 1
      Debugger/ILSpy.Debugger/UI/AttachToProcessWindow.xaml
  9. 22
      Debugger/ILSpy.Debugger/UI/AttachToProcessWindow.xaml.cs
  10. 60
      ICSharpCode.Decompiler/Disassembler/CodeMappings.cs
  11. 8
      ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs
  12. 14
      ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs
  13. 12
      ILSpy/Commands/RoutedUICommands.cs
  14. 4
      ILSpy/ILSpy.csproj
  15. BIN
      ILSpy/Images/ContinueDebugging.png
  16. BIN
      ILSpy/Images/StepInto.png
  17. BIN
      ILSpy/Images/StepOut.png
  18. BIN
      ILSpy/Images/StepOver.png
  19. 36
      ILSpy/MainWindow.xaml
  20. 63
      ILSpy/MainWindow.xaml.cs
  21. 12
      ILSpy/TextView/DecompilerTextView.cs
  22. 2
      ILSpy/TreeNodes/TypeTreeNode.cs

45
Debugger/Debugger.Core/Breakpoint.cs

@ -19,9 +19,9 @@ namespace Debugger @@ -19,9 +19,9 @@ namespace Debugger
int column;
bool enabled;
SourcecodeSegment originalLocation;
protected SourcecodeSegment originalLocation;
List<ICorDebugFunctionBreakpoint> corBreakpoints = new List<ICorDebugFunctionBreakpoint>();
protected List<ICorDebugFunctionBreakpoint> corBreakpoints = new List<ICorDebugFunctionBreakpoint>();
public event EventHandler<BreakpointEventArgs> Hit;
public event EventHandler<BreakpointEventArgs> Set;
@ -29,6 +29,7 @@ namespace Debugger @@ -29,6 +29,7 @@ namespace Debugger
[Debugger.Tests.Ignore]
public NDebugger Debugger {
get { return debugger; }
protected set { debugger = value; }
}
public string FileName {
@ -46,6 +47,7 @@ namespace Debugger @@ -46,6 +47,7 @@ namespace Debugger
public int Column {
get { return column; }
protected set { column = value; }
}
public bool Enabled {
@ -94,6 +96,8 @@ namespace Debugger @@ -94,6 +96,8 @@ namespace Debugger
this.corBreakpoints.Add(corBreakpoint);
}
public Breakpoint() { }
public Breakpoint(NDebugger debugger, string fileName, byte[] checkSum, int line, int column, bool enabled)
{
this.debugger = debugger;
@ -138,7 +142,7 @@ namespace Debugger @@ -138,7 +142,7 @@ namespace Debugger
corBreakpoints.Clear();
}
internal bool SetBreakpoint(Module module)
internal virtual bool SetBreakpoint(Module module)
{
if (this.fileName == null)
return false;
@ -165,6 +169,41 @@ namespace Debugger @@ -165,6 +169,41 @@ namespace Debugger
}
}
public class ILBreakpoint : Breakpoint
{
public ILBreakpoint(NDebugger debugger, int line, uint metadataToken, int ilOffset, bool enabled)
{
this.Debugger = debugger;
this.Line = line;
this.MetadataToken = metadataToken;
this.ILOffset = ilOffset;
this.Enabled = enabled;
}
public uint MetadataToken { get; private set; }
public int ILOffset { get; private set; }
internal override bool SetBreakpoint(Module module)
{
SourcecodeSegment segment = SourcecodeSegment.CreateForIL(module, this.Line, (int)MetadataToken, ILOffset);
try {
ICorDebugFunctionBreakpoint corBreakpoint = segment.CorFunction.GetILCode().CreateBreakpoint((uint)segment.ILStart);
corBreakpoint.Activate(Enabled ? 1 : 0);
corBreakpoints.Add(corBreakpoint);
OnSet(new BreakpointEventArgs(this));
return true;
}
catch (COMException) {
return false;
}
}
}
[Serializable]
public class BreakpointEventArgs : DebuggerEventArgs
{

27
Debugger/Debugger.Core/SourcecodeSegment.cs

@ -16,6 +16,7 @@ namespace Debugger @@ -16,6 +16,7 @@ namespace Debugger
Module module;
string filename;
string typename;
byte[] checkSum;
int startLine;
int startColumn;
@ -35,6 +36,10 @@ namespace Debugger @@ -35,6 +36,10 @@ namespace Debugger
get { return filename; }
}
public string Typename {
get { return typename; }
}
public byte[] CheckSum {
get { return checkSum; }
}
@ -336,7 +341,27 @@ namespace Debugger @@ -336,7 +341,27 @@ namespace Debugger
public override string ToString()
{
return string.Format("{0}:{1},{2}-{3},{4}", Path.GetFileName(this.Filename), this.startLine, this.startColumn, this.endLine, this.endColumn);
return string.Format("{0}:{1},{2}-{3},{4}",
Path.GetFileName(this.Filename ?? string.Empty),
this.startLine, this.startColumn, this.endLine, this.endColumn);
}
public static SourcecodeSegment CreateForIL(Module module, int line, int metadataToken, int iLOffset)
{
SourcecodeSegment segment = new SourcecodeSegment();
segment.module = module;
segment.typename = null;
segment.checkSum = null;
segment.startLine = line;
segment.startColumn = 0;
segment.endLine = line;
segment.endColumn = 0;
segment.corFunction = module.CorModule.GetFunctionFromToken((uint)metadataToken);
segment.ilStart = iLOffset;
segment.ilEnd = iLOffset;
segment.stepRanges = null;
return segment;
}
}
}

30
Debugger/ILSpy.Debugger/AvalonEdit/IconBarMargin.cs

@ -27,31 +27,23 @@ using ICSharpCode.AvalonEdit.Rendering; @@ -27,31 +27,23 @@ using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.AvalonEdit.Utils;
using ILSpy.Debugger.Bookmarks;
using ILSpy.Debugger.Services;
using Mono.Cecil;
namespace ILSpy.Debugger.AvalonEdit
{
public class IconBarMargin : AbstractMargin, IDisposable
{
static string currentTypeName;
static TypeDefinition currentTypeName;
public static string CurrentTypeName {
get {
return currentTypeName; }
public static TypeDefinition CurrentType {
get { return currentTypeName; }
set { currentTypeName = value; }
}
#region OnTextViewChanged
/// <inheritdoc/>
protected override void OnTextViewChanged(TextView oldTextView, TextView newTextView)
{
InvalidateVisual();
}
public virtual void Dispose()
{
this.TextView = null; // detach from TextView (will also detach from manager)
}
#endregion
/// <inheritdoc/>
protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
@ -80,7 +72,7 @@ namespace ILSpy.Debugger.AvalonEdit @@ -80,7 +72,7 @@ namespace ILSpy.Debugger.AvalonEdit
// create a dictionary line number => first bookmark
Dictionary<int, BookmarkBase> bookmarkDict = new Dictionary<int, BookmarkBase>();
foreach (var bm in BookmarkManager.Bookmarks) {
if (bm.TypeName != IconBarMargin.CurrentTypeName)
if (IconBarMargin.CurrentType == null || bm.TypeName != IconBarMargin.CurrentType.FullName)
continue;
int line = bm.LineNumber;
@ -139,7 +131,9 @@ namespace ILSpy.Debugger.AvalonEdit @@ -139,7 +131,9 @@ namespace ILSpy.Debugger.AvalonEdit
{
BookmarkBase result = null;
foreach (BookmarkBase bm in BookmarkManager.Bookmarks) {
if (bm.LineNumber == line && bm.TypeName == IconBarMargin.CurrentTypeName) {
if (bm.LineNumber == line &&
IconBarMargin.CurrentType != null &&
bm.TypeName == IconBarMargin.CurrentType.FullName) {
if (result == null || bm.ZOrder > result.ZOrder)
result = bm;
}
@ -225,17 +219,17 @@ namespace ILSpy.Debugger.AvalonEdit @@ -225,17 +219,17 @@ namespace ILSpy.Debugger.AvalonEdit
IBookmark bm = GetBookmarkFromLine(line);
if (bm != null) {
bm.MouseUp(e);
if (!string.IsNullOrEmpty(IconBarMargin.CurrentTypeName)) {
DebuggerService.ToggleBreakpointAt(IconBarMargin.CurrentTypeName, line);
if (IconBarMargin.CurrentType != null) {
DebuggerService.ToggleBreakpointAt(IconBarMargin.CurrentType.FullName, line);
}
InvalidateVisual();
if (e.Handled)
return;
}
if (e.ChangedButton == MouseButton.Left && TextView != null) {
if (!string.IsNullOrEmpty(IconBarMargin.CurrentTypeName)) {
if (IconBarMargin.CurrentType != null) {
// no bookmark on the line: create a new breakpoint
DebuggerService.ToggleBreakpointAt(IconBarMargin.CurrentTypeName, line);
DebuggerService.ToggleBreakpointAt(IconBarMargin.CurrentType.FullName, line);
}
}
InvalidateVisual();

53
Debugger/ILSpy.Debugger/Bookmarks/CurrentLineBookmark.cs

@ -17,40 +17,19 @@ namespace ILSpy.Debugger.Bookmarks @@ -17,40 +17,19 @@ namespace ILSpy.Debugger.Bookmarks
static int endLine;
static int endColumn;
// public static void SetPosition(IViewContent viewContent, int makerStartLine, int makerStartColumn, int makerEndLine, int makerEndColumn)
// {
// ITextEditorProvider tecp = viewContent as ITextEditorProvider;
// if (tecp != null)
// SetPosition(tecp.TextEditor.FileName, tecp.TextEditor.Document, makerStartLine, makerStartColumn, makerEndLine, makerEndColumn);
// else
// Remove();
// }
//
// public static void SetPosition(FileName fileName, IDocument document, int makerStartLine, int makerStartColumn, int makerEndLine, int makerEndColumn)
// {
// Remove();
//
// startLine = makerStartLine;
// startColumn = makerStartColumn;
// endLine = makerEndLine;
// endColumn = makerEndColumn;
//
// if (startLine < 1 || startLine > document.TotalNumberOfLines)
// return;
// if (endLine < 1 || endLine > document.TotalNumberOfLines) {
// endLine = startLine;
// endColumn = int.MaxValue;
// }
// if (startColumn < 1)
// startColumn = 1;
//
// IDocumentLine line = document.GetLine(startLine);
// if (endColumn < 1 || endColumn > line.Length)
// endColumn = line.Length;
// instance = new CurrentLineBookmark(fileName, new Location(startColumn, startLine));
// BookmarkManager.AddMark(instance);
// }
//
public static void SetPosition(string typeName, int makerStartLine, int makerStartColumn, int makerEndLine, int makerEndColumn)
{
Remove();
startLine = makerStartLine;
startColumn = makerStartColumn;
endLine = makerEndLine;
endColumn = makerEndColumn;
instance = new CurrentLineBookmark(typeName, new Location(startColumn, startLine));
BookmarkManager.AddMark(instance);
}
public static void Remove()
{
if (instance != null) {
@ -60,9 +39,7 @@ namespace ILSpy.Debugger.Bookmarks @@ -60,9 +39,7 @@ namespace ILSpy.Debugger.Bookmarks
}
public override bool CanToggle {
get {
return false;
}
get { return false; }
}
public override int ZOrder {
@ -79,7 +56,7 @@ namespace ILSpy.Debugger.Bookmarks @@ -79,7 +56,7 @@ namespace ILSpy.Debugger.Bookmarks
}
public override bool CanDragDrop {
get { return true; }
get { return false; }
}
public override void Drop(int lineNumber)

8
Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj

@ -108,6 +108,14 @@ @@ -108,6 +108,14 @@
<Project>{3A9AE6AA-BC07-4A2F-972C-581E3AE2F195}</Project>
<Name>NRefactory</Name>
</ProjectReference>
<ProjectReference Include="..\..\ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj">
<Project>{984CC812-9470-4A13-AFF9-CC44068D666C}</Project>
<Name>ICSharpCode.Decompiler</Name>
</ProjectReference>
<ProjectReference Include="..\..\Mono.Cecil\Mono.Cecil.csproj">
<Project>{D68133BD-1E63-496E-9EDE-4FBDBF77B486}</Project>
<Name>Mono.Cecil</Name>
</ProjectReference>
<ProjectReference Include="..\Debugger.Core\Debugger.Core.csproj">
<Project>{1D18D788-F7EE-4585-A23B-34DC8EC63CB8}</Project>
<Name>Debugger.Core</Name>

7
Debugger/ILSpy.Debugger/Services/Debugger/DebuggerService.cs

@ -182,12 +182,9 @@ namespace ILSpy.Debugger.Services @@ -182,12 +182,9 @@ namespace ILSpy.Debugger.Services
CurrentLineBookmark.Remove();
}
public static void JumpToCurrentLine(string SourceFullFilename, int StartLine, int StartColumn, int EndLine, int EndColumn)
public static void JumpToCurrentLine(string typeName, int startLine, int startColumn, int endLine, int endColumn)
{
// IViewContent viewContent = FileService.OpenFile(SourceFullFilename);
// if (viewContent is ITextEditorProvider)
// ((ITextEditorProvider)viewContent).TextEditor.JumpTo(StartLine, StartColumn);
// CurrentLineBookmark.SetPosition(viewContent, StartLine, StartColumn, EndLine, EndColumn);
CurrentLineBookmark.SetPosition(typeName, startLine, startColumn, endLine, endColumn);
}
#region Tool tips

24
Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs

@ -9,12 +9,14 @@ using System.Windows.Media; @@ -9,12 +9,14 @@ using System.Windows.Media;
using Debugger;
using Debugger.Interop.CorPublish;
using ICSharpCode.Decompiler.Disassembler;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.NRefactory.Visitors;
using ILSpy.Debugger.Bookmarks;
using ILSpy.Debugger.Models.TreeModel;
using ILSpy.Debugger.Services.Debugger;
using Mono.Cecil.Cil;
using CorDbg = Debugger;
using Process = Debugger.Process;
@ -486,7 +488,13 @@ namespace ILSpy.Debugger.Services @@ -486,7 +488,13 @@ namespace ILSpy.Debugger.Services
void AddBreakpoint(BreakpointBookmark bookmark)
{
Breakpoint breakpoint = debugger.Breakpoints.Add(bookmark.TypeName, null, bookmark.LineNumber, 0, bookmark.IsEnabled);
uint token;
ILCodeMapping map = CodeMappings.GetInstructionByTypeAndLine(bookmark.TypeName, bookmark.LineNumber, out token);
if (map != null) {
Breakpoint breakpoint = new ILBreakpoint(debugger, bookmark.LineNumber, token, map.ILInstruction.Offset , bookmark.IsEnabled);
debugger.Breakpoints.Add(breakpoint);
// Action setBookmarkColor = delegate {
// if (debugger.Processes.Count == 0) {
// bookmark.IsHealthy = true;
@ -574,6 +582,7 @@ namespace ILSpy.Debugger.Services @@ -574,6 +582,7 @@ namespace ILSpy.Debugger.Services
breakpoint.Hit += bp_debugger_BreakpointHit;
BookmarkManager.Removed += bp_bookmarkManager_Removed;
}
}
bool Evaluate(string code, string language)
{
@ -711,10 +720,15 @@ namespace ILSpy.Debugger.Services @@ -711,10 +720,15 @@ namespace ILSpy.Debugger.Services
public void JumpToCurrentLine()
{
DebuggerService.RemoveCurrentLineMarker();
if (debuggedProcess != null) {
SourcecodeSegment nextStatement = debuggedProcess.NextStatement;
if (nextStatement != null) {
DebuggerService.JumpToCurrentLine(nextStatement.Filename, nextStatement.StartLine, nextStatement.StartColumn, nextStatement.EndLine, nextStatement.EndColumn);
if (debuggedProcess != null && debuggedProcess.SelectedStackFrame != null) {
uint token = (uint)debuggedProcess.SelectedStackFrame.MethodInfo.MetadataToken;
int ilOffset = debuggedProcess.SelectedStackFrame.IP;
int line;
string typeName;
CodeMappings.GetSourceCodeFromMetadataTokenAndOffset(token, ilOffset, out typeName, out line);
if (typeName != null) {
DebuggerService.JumpToCurrentLine(typeName, line, 0, line, 0);
}
}
}

1
Debugger/ILSpy.Debugger/UI/AttachToProcessWindow.xaml

@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
Height="0.17*" />
</Grid.RowDefinitions>
<ListView
MouseDoubleClick="RunningProcesses_MouseDoubleClick"
x:Name="RunningProcesses">
<ListView.View>
<GridView>

22
Debugger/ILSpy.Debugger/UI/AttachToProcessWindow.xaml.cs

@ -85,12 +85,7 @@ namespace ILSpy.Debugger.UI @@ -85,12 +85,7 @@ namespace ILSpy.Debugger.UI
RunningProcesses.ItemsSource = list;
}
void OnLoaded(object sender, RoutedEventArgs e)
{
RefreshProcessList();
}
void AttachButton_Click(object sender, RoutedEventArgs e)
void Attach()
{
if (this.RunningProcesses.SelectedItem == null)
return;
@ -101,6 +96,16 @@ namespace ILSpy.Debugger.UI @@ -101,6 +96,16 @@ namespace ILSpy.Debugger.UI
this.DialogResult = true;
}
void OnLoaded(object sender, RoutedEventArgs e)
{
RefreshProcessList();
}
void AttachButton_Click(object sender, RoutedEventArgs e)
{
Attach();
}
void CancelButton_Click(object sender, RoutedEventArgs e)
{
this.Close();
@ -110,5 +115,10 @@ namespace ILSpy.Debugger.UI @@ -110,5 +115,10 @@ namespace ILSpy.Debugger.UI
{
RefreshProcessList();
}
void RunningProcesses_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
Attach();
}
}
}

60
ICSharpCode.Decompiler/Disassembler/CodeMappings.cs

@ -17,7 +17,7 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -17,7 +17,7 @@ namespace ICSharpCode.Decompiler.Disassembler
{
public string TypeName { get; set; }
public int MetadataToken { get; set; }
public uint MetadataToken { get; set; }
public List<ILCodeMapping> MethodCodeMappings { get; set; }
@ -63,4 +63,62 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -63,4 +63,62 @@ namespace ICSharpCode.Decompiler.Disassembler
return -1;
}
}
public static class CodeMappings
{
static Dictionary<string, List<MethodMapping>> ilCodeMappings = new Dictionary<string, List<MethodMapping>>();
/// <summary>
/// Stores the source codes mappings: IL &lt;-&gt; editor lines
/// </summary>
public static Dictionary<string, List<MethodMapping>> ILSourceCodeMappings {
get { return ilCodeMappings; }
set { ilCodeMappings = value; }
}
public static ILCodeMapping GetInstructionByTypeAndLine(string typeName, int lineNumber, out uint metadataToken)
{
if (!ilCodeMappings.ContainsKey(typeName)) {
metadataToken = 0;
return null;
}
if (lineNumber <= 0) {
metadataToken = 0;
return null;
}
var methodMappings = ilCodeMappings[typeName];
foreach (var maping in methodMappings) {
var ilMap = maping.MethodCodeMappings.Find(m => m.SourceCodeLine == lineNumber);
if (ilMap != null) {
metadataToken = maping.MetadataToken;
return ilMap;
}
}
metadataToken = 0;
return null;
}
public static void GetSourceCodeFromMetadataTokenAndOffset(uint token, int ilOffset, out string typeName, out int line)
{
typeName = null;
line = 0;
foreach (var typename in ilCodeMappings.Keys) {
var mapping = ilCodeMappings[typename].Find(m => m.MetadataToken == token);
if (mapping == null)
continue;
var ilCodeMapping = mapping.MethodCodeMappings.Find(cm => cm.ILInstruction.Offset == ilOffset);
if (ilCodeMapping == null)
continue;
typeName = typename;
line = ilCodeMapping.SourceCodeLine;
break;
}
}
}
}

8
ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs

@ -49,11 +49,11 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -49,11 +49,11 @@ namespace ICSharpCode.Decompiler.Disassembler
{
// create mappings
MethodMapping currentMethodMapping = null;
if (ReflectionDisassembler.ILSourceCodeMappings.ContainsKey(body.Method.DeclaringType.FullName)) {
var mapping = ReflectionDisassembler.ILSourceCodeMappings[body.Method.DeclaringType.FullName];
if (mapping.Find(map => map.MetadataToken == body.Method.MetadataToken.ToInt32()) == null) {
if (CodeMappings.ILSourceCodeMappings.ContainsKey(body.Method.DeclaringType.FullName)) {
var mapping = CodeMappings.ILSourceCodeMappings[body.Method.DeclaringType.FullName];
if (mapping.Find(map => (int)map.MetadataToken == body.Method.MetadataToken.ToInt32()) == null) {
currentMethodMapping = new MethodMapping() {
MetadataToken = body.Method.MetadataToken.ToInt32(),
MetadataToken = (uint)body.Method.MetadataToken.ToInt32(),
TypeName = body.Method.DeclaringType.FullName,
MethodCodeMappings = new List<ILCodeMapping>()
};

14
ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs

@ -36,16 +36,6 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -36,16 +36,6 @@ namespace ICSharpCode.Decompiler.Disassembler
bool isInType; // whether we are currently disassembling a whole type (-> defaultCollapsed for foldings)
MethodBodyDisassembler methodBodyDisassembler;
static Dictionary<string, List<MethodMapping>> ilCodeMappings = new Dictionary<string, List<MethodMapping>>();
/// <summary>
/// Stores the source codes mappings: IL &lt;-&gt; editor lines
/// </summary>
public static Dictionary<string, List<MethodMapping>> ILSourceCodeMappings {
get { return ilCodeMappings; }
set { ilCodeMappings = value; }
}
public ReflectionDisassembler(ITextOutput output, bool detectControlStructure, CancellationToken cancellationToken)
{
if (output == null)
@ -112,8 +102,8 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -112,8 +102,8 @@ namespace ICSharpCode.Decompiler.Disassembler
void DisassembleMethodInternal(MethodDefinition method)
{
// create mappings for types that were not disassebled
if (!ilCodeMappings.ContainsKey(method.DeclaringType.FullName)) {
ilCodeMappings.Add(method.DeclaringType.FullName, new List<MethodMapping>());
if (!CodeMappings.ILSourceCodeMappings.ContainsKey(method.DeclaringType.FullName)) {
CodeMappings.ILSourceCodeMappings.Add(method.DeclaringType.FullName, new List<MethodMapping>());
}
// .method public hidebysig specialname

12
ILSpy/Commands/RoutedUICommands.cs

@ -27,10 +27,22 @@ namespace ICSharpCode.ILSpy.Commands @@ -27,10 +27,22 @@ namespace ICSharpCode.ILSpy.Commands
{
AttachToProcess = new RoutedUICommand("Attach to running process...", "AttachToProcess", typeof(RoutedUICommands));
DetachFromProcess = new RoutedUICommand("Detach from process...", "DetachFromProcess", typeof(RoutedUICommands));
ContinueDebugging = new RoutedUICommand("Continue debugging", "ContinueDebugging", typeof(RoutedUICommands));
StepInto = new RoutedUICommand("Step into", "StepInto", typeof(RoutedUICommands));
StepOver = new RoutedUICommand("Step over", "StepOver", typeof(RoutedUICommands));
StepOut = new RoutedUICommand("Step out", "StepOut", typeof(RoutedUICommands));
}
public static RoutedUICommand AttachToProcess { get; private set; }
public static RoutedUICommand DetachFromProcess { get; private set; }
public static RoutedUICommand ContinueDebugging { get; private set; }
public static RoutedUICommand StepInto { get; private set; }
public static RoutedUICommand StepOver { get; private set; }
public static RoutedUICommand StepOut { get; private set; }
}
}

4
ILSpy/ILSpy.csproj

@ -115,6 +115,10 @@ @@ -115,6 +115,10 @@
<Resource Include="Images\Delete.png" />
<Resource Include="Images\bug.png" />
<None Include="app.config" />
<Resource Include="Images\ContinueDebugging.png" />
<Resource Include="Images\StepInto.png" />
<Resource Include="Images\StepOut.png" />
<Resource Include="Images\StepOver.png" />
<None Include="Properties\AssemblyInfo.template.cs" />
<Compile Include="Properties\WPFAssemblyInfo.cs" />
<Compile Include="MainWindow.xaml.cs">

BIN
ILSpy/Images/ContinueDebugging.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 B

BIN
ILSpy/Images/StepInto.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 B

BIN
ILSpy/Images/StepOut.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 B

BIN
ILSpy/Images/StepOver.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 B

36
ILSpy/MainWindow.xaml

@ -27,6 +27,18 @@ @@ -27,6 +27,18 @@
<CommandBinding
Command="routedCommands:RoutedUICommands.DetachFromProcess"
Executed="DetachFromProcessExecuted" />
<CommandBinding
Command="routedCommands:RoutedUICommands.ContinueDebugging"
Executed="ContinueDebuggingExecuted" />
<CommandBinding
Command="routedCommands:RoutedUICommands.StepInto"
Executed="StepIntoExecuted" />
<CommandBinding
Command="routedCommands:RoutedUICommands.StepOver"
Executed="StepOverExecuted" />
<CommandBinding
Command="routedCommands:RoutedUICommands.StepOut"
Executed="StepOutExecuted" />
<CommandBinding
Command="Open"
Executed="OpenCommandExecuted" />
@ -71,7 +83,29 @@ @@ -71,7 +83,29 @@
<Image Width="16" Height="16" Source="Images/bug.png" />
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="DetachMenuItem" IsEnabled="False" Header="Detach from running application" Command="routedCommands:RoutedUICommands.DetachFromProcess"/>
<MenuItem x:Name="ContinueDebuggingMenuItem" IsEnabled="False" Header="Continue debugging" InputGestureText="F5" Command="routedCommands:RoutedUICommands.ContinueDebugging">
<MenuItem.Icon>
<Image Width="16" Height="16" Source="Images/ContinueDebugging.png" />
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="StepIntoMenuItem" IsEnabled="False" Header="Step into" InputGestureText="F11" Command="routedCommands:RoutedUICommands.StepInto">
<MenuItem.Icon>
<Image Width="16" Height="16" Source="Images/StepInto.png" />
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="StepOverMenuItem" IsEnabled="False" Header="Step over" InputGestureText="F10" Command="routedCommands:RoutedUICommands.StepOver">
<MenuItem.Icon>
<Image Width="16" Height="16" Source="Images/StepOver.png" />
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="StepOutMenuItem" IsEnabled="False" Header="Step out" Command="routedCommands:RoutedUICommands.StepOut">
<MenuItem.Icon>
<Image Width="16" Height="16" Source="Images/StepOut.png" />
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="DetachMenuItem" IsEnabled="False"
Header="Detach from running application"
Command="routedCommands:RoutedUICommands.DetachFromProcess"/>
</MenuItem>
<MenuItem Header="_Help">
<MenuItem Header="_About" Click="AboutClick" />

63
ILSpy/MainWindow.xaml.cs

@ -322,6 +322,8 @@ namespace ICSharpCode.ILSpy @@ -322,6 +322,8 @@ namespace ICSharpCode.ILSpy
treeView.ScrollIntoView(lastNode);
}
#region Debugger commands
void RefreshCommandExecuted(object sender, ExecutedRoutedEventArgs e)
{
e.Handled = true;
@ -332,22 +334,83 @@ namespace ICSharpCode.ILSpy @@ -332,22 +334,83 @@ namespace ICSharpCode.ILSpy
void AttachToProcessExecuted(object sender, ExecutedRoutedEventArgs e)
{
if (!AttachToProcessWindow.Debugger.IsDebugging) {
var window = new AttachToProcessWindow();
window.Owner = this;
if (window.ShowDialog() == true)
{
AttachMenuItem.IsEnabled = AttachButton.IsEnabled = false;
ContinueDebuggingMenuItem.IsEnabled =
StepIntoMenuItem.IsEnabled =
StepOverMenuItem.IsEnabled =
StepOutMenuItem.IsEnabled =
DetachMenuItem.IsEnabled = true;
}
}
}
void DetachFromProcessExecuted(object sender, ExecutedRoutedEventArgs e)
{
if (AttachToProcessWindow.Debugger.IsDebugging){
AttachToProcessWindow.Debugger.Detach();
AttachMenuItem.IsEnabled = AttachButton.IsEnabled = true;
ContinueDebuggingMenuItem.IsEnabled =
StepIntoMenuItem.IsEnabled =
StepOverMenuItem.IsEnabled =
StepOutMenuItem.IsEnabled =
DetachMenuItem.IsEnabled = false;
}
}
void ContinueDebuggingExecuted(object sender, ExecutedRoutedEventArgs e)
{
if (AttachToProcessWindow.Debugger.IsDebugging)
AttachToProcessWindow.Debugger.Continue();
}
void StepIntoExecuted(object sender, ExecutedRoutedEventArgs e)
{
if (AttachToProcessWindow.Debugger.IsDebugging)
AttachToProcessWindow.Debugger.StepInto();
}
void StepOverExecuted(object sender, ExecutedRoutedEventArgs e)
{
if (AttachToProcessWindow.Debugger.IsDebugging)
AttachToProcessWindow.Debugger.StepOver();
}
void StepOutExecuted(object sender, ExecutedRoutedEventArgs e)
{
if (AttachToProcessWindow.Debugger.IsDebugging)
AttachToProcessWindow.Debugger.StepOut();
}
protected override void OnKeyUp(KeyEventArgs e)
{
switch (e.Key) {
case Key.F5:
ContinueDebuggingExecuted(null, null);
e.Handled = true;
break;
case Key.System:
StepOverExecuted(null, null);
e.Handled = true;
break;
case Key.F11:
StepIntoExecuted(null, null);
e.Handled = true;
break;
default:
// do nothing
break;
}
base.OnKeyUp(e);
}
#endregion
void ExitClick(object sender, RoutedEventArgs e)
{

12
ILSpy/TextView/DecompilerTextView.cs

@ -80,6 +80,16 @@ namespace ICSharpCode.ILSpy.TextView @@ -80,6 +80,16 @@ namespace ICSharpCode.ILSpy.TextView
// add margin
iconMargin = new IconBarMargin();
textEditor.TextArea.LeftMargins.Add(iconMargin);
textEditor.TextArea.TextView.VisualLinesChanged += (s, e) => iconMargin.InvalidateVisual();
BookmarkManager.Added += BookmarkManager_Added;
BookmarkManager.Removed += (s, e) => iconMargin.InvalidateVisual();
}
void BookmarkManager_Added(object sender, BookmarkEventArgs e)
{
if (e.Bookmark is CurrentLineBookmark) {
iconMargin.InvalidateVisual();
}
}
#endregion
@ -196,7 +206,7 @@ namespace ICSharpCode.ILSpy.TextView @@ -196,7 +206,7 @@ namespace ICSharpCode.ILSpy.TextView
/// </summary>
public void Decompile(ILSpy.Language language, IEnumerable<ILSpyTreeNodeBase> treeNodes, DecompilationOptions options)
{
IconBarMargin.CurrentTypeName = string.Empty;
IconBarMargin.CurrentType = null;
// Some actions like loading an assembly list cause several selection changes in the tree view,
// and each of those will start a decompilation action.
bool isDecompilationScheduled = this.nextDecompilationRun != null;

2
ILSpy/TreeNodes/TypeTreeNode.cs

@ -122,7 +122,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -122,7 +122,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
IconBarMargin.CurrentTypeName = type.FullName;
IconBarMargin.CurrentType = type;
language.DecompileType(type, output, options);
}

Loading…
Cancel
Save