diff --git a/Debugger/Debugger.Core/Breakpoint.cs b/Debugger/Debugger.Core/Breakpoint.cs index 4c18a81de..daad88595 100644 --- a/Debugger/Debugger.Core/Breakpoint.cs +++ b/Debugger/Debugger.Core/Breakpoint.cs @@ -19,9 +19,9 @@ namespace Debugger int column; bool enabled; - SourcecodeSegment originalLocation; + protected SourcecodeSegment originalLocation; - List corBreakpoints = new List(); + protected List corBreakpoints = new List(); public event EventHandler Hit; public event EventHandler Set; @@ -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 public int Column { get { return column; } + protected set { column = value; } } public bool Enabled { @@ -62,8 +64,8 @@ namespace Debugger get { return originalLocation; } } - public bool IsSet { - get { + public bool IsSet { + get { return corBreakpoints.Count > 0; } } @@ -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; @@ -104,7 +108,7 @@ namespace Debugger this.enabled = enabled; } - internal bool IsOwnerOf(ICorDebugBreakpoint breakpoint) + internal bool IsOwnerOf(ICorDebugBreakpoint breakpoint) { foreach(ICorDebugFunctionBreakpoint corFunBreakpoint in corBreakpoints) { if (((ICorDebugBreakpoint)corFunBreakpoint).Equals(breakpoint)) return true; @@ -116,18 +120,18 @@ namespace Debugger { foreach(ICorDebugFunctionBreakpoint corBreakpoint in corBreakpoints) { #if DEBUG - // Get repro - corBreakpoint.Activate(0); + // Get repro + corBreakpoint.Activate(0); #else - try { - corBreakpoint.Activate(0); - } catch(COMException e) { - // Sometimes happens, but we had not repro yet. - // 0x80131301: Process was terminated. - if ((uint)e.ErrorCode == 0x80131301) - continue; - throw; - } + try { + corBreakpoint.Activate(0); + } catch(COMException e) { + // Sometimes happens, but we had not repro yet. + // 0x80131301: Process was terminated. + if ((uint)e.ErrorCode == 0x80131301) + continue; + throw; + } #endif } corBreakpoints.Clear(); @@ -138,9 +142,9 @@ namespace Debugger corBreakpoints.Clear(); } - internal bool SetBreakpoint(Module module) + internal virtual bool SetBreakpoint(Module module) { - if (this.fileName == null) + if (this.fileName == null) return false; SourcecodeSegment segment = SourcecodeSegment.Resolve(module, FileName, CheckSum, Line, Column); @@ -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 { diff --git a/Debugger/Debugger.Core/SourcecodeSegment.cs b/Debugger/Debugger.Core/SourcecodeSegment.cs index d9150899e..da433bc88 100644 --- a/Debugger/Debugger.Core/SourcecodeSegment.cs +++ b/Debugger/Debugger.Core/SourcecodeSegment.cs @@ -16,6 +16,7 @@ namespace Debugger Module module; string filename; + string typename; byte[] checkSum; int startLine; int startColumn; @@ -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 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; } } } diff --git a/Debugger/ILSpy.Debugger/AvalonEdit/IconBarMargin.cs b/Debugger/ILSpy.Debugger/AvalonEdit/IconBarMargin.cs index b9118ad9a..d790ad0ec 100644 --- a/Debugger/ILSpy.Debugger/AvalonEdit/IconBarMargin.cs +++ b/Debugger/ILSpy.Debugger/AvalonEdit/IconBarMargin.cs @@ -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 - /// - 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 /// protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters) @@ -80,7 +72,7 @@ namespace ILSpy.Debugger.AvalonEdit // create a dictionary line number => first bookmark Dictionary bookmarkDict = new Dictionary(); 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 { 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 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(); diff --git a/Debugger/ILSpy.Debugger/Bookmarks/CurrentLineBookmark.cs b/Debugger/ILSpy.Debugger/Bookmarks/CurrentLineBookmark.cs index ca67b5127..68a2666c1 100644 --- a/Debugger/ILSpy.Debugger/Bookmarks/CurrentLineBookmark.cs +++ b/Debugger/ILSpy.Debugger/Bookmarks/CurrentLineBookmark.cs @@ -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 } public override bool CanToggle { - get { - return false; - } + get { return false; } } public override int ZOrder { @@ -79,7 +56,7 @@ namespace ILSpy.Debugger.Bookmarks } public override bool CanDragDrop { - get { return true; } + get { return false; } } public override void Drop(int lineNumber) diff --git a/Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj b/Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj index 6e2a95d44..3ef006a59 100644 --- a/Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj +++ b/Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj @@ -108,6 +108,14 @@ {3A9AE6AA-BC07-4A2F-972C-581E3AE2F195} NRefactory + + {984CC812-9470-4A13-AFF9-CC44068D666C} + ICSharpCode.Decompiler + + + {D68133BD-1E63-496E-9EDE-4FBDBF77B486} + Mono.Cecil + {1D18D788-F7EE-4585-A23B-34DC8EC63CB8} Debugger.Core diff --git a/Debugger/ILSpy.Debugger/Services/Debugger/DebuggerService.cs b/Debugger/ILSpy.Debugger/Services/Debugger/DebuggerService.cs index 75906b6ce..544c2670b 100644 --- a/Debugger/ILSpy.Debugger/Services/Debugger/DebuggerService.cs +++ b/Debugger/ILSpy.Debugger/Services/Debugger/DebuggerService.cs @@ -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 diff --git a/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs b/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs index 3a3f85139..f19672409 100644 --- a/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs +++ b/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs @@ -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 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; @@ -515,64 +523,65 @@ namespace ILSpy.Debugger.Services // } // } // }; - - // event handlers on bookmark and breakpoint don't need deregistration - bookmark.IsEnabledChanged += delegate { - breakpoint.Enabled = bookmark.IsEnabled; - }; - breakpoint.Set += delegate { - //setBookmarkColor(); - }; - - //setBookmarkColor(); - - EventHandler> bp_debugger_ProcessStarted = (sender, e) => { - //setBookmarkColor(); - // User can change line number by inserting or deleting lines - breakpoint.Line = bookmark.LineNumber; - }; - EventHandler> bp_debugger_ProcessExited = (sender, e) => { + + // event handlers on bookmark and breakpoint don't need deregistration + bookmark.IsEnabledChanged += delegate { + breakpoint.Enabled = bookmark.IsEnabled; + }; + breakpoint.Set += delegate { + //setBookmarkColor(); + }; + //setBookmarkColor(); - }; - - EventHandler bp_debugger_BreakpointHit = - new EventHandler( - delegate(object sender, BreakpointEventArgs e) - { - //LoggingService.Debug(bookmark.Action + " " + bookmark.ScriptLanguage + " " + bookmark.Condition); - - switch (bookmark.Action) { - case BreakpointAction.Break: - break; - case BreakpointAction.Condition: + + EventHandler> bp_debugger_ProcessStarted = (sender, e) => { + //setBookmarkColor(); + // User can change line number by inserting or deleting lines + breakpoint.Line = bookmark.LineNumber; + }; + EventHandler> bp_debugger_ProcessExited = (sender, e) => { + //setBookmarkColor(); + }; + + EventHandler bp_debugger_BreakpointHit = + new EventHandler( + delegate(object sender, BreakpointEventArgs e) + { + //LoggingService.Debug(bookmark.Action + " " + bookmark.ScriptLanguage + " " + bookmark.Condition); + + switch (bookmark.Action) { + case BreakpointAction.Break: + break; + case BreakpointAction.Condition: // if (Evaluate(bookmark.Condition, bookmark.ScriptLanguage)) // DebuggerService.PrintDebugMessage(string.Format(StringParser.Parse("${res:MainWindow.Windows.Debug.Conditional.Breakpoints.BreakpointHitAtBecause}") + "\n", bookmark.LineNumber, bookmark.FileName, bookmark.Condition)); // else // this.debuggedProcess.AsyncContinue(); - break; - case BreakpointAction.Trace: - //DebuggerService.PrintDebugMessage(string.Format(StringParser.Parse("${res:MainWindow.Windows.Debug.Conditional.Breakpoints.BreakpointHitAt}") + "\n", bookmark.LineNumber, bookmark.FileName)); - break; - } - }); - - BookmarkEventHandler bp_bookmarkManager_Removed = null; - bp_bookmarkManager_Removed = (sender, e) => { - if (bookmark == e.Bookmark) { - debugger.Breakpoints.Remove(breakpoint); - - // unregister the events - debugger.Processes.Added -= bp_debugger_ProcessStarted; - debugger.Processes.Removed -= bp_debugger_ProcessExited; - breakpoint.Hit -= bp_debugger_BreakpointHit; - BookmarkManager.Removed -= bp_bookmarkManager_Removed; - } - }; - // register the events - debugger.Processes.Added += bp_debugger_ProcessStarted; - debugger.Processes.Removed += bp_debugger_ProcessExited; - breakpoint.Hit += bp_debugger_BreakpointHit; - BookmarkManager.Removed += bp_bookmarkManager_Removed; + break; + case BreakpointAction.Trace: + //DebuggerService.PrintDebugMessage(string.Format(StringParser.Parse("${res:MainWindow.Windows.Debug.Conditional.Breakpoints.BreakpointHitAt}") + "\n", bookmark.LineNumber, bookmark.FileName)); + break; + } + }); + + BookmarkEventHandler bp_bookmarkManager_Removed = null; + bp_bookmarkManager_Removed = (sender, e) => { + if (bookmark == e.Bookmark) { + debugger.Breakpoints.Remove(breakpoint); + + // unregister the events + debugger.Processes.Added -= bp_debugger_ProcessStarted; + debugger.Processes.Removed -= bp_debugger_ProcessExited; + breakpoint.Hit -= bp_debugger_BreakpointHit; + BookmarkManager.Removed -= bp_bookmarkManager_Removed; + } + }; + // register the events + debugger.Processes.Added += bp_debugger_ProcessStarted; + debugger.Processes.Removed += bp_debugger_ProcessExited; + breakpoint.Hit += bp_debugger_BreakpointHit; + BookmarkManager.Removed += bp_bookmarkManager_Removed; + } } bool Evaluate(string code, string language) @@ -652,7 +661,7 @@ namespace ILSpy.Debugger.Services OnIsProcessRunningChanged(EventArgs.Empty); //using(new PrintTimes("Jump to current line")) { - JumpToCurrentLine(); + JumpToCurrentLine(); //} // TODO update tooltip /*if (currentTooltipRow != null && currentTooltipRow.IsShown) { @@ -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); } } } diff --git a/Debugger/ILSpy.Debugger/UI/AttachToProcessWindow.xaml b/Debugger/ILSpy.Debugger/UI/AttachToProcessWindow.xaml index 48082cd66..d0184f016 100644 --- a/Debugger/ILSpy.Debugger/UI/AttachToProcessWindow.xaml +++ b/Debugger/ILSpy.Debugger/UI/AttachToProcessWindow.xaml @@ -16,6 +16,7 @@ Height="0.17*" /> diff --git a/Debugger/ILSpy.Debugger/UI/AttachToProcessWindow.xaml.cs b/Debugger/ILSpy.Debugger/UI/AttachToProcessWindow.xaml.cs index d7c3d8cc5..d9fe9e4a2 100644 --- a/Debugger/ILSpy.Debugger/UI/AttachToProcessWindow.xaml.cs +++ b/Debugger/ILSpy.Debugger/UI/AttachToProcessWindow.xaml.cs @@ -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 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 { RefreshProcessList(); } + + void RunningProcesses_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e) + { + Attach(); + } } } \ No newline at end of file diff --git a/ICSharpCode.Decompiler/Disassembler/CodeMappings.cs b/ICSharpCode.Decompiler/Disassembler/CodeMappings.cs index ff4e710b2..4d355b1b6 100644 --- a/ICSharpCode.Decompiler/Disassembler/CodeMappings.cs +++ b/ICSharpCode.Decompiler/Disassembler/CodeMappings.cs @@ -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 MethodCodeMappings { get; set; } @@ -63,4 +63,62 @@ namespace ICSharpCode.Decompiler.Disassembler return -1; } } + + public static class CodeMappings + { + static Dictionary> ilCodeMappings = new Dictionary>(); + + /// + /// Stores the source codes mappings: IL <-> editor lines + /// + public static Dictionary> 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; + } + } + } } diff --git a/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs index fc36bcf41..74a1f940b 100644 --- a/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs @@ -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() }; diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs index 60e88fda8..3989b46fb 100644 --- a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs @@ -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> ilCodeMappings = new Dictionary>(); - - /// - /// Stores the source codes mappings: IL <-> editor lines - /// - public static Dictionary> 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 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()); + if (!CodeMappings.ILSourceCodeMappings.ContainsKey(method.DeclaringType.FullName)) { + CodeMappings.ILSourceCodeMappings.Add(method.DeclaringType.FullName, new List()); } // .method public hidebysig specialname diff --git a/ILSpy/Commands/RoutedUICommands.cs b/ILSpy/Commands/RoutedUICommands.cs index 91b431c09..923fb2660 100644 --- a/ILSpy/Commands/RoutedUICommands.cs +++ b/ILSpy/Commands/RoutedUICommands.cs @@ -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; } } } diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index d36b56a97..b077a30fe 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -115,6 +115,10 @@ + + + + diff --git a/ILSpy/Images/ContinueDebugging.png b/ILSpy/Images/ContinueDebugging.png new file mode 100644 index 000000000..c72a5a085 Binary files /dev/null and b/ILSpy/Images/ContinueDebugging.png differ diff --git a/ILSpy/Images/StepInto.png b/ILSpy/Images/StepInto.png new file mode 100644 index 000000000..df1dc42a3 Binary files /dev/null and b/ILSpy/Images/StepInto.png differ diff --git a/ILSpy/Images/StepOut.png b/ILSpy/Images/StepOut.png new file mode 100644 index 000000000..a31ebd53e Binary files /dev/null and b/ILSpy/Images/StepOut.png differ diff --git a/ILSpy/Images/StepOver.png b/ILSpy/Images/StepOver.png new file mode 100644 index 000000000..3531ac8f6 Binary files /dev/null and b/ILSpy/Images/StepOver.png differ diff --git a/ILSpy/MainWindow.xaml b/ILSpy/MainWindow.xaml index ed4fcfe40..ad4d20848 100644 --- a/ILSpy/MainWindow.xaml +++ b/ILSpy/MainWindow.xaml @@ -27,6 +27,18 @@ + + + + @@ -71,7 +83,29 @@ - + + + + + + + + + + + + + + + + + + + + + diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 2445e675c..1b11295ce 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -322,6 +322,8 @@ namespace ICSharpCode.ILSpy treeView.ScrollIntoView(lastNode); } + #region Debugger commands + void RefreshCommandExecuted(object sender, ExecutedRoutedEventArgs e) { e.Handled = true; @@ -332,23 +334,84 @@ namespace ICSharpCode.ILSpy void AttachToProcessExecuted(object sender, ExecutedRoutedEventArgs e) { - var window = new AttachToProcessWindow(); - window.Owner = this; - if (window.ShowDialog() == true) - { - AttachMenuItem.IsEnabled = AttachButton.IsEnabled = false; - DetachMenuItem.IsEnabled = true; + 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) { - AttachToProcessWindow.Debugger.Detach(); + 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; + } - AttachMenuItem.IsEnabled = AttachButton.IsEnabled = true; - DetachMenuItem.IsEnabled = false; + base.OnKeyUp(e); } + #endregion + void ExitClick(object sender, RoutedEventArgs e) { Close(); diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 752aadd17..a6c35845b 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -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 /// public void Decompile(ILSpy.Language language, IEnumerable 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; diff --git a/ILSpy/TreeNodes/TypeTreeNode.cs b/ILSpy/TreeNodes/TypeTreeNode.cs index 98dc59de5..596ebe4fc 100644 --- a/ILSpy/TreeNodes/TypeTreeNode.cs +++ b/ILSpy/TreeNodes/TypeTreeNode.cs @@ -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); }