diff --git a/src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs b/src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs index 784092435f..316f345a75 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs @@ -464,19 +464,22 @@ namespace ICSharpCode.SharpDevelop.Services SourceCodeMapping GetCurrentCodeMapping(out bool isMatch) { - DecompileInformation debugInformation = (DecompileInformation)DebuggerService.ExternalDebugInformation; + var frame = debuggedProcess.SelectedThread.MostRecentStackFrame; + int typeToken = frame.MethodInfo.DeclaringType.MetadataToken; + int methodToken = frame.MethodInfo.MetadataToken; + + DecompileInformation debugInformation = (DecompileInformation)DebuggerService.ExternalDebugInformation[typeToken]; isMatch = false; if (debugInformation == null) return null; - var frame = debuggedProcess.SelectedThread.MostRecentStackFrame; - int token = frame.MethodInfo.MetadataToken; + // get the mapped instruction from the current line marker or the next one - if (!debugInformation.CodeMappings.ContainsKey(token)) + if (!debugInformation.CodeMappings.ContainsKey(methodToken)) return null; - var mappings = (List)debugInformation.CodeMappings[token]; - return mappings.GetInstructionByTokenAndOffset(token, frame.IP, out isMatch); + var mappings = (List)debugInformation.CodeMappings[methodToken]; + return mappings.GetInstructionByTokenAndOffset(methodToken, frame.IP, out isMatch); } Debugger.StackFrame GetStackFrame() @@ -745,7 +748,44 @@ namespace ICSharpCode.SharpDevelop.Services void AddBreakpoint(BreakpointBookmark bookmark) { - Breakpoint breakpoint = debugger.Breakpoints.Add(bookmark.FileName, null, bookmark.LineNumber, 0, bookmark.IsEnabled); + Breakpoint breakpoint = null; + + if (bookmark is DecompiledBreakpointBookmark) { + var dbb = (DecompiledBreakpointBookmark)bookmark; + var memberReference = dbb.MemberReference; + if (!DebuggerService.ExternalDebugInformation.ContainsKey(memberReference.MetadataToken.ToInt32())) + return; + + // check if the codemappings exists for bookmark line + DecompileInformation data = (DecompileInformation)DebuggerService.ExternalDebugInformation[memberReference.MetadataToken.ToInt32()]; + var storage = data.CodeMappings; + int token = 0; + foreach (var key in storage.Keys) { + var instruction = storage[key].GetInstructionByLineNumber(dbb.LineNumber, out token); + + if (instruction == null) { + continue; + } + + dbb.ILFrom = instruction.ILInstructionOffset.From; + dbb.ILTo = instruction.ILInstructionOffset.To; + + breakpoint = new ILBreakpoint( + debugger, + dbb.MemberReference.FullName, + dbb.LineNumber, + memberReference.MetadataToken.ToInt32(), + instruction.MemberMapping.MetadataToken, + dbb.ILFrom, + dbb.IsEnabled); + + debugger.Breakpoints.Add(breakpoint); + break; + } + } else { + breakpoint = debugger.Breakpoints.Add(bookmark.FileName, null, bookmark.LineNumber, 0, bookmark.IsEnabled); + } + MethodInvoker setBookmarkColor = delegate { if (debugger.Processes.Count == 0) { bookmark.IsHealthy = true; @@ -753,10 +793,13 @@ namespace ICSharpCode.SharpDevelop.Services } else if (!breakpoint.IsSet) { bookmark.IsHealthy = false; bookmark.Tooltip = "Breakpoint was not found in any loaded modules"; - } else if (breakpoint.OriginalLocation.CheckSum == null) { + } else if (breakpoint.OriginalLocation == null || breakpoint.OriginalLocation.CheckSum == null) { bookmark.IsHealthy = true; bookmark.Tooltip = null; } else { + if (!File.Exists(bookmark.FileName)) + return; + byte[] fileMD5; IEditable file = FileService.GetOpenFile(bookmark.FileName) as IEditable; if (file != null) { @@ -888,12 +931,14 @@ namespace ICSharpCode.SharpDevelop.Services debuggedProcess.Paused -= debuggedProcess_DebuggingPaused; debuggedProcess.ExceptionThrown -= debuggedProcess_ExceptionThrown; debuggedProcess.Resumed -= debuggedProcess_DebuggingResumed; + debuggedProcess.ModulesAdded -= debuggedProcess_ModulesAdded; } debuggedProcess = process; if (debuggedProcess != null) { debuggedProcess.Paused += debuggedProcess_DebuggingPaused; debuggedProcess.ExceptionThrown += debuggedProcess_ExceptionThrown; debuggedProcess.Resumed += debuggedProcess_DebuggingResumed; + debuggedProcess.ModulesAdded += debuggedProcess_ModulesAdded; debuggedProcess.BreakAtBeginning = BreakAtBeginning; } @@ -904,6 +949,23 @@ namespace ICSharpCode.SharpDevelop.Services OnProcessSelected(new ProcessEventArgs(process)); } + void debuggedProcess_ModulesAdded(object sender, ModuleEventArgs e) + { + var currentModuleTypes = e.Module.GetNamesOfDefinedTypes(); + foreach (var bookmark in DebuggerService.Breakpoints.OfType()) { + var breakpoint = debugger.Breakpoints.FirstOrDefault( + b => b is ILBreakpoint && b.Line == bookmark.LineNumber && + ((ILBreakpoint)b).MetadataToken == bookmark.MemberReference.MetadataToken.ToInt32()); + if (breakpoint == null) + continue; + // set the breakpoint only if the module contains the type + if (!currentModuleTypes.Contains(breakpoint.TypeName)) + continue; + + breakpoint.SetBreakpoint(e.Module); + } + } + void debuggedProcess_DebuggingPaused(object sender, ProcessEventArgs e) { OnIsProcessRunningChanged(EventArgs.Empty); @@ -980,14 +1042,16 @@ namespace ICSharpCode.SharpDevelop.Services DebuggerService.JumpToCurrentLine(nextStatement.Filename, nextStatement.StartLine, nextStatement.StartColumn, nextStatement.EndLine, nextStatement.EndColumn); } } else { - DecompileInformation externalData = (DecompileInformation)DebuggerService.ExternalDebugInformation; - // use most recent stack frame because we don't have the symbols var frame = debuggedProcess.SelectedThread.MostRecentStackFrame; - if (frame == null) return; + int typeToken = frame.MethodInfo.DeclaringType.MetadataToken; + // get external data + object data = null; + DebuggerService.ExternalDebugInformation.TryGetValue(typeToken, out data); + DecompileInformation externalData = data as DecompileInformation; int token = frame.MethodInfo.MetadataToken; int ilOffset = frame.IP; int line; @@ -1050,7 +1114,8 @@ namespace ICSharpCode.SharpDevelop.Services public static object GetLocalVariableIndex(Debugger.StackFrame stackFrame, string variableName) { // get the target name - int token = stackFrame.MethodInfo.MetadataToken; + int typeToken = stackFrame.MethodInfo.DeclaringType.MetadataToken; + int methodToken = stackFrame.MethodInfo.MetadataToken; int index = variableName.IndexOf('.'); string targetName = variableName; if (index != -1) { @@ -1060,11 +1125,11 @@ namespace ICSharpCode.SharpDevelop.Services // get local variable index and store it in UserData property - used in evaluator object data = null; IEnumerable list; - DecompileInformation externalData = (DecompileInformation)DebuggerService.ExternalDebugInformation; + DecompileInformation externalData = (DecompileInformation)DebuggerService.ExternalDebugInformation[typeToken]; if (externalData == null) return null; - if (externalData.LocalVariables.TryGetValue(token, out list)) { + if (externalData.LocalVariables.TryGetValue(methodToken, out list)) { var variable = list.FirstOrDefault(v => v.Name == targetName); if (variable != null && variable.OriginalVariable != null) { data = new[] { variable.OriginalVariable.Index }; diff --git a/src/AddIns/Debugger/Debugger.Core/Breakpoint.cs b/src/AddIns/Debugger/Debugger.Core/Breakpoint.cs index a9d2278dce..a1f4f7ab6c 100644 --- a/src/AddIns/Debugger/Debugger.Core/Breakpoint.cs +++ b/src/AddIns/Debugger/Debugger.Core/Breakpoint.cs @@ -21,7 +21,7 @@ namespace Debugger 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 { @@ -62,12 +63,16 @@ namespace Debugger get { return originalLocation; } } - public bool IsSet { - get { + public bool IsSet { + get { return corBreakpoints.Count > 0; } } + public string TypeName { + get; protected set; + } + protected virtual void OnHit(BreakpointEventArgs e) { if (Hit != null) { @@ -88,6 +93,8 @@ namespace Debugger } } + public Breakpoint() { } + public Breakpoint(NDebugger debugger, ICorDebugFunctionBreakpoint corBreakpoint) { this.debugger = debugger; @@ -104,7 +111,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 +123,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 +145,9 @@ namespace Debugger corBreakpoints.Clear(); } - internal bool SetBreakpoint(Module module) + public 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,20 +172,57 @@ namespace Debugger } } + public class ILBreakpoint : Breakpoint + { + public ILBreakpoint(NDebugger debugger, string typeName, int line, int metadataToken, int memberToken, int offset, bool enabled) + { + this.Debugger = debugger; + this.Line = line; + this.TypeName = typeName; + this.MetadataToken = metadataToken; + this.MemberMetadataToken = memberToken; + this.ILOffset = offset; + this.Enabled = enabled; + } + + public int MetadataToken { get; private set; } + + public int MemberMetadataToken { get; private set; } + + public int ILOffset { get; private set; } + + public override bool SetBreakpoint(Module module) + { + SourcecodeSegment segment = SourcecodeSegment.CreateForIL(module, this.Line, MemberMetadataToken, ILOffset); + if (segment == null) + return false; + 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 + #if DEBUG + (System.Exception ex) + #endif + { + return false; + } + } + } + [Serializable] public class BreakpointEventArgs : DebuggerEventArgs { - Breakpoint breakpoint; - public Breakpoint Breakpoint { - get { - return breakpoint; - } + get; private set; } public BreakpointEventArgs(Breakpoint breakpoint): base(breakpoint.Debugger) { - this.breakpoint = breakpoint; + this.Breakpoint = breakpoint; } } } diff --git a/src/AddIns/Debugger/Debugger.Core/BreakpointCollection.cs b/src/AddIns/Debugger/Debugger.Core/BreakpointCollection.cs index 41912b8ad9..0e114f5cd8 100644 --- a/src/AddIns/Debugger/Debugger.Core/BreakpointCollection.cs +++ b/src/AddIns/Debugger/Debugger.Core/BreakpointCollection.cs @@ -54,6 +54,12 @@ namespace Debugger { foreach(Process process in this.Debugger.Processes) { foreach(Module module in process.Modules) { + if (breakpoint is ILBreakpoint) { + var currentModuleTypes = module.GetNamesOfDefinedTypes(); + // set the breakpoint only if the module contains the type + if (!currentModuleTypes.Contains(breakpoint.TypeName)) + continue; + } breakpoint.SetBreakpoint(module); } } @@ -73,15 +79,23 @@ namespace Debugger base.OnRemoved(breakpoint); } - internal void SetInModule(Module module) + internal void SetInModule(Module module) { // This is in case that the client modifies the collection as a response to set breakpoint // NB: If client adds new breakpoint, it will be set directly as a result of his call, not here (because module is already loaded) List collection = new List(); collection.AddRange(this); + var currentModuleTypes = module.GetNamesOfDefinedTypes(); foreach (Breakpoint b in collection) { - b.SetBreakpoint(module); + if (b is ILBreakpoint) { + // set the breakpoint only if the module contains the type + if (!currentModuleTypes.Contains(b.TypeName)) + continue; + b.SetBreakpoint(module); + } else { + b.SetBreakpoint(module); + } } } } diff --git a/src/AddIns/Debugger/Debugger.Core/Module.cs b/src/AddIns/Debugger/Debugger.Core/Module.cs index dafb26bbb9..b911a83399 100644 --- a/src/AddIns/Debugger/Debugger.Core/Module.cs +++ b/src/AddIns/Debugger/Debugger.Core/Module.cs @@ -294,7 +294,13 @@ namespace Debugger List collection = new List(); collection.AddRange(this.Debugger.Breakpoints); + var currentModuleTypes = this.GetNamesOfDefinedTypes(); foreach (Breakpoint b in collection) { + if (b is ILBreakpoint) { + // set the breakpoint only if the module contains the type + if (!currentModuleTypes.Contains(b.TypeName)) + continue; + } b.SetBreakpoint(this); } } @@ -339,7 +345,7 @@ namespace Debugger return; } try { - this.CorModule2.SetJMCStatus(1, 0, ref unused); + this.CorModule2.SetJMCStatus(process.Options.EnableJustMyCode ? 1 : 0, 0, ref unused); } catch (COMException e) { // Cannot use JMC on this code (likely wrong JIT settings). if ((uint)e.ErrorCode == 0x80131323) { diff --git a/src/AddIns/Debugger/Debugger.Core/Process.cs b/src/AddIns/Debugger/Debugger.Core/Process.cs index d50e697cf9..2c617919f1 100644 --- a/src/AddIns/Debugger/Debugger.Core/Process.cs +++ b/src/AddIns/Debugger/Debugger.Core/Process.cs @@ -136,6 +136,12 @@ namespace Debugger public bool IsInExternalCode { get { + if (SelectedStackFrame == null && SelectedThread.MostRecentStackFrame == null) + return true; + + if (SelectedStackFrame == null && SelectedThread.MostRecentStackFrame != null) + return true; + return SelectedStackFrame.ToString() != SelectedThread.MostRecentStackFrame.ToString(); } } diff --git a/src/AddIns/Debugger/Debugger.Core/SourcecodeSegment.cs b/src/AddIns/Debugger/Debugger.Core/SourcecodeSegment.cs index 459d34451e..55b5f92b68 100644 --- a/src/AddIns/Debugger/Debugger.Core/SourcecodeSegment.cs +++ b/src/AddIns/Debugger/Debugger.Core/SourcecodeSegment.cs @@ -346,7 +346,7 @@ namespace Debugger this.startLine, this.startColumn, this.endLine, this.endColumn); } - #region ILSpy + #region Decompiled breakpoint public static SourcecodeSegment CreateForIL(Module module, int line, int metadataToken, int iLOffset) { diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj index f169a2d3fb..3ed5a3ef9e 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj @@ -43,6 +43,7 @@ + 3.0 @@ -191,6 +192,11 @@ ICSharpCode.AvalonEdit False + + {D68133BD-1E63-496E-9EDE-4FBDBF77B486} + Mono.Cecil + False + {3A9AE6AA-BC07-4A2F-972C-581E3AE2F195} NRefactory diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/IconBarMargin.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/IconBarMargin.cs index 20cd1bf9f3..417fe4e2c2 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/IconBarMargin.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/IconBarMargin.cs @@ -11,7 +11,10 @@ using ICSharpCode.AvalonEdit.Editing; using ICSharpCode.AvalonEdit.Rendering; using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.SharpDevelop.Bookmarks; +using ICSharpCode.SharpDevelop.Debugging; using ICSharpCode.SharpDevelop.Editor; +using ICSharpCode.SharpDevelop.Gui; +using Mono.Cecil; namespace ICSharpCode.AvalonEdit.AddIn { @@ -232,7 +235,17 @@ namespace ICSharpCode.AvalonEdit.AddIn // no bookmark on the line: create a new breakpoint ITextEditor textEditor = TextView.Services.GetService(typeof(ITextEditor)) as ITextEditor; if (textEditor != null) { - ICSharpCode.SharpDevelop.Debugging.DebuggerService.ToggleBreakpointAt(textEditor, line); + DebuggerService.ToggleBreakpointAt(textEditor, line); + return; + } + // create breakpoint for the decompiled content + dynamic viewContent = WorkbenchSingleton.Workbench.ActiveContent; + if (viewContent is AbstractViewContentWithoutFile) { + dynamic codeView = ((AbstractViewContentWithoutFile)viewContent).Control; + var editor = codeView.TextEditor as ITextEditor; + var memberReference = viewContent.MemberReference as MemberReference; + if (editor != null && !string.IsNullOrEmpty(editor.FileName)) + DebuggerService.ToggleBreakpointAt(memberReference, editor, line); } } } diff --git a/src/AddIns/DisplayBindings/ILSpyAddIn/NavigateToDecompiledEntityService.cs b/src/AddIns/DisplayBindings/ILSpyAddIn/NavigateToDecompiledEntityService.cs index 30621172cf..86ab9d5fbd 100644 --- a/src/AddIns/DisplayBindings/ILSpyAddIn/NavigateToDecompiledEntityService.cs +++ b/src/AddIns/DisplayBindings/ILSpyAddIn/NavigateToDecompiledEntityService.cs @@ -5,9 +5,7 @@ using System; using System.IO; using System.Linq; -using ICSharpCode.Core.Services; using ICSharpCode.SharpDevelop; -using ICSharpCode.SharpDevelop.Debugging; using ICSharpCode.SharpDevelop.Dom; using ICSharpCode.SharpDevelop.Gui; @@ -60,7 +58,7 @@ namespace ICSharpCode.ILSpyAddIn WorkbenchSingleton.Workbench.ShowView(new DecompiledViewContent(assemblyFile, typeName, entityTag)); } - public bool NavigateToMember(string assemblyFile, string typeName, string entityTag) + public bool NavigateToMember(string assemblyFile, string typeName, string entityTag, int lineNumber) { //close the window if exists - this is a workaround foreach (var vc in WorkbenchSingleton.Workbench.ViewContentCollection.OfType()) { @@ -69,7 +67,10 @@ namespace ICSharpCode.ILSpyAddIn break; } } - WorkbenchSingleton.Workbench.ShowView(new DecompiledViewContent(assemblyFile, typeName, entityTag)); + + var view = new DecompiledViewContent(assemblyFile, typeName, entityTag); + view.DecompilationFinished += delegate { view.JumpTo(lineNumber); }; + WorkbenchSingleton.Workbench.ShowView(view); return true; } } diff --git a/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/CodeView.cs b/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/CodeView.cs index 4e8dcb56ad..ed0b494bb0 100644 --- a/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/CodeView.cs +++ b/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/CodeView.cs @@ -14,11 +14,24 @@ using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.AvalonEdit.Rendering; using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Bookmarks; using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Editor.AvalonEdit; namespace ICSharpCode.ILSpyAddIn.ViewContent { + class DecompiledTextEditorAdapter : AvalonEditTextEditorAdapter + { + public DecompiledTextEditorAdapter(TextEditor textEditor) : base(textEditor) + {} + + public string DecompiledFullTypeName { get; set; } + + public override ICSharpCode.Core.FileName FileName { + get { return ICSharpCode.Core.FileName.Create(DecompiledFullTypeName); } + } + } + /// /// Equivalent to AE.AddIn CodeEditor, but without editing capabilities. /// @@ -26,26 +39,31 @@ namespace ICSharpCode.ILSpyAddIn.ViewContent { public event EventHandler DocumentChanged; - readonly AvalonEditTextEditorAdapter adapter = new AvalonEditTextEditorAdapter(new SharpDevelopTextEditor { IsReadOnly = true }); + readonly DecompiledTextEditorAdapter adapter; readonly IconBarManager iconBarManager; readonly IconBarMargin iconMargin; readonly TextMarkerService textMarkerService; - public CodeView() + public CodeView(string decompiledFullTypeName) { + DecompiledFullTypeName = decompiledFullTypeName; + this.adapter = new DecompiledTextEditorAdapter(new SharpDevelopTextEditor { IsReadOnly = true }) { + DecompiledFullTypeName = decompiledFullTypeName + }; this.Children.Add(adapter.TextEditor); adapter.TextEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("C#"); // add margin - iconMargin = new IconBarMargin(iconBarManager = new IconBarManager()); - adapter.TextEditor.TextArea.LeftMargins.Insert(0, iconMargin); - adapter.TextEditor.TextArea.TextView.VisualLinesChanged += delegate { iconMargin.InvalidateVisual(); }; + this.iconMargin = new IconBarMargin(iconBarManager = new IconBarManager()); + this.adapter.TextEditor.TextArea.LeftMargins.Insert(0, iconMargin); + this.adapter.TextEditor.TextArea.TextView.VisualLinesChanged += delegate { iconMargin.InvalidateVisual(); }; // add marker service - textMarkerService = new TextMarkerService(this); - adapter.TextEditor.TextArea.TextView.BackgroundRenderers.Add(textMarkerService); - adapter.TextEditor.TextArea.TextView.LineTransformers.Add(textMarkerService); - adapter.TextEditor.TextArea.TextView.Services.AddService(typeof(ITextMarkerService), textMarkerService); + this.textMarkerService = new TextMarkerService(this); + this.adapter.TextEditor.TextArea.TextView.BackgroundRenderers.Add(textMarkerService); + this.adapter.TextEditor.TextArea.TextView.LineTransformers.Add(textMarkerService); + this.adapter.TextEditor.TextArea.TextView.Services.AddService(typeof(ITextMarkerService), textMarkerService); + this.adapter.TextEditor.TextArea.TextView.Services.AddService(typeof(IBookmarkMargin), iconBarManager); // add events this.adapter.TextEditor.MouseHover += TextEditorMouseHover; @@ -212,6 +230,16 @@ namespace ICSharpCode.ILSpyAddIn.ViewContent get { return iconBarManager; } } + public AvalonEditTextEditorAdapter Adapter { + get { + return adapter; + } + } + + public string DecompiledFullTypeName { + get; private set; + } + public void Dispose() { } diff --git a/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/DecompiledViewContent.cs b/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/DecompiledViewContent.cs index 62c05b98ad..705cc0b8ab 100644 --- a/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/DecompiledViewContent.cs +++ b/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/DecompiledViewContent.cs @@ -2,25 +2,19 @@ // This code is distributed under MIT X11 license (for details please see \doc\license.txt) using System; -using System.Collections.Concurrent; -using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; -using ICSharpCode.AvalonEdit.Document; using ICSharpCode.Core; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Ast; -using ICSharpCode.Decompiler.ILAst; using ICSharpCode.ILSpyAddIn.ViewContent; using ICSharpCode.NRefactory.CSharp; -using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.Utils; using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Bookmarks; using ICSharpCode.SharpDevelop.Debugging; -using ICSharpCode.SharpDevelop.Dom; using ICSharpCode.SharpDevelop.Gui; using Mono.Cecil; @@ -41,12 +35,13 @@ namespace ICSharpCode.ILSpyAddIn bool decompilationFinished; - readonly CodeView codeView = new CodeView(); + readonly CodeView codeView; readonly CancellationTokenSource cancellation = new CancellationTokenSource(); #region Constructor public DecompiledViewContent(string assemblyFile, string fullTypeName, string entityTag) { + codeView = new CodeView(string.Format("{0},{1}", assemblyFile, fullTypeName)); this.assemblyFile = assemblyFile; this.fullTypeName = fullTypeName; this.jumpToEntityTagWhenDecompilationFinished = entityTag; @@ -57,9 +52,10 @@ namespace ICSharpCode.ILSpyAddIn Thread thread = new Thread(DecompilationThread); thread.Name = "Decompiler (" + shortTypeName + ")"; thread.Start(); + BookmarkManager.Removed += BookmarkManager_Removed; + BookmarkManager.Added += BookmarkManager_Added; } - #endregion #region Properties @@ -78,6 +74,11 @@ namespace ICSharpCode.ILSpyAddIn public override bool IsReadOnly { get { return true; } } + + public MemberReference MemberReference { + get; private set; + } + #endregion #region Dispose @@ -85,6 +86,7 @@ namespace ICSharpCode.ILSpyAddIn { cancellation.Cancel(); codeView.Dispose(); + BookmarkManager.Added -= BookmarkManager_Added; BookmarkManager.Removed -= BookmarkManager_Removed; base.Dispose(); } @@ -141,7 +143,7 @@ namespace ICSharpCode.ILSpyAddIn } } - static void RunDecompiler(string assemblyFile, string fullTypeName, ITextOutput textOutput, CancellationToken cancellationToken) + 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 @@ -162,12 +164,23 @@ namespace ICSharpCode.ILSpyAddIn var nodes = TreeTraversal .PreOrder((AstNode)astBuilder.CompilationUnit, n => n.Children) .Where(n => n is AttributedNode && n.Annotation>() != null); - DebuggerService.ExternalDebugInformation = new DecompileInformation { - CodeMappings = astBuilder.CodeMappings, - LocalVariables = astBuilder.LocalVariables, - DecompiledMemberReferences = astBuilder.DecompiledMemberReferences, - AstNodes = nodes - }; + MemberReference = typeDefinition; + int token = MemberReference.MetadataToken.ToInt32(); + if (!DebuggerService.ExternalDebugInformation.ContainsKey(token)) { + DebuggerService.ExternalDebugInformation.Add(token, new DecompileInformation { + CodeMappings = astBuilder.CodeMappings, + LocalVariables = astBuilder.LocalVariables, + DecompiledMemberReferences = astBuilder.DecompiledMemberReferences, + AstNodes = nodes + }); + } else { + DebuggerService.ExternalDebugInformation[token] = new DecompileInformation { + CodeMappings = astBuilder.CodeMappings, + LocalVariables = astBuilder.LocalVariables, + DecompiledMemberReferences = astBuilder.DecompiledMemberReferences, + AstNodes = nodes + }; + } } void OnDecompilationFinished(StringWriter output) @@ -183,6 +196,9 @@ namespace ICSharpCode.ILSpyAddIn // update UI UpdateIconMargin(output.ToString()); UpdateDebuggingUI(); + + // fire event + OnDecompilationFinished(EventArgs.Empty); } #endregion @@ -192,6 +208,12 @@ namespace ICSharpCode.ILSpyAddIn string tempFileName = string.Format("decompiled/{0}.cs", fullTypeName); codeView.IconBarManager.UpdateClassMemberBookmarks(ParserService.ParseFile(tempFileName, new StringTextBuffer(text))); + + // load bookmarks + foreach (SDBookmark bookmark in BookmarkManager.GetBookmarks(codeView.Adapter.FileName)) { + bookmark.Document = codeView.Adapter.Document; + codeView.IconBarManager.Bookmarks.Add(bookmark); + } } void UpdateDebuggingUI() @@ -201,12 +223,12 @@ namespace ICSharpCode.ILSpyAddIn if (DebuggerService.DebugStepInformation != null) { // get debugging information - DecompileInformation debugInformation = (DecompileInformation)DebuggerService.ExternalDebugInformation; + DecompileInformation debugInformation = (DecompileInformation)DebuggerService.ExternalDebugInformation[MemberReference.MetadataToken.ToInt32()]; int token = DebuggerService.DebugStepInformation.Item1; int ilOffset = DebuggerService.DebugStepInformation.Item2; int line; MemberReference member; - if (!debugInformation.CodeMappings.ContainsKey(token)) + if (debugInformation.CodeMappings == null || !debugInformation.CodeMappings.ContainsKey(token)) return; debugInformation.CodeMappings[token].GetInstructionByTokenAndOffset(token, ilOffset, out member, out line); @@ -216,6 +238,17 @@ namespace ICSharpCode.ILSpyAddIn CurrentLineBookmark.SetPosition(this, line, 0, line, 0); } } + + public void JumpTo(int lineNumber) + { + if (lineNumber <= 0) + return; + + if (codeView == null) + return; + + codeView.UnfoldAndScroll(lineNumber); + } #endregion #region Bookmarks @@ -227,6 +260,26 @@ namespace ICSharpCode.ILSpyAddIn mark.Document = null; } } + + void BookmarkManager_Added(object sender, BookmarkEventArgs e) + { + var mark = e.Bookmark; + if (mark != null && mark is BreakpointBookmark && mark.FileName == codeView.DecompiledFullTypeName) { + codeView.IconBarManager.Bookmarks.Add(mark); + mark.Document = codeView.Adapter.Document; + } + } + #endregion + + #region Events + public event EventHandler DecompilationFinished; + + protected virtual void OnDecompilationFinished(EventArgs e) + { + if (DecompilationFinished != null) { + DecompilationFinished(this, e); + } + } #endregion } } diff --git a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj index 9518e0dcff..aa33d177ba 100644 --- a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj +++ b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj @@ -315,6 +315,7 @@ + diff --git a/src/Main/Base/Project/Src/Bookmarks/BookmarkConverter.cs b/src/Main/Base/Project/Src/Bookmarks/BookmarkConverter.cs index 1caf27154d..5ae0660ec2 100644 --- a/src/Main/Base/Project/Src/Bookmarks/BookmarkConverter.cs +++ b/src/Main/Base/Project/Src/Bookmarks/BookmarkConverter.cs @@ -44,7 +44,7 @@ namespace ICSharpCode.SharpDevelop.Bookmarks action = (Debugging.BreakpointAction)Enum.Parse(typeof(Debugging.BreakpointAction), v[5]); scriptLanguage = v[6]; script = v[7]; - + var bbm = new Debugging.BreakpointBookmark(fileName, new Location(columnNumber, lineNumber), action, scriptLanguage, script); bbm.IsEnabled = bool.Parse(v[4]); bbm.Action = action; @@ -52,16 +52,30 @@ namespace ICSharpCode.SharpDevelop.Bookmarks bbm.Condition = script; bookmark = bbm; break; + case "DecompiledBreakpointBookmark": + action = (Debugging.BreakpointAction)Enum.Parse(typeof(Debugging.BreakpointAction), v[5]); + scriptLanguage = v[6]; + script = v[7]; + int ilfrom = Convert.ToInt32(v[8]); + int ilto = Convert.ToInt32(v[9]); + + bbm = new DecompiledBreakpointBookmark(null, ilfrom, ilto, fileName, new Location(columnNumber, lineNumber), action, scriptLanguage, script); + bbm.IsEnabled = bool.Parse(v[4]); + bbm.Action = action; + bbm.ScriptLanguage = scriptLanguage; + bbm.Condition = script; + bookmark = bbm; + break; case "PinBookmark": var pin = new PinBookmark(fileName, new Location(columnNumber, lineNumber)); pin.Comment = v[4]; - pin.PinPosition = + pin.PinPosition = new Point - { - X = double.Parse(v[5], culture), - Y = double.Parse(v[6], culture) - }; - + { + X = double.Parse(v[5], culture), + Y = double.Parse(v[6], culture) + }; + // pop-up nodes pin.SavedNodes = new System.Collections.Generic.List>(); for (int i = 7; i < v.Length; i+=3) { @@ -85,11 +99,13 @@ namespace ICSharpCode.SharpDevelop.Bookmarks SDBookmark bookmark = value as SDBookmark; if (destinationType == typeof(string) && bookmark != null) { StringBuilder b = new StringBuilder(); - if (bookmark is Debugging.BreakpointBookmark) { + if (bookmark is DecompiledBreakpointBookmark) { + b.Append("DecompiledBreakpointBookmark"); + } else if (bookmark is Debugging.BreakpointBookmark) { b.Append("Breakpoint"); } else { if (bookmark is PinBookmark) - b.Append("PinBookmark"); + b.Append("PinBookmark"); else b.Append("Bookmark"); } @@ -100,8 +116,22 @@ namespace ICSharpCode.SharpDevelop.Bookmarks b.Append('|'); b.Append(bookmark.ColumnNumber); - if (bookmark is Debugging.BreakpointBookmark) { - Debugging.BreakpointBookmark bbm = (Debugging.BreakpointBookmark)bookmark; + if (bookmark is DecompiledBreakpointBookmark) { + var bbm = (DecompiledBreakpointBookmark)bookmark; + b.Append('|'); + b.Append(bbm.IsEnabled.ToString()); + b.Append('|'); + b.Append(bbm.Action.ToString()); + b.Append('|'); + b.Append(bbm.ScriptLanguage); + b.Append('|'); + b.Append(bbm.Condition); + b.Append('|'); + b.Append(bbm.ILFrom); + b.Append('|'); + b.Append(bbm.ILTo); + } else if (bookmark is Debugging.BreakpointBookmark) { + var bbm = (Debugging.BreakpointBookmark)bookmark; b.Append('|'); b.Append(bbm.IsEnabled.ToString()); b.Append('|'); @@ -122,7 +152,7 @@ namespace ICSharpCode.SharpDevelop.Bookmarks b.Append(pin.PinPosition.Value.X); b.Append('|'); b.Append(pin.PinPosition.Value.Y); - + //popup nodes foreach(var node in pin.Nodes) { b.Append('|'); @@ -131,7 +161,7 @@ namespace ICSharpCode.SharpDevelop.Bookmarks b.Append(node.FullName); b.Append('|'); b.Append(node.Text); - } + } } return b.ToString(); } else { diff --git a/src/Main/Base/Project/Src/Bookmarks/BookmarkManager.cs b/src/Main/Base/Project/Src/Bookmarks/BookmarkManager.cs index f02c28c724..1afac251a8 100644 --- a/src/Main/Base/Project/Src/Bookmarks/BookmarkManager.cs +++ b/src/Main/Base/Project/Src/Bookmarks/BookmarkManager.cs @@ -98,7 +98,7 @@ namespace ICSharpCode.SharpDevelop.Bookmarks { List projectBookmarks = new List(); foreach (SDBookmark mark in bookmarks) { - if (mark.IsSaved && mark.FileName != null && project.IsFileInProject(mark.FileName)) { + if (mark.IsSaved && mark.FileName != null) { projectBookmarks.Add(mark); } } diff --git a/src/Main/Base/Project/Src/Commands/DebugCommands.cs b/src/Main/Base/Project/Src/Commands/DebugCommands.cs index 89e348a0d7..4e3d89e78f 100644 --- a/src/Main/Base/Project/Src/Commands/DebugCommands.cs +++ b/src/Main/Base/Project/Src/Commands/DebugCommands.cs @@ -86,7 +86,7 @@ namespace ICSharpCode.SharpDevelop.Project.Commands if (!DebuggerService.CurrentDebugger.IsDebugging) { DebuggerService.CurrentDebugger.BreakAtBeginning = true; new Execute().Run(); - } else { + } else { DebuggerService.CurrentDebugger.StepOver(); } } @@ -100,7 +100,7 @@ namespace ICSharpCode.SharpDevelop.Project.Commands if (!DebuggerService.CurrentDebugger.IsDebugging) { DebuggerService.CurrentDebugger.BreakAtBeginning = true; new Execute().Run(); - } else { + } else { DebuggerService.CurrentDebugger.StepInto(); } } @@ -119,13 +119,21 @@ namespace ICSharpCode.SharpDevelop.Project.Commands { public override void Run() { - ITextEditorProvider provider = WorkbenchSingleton.Workbench.ActiveContent as ITextEditorProvider; + var viewContent = WorkbenchSingleton.Workbench.ActiveContent; + ITextEditorProvider provider = viewContent as ITextEditorProvider; if (provider != null) { ITextEditor editor = provider.TextEditor; if (!string.IsNullOrEmpty(editor.FileName)) { DebuggerService.ToggleBreakpointAt(editor, editor.Caret.Line); } + } else { + if (viewContent is AbstractViewContentWithoutFile) { + dynamic codeView = ((AbstractViewContentWithoutFile)viewContent).Control; + var editor = codeView.TextEditor as ITextEditor; + if (editor != null && !string.IsNullOrEmpty(editor.FileName)) + DebuggerService.ToggleBreakpointAt(editor, editor.Caret.Line); + } } } } diff --git a/src/Main/Base/Project/Src/Services/Debugger/DebuggerService.cs b/src/Main/Base/Project/Src/Services/Debugger/DebuggerService.cs index 6dc440d0ad..15b5e6714e 100644 --- a/src/Main/Base/Project/Src/Services/Debugger/DebuggerService.cs +++ b/src/Main/Base/Project/Src/Services/Debugger/DebuggerService.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Text; +using System.Windows; using System.Windows.Forms; using ICSharpCode.Core; @@ -33,6 +34,8 @@ namespace ICSharpCode.SharpDevelop.Debugging BookmarkManager.Added += BookmarkAdded; BookmarkManager.Removed += BookmarkRemoved; + + ExternalDebugInformation = new Dictionary(); } static void GetDescriptors() @@ -107,7 +110,7 @@ namespace ICSharpCode.SharpDevelop.Debugging /// Gets or sets the external debug information. /// This constains the code mappings and local variables. /// - public static object ExternalDebugInformation { get; set; } + public static Dictionary ExternalDebugInformation { get; set; } /// /// Gets or sets the current token and IL offset. Used for step in/out. @@ -248,6 +251,16 @@ namespace ICSharpCode.SharpDevelop.Debugging location => new BreakpointBookmark(editor.FileName, location, BreakpointAction.Break, "", "")); } + public static void ToggleBreakpointAt(MemberReference memberReference, ITextEditor editor, int lineNumber) + { + // no bookmark on the line: create a new breakpoint + BookmarkManager.ToggleBookmark( + editor, lineNumber, + b => b.CanToggle, + location => new DecompiledBreakpointBookmark( + memberReference, 0, 0, editor.FileName, location, BreakpointAction.Break, "", "")); + } + /* TODO: reimplement this stuff static void ViewContentOpened(object sender, ViewContentEventArgs e) { @@ -261,12 +274,12 @@ namespace ICSharpCode.SharpDevelop.Debugging CurrentLineBookmark.Remove(); } - public static void JumpToCurrentLine(string SourceFullFilename, int StartLine, int StartColumn, int EndLine, int EndColumn) + public static void JumpToCurrentLine(string sourceFullFilename, int startLine, int startColumn, int endLine, int endColumn) { - IViewContent viewContent = FileService.OpenFile(SourceFullFilename); + IViewContent viewContent = FileService.OpenFile(sourceFullFilename); if (viewContent is ITextEditorProvider) - ((ITextEditorProvider)viewContent).TextEditor.JumpTo(StartLine, StartColumn); - CurrentLineBookmark.SetPosition(viewContent, StartLine, StartColumn, EndLine, EndColumn); + ((ITextEditorProvider)viewContent).TextEditor.JumpTo(startLine, startColumn); + CurrentLineBookmark.SetPosition(viewContent, startLine, startColumn, endLine, endColumn); } #region Tool tips @@ -283,7 +296,7 @@ namespace ICSharpCode.SharpDevelop.Debugging Location logicPos = e.LogicalPosition; var doc = e.Editor.Document; string fileName; - if (string.IsNullOrEmpty(e.Editor.FileName)) { + if (!File.Exists(e.Editor.FileName)) { dynamic viewContent = WorkbenchSingleton.Workbench.ActiveViewContent; fileName = string.Format("decompiled/{0}.cs", viewContent.FullTypeName); } else { diff --git a/src/Main/Base/Project/Src/Services/Debugger/DecompiledBreakpointBookmark.cs b/src/Main/Base/Project/Src/Services/Debugger/DecompiledBreakpointBookmark.cs new file mode 100644 index 0000000000..d67f99b952 --- /dev/null +++ b/src/Main/Base/Project/Src/Services/Debugger/DecompiledBreakpointBookmark.cs @@ -0,0 +1,56 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) +using System; +using ICSharpCode.Core; +using ICSharpCode.NRefactory; +using ICSharpCode.SharpDevelop.Debugging; +using Mono.Cecil; + +namespace ICSharpCode.SharpDevelop.Bookmarks +{ + public class DecompiledBreakpointBookmark : BreakpointBookmark + { + public DecompiledBreakpointBookmark(MemberReference member, int ilFrom, int ilTo, FileName fileName, Location location, BreakpointAction action, string scriptLanguage, string script) : base(fileName, location, action, scriptLanguage, script) + { + this.MemberReference = member; + this.ILFrom = ilFrom; + this.ILTo = ILTo; + } + + public int ILFrom { + get; set; + } + + public int ILTo { + get; set; + } + + MemberReference memberReference; + + public MemberReference MemberReference { + get { + if (memberReference != null) + return memberReference; + + // reload from filename + 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(); + + string fileName = FileName.ToString(); + int index = fileName.IndexOf(","); + string assemblyFile = fileName.Substring(0, index); + string fullTypeName = fileName.Substring(index + 1, fileName.Length - index - 1); + + ModuleDefinition module = ModuleDefinition.ReadModule(assemblyFile, readerParameters); + TypeDefinition typeDefinition = module.GetType(fullTypeName); + if (typeDefinition == null) + throw new InvalidOperationException("Could not find type"); + memberReference = typeDefinition; + return memberReference; + } + private set { memberReference = value; } + } + } +} diff --git a/src/Main/Base/Project/Src/Services/File/FileService.cs b/src/Main/Base/Project/Src/Services/File/FileService.cs index f3f115975f..17529f3103 100644 --- a/src/Main/Base/Project/Src/Services/File/FileService.cs +++ b/src/Main/Base/Project/Src/Services/File/FileService.cs @@ -533,6 +533,14 @@ namespace ICSharpCode.SharpDevelop bool loggingResumed = false; try { + // jump to decompiled type from filename + if (fileName.Contains(",")) { + int index = fileName.IndexOf(","); + string assemblyName = fileName.Substring(0, index); + string typeName = fileName.Substring(index + 1, fileName.Length - index - 1); + NavigationService.NavigateTo(assemblyName, typeName, string.Empty, line); + return null; + } IViewContent content = OpenFile(fileName); if (content is IPositionable) { @@ -547,11 +555,11 @@ namespace ICSharpCode.SharpDevelop NavigationService.Log(content); } - LoggingService.InfoFormatted("FileService\n\tJumped to File Position: [{0} : {1}x{2}]", fileName, line, column); - return content; } finally { + LoggingService.InfoFormatted("FileService\n\tJumped to File Position: [{0} : {1}x{2}]", fileName, line, column); + if (!loggingResumed) { NavigationService.ResumeLogging(); } diff --git a/src/Main/Base/Project/Src/Services/NavigationService/NavigationService.cs b/src/Main/Base/Project/Src/Services/NavigationService/NavigationService.cs index cf42a58d4e..0712af2204 100644 --- a/src/Main/Base/Project/Src/Services/NavigationService/NavigationService.cs +++ b/src/Main/Base/Project/Src/Services/NavigationService/NavigationService.cs @@ -475,7 +475,7 @@ namespace ICSharpCode.SharpDevelop #region Navigate to Member - public static bool NavigateTo(string assemblyFile, string typeName, string entityTag) + public static bool NavigateTo(string assemblyFile, string typeName, string entityTag, int lineNumber = 0) { if (string.IsNullOrEmpty(assemblyFile)) throw new ArgumentException("assemblyFile is null or empty"); @@ -484,7 +484,7 @@ namespace ICSharpCode.SharpDevelop throw new ArgumentException("typeName is null or empty"); foreach (var item in AddInTree.BuildItems("/SharpDevelop/Services/NavigateToEntityService", null, false)) { - if (item.NavigateToMember(assemblyFile, typeName, entityTag)) + if (item.NavigateToMember(assemblyFile, typeName, entityTag, lineNumber)) return true; } return false; @@ -512,6 +512,6 @@ namespace ICSharpCode.SharpDevelop /// public interface INavigateToMemberService { - bool NavigateToMember(string assemblyFile, string typeName, string entityTag); + bool NavigateToMember(string assemblyFile, string typeName, string entityTag, int lineNumber); } }