From 10f225ffdd1574e47f519f5bb0589bf824b1ee48 Mon Sep 17 00:00:00 2001 From: Eusebiu Marcu Date: Sun, 13 Mar 2011 15:47:30 +0200 Subject: [PATCH] synchronize CurrentLineBookmark fix stepping issue --- .../Bookmarks/BookmarkManager.cs | 152 ++++++++++++------ .../Bookmarks/CurrentLineBookmark.cs | 2 +- .../Services/Debugger/WindowsDebugger.cs | 54 ++++--- .../Ast/TextOutputFormatter.cs | 6 +- ICSharpCode.Decompiler/CodeMappings.cs | 62 +++++-- .../Disassembler/MethodBodyDisassembler.cs | 6 +- 6 files changed, 192 insertions(+), 90 deletions(-) diff --git a/Debugger/ILSpy.Debugger/Bookmarks/BookmarkManager.cs b/Debugger/ILSpy.Debugger/Bookmarks/BookmarkManager.cs index 4405a0231..cf9907c8e 100644 --- a/Debugger/ILSpy.Debugger/Bookmarks/BookmarkManager.cs +++ b/Debugger/ILSpy.Debugger/Bookmarks/BookmarkManager.cs @@ -13,55 +13,8 @@ namespace ILSpy.Debugger.Bookmarks /// /// Static class that maintains the list of bookmarks and breakpoints. /// - public static class BookmarkManager + public static partial class BookmarkManager { - static BookmarkManager() - { - DebugData.LanguageChanged += OnLanguageChanged; - } - - static void OnLanguageChanged(object sender, LanguageEventArgs e) - { - var oldLanguage = e.OldLanguage; - var newLanguage = e.NewLanguage; - - // synchronize the IL<->C# breakpoints - - // 1. map the breakpoint lines - var oldbps = bookmarks.FindAll(b => b is BreakpointBookmark && ((BreakpointBookmark)b).Language == oldLanguage); - if (oldbps == null || oldbps.Count == 0) - return; - - var oldMappings = CodeMappings.GetStorage(oldLanguage); - var newMappings = CodeMappings.GetStorage(newLanguage); - - if (oldMappings == null || oldMappings.Count == 0 || - newMappings == null || newMappings.Count == 0) - return; - - foreach (var bp in oldbps) { - uint token; - var instruction = oldMappings.GetInstructionByTypeAndLine(DebugData.CurrentType.FullName, bp.LineNumber, out token); - if (instruction == null) - continue; - - TypeDefinition type; - int line; - if (newMappings.GetSourceCodeFromMetadataTokenAndOffset(token, instruction.ILInstructionOffset.From, out type, out line)) { - // 2. create breakpoint for new languages - var bookmark = new BreakpointBookmark(type, new AstLocation(line, 0), BreakpointAction.Break, newLanguage); - AddMark(bookmark); - } - } - - // 3. remove all breakpoints for the old language - for (int i = bookmarks.Count - 1; i >= 0; --i) { - var bm = bookmarks[i]; - if (bm is BreakpointBookmark && ((BreakpointBookmark)bm).Language == oldLanguage) - RemoveMark(bm); - } - } - static List bookmarks = new List(); public static List Bookmarks { @@ -138,7 +91,7 @@ namespace ILSpy.Debugger.Bookmarks static void OnAdded(BookmarkEventArgs e) { if (Added != null) { - Added(null, e); + Added(null, e); } } @@ -153,11 +106,110 @@ namespace ILSpy.Debugger.Bookmarks } } - // no bookmark at that line: create a new bookmark + // no bookmark at that line: create a new bookmark BookmarkManager.AddMark(bookmarkFactory(new AstLocation(line, 0))); } public static event BookmarkEventHandler Removed; public static event BookmarkEventHandler Added; } + + // This is for the synchronize bookmarks logic + public static partial class BookmarkManager + { + static BookmarkManager() + { + DebugData.LanguageChanged += OnLanguageChanged; + } + + static void OnLanguageChanged(object sender, LanguageEventArgs e) + { + var oldLanguage = e.OldLanguage; + var newLanguage = e.NewLanguage; + + SyncCurrentLineBookmark(oldLanguage, newLanguage); + SyncBreakpointBookmarks(oldLanguage, newLanguage); + } + + /// + /// Synchronize the IL<->C# current line marker. + /// + /// Old language. + /// New language. + static void SyncCurrentLineBookmark(DecompiledLanguages oldLanguage, DecompiledLanguages newLanguage) + { + // checks + if (CurrentLineBookmark.Instance == null) + return; + + var oldMappings = CodeMappings.GetStorage(oldLanguage); + var newMappings = CodeMappings.GetStorage(newLanguage); + + if (oldMappings == null || oldMappings.Count == 0 || newMappings == null || newMappings.Count == 0) + return; + + // 1. Save it's data + int line = CurrentLineBookmark.Instance.LineNumber; + var markerType = CurrentLineBookmark.Instance.Type; + + // 2. Remove it + CurrentLineBookmark.Remove(); + + // 3. map the marker line + uint token; + var instruction = oldMappings.GetInstructionByTypeAndLine(markerType.FullName, line, out token); + if (instruction == null) + return; + + TypeDefinition type; + int newline; + if (newMappings.GetSourceCodeFromMetadataTokenAndOffset(token, instruction.ILInstructionOffset.From, out type, out newline)) { + // 4. create breakpoint for new languages + CurrentLineBookmark.SetPosition(type, newline, 0, newline, 0); + } + } + + /// + /// Synchronize the IL<->C# breakpoints bookmarks. + /// + /// Old language. + /// New language. + static void SyncBreakpointBookmarks(DecompiledLanguages oldLanguage, DecompiledLanguages newLanguage) + { + // checks + var oldMappings = CodeMappings.GetStorage(oldLanguage); + var newMappings = CodeMappings.GetStorage(newLanguage); + + if (oldMappings == null || oldMappings.Count == 0 || newMappings == null || newMappings.Count == 0) + return; + + // 1. map the breakpoint lines + var oldbps = bookmarks.FindAll(b => b is BreakpointBookmark && + ((BreakpointBookmark)b).Language == oldLanguage); + if (oldbps == null || oldbps.Count == 0) + return; + + foreach (var bp in oldbps) { + uint token; + var instruction = oldMappings.GetInstructionByTypeAndLine(bp.Type.FullName, bp.LineNumber, out token); + if (instruction == null) + continue; + + TypeDefinition type; + int line; + if (newMappings.GetSourceCodeFromMetadataTokenAndOffset(token, instruction.ILInstructionOffset.From, out type, out line)) { + // 2. create breakpoint for new languages + var bookmark = new BreakpointBookmark(type, new AstLocation(line, 0), BreakpointAction.Break, newLanguage); + AddMark(bookmark); + } + } + + // 3. remove all breakpoints for the old language + for (int i = bookmarks.Count - 1; i >= 0; --i) { + var bm = bookmarks[i]; + if (bm is BreakpointBookmark && ((BreakpointBookmark)bm).Language == oldLanguage) + RemoveMark(bm); + } + } + } } diff --git a/Debugger/ILSpy.Debugger/Bookmarks/CurrentLineBookmark.cs b/Debugger/ILSpy.Debugger/Bookmarks/CurrentLineBookmark.cs index 9cfb6da45..9b38d26eb 100644 --- a/Debugger/ILSpy.Debugger/Bookmarks/CurrentLineBookmark.cs +++ b/Debugger/ILSpy.Debugger/Bookmarks/CurrentLineBookmark.cs @@ -78,7 +78,7 @@ namespace ILSpy.Debugger.Bookmarks public override ITextMarker CreateMarker(ITextMarkerService markerService, int offset, int length) { - ITextMarker marker = markerService.Create(offset + startColumn - 1, length); + ITextMarker marker = markerService.Create(offset + startColumn - 1, length + 1); marker.BackgroundColor = Colors.Yellow; marker.ForegroundColor = Colors.Blue; marker.IsVisible = b => b is MarkerBookmark && ((MarkerBookmark)b).Type == DebugData.CurrentType; diff --git a/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs b/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs index f6cb21e6c..4dd3068a0 100644 --- a/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs +++ b/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs @@ -299,9 +299,9 @@ namespace ILSpy.Debugger.Services CurrentLineBookmark.Instance.LineNumber, out token); // var val = CodeMappingsStorage[CurrentLineBookmark.Instance.Type.FullName]; -// +// // var mapping = val.Find(m => m.MetadataToken == token); -// +// // return mapping.MemberCodeMappings.FirstOrDefault(s => s.ILInstructionOffset.From == instruction.ILInstructionOffset.From); } @@ -829,32 +829,38 @@ namespace ILSpy.Debugger.Services var debugType = frame.MethodInfo.DeclaringType; string fullName = debugType.Namespace + "." + debugType.Name; - // search for type in the current assembly list - TypeReference typeRef = null; - foreach (var assembly in DebugData.LoadedAssemblies) { - foreach (var module in assembly.Modules) { - if (module.TryGetTypeReference(fullName, out typeRef)) { - break; + if (DebugData.LoadedAssemblies == null) + Continue(); + else { + // search for type in the current assembly list + TypeReference typeRef = null; + foreach (var assembly in DebugData.LoadedAssemblies) { + foreach (var module in assembly.Modules) { + if (module.TryGetTypeReference(fullName, out typeRef)) { + break; + } } + + if (typeRef != null) + break; } - if (typeRef != null) - break; - } - - if (typeRef != null) { - // decompile on demand - AstBuilder builder = new AstBuilder(new DecompilerContext()); - builder.AddType(typeRef.Resolve()); - builder.GenerateCode(new PlainTextOutput()); - - // jump - if (CodeMappingsStorage.GetSourceCodeFromMetadataTokenAndOffset(token, ilOffset, out type, out line)) { - DebuggerService.JumpToCurrentLine(type, line, 0, line, 0); + if (typeRef != null) { + // decompile on demand + AstBuilder builder = new AstBuilder(new DecompilerContext()); + builder.AddType(typeRef.Resolve()); + builder.GenerateCode(new PlainTextOutput()); + + // jump + if (CodeMappingsStorage.GetSourceCodeFromMetadataTokenAndOffset(token, ilOffset, out type, out line)) { + DebuggerService.JumpToCurrentLine(type, line, 0, line, 0); + } else { + StepOut(); + } + } else { + // continue since we cannot find the debugged type + StepOut(); } - } else { - // continue since we cannot find the debugged type - Continue(); } } } diff --git a/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs b/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs index 4c4693660..8f5c8104b 100644 --- a/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs +++ b/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs @@ -124,7 +124,7 @@ namespace ICSharpCode.Decompiler.Ast public void StartNode(AstNode node) { var ranges = node.Annotation>(); - if (ranges != null) + if (ranges != null && ranges.Count > 0) { // find the ancestor that has method mapping as annotation if (node.Ancestors != null && node.Ancestors.Count() > 0) @@ -135,10 +135,12 @@ namespace ICSharpCode.Decompiler.Ast var map = mapping.MemberCodeMappings.Find(s => s.SourceCodeLine == output.CurrentLine); foreach (var range in ranges) { + // make sure we have one ILRange per source code line if (map == null) { mapping.MemberCodeMappings.Add(new SourceCodeMapping { ILInstructionOffset = range, - SourceCodeLine = output.CurrentLine + SourceCodeLine = output.CurrentLine, + MemberMapping = mapping }); } else { if (map.ILInstructionOffset.From > range.From) diff --git a/ICSharpCode.Decompiler/CodeMappings.cs b/ICSharpCode.Decompiler/CodeMappings.cs index d5535a900..cbe2a51e6 100644 --- a/ICSharpCode.Decompiler/CodeMappings.cs +++ b/ICSharpCode.Decompiler/CodeMappings.cs @@ -34,13 +34,32 @@ namespace ICSharpCode.Decompiler /// public ILRange ILInstructionOffset { get; set; } + /// + /// Gets or sets the member mapping this source code mapping belongs to. + /// + public MemberMapping MemberMapping { get; set; } + + /// + /// Retrieves the array that contains the IL range and the missing gaps between ranges. + /// + /// public int[] ToArray() { - int[] result = new int[2]; - result[0] = ILInstructionOffset.From; - result[1] = ILInstructionOffset.To; + var resultList = new List(); + resultList.Add(ILInstructionOffset.From); + resultList.Add(ILInstructionOffset.To); - return result; + var map = MemberMapping.MemberCodeMappings.Find(m => m.ILInstructionOffset.From >= ILInstructionOffset.To); + if (map != null && map.ILInstructionOffset.From != ILInstructionOffset.To) { + resultList.Add(ILInstructionOffset.To); + resultList.Add(map.ILInstructionOffset.From); + } + +// var tempList = MemberMapping.GetAllUnknownMappings(ILInstructionOffset.To); +// if (tempList.Count != 0) +// resultList.AddRange(tempList); + + return resultList.ToArray(); } } @@ -64,14 +83,35 @@ namespace ICSharpCode.Decompiler /// public List MemberCodeMappings { get; set; } - public int[] ToArray() + /// + /// Gets the list of all unknown/gaps code mappings greater than a value.
+ /// Eg.: for (0-9, 11-14, 16-27) the return list is (9,11,14,16) for start value 0 (or lower than 9). + ///
+ /// Start value. + /// + public List GetAllUnknownMappings(int startValue) { - int[] result = new int[MemberCodeMappings.Count * 2]; - int i = 0; - foreach (var element in MemberCodeMappings) { - result[i] = element.ILInstructionOffset.From; - result[i+1] = element.ILInstructionOffset.To; - i+=2; + var result = new List(); + var data = MemberCodeMappings.OrderBy(m => m.ILInstructionOffset.From); + var prevMap = data.ElementAt(0); + + for (int i = 1; i < data.Count(); ++i) { + var map = data.ElementAt(i); + // consider only the next mappings + if (map.ILInstructionOffset.To <= startValue) { + prevMap = map; + continue; + } + + // if there is not gap, move on + if (prevMap.ILInstructionOffset.To == map.ILInstructionOffset.From) { + prevMap = map; + continue; + } + + result.Add(prevMap.ILInstructionOffset.To); + result.Add(map.ILInstructionOffset.From); + prevMap = map; } return result; diff --git a/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs index 88056ee71..1f58c123f 100644 --- a/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs @@ -87,7 +87,8 @@ namespace ICSharpCode.Decompiler.Disassembler methodMapping.MemberCodeMappings.Add( new SourceCodeMapping() { SourceCodeLine = output.CurrentLine, - ILInstructionOffset = new ILRange { From = inst.Offset, To = inst.Next == null ? method.Body.CodeSize : inst.Next.Offset } + ILInstructionOffset = new ILRange { From = inst.Offset, To = inst.Next == null ? method.Body.CodeSize : inst.Next.Offset }, + MemberMapping = methodMapping }); inst.WriteTo(output); @@ -155,7 +156,8 @@ namespace ICSharpCode.Decompiler.Disassembler currentMethodMapping.MemberCodeMappings.Add( new SourceCodeMapping() { SourceCodeLine = output.CurrentLine, - ILInstructionOffset = new ILRange { From = inst.Offset, To = inst.Next == null ? codeSize : inst.Next.Offset } + ILInstructionOffset = new ILRange { From = inst.Offset, To = inst.Next == null ? codeSize : inst.Next.Offset }, + MemberMapping = currentMethodMapping }); }