diff --git a/Debugger/Debugger.Core/Breakpoint.cs b/Debugger/Debugger.Core/Breakpoint.cs index dd8e8fef9..551bf6390 100644 --- a/Debugger/Debugger.Core/Breakpoint.cs +++ b/Debugger/Debugger.Core/Breakpoint.cs @@ -176,11 +176,12 @@ namespace Debugger public class ILBreakpoint : Breakpoint { - public ILBreakpoint(NDebugger debugger, string typeName, int line, uint metadataToken, int offset, bool enabled) + public ILBreakpoint(NDebugger debugger, string typeName, string memberReferenceName, int line, uint metadataToken, int offset, bool enabled) { this.Debugger = debugger; this.Line = line; this.TypeName = typeName; + this.MemberReferenceName = memberReferenceName; this.MetadataToken = metadataToken; this.ILOffset = offset; this.Enabled = enabled; @@ -190,6 +191,8 @@ namespace Debugger public int ILOffset { get; private set; } + public string MemberReferenceName { get; private set; } + public override bool SetBreakpoint(Module module) { SourcecodeSegment segment = SourcecodeSegment.CreateForIL(module, this.Line, (int)MetadataToken, ILOffset); diff --git a/Debugger/Debugger.Core/MetaData/DebugMethodInfo.cs b/Debugger/Debugger.Core/MetaData/DebugMethodInfo.cs index 0a72f01f6..35bb84845 100644 --- a/Debugger/Debugger.Core/MetaData/DebugMethodInfo.cs +++ b/Debugger/Debugger.Core/MetaData/DebugMethodInfo.cs @@ -68,7 +68,7 @@ namespace Debugger.MetaData sb.Append(this.ReturnType.Name); sb.Append(" "); } else { - sb.Append("void "); + sb.Append("System.Void "); } sb.Append(this.DeclaringType.FullName); @@ -80,7 +80,7 @@ namespace Debugger.MetaData if (!first) sb.Append(", "); first = false; - sb.Append(p.ParameterType.Name); + sb.Append(p.ParameterType.FullName); sb.Append(" "); sb.Append(p.Name); } @@ -89,6 +89,37 @@ namespace Debugger.MetaData } } + /// Name including the declaring type, return type without parameters names + public string FullNameWithoutParameterNames { + get { + StringBuilder sb = new StringBuilder(); + + if (this.IsStatic) { + sb.Append("static "); + } + if (this.ReturnType != null) { + sb.Append(this.ReturnType.Name); + sb.Append(" "); + } else { + sb.Append("System.Void "); + } + + sb.Append(this.DeclaringType.FullName); + sb.Append("."); + sb.Append(this.Name); + sb.Append("("); + bool first = true; + foreach(DebugParameterInfo p in GetParameters()) { + if (!first) + sb.Append(", "); + first = false; + sb.Append(p.ParameterType.FullName); + } + sb.Append(")"); + return sb.ToString(); + } + } + /// public override string Name { get { return methodProps.Name; } diff --git a/Debugger/ILSpy.Debugger/AvalonEdit/IconBarMargin.cs b/Debugger/ILSpy.Debugger/AvalonEdit/IconBarMargin.cs index f3a1931b7..c2c1236e5 100644 --- a/Debugger/ILSpy.Debugger/AvalonEdit/IconBarMargin.cs +++ b/Debugger/ILSpy.Debugger/AvalonEdit/IconBarMargin.cs @@ -4,10 +4,10 @@ using System; using System.Collections.Generic; using System.ComponentModel.Composition; +using System.Linq; using System.Windows; using System.Windows.Input; using System.Windows.Media; - using ICSharpCode.AvalonEdit.Editing; using ICSharpCode.AvalonEdit.Rendering; using ICSharpCode.AvalonEdit.Utils; @@ -59,7 +59,8 @@ namespace ICSharpCode.ILSpy.Debugger.AvalonEdit // create a dictionary line number => first bookmark Dictionary bookmarkDict = new Dictionary(); foreach (var bm in BookmarkManager.Bookmarks) { - if (DebugData.CurrentMemberReference == null || bm.Member.FullName != DebugData.CurrentMemberReference.FullName) + if (DebugData.DecompiledMemberReferences == null || DebugData.DecompiledMemberReferences.Count == 0 || + !DebugData.DecompiledMemberReferences.ContainsKey(bm.MemberReference.FullName)) continue; if (bm is BreakpointBookmark && ((BreakpointBookmark)bm).Language != DebugData.Language) @@ -121,8 +122,7 @@ namespace ICSharpCode.ILSpy.Debugger.AvalonEdit BookmarkBase result = null; foreach (BookmarkBase bm in BookmarkManager.Bookmarks) { if (bm.LineNumber == line && - DebugData.CurrentMemberReference != null && - bm.Member.FullName == DebugData.CurrentMemberReference.FullName) { + DebugData.DecompiledMemberReferences != null && DebugData.DecompiledMemberReferences.ContainsKey(bm.MemberReference.FullName)) { if (result == null || bm.ZOrder > result.ZOrder) result = bm; } @@ -190,16 +190,6 @@ namespace ICSharpCode.ILSpy.Debugger.AvalonEdit dragStarted = true; InvalidateVisual(); } - - if (DebugData.CurrentMemberReference == null) - return; - - BreakpointBookmark bm = BookmarkManager.Bookmarks.Find( - b => b.Member.FullName == DebugData.CurrentMemberReference.FullName && - b.LineNumber == GetLineFromMousePosition(e) - && b is BreakpointBookmark) as BreakpointBookmark; - - this.ToolTip = (bm != null) ? bm.Tooltip : null; } protected override void OnMouseUp(MouseButtonEventArgs e) @@ -228,24 +218,32 @@ namespace ICSharpCode.ILSpy.Debugger.AvalonEdit return; } if (e.ChangedButton == MouseButton.Left) { - if (DebugData.CurrentMemberReference != null) { + if (DebugData.DecompiledMemberReferences != null && DebugData.DecompiledMemberReferences.Count > 0) { // check if the codemappings exists for this line var storage = DebugData.CodeMappings; - uint token; - var instruction = storage.GetInstructionByTypeAndLine(DebugData.CurrentMemberReference.FullName, line, out token); + uint token = 0; + foreach (var member in DebugData.DecompiledMemberReferences.Values) { + string memberName = member.FullName; + var instruction = storage[memberName].GetInstructionByTypeAndLine(memberName, line, out token); + + if (instruction == null) { + continue; + } + + // no bookmark on the line: create a new breakpoint + DebuggerService.ToggleBreakpointAt( + member, + line, + DebugData.Language); + break; + } - if (instruction == null) { - MessageBox.Show(string.Format("Missing code mappings for {0} at line {1}", DebugData.CurrentMemberReference.FullName, line), + if (token == 0) { + MessageBox.Show(string.Format("Missing code mappings at line {0}.", line), "Code mappings", MessageBoxButton.OK, MessageBoxImage.Information); return; } - - // no bookmark on the line: create a new breakpoint - DebuggerService.ToggleBreakpointAt( - DebugData.CurrentMemberReference, - line, - DebugData.Language); } } InvalidateVisual(); diff --git a/Debugger/ILSpy.Debugger/AvalonEdit/TextMarkerService.cs b/Debugger/ILSpy.Debugger/AvalonEdit/TextMarkerService.cs index 9b1a31be1..92c174391 100644 --- a/Debugger/ILSpy.Debugger/AvalonEdit/TextMarkerService.cs +++ b/Debugger/ILSpy.Debugger/AvalonEdit/TextMarkerService.cs @@ -27,7 +27,7 @@ namespace ICSharpCode.ILSpy.Debugger.AvalonEdit TextSegmentCollection markers = new TextSegmentCollection(); public TextMarkerService() - { + { BookmarkManager.Added += new BookmarkEventHandler(BookmarkManager_Added); BookmarkManager.Removed += new BookmarkEventHandler(BookmarkManager_Removed); } @@ -53,11 +53,9 @@ namespace ICSharpCode.ILSpy.Debugger.AvalonEdit { if (e.Bookmark is MarkerBookmark) { var bm = (MarkerBookmark)e.Bookmark; - if (DebugData.CurrentMemberReference != null && DebugData.CurrentMemberReference == bm.Member) { - // add bookmark for the current type - DocumentLine line = codeEditor.Document.GetLineByNumber(bm.LineNumber); - bm.CreateMarker(this, line.Offset, line.Length); - } + // add bookmark for the current type + DocumentLine line = codeEditor.Document.GetLineByNumber(bm.LineNumber); + bm.CreateMarker(this, line.Offset, line.Length); } } @@ -305,7 +303,7 @@ namespace ICSharpCode.ILSpy.Debugger.AvalonEdit Redraw(); } } - + } /// public object ToolTip { get; set; } diff --git a/Debugger/ILSpy.Debugger/Bookmarks/BookmarkBase.cs b/Debugger/ILSpy.Debugger/Bookmarks/BookmarkBase.cs index b452aaa8e..dadee8d2d 100644 --- a/Debugger/ILSpy.Debugger/Bookmarks/BookmarkBase.cs +++ b/Debugger/ILSpy.Debugger/Bookmarks/BookmarkBase.cs @@ -41,7 +41,7 @@ namespace ICSharpCode.ILSpy.Debugger.Bookmarks } - public MemberReference Member { get; set; } + public MemberReference MemberReference { get; set; } public int LineNumber { get { return location.Line; } @@ -66,7 +66,7 @@ namespace ICSharpCode.ILSpy.Debugger.Bookmarks public BookmarkBase(MemberReference member, AstLocation location) { - this.Member = member; + this.MemberReference = member; this.Location = location; } diff --git a/Debugger/ILSpy.Debugger/Bookmarks/BookmarkManager.cs b/Debugger/ILSpy.Debugger/Bookmarks/BookmarkManager.cs index 3ec7a14b9..500e1b92f 100644 --- a/Debugger/ILSpy.Debugger/Bookmarks/BookmarkManager.cs +++ b/Debugger/ILSpy.Debugger/Bookmarks/BookmarkManager.cs @@ -31,7 +31,7 @@ namespace ICSharpCode.ILSpy.Debugger.Bookmarks List marks = new List(); foreach (BookmarkBase mark in bookmarks) { - if (typeName == mark.Member.FullName) { + if (typeName == mark.MemberReference.FullName) { marks.Add(mark); } } @@ -56,7 +56,7 @@ namespace ICSharpCode.ILSpy.Debugger.Bookmarks return false; if (a.GetType() != b.GetType()) return false; - if (a.Member.FullName != b.Member.FullName) + if (a.MemberReference.FullName != b.MemberReference.FullName) return false; return a.LineNumber == b.LineNumber; } @@ -150,20 +150,23 @@ namespace ICSharpCode.ILSpy.Debugger.Bookmarks // 1. Save it's data int line = CurrentLineBookmark.Instance.LineNumber; - var markerType = CurrentLineBookmark.Instance.Member; + var markerType = CurrentLineBookmark.Instance.MemberReference; + + if (!oldMappings.ContainsKey(markerType.FullName) || !oldMappings.ContainsKey(markerType.FullName)) + return; // 2. Remove it CurrentLineBookmark.Remove(); // 3. map the marker line uint token; - var instruction = oldMappings.GetInstructionByTypeAndLine(markerType.FullName, line, out token); + var instruction = oldMappings[markerType.FullName].GetInstructionByTypeAndLine(markerType.FullName, line, out token); if (instruction == null) return; MemberReference memberReference; int newline; - if (newMappings.GetSourceCodeFromMetadataTokenAndOffset(markerType.FullName, token, instruction.ILInstructionOffset.From, out memberReference, out newline)) { + if (newMappings[markerType.FullName].GetSourceCodeFromMetadataTokenAndOffset(token, instruction.ILInstructionOffset.From, out memberReference, out newline)) { // 4. create breakpoint for new languages CurrentLineBookmark.SetPosition(memberReference, newline, 0, newline, 0); } @@ -191,13 +194,17 @@ namespace ICSharpCode.ILSpy.Debugger.Bookmarks foreach (var bp in oldbps) { uint token; - var instruction = oldMappings.GetInstructionByTypeAndLine(bp.Member.FullName, bp.LineNumber, out token); + string name = bp.MemberReference.FullName; + if (!oldMappings.ContainsKey(name) || !oldMappings.ContainsKey(name)) + continue; + + var instruction = oldMappings[name].GetInstructionByTypeAndLine(bp.MemberReference.FullName, bp.LineNumber, out token); if (instruction == null) continue; MemberReference memberReference; int line; - if (newMappings.GetSourceCodeFromMetadataTokenAndOffset(bp.Member.FullName, token, instruction.ILInstructionOffset.From, out memberReference, out line)) { + if (newMappings[name].GetSourceCodeFromMetadataTokenAndOffset(token, instruction.ILInstructionOffset.From, out memberReference, out line)) { // 2. create breakpoint for new languages var bookmark = new BreakpointBookmark(memberReference, new AstLocation(line, 0), BreakpointAction.Break, newLanguage); AddMark(bookmark); diff --git a/Debugger/ILSpy.Debugger/Bookmarks/BreakpointBookmark.cs b/Debugger/ILSpy.Debugger/Bookmarks/BreakpointBookmark.cs index 4509abb80..cc4189c83 100644 --- a/Debugger/ILSpy.Debugger/Bookmarks/BreakpointBookmark.cs +++ b/Debugger/ILSpy.Debugger/Bookmarks/BreakpointBookmark.cs @@ -91,7 +91,7 @@ namespace ICSharpCode.ILSpy.Debugger.Bookmarks ITextMarker marker = markerService.Create(offset, length); marker.BackgroundColor = Color.FromRgb(180, 38, 38); marker.ForegroundColor = Colors.White; - marker.IsVisible = b => b is MarkerBookmark && ((MarkerBookmark)b).Member == DebugData.CurrentMemberReference; + marker.IsVisible = b => b is MarkerBookmark && DebugData.DecompiledMemberReferences.ContainsKey(((MarkerBookmark)b).MemberReference.FullName); marker.Bookmark = this; this.Marker = marker; diff --git a/Debugger/ILSpy.Debugger/Bookmarks/CurrentLineBookmark.cs b/Debugger/ILSpy.Debugger/Bookmarks/CurrentLineBookmark.cs index f1ace04fc..b83f1ca77 100644 --- a/Debugger/ILSpy.Debugger/Bookmarks/CurrentLineBookmark.cs +++ b/Debugger/ILSpy.Debugger/Bookmarks/CurrentLineBookmark.cs @@ -81,7 +81,7 @@ namespace ICSharpCode.ILSpy.Debugger.Bookmarks 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).Member == DebugData.CurrentMemberReference; + marker.IsVisible = b => b is MarkerBookmark && DebugData.DecompiledMemberReferences.ContainsKey(((MarkerBookmark)b).MemberReference.FullName); marker.Bookmark = this; this.Marker = marker; return marker; diff --git a/Debugger/ILSpy.Debugger/DebuggedData.cs b/Debugger/ILSpy.Debugger/DebuggedData.cs index 426ebd8df..179662bbc 100644 --- a/Debugger/ILSpy.Debugger/DebuggedData.cs +++ b/Debugger/ILSpy.Debugger/DebuggedData.cs @@ -18,11 +18,6 @@ namespace ICSharpCode.ILSpy.Debugger { static DecompiledLanguages language; - /// - /// Gets or sets the current debugged member reference. Can be a type or a member of a type (method, property). - /// - public static MemberReference CurrentMemberReference { get; set; } - /// /// Gets or sets the decompiled language. /// @@ -42,19 +37,10 @@ namespace ICSharpCode.ILSpy.Debugger /// public static IEnumerable LoadedAssemblies { get; set; } - /// - /// Returns true if the CurrentMember is a type (TypeDefinition). Otherwise, returns false (is MethodDefinition or PropertyDefinition). - /// - public static bool IsCurrentMemberReferenceType { - get { - return CurrentMemberReference is TypeDefinition; - } - } - /// /// Gets or sets the current code mappings. /// - public static Tuple> CodeMappings { get; set; } + public static Dictionary> CodeMappings { get; set; } /// /// Gets or sets the local variables of the current decompiled type, method, etc. @@ -64,7 +50,17 @@ namespace ICSharpCode.ILSpy.Debugger /// /// Gets or sets the old code mappings. /// - public static Tuple> OldCodeMappings { get; set; } + public static Dictionary> OldCodeMappings { get; set; } + + /// + /// Gets or sets the MembeReference that was decompiled (a TypeDefinition, MethodDefinition, etc) + /// + public static Dictionary DecompiledMemberReferences { get; set; } + + /// + /// Gets or sets the debug type. + /// + public static bool DebugWholeTypesOnly { get; set; } /// /// Occures when the language is changed. diff --git a/Debugger/ILSpy.Debugger/DebuggerSettings.cs b/Debugger/ILSpy.Debugger/DebuggerSettings.cs index 7177ac4a5..7599b2527 100644 --- a/Debugger/ILSpy.Debugger/DebuggerSettings.cs +++ b/Debugger/ILSpy.Debugger/DebuggerSettings.cs @@ -9,10 +9,13 @@ namespace ICSharpCode.ILSpy.Debugger public class DebuggerSettings : INotifyPropertyChanged { bool showWarnings = true; + bool debugWholeTypesOnly = false; /// /// Show warnings messages. + /// Default value is true. /// + [DefaultValue(true)] public bool ShowWarnings { get { return showWarnings; } set { @@ -23,6 +26,21 @@ namespace ICSharpCode.ILSpy.Debugger } } + /// + /// True, if debug only whole types; otherwise false (debug only methods and properties). + /// Default value is false. + /// + [DefaultValue(false)] + public bool DebugWholeTypesOnly { + get { return debugWholeTypesOnly; } + set { + if (debugWholeTypesOnly != value) { + debugWholeTypesOnly = value; + OnPropertyChanged("DebugWholeTypesOnly"); + } + } + } + public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) diff --git a/Debugger/ILSpy.Debugger/Services/Debugger/DebuggerService.cs b/Debugger/ILSpy.Debugger/Services/Debugger/DebuggerService.cs index eedcb1cec..5ed475333 100644 --- a/Debugger/ILSpy.Debugger/Services/Debugger/DebuggerService.cs +++ b/Debugger/ILSpy.Debugger/Services/Debugger/DebuggerService.cs @@ -166,7 +166,7 @@ namespace ICSharpCode.ILSpy.Debugger.Services public static void ToggleBreakpointAt(MemberReference member, int lineNumber, DecompiledLanguages language) { BookmarkManager.ToggleBookmark( - member.FullName, lineNumber, + member.FullName.Replace("::", "."), lineNumber, b => b.CanToggle && b is BreakpointBookmark, location => new BreakpointBookmark(member, location, BreakpointAction.Break, language)); } diff --git a/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs b/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs index ce7216481..07c3275f4 100644 --- a/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs +++ b/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs @@ -288,10 +288,11 @@ namespace ICSharpCode.ILSpy.Debugger.Services { isMatch = false; frame = debuggedProcess.SelectedThread.MostRecentStackFrame; + var debugType = (DebugType)frame.MethodInfo.DeclaringType; + string nameKey = DebugData.DebugWholeTypesOnly ? debugType.FullNameWithoutGenericArguments : frame.MethodInfo.FullNameWithoutParameterNames; // get the mapped instruction from the current line marker or the next one - return DebugData.CodeMappings.GetInstructionByTypeTokenAndOffset( - ((DebugType)frame.MethodInfo.DeclaringType).FullNameWithoutGenericArguments, + return DebugData.CodeMappings[nameKey].GetInstructionByTypeTokenAndOffset( (uint)frame.MethodInfo.MetadataToken, frame.IP, out isMatch); } @@ -560,13 +561,16 @@ namespace ICSharpCode.ILSpy.Debugger.Services Breakpoint breakpoint = null; uint token; - SourceCodeMapping map = DebugData.CodeMappings - .GetInstructionByTypeAndLine(bookmark.Member.FullName, bookmark.LineNumber, out token); + SourceCodeMapping map = DebugData.CodeMappings[bookmark.MemberReference.FullName] + .GetInstructionByTypeAndLine(bookmark.MemberReference.FullName, bookmark.LineNumber, out token); if (map != null) { + var declaringType = bookmark.MemberReference.DeclaringType; + breakpoint = new ILBreakpoint( debugger, - bookmark.Member.FullName, + (declaringType ?? bookmark.MemberReference).FullName, + bookmark.MemberReference.FullName, bookmark.LineNumber, token, map.ILInstructionOffset.From, @@ -745,7 +749,7 @@ namespace ICSharpCode.ILSpy.Debugger.Services foreach (var bookmark in DebuggerService.Breakpoints) { var breakpoint = debugger.Breakpoints.FirstOrDefault( - b => b.Line == bookmark.LineNumber && b.TypeName == bookmark.Member.FullName); + b => b.Line == bookmark.LineNumber && (b as ILBreakpoint).MemberReferenceName.CreateKey() == bookmark.MemberReference.FullName.CreateKey()); if (breakpoint == null) continue; // set the breakpoint only if the module contains the type @@ -818,14 +822,18 @@ namespace ICSharpCode.ILSpy.Debugger.Services int ilOffset = frame.IP; int line; MemberReference memberReference; + string nameKey = DebugData.DebugWholeTypesOnly ? debugType.FullNameWithoutGenericArguments : frame.MethodInfo.FullNameWithoutParameterNames; - if (DebugData.CodeMappings.GetSourceCodeFromMetadataTokenAndOffset(debugType.FullNameWithoutGenericArguments, token, ilOffset, out memberReference, out line) - && memberReference.DeclaringType == null) { - DebuggerService.RemoveCurrentLineMarker(); - DebuggerService.JumpToCurrentLine(memberReference, line, 0, line, 0); - } else { - // is possible that the type is not decompiled yet, so we must do a decompilation on demand - DecompileOnDemand(frame); + foreach (var key in DebugData.CodeMappings.Keys) { + if (key.CreateKey() == nameKey.CreateKey()) { + if (DebugData.CodeMappings[key].GetSourceCodeFromMetadataTokenAndOffset(token, ilOffset, out memberReference, out line)) { + DebuggerService.RemoveCurrentLineMarker(); + DebuggerService.JumpToCurrentLine(memberReference, line, 0, line, 0); + } else { + // is possible that the type is not decompiled yet, so we must do a decompilation on demand + DecompileOnDemand(frame); + } + } } } } @@ -837,7 +845,6 @@ namespace ICSharpCode.ILSpy.Debugger.Services uint token = (uint)frame.MethodInfo.MetadataToken; int ilOffset = frame.IP; string fullName = debugType.FullNameWithoutGenericArguments; - fullName = fullName.Replace("+", "/"); if (DebugData.LoadedAssemblies == null) throw new NullReferenceException("No DebugData assemblies!"); @@ -845,6 +852,7 @@ namespace ICSharpCode.ILSpy.Debugger.Services // search for type in the current assembly list TypeDefinition typeDef = null; TypeDefinition nestedTypeDef = null; + MemberReference member = null; foreach (var assembly in DebugData.LoadedAssemblies) { if ((assembly.FullName.StartsWith("System") || assembly.FullName.StartsWith("Microsoft") || assembly.FullName.StartsWith("mscorlib")) && @@ -854,9 +862,9 @@ namespace ICSharpCode.ILSpy.Debugger.Services foreach (var module in assembly.Modules) { var localType = module.GetType(fullName); if (localType != null) { - if (localType.DeclaringType == null) + if (localType.DeclaringType == null) { typeDef = localType; - else { + } else { nestedTypeDef = localType; typeDef = localType.DeclaringType; } @@ -868,12 +876,22 @@ namespace ICSharpCode.ILSpy.Debugger.Services } if (typeDef != null) { - // decompile on demand - Tuple> codeMappings = null; - if (DebugData.CodeMappings.Item1 != (nestedTypeDef ?? typeDef).FullName) { + member = nestedTypeDef ?? typeDef; + + // decompile on demand if the type was not decompiled + Dictionary> codeMappings = null; + if (!DebugData.CodeMappings.ContainsKey(member.FullName)) { if (DebugData.Language == DecompiledLanguages.IL) { var dis = new ReflectionDisassembler(new PlainTextOutput(), true, CancellationToken.None); + dis.DisassembleType(nestedTypeDef ?? typeDef); +// else if (ICSharpCode.Decompiler.CodeMappings.DecompiledMember is MethodDefinition) +// dis.DisassembleMethod(ICSharpCode.Decompiler.CodeMappings.DecompiledMember as MethodDefinition); +// else if (ICSharpCode.Decompiler.CodeMappings.DecompiledMember is PropertyDefinition) +// dis.DisassembleProperty(ICSharpCode.Decompiler.CodeMappings.DecompiledMember as PropertyDefinition); +// else if (ICSharpCode.Decompiler.CodeMappings.DecompiledMember is EventDefinition) +// dis.DisassembleEvent(ICSharpCode.Decompiler.CodeMappings.DecompiledMember as EventDefinition); + codeMappings = dis.CodeMappings; } else { AstBuilder builder = new AstBuilder(new DecompilerContext(typeDef.Module)); @@ -886,7 +904,8 @@ namespace ICSharpCode.ILSpy.Debugger.Services int line; MemberReference memberReference; codeMappings = codeMappings ?? DebugData.CodeMappings; - if (codeMappings.GetSourceCodeFromMetadataTokenAndOffset((nestedTypeDef ?? typeDef).FullName, token, ilOffset, out memberReference, out line)) { + string name = (nestedTypeDef ?? typeDef).FullName; + if (codeMappings[name].GetSourceCodeFromMetadataTokenAndOffset(token, ilOffset, out memberReference, out line)) { DebuggerService.RemoveCurrentLineMarker(); DebuggerService.JumpToCurrentLine(nestedTypeDef ?? typeDef, line, 0, line, 0); } else { diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index daab20f0f..aa5dc14f0 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -46,6 +46,8 @@ namespace ICSharpCode.Decompiler.Ast this.DecompileMethodBodies = true; this.LocalVariables = new ConcurrentDictionary>(); + this.CodeMappings = new Dictionary>(); + this.DecompiledMemberReferences = new Dictionary(); } public static bool MemberIsHidden(MemberReference member, DecompilerSettings settings) @@ -159,6 +161,10 @@ namespace ICSharpCode.Decompiler.Ast public void AddType(TypeDefinition typeDef) { + // add to mappings information + this.DecompiledMemberReferences.Add(typeDef.FullName, typeDef); + this.CodeMappings.Add(typeDef.FullName, new List()); + var astType = CreateType(typeDef); NamespaceDeclaration astNS = GetCodeNamespace(typeDef.Namespace); if (astNS != null) { @@ -170,22 +176,36 @@ namespace ICSharpCode.Decompiler.Ast public void AddMethod(MethodDefinition method) { + // add to mappings information + this.CodeMappings.Add(method.FullName, new List()); + this.DecompiledMemberReferences.Add(method.FullName, method); + AstNode node = method.IsConstructor ? (AstNode)CreateConstructor(method) : CreateMethod(method); astCompileUnit.AddChild(node, CompilationUnit.MemberRole); } public void AddProperty(PropertyDefinition property) { + // add to mappings information + this.CodeMappings.Add(property.FullName, new List()); + this.DecompiledMemberReferences.Add(property.FullName, property); + astCompileUnit.AddChild(CreateProperty(property), CompilationUnit.MemberRole); } public void AddField(FieldDefinition field) { + this.DecompiledMemberReferences.Add(field.FullName, field); + astCompileUnit.AddChild(CreateField(field), CompilationUnit.MemberRole); } public void AddEvent(EventDefinition ev) { + // add to mappings information + this.DecompiledMemberReferences.Add(ev.FullName, ev); + this.CodeMappings.Add(ev.FullName, new List()); + astCompileUnit.AddChild(CreateEvent(ev), CompilationUnit.MemberRole); } @@ -195,11 +215,7 @@ namespace ICSharpCode.Decompiler.Ast /// /// TypeDeclaration or DelegateDeclaration. public AttributedNode CreateType(TypeDefinition typeDef) - { - // create CSharp code mappings - used for debugger - if (this.CodeMappings == null) - this.CodeMappings = new Tuple>(typeDef.FullName, new List()); - + { // create type TypeDefinition oldCurrentType = context.CurrentType; context.CurrentType = typeDef; @@ -603,12 +619,12 @@ namespace ICSharpCode.Decompiler.Ast // Add events foreach(EventDefinition eventDef in typeDef.Events) { - astType.AddChild(CreateEvent(eventDef), TypeDeclaration.MemberRole); + astType.AddChild(CreateEvent(eventDef, true), TypeDeclaration.MemberRole); } // Add properties foreach(PropertyDefinition propDef in typeDef.Properties) { - astType.Members.Add(CreateProperty(propDef)); + astType.Members.Add(CreateProperty(propDef, true)); } // Add methods @@ -616,16 +632,17 @@ namespace ICSharpCode.Decompiler.Ast if (MemberIsHidden(methodDef, context.Settings)) continue; if (methodDef.IsConstructor) - astType.Members.Add(CreateConstructor(methodDef)); + astType.Members.Add(CreateConstructor(methodDef, true)); else - astType.Members.Add(CreateMethod(methodDef)); + astType.Members.Add(CreateMethod(methodDef, true)); } } - AttributedNode CreateMethod(MethodDefinition methodDef) + AttributedNode CreateMethod(MethodDefinition methodDef, bool isTypeDecompiled = false) { // Create mapping - used in debugger - MemberMapping methodMapping = methodDef.CreateCodeMapping(this.CodeMappings); + string name = isTypeDecompiled ? methodDef.DeclaringType.FullName : methodDef.FullName; + MemberMapping methodMapping = methodDef.CreateCodeMapping(this.CodeMappings[name], isTypeDecompiled); MethodDeclaration astMethod = new MethodDeclaration(); astMethod.AddAnnotation(methodDef); @@ -717,10 +734,11 @@ namespace ICSharpCode.Decompiler.Ast } } - ConstructorDeclaration CreateConstructor(MethodDefinition methodDef) + ConstructorDeclaration CreateConstructor(MethodDefinition methodDef, bool isTypeDecompiled = false) { // Create mapping - used in debugger - MemberMapping methodMapping = methodDef.CreateCodeMapping(this.CodeMappings); + string name = isTypeDecompiled ? methodDef.DeclaringType.FullName : methodDef.FullName; + MemberMapping methodMapping = methodDef.CreateCodeMapping(this.CodeMappings[name], isTypeDecompiled); ConstructorDeclaration astMethod = new ConstructorDeclaration(); astMethod.AddAnnotation(methodDef); @@ -750,7 +768,7 @@ namespace ICSharpCode.Decompiler.Ast return m & ~Modifiers.Private; } - MemberDeclaration CreateProperty(PropertyDefinition propDef) + MemberDeclaration CreateProperty(PropertyDefinition propDef, bool isTypeDecompiled = false) { PropertyDeclaration astProp = new PropertyDeclaration(); astProp.AddAnnotation(propDef); @@ -785,9 +803,12 @@ namespace ICSharpCode.Decompiler.Ast } astProp.Name = CleanName(propDef.Name); astProp.ReturnType = ConvertType(propDef.PropertyType, propDef); + + string name = isTypeDecompiled ? propDef.DeclaringType.FullName : propDef.FullName; + if (propDef.GetMethod != null) { // Create mapping - used in debugger - MemberMapping methodMapping = propDef.GetMethod.CreateCodeMapping(this.CodeMappings); + MemberMapping methodMapping = propDef.GetMethod.CreateCodeMapping(this.CodeMappings[name], isTypeDecompiled); astProp.Getter = new Accessor(); astProp.Getter.Body = CreateMethodBody(propDef.GetMethod); @@ -801,7 +822,7 @@ namespace ICSharpCode.Decompiler.Ast } if (propDef.SetMethod != null) { // Create mapping - used in debugger - MemberMapping methodMapping = propDef.SetMethod.CreateCodeMapping(this.CodeMappings); + MemberMapping methodMapping = propDef.SetMethod.CreateCodeMapping(this.CodeMappings[name], isTypeDecompiled); astProp.Setter = new Accessor(); astProp.Setter.Body = CreateMethodBody(propDef.SetMethod); @@ -837,7 +858,7 @@ namespace ICSharpCode.Decompiler.Ast return astIndexer; } - AttributedNode CreateEvent(EventDefinition eventDef) + AttributedNode CreateEvent(EventDefinition eventDef, bool isTypeDecompiled = false) { if (eventDef.AddMethod != null && eventDef.AddMethod.IsAbstract) { // An abstract event cannot be custom @@ -859,9 +880,12 @@ namespace ICSharpCode.Decompiler.Ast astEvent.Modifiers = ConvertModifiers(eventDef.AddMethod); else astEvent.PrivateImplementationType = ConvertType(eventDef.AddMethod.Overrides.First().DeclaringType); + + string name = isTypeDecompiled ? eventDef.DeclaringType.FullName : eventDef.FullName; + if (eventDef.AddMethod != null) { // Create mapping - used in debugger - MemberMapping methodMapping = eventDef.AddMethod.CreateCodeMapping(this.CodeMappings); + MemberMapping methodMapping = eventDef.AddMethod.CreateCodeMapping(this.CodeMappings[name], isTypeDecompiled); astEvent.AddAccessor = new Accessor { Body = CreateMethodBody(eventDef.AddMethod) @@ -872,7 +896,7 @@ namespace ICSharpCode.Decompiler.Ast } if (eventDef.RemoveMethod != null) { // Create mapping - used in debugger - MemberMapping methodMapping = eventDef.RemoveMethod.CreateCodeMapping(this.CodeMappings); + MemberMapping methodMapping = eventDef.AddMethod.CreateCodeMapping(this.CodeMappings[name], isTypeDecompiled); astEvent.RemoveAccessor = new Accessor { Body = CreateMethodBody(eventDef.RemoveMethod) @@ -1371,12 +1395,17 @@ namespace ICSharpCode.Decompiler.Ast /// /// /// - public Tuple> CodeMappings { get; private set; } + public Dictionary> CodeMappings { get; private set; } /// /// Gets the local variables for the current decompiled type, method, etc. /// The key is the metadata token. /// public ConcurrentDictionary> LocalVariables { get; private set; } + + /// + /// + /// + public Dictionary DecompiledMemberReferences { get; private set; } } } diff --git a/ICSharpCode.Decompiler/CodeMappings.cs b/ICSharpCode.Decompiler/CodeMappings.cs index c169d0e6f..1cedacb45 100644 --- a/ICSharpCode.Decompiler/CodeMappings.cs +++ b/ICSharpCode.Decompiler/CodeMappings.cs @@ -27,7 +27,12 @@ namespace ICSharpCode.Decompiler /// /// Gets the code mappings. /// - Tuple> CodeMappings { get; } + Dictionary> CodeMappings { get; } + + /// + /// Gets the MembeReference that is decompiled (a TypeDefinition, MethodDefinition, etc) + /// + Dictionary DecompiledMemberReferences { get; } } /// @@ -133,15 +138,17 @@ namespace ICSharpCode.Decompiler /// Code mappings helper class. /// public static class CodeMappings - { + { /// /// Create code mapping for a method. /// /// Method to create the mapping for. - /// Source code mapping storage. + /// Source code mapping storage. + /// True, if a full type was decompiled; false otherwise. internal static MemberMapping CreateCodeMapping( this MethodDefinition member, - Tuple> codeMappings) + List codeMappings, + bool isTypeDecompiled) { if (member == null || !member.HasBody) return null; @@ -151,17 +158,21 @@ namespace ICSharpCode.Decompiler // create IL/CSharp code mappings - used in debugger MemberMapping currentMemberMapping = null; - if (codeMappings.Item1 == member.DeclaringType.FullName) { - var mapping = codeMappings.Item2; - if (mapping.Find(map => (int)map.MetadataToken == member.MetadataToken.ToInt32()) == null) { - currentMemberMapping = new MemberMapping() { - MetadataToken = (uint)member.MetadataToken.ToInt32(), - MemberReference = member.DeclaringType.Resolve(), - MemberCodeMappings = new List(), - CodeSize = member.Body.CodeSize - }; - mapping.Add(currentMemberMapping); - } + string key = isTypeDecompiled ? member.DeclaringType.FullName : member.FullName; + + if (codeMappings.Find(map => (int)map.MetadataToken == member.MetadataToken.ToInt32()) == null) { + currentMemberMapping = new MemberMapping() { + MetadataToken = (uint)member.MetadataToken.ToInt32(), + MemberCodeMappings = new List(), + CodeSize = member.Body.CodeSize + }; + + if (isTypeDecompiled) + currentMemberMapping.MemberReference = member.DeclaringType.Resolve(); + else + currentMemberMapping.MemberReference = member.Resolve(); + + codeMappings.Add(currentMemberMapping); } return currentMemberMapping; @@ -171,31 +182,20 @@ namespace ICSharpCode.Decompiler /// Gets source code mapping and metadata token based on type name and line number. /// /// Code mappings storage. - /// Type name. + /// Member reference name. /// Line number. /// Metadata token. /// public static SourceCodeMapping GetInstructionByTypeAndLine( - this Tuple> codeMappings, + this List codeMappings, string memberReferenceName, int lineNumber, out uint metadataToken) { if (codeMappings == null) - throw new ArgumentNullException("CodeMappings storage must be valid!"); - - if (codeMappings.Item1 != memberReferenceName) { - metadataToken = 0; - return null; - } - - if (lineNumber <= 0) { - metadataToken = 0; - return null; - } + throw new ArgumentException("CodeMappings storage must be valid!"); - var methodMappings = codeMappings.Item2; - foreach (var maping in methodMappings) { + foreach (var maping in codeMappings) { var map = maping.MemberCodeMappings.Find(m => m.SourceCodeLine == lineNumber); if (map != null) { metadataToken = maping.MetadataToken; @@ -211,33 +211,26 @@ namespace ICSharpCode.Decompiler /// Gets a mapping given a type, a token and an IL offset. /// /// Code mappings storage. - /// Type name. + /// Member reference name. /// Token. /// IL offset. /// True, if perfect match. /// A code mapping. public static SourceCodeMapping GetInstructionByTypeTokenAndOffset( - this Tuple> codeMappings, - string memberReferenceName, + this List codeMappings, uint token, - int ilOffset, out bool isMatch) + int ilOffset, + out bool isMatch) { isMatch = false; - memberReferenceName = memberReferenceName.Replace("+", "/"); if (codeMappings == null) throw new ArgumentNullException("CodeMappings storage must be valid!"); - if (codeMappings.Item1 != memberReferenceName) { - return null; - } - - var methodMappings = codeMappings.Item2; - var maping = methodMappings.Find(m => m.MetadataToken == token); + var maping = codeMappings.Find(m => m.MetadataToken == token); - if (maping == null) { + if (maping == null) return null; - } // try find an exact match var map = maping.MemberCodeMappings.Find(m => m.ILInstructionOffset.From <= ilOffset && ilOffset < m.ILInstructionOffset.To); @@ -260,31 +253,25 @@ namespace ICSharpCode.Decompiler /// Gets the source code and type name from metadata token and offset. /// /// Code mappings storage. - /// Current type name. /// Metadata token. /// IL offset. /// Type definition. /// Line number. /// It is possible to exist to different types from different assemblies with the same metadata token. public static bool GetSourceCodeFromMetadataTokenAndOffset( - this Tuple> codeMappings, - string memberReferenceName, + this List codeMappings, uint token, int ilOffset, - out MemberReference type, + out MemberReference member, out int line) { - type = null; + member = null; line = 0; if (codeMappings == null) - throw new ArgumentNullException("CodeMappings storage must be valid!"); + throw new ArgumentException("CodeMappings storage must be valid!"); - memberReferenceName = memberReferenceName.Replace("+", "/"); - if (codeMappings.Item1 != memberReferenceName) - return false; - - var mapping = codeMappings.Item2.Find(m => m.MetadataToken == token); + var mapping = codeMappings.Find(m => m.MetadataToken == token); if (mapping == null) return false; @@ -299,9 +286,19 @@ namespace ICSharpCode.Decompiler } } - type = mapping.MemberReference; + member = mapping.MemberReference; line = codeMapping.SourceCodeLine; return true; } + + /// + /// Create a key by replacing "::" with ".", "+" with "/", " " with ""; + /// + /// Item to convert. + /// + public static string CreateKey(this string item) + { + return item.Replace("+", "/").Replace("::", ".").Replace(" ", string.Empty); + } } } diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs index 5231b78c1..e898ac86f 100644 --- a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs @@ -44,6 +44,9 @@ namespace ICSharpCode.Decompiler.Disassembler this.cancellationToken = cancellationToken; this.detectControlStructure = detectControlStructure; this.methodBodyDisassembler = new MethodBodyDisassembler(output, detectControlStructure, cancellationToken); + + this.CodeMappings = new Dictionary>(); + this.DecompiledMemberReferences = new Dictionary(); } #region Disassemble Method @@ -92,14 +95,21 @@ namespace ICSharpCode.Decompiler.Disassembler { MethodImplAttributes.NoOptimization, "nooptimization" }, }; - public void DisassembleMethod(MethodDefinition method) + public void DisassembleMethod(MethodDefinition method, bool isTypeDecompiled = false) { + // create mappings for decompiled methods only + string name = isTypeDecompiled ? method.DeclaringType.FullName : method.FullName; + if (!isTypeDecompiled) { + this.CodeMappings.Add(name, new List()); + this.DecompiledMemberReferences.Add(method.FullName, method); + } + // write method header output.WriteDefinition(".method ", method); - DisassembleMethodInternal(method); + DisassembleMethodInternal(method, isTypeDecompiled); } - void DisassembleMethodInternal(MethodDefinition method) + void DisassembleMethodInternal(MethodDefinition method, bool isTypeDecompiled) { // .method public hidebysig specialname // instance default class [mscorlib]System.IO.TextWriter get_BaseWriter () cil managed @@ -118,7 +128,6 @@ namespace ICSharpCode.Decompiler.Disassembler //call convention WriteEnum(method.CallingConvention & (MethodCallingConvention)0x1f, callingConvention); - //return type method.ReturnType.WriteTo(output); output.Write(' '); @@ -149,7 +158,9 @@ namespace ICSharpCode.Decompiler.Disassembler if (method.HasBody) { // create IL code mappings - used in debugger - MemberMapping methodMapping = method.CreateCodeMapping(this.CodeMappings); + string name = isTypeDecompiled ? method.DeclaringType.FullName : method.FullName; + MemberMapping methodMapping = method.CreateCodeMapping(this.CodeMappings[name], isTypeDecompiled); + methodBodyDisassembler.Disassemble(method.Body, methodMapping); } @@ -192,8 +203,13 @@ namespace ICSharpCode.Decompiler.Disassembler { FieldAttributes.NotSerialized, "notserialized" }, }; - public void DisassembleField(FieldDefinition field) + public void DisassembleField(FieldDefinition field, bool isTypeDecompiled = false) { + // create mappings for decompiled fields only + if (!isTypeDecompiled) { + this.DecompiledMemberReferences.Add(field.FullName, field); + } + output.WriteDefinition(".field ", field); WriteEnum(field.Attributes & FieldAttributes.FieldAccessMask, fieldVisibility); WriteFlags(field.Attributes & ~(FieldAttributes.FieldAccessMask | FieldAttributes.HasDefault), fieldAttributes); @@ -221,8 +237,15 @@ namespace ICSharpCode.Decompiler.Disassembler { PropertyAttributes.HasDefault, "hasdefault" }, }; - public void DisassembleProperty(PropertyDefinition property) + public void DisassembleProperty(PropertyDefinition property, bool isTypeDecompiled = false) { + // create mappings for decompiled properties only + string name = isTypeDecompiled ? property.DeclaringType.FullName : property.FullName; + if (!isTypeDecompiled) { + this.CodeMappings.Add(name, new List()); + this.DecompiledMemberReferences.Add(property.FullName, property); + } + output.WriteDefinition(".property ", property); WriteFlags(property.Attributes, propertyAttributes); property.PropertyType.WriteTo(output); @@ -230,22 +253,22 @@ namespace ICSharpCode.Decompiler.Disassembler output.Write(DisassemblerHelpers.Escape(property.Name)); OpenBlock(false); WriteAttributes(property.CustomAttributes); - WriteNestedMethod(".get", property.GetMethod); - WriteNestedMethod(".set", property.SetMethod); + WriteNestedMethod(".get", property.GetMethod, isTypeDecompiled); + WriteNestedMethod(".set", property.SetMethod, isTypeDecompiled); foreach (var method in property.OtherMethods) { - WriteNestedMethod(".method", method); + WriteNestedMethod(".method", method, isTypeDecompiled); } CloseBlock(); } - void WriteNestedMethod(string keyword, MethodDefinition method) + void WriteNestedMethod(string keyword, MethodDefinition method, bool isTypeDecompiled) { if (method == null) return; if (detectControlStructure) { output.WriteDefinition(keyword, method); output.Write(' '); - DisassembleMethodInternal(method); + DisassembleMethodInternal(method, isTypeDecompiled); } else { output.Write(keyword); output.Write(' '); @@ -261,8 +284,15 @@ namespace ICSharpCode.Decompiler.Disassembler { EventAttributes.RTSpecialName, "rtspecialname" }, }; - public void DisassembleEvent(EventDefinition ev) + public void DisassembleEvent(EventDefinition ev, bool isTypeDecompiled = false) { + // create mappings for decompiled events only + string name = isTypeDecompiled ? ev.DeclaringType.FullName : ev.FullName; + if (!isTypeDecompiled) { + this.CodeMappings.Add(name, new List()); + this.DecompiledMemberReferences.Add(ev.FullName, ev); + } + output.WriteDefinition(".event ", ev); WriteFlags(ev.Attributes, eventAttributes); ev.EventType.WriteTo(output); @@ -270,11 +300,11 @@ namespace ICSharpCode.Decompiler.Disassembler output.Write(DisassemblerHelpers.Escape(ev.Name)); OpenBlock(false); WriteAttributes(ev.CustomAttributes); - WriteNestedMethod(".add", ev.AddMethod); - WriteNestedMethod(".remove", ev.RemoveMethod); - WriteNestedMethod(".invoke", ev.InvokeMethod); + WriteNestedMethod(".add", ev.AddMethod, isTypeDecompiled); + WriteNestedMethod(".remove", ev.RemoveMethod, isTypeDecompiled); + WriteNestedMethod(".invoke", ev.InvokeMethod, isTypeDecompiled); foreach (var method in ev.OtherMethods) { - WriteNestedMethod(".method", method); + WriteNestedMethod(".method", method, isTypeDecompiled); } CloseBlock(); } @@ -317,8 +347,10 @@ namespace ICSharpCode.Decompiler.Disassembler public void DisassembleType(TypeDefinition type) { // create IL code mappings - used for debugger - if (this.CodeMappings == null) - this.CodeMappings = new Tuple>(type.FullName, new List()); + if (this.CodeMappings == null) { + this.CodeMappings.Add(type.FullName, new List()); + this.DecompiledMemberReferences.Add(type.FullName, type); + } // start writing IL output.WriteDefinition(".class ", type); @@ -383,7 +415,7 @@ namespace ICSharpCode.Decompiler.Disassembler output.WriteLine("// Fields"); foreach (var field in type.Fields) { cancellationToken.ThrowIfCancellationRequested(); - DisassembleField(field); + DisassembleField(field, true); } output.WriteLine(); } @@ -391,7 +423,7 @@ namespace ICSharpCode.Decompiler.Disassembler output.WriteLine("// Properties"); foreach (var prop in type.Properties) { cancellationToken.ThrowIfCancellationRequested(); - DisassembleProperty(prop); + DisassembleProperty(prop, true); } output.WriteLine(); } @@ -399,7 +431,7 @@ namespace ICSharpCode.Decompiler.Disassembler output.WriteLine("// Events"); foreach (var ev in type.Events) { cancellationToken.ThrowIfCancellationRequested(); - DisassembleEvent(ev); + DisassembleEvent(ev, true); output.WriteLine(); } output.WriteLine(); @@ -410,7 +442,7 @@ namespace ICSharpCode.Decompiler.Disassembler foreach (var m in type.Methods) { cancellationToken.ThrowIfCancellationRequested(); if (!(detectControlStructure && accessorMethods.Contains(m))) { - DisassembleMethod(m); + DisassembleMethod(m, true); output.WriteLine(); } } @@ -605,10 +637,14 @@ namespace ICSharpCode.Decompiler.Disassembler CloseBlock(); } + /// /// - public Tuple> CodeMappings { - get; - private set; - } + /// + public Dictionary> CodeMappings { get; private set; } + + /// + /// + /// + public Dictionary DecompiledMemberReferences { get; private set; } } } diff --git a/ILSpy/CSharpLanguage.cs b/ILSpy/CSharpLanguage.cs index 67ba3d078..ceddd89e2 100644 --- a/ILSpy/CSharpLanguage.cs +++ b/ILSpy/CSharpLanguage.cs @@ -92,7 +92,7 @@ namespace ICSharpCode.ILSpy codeDomBuilder.AddMethod(method); codeDomBuilder.RunTransformations(transformAbortCondition); codeDomBuilder.GenerateCode(output); - OnDecompilationFinished(new DecompileEventArgs { CodeMappings = codeDomBuilder.CodeMappings, LocalVariables = codeDomBuilder.LocalVariables }); + OnDecompilationFinished(new DecompileEventArgs { CodeMappings = codeDomBuilder.CodeMappings, LocalVariables = codeDomBuilder.LocalVariables, DecompiledMemberReferences = codeDomBuilder.DecompiledMemberReferences }); } public override void DecompileProperty(PropertyDefinition property, ITextOutput output, DecompilationOptions options) @@ -102,7 +102,7 @@ namespace ICSharpCode.ILSpy codeDomBuilder.AddProperty(property); codeDomBuilder.RunTransformations(transformAbortCondition); codeDomBuilder.GenerateCode(output); - OnDecompilationFinished(new DecompileEventArgs { CodeMappings = codeDomBuilder.CodeMappings, LocalVariables = codeDomBuilder.LocalVariables }); + OnDecompilationFinished(new DecompileEventArgs { CodeMappings = codeDomBuilder.CodeMappings, LocalVariables = codeDomBuilder.LocalVariables, DecompiledMemberReferences = codeDomBuilder.DecompiledMemberReferences }); } public override void DecompileField(FieldDefinition field, ITextOutput output, DecompilationOptions options) @@ -112,7 +112,7 @@ namespace ICSharpCode.ILSpy codeDomBuilder.AddField(field); codeDomBuilder.RunTransformations(transformAbortCondition); codeDomBuilder.GenerateCode(output); - OnDecompilationFinished(null); + OnDecompilationFinished(new DecompileEventArgs { DecompiledMemberReferences = codeDomBuilder.DecompiledMemberReferences }); } public override void DecompileEvent(EventDefinition ev, ITextOutput output, DecompilationOptions options) @@ -122,7 +122,7 @@ namespace ICSharpCode.ILSpy codeDomBuilder.AddEvent(ev); codeDomBuilder.RunTransformations(transformAbortCondition); codeDomBuilder.GenerateCode(output); - OnDecompilationFinished(new DecompileEventArgs { CodeMappings = codeDomBuilder.CodeMappings, LocalVariables = codeDomBuilder.LocalVariables }); + OnDecompilationFinished(new DecompileEventArgs { CodeMappings = codeDomBuilder.CodeMappings, LocalVariables = codeDomBuilder.LocalVariables, DecompiledMemberReferences = codeDomBuilder.DecompiledMemberReferences }); } public override void DecompileType(TypeDefinition type, ITextOutput output, DecompilationOptions options) @@ -131,7 +131,7 @@ namespace ICSharpCode.ILSpy codeDomBuilder.AddType(type); codeDomBuilder.RunTransformations(transformAbortCondition); codeDomBuilder.GenerateCode(output); - OnDecompilationFinished(new DecompileEventArgs { CodeMappings = codeDomBuilder.CodeMappings, LocalVariables = codeDomBuilder.LocalVariables }); + OnDecompilationFinished(new DecompileEventArgs { CodeMappings = codeDomBuilder.CodeMappings, LocalVariables = codeDomBuilder.LocalVariables, DecompiledMemberReferences = codeDomBuilder.DecompiledMemberReferences }); } public override void DecompileAssembly(AssemblyDefinition assembly, string fileName, ITextOutput output, DecompilationOptions options) diff --git a/ILSpy/Commands/DebuggerCommands.cs b/ILSpy/Commands/DebuggerCommands.cs index cdc48d51a..461a2d538 100644 --- a/ILSpy/Commands/DebuggerCommands.cs +++ b/ILSpy/Commands/DebuggerCommands.cs @@ -163,8 +163,8 @@ namespace ICSharpCode.ILSpy.Commands // jump to type & expand folding if (CurrentLineBookmark.Instance != null) { - if (CurrentLineBookmark.Instance.Member != DebugData.CurrentMemberReference) - MainWindow.Instance.JumpToReference(CurrentLineBookmark.Instance.Member); + if (!DebugData.DecompiledMemberReferences.ContainsKey(CurrentLineBookmark.Instance.MemberReference.FullName)) + MainWindow.Instance.JumpToReference(CurrentLineBookmark.Instance.MemberReference); MainWindow.Instance.TextView.UnfoldAndScroll(CurrentLineBookmark.Instance.LineNumber); } diff --git a/ILSpy/ILLanguage.cs b/ILSpy/ILLanguage.cs index ae503d23c..684d57dc3 100644 --- a/ILSpy/ILLanguage.cs +++ b/ILSpy/ILLanguage.cs @@ -55,35 +55,35 @@ namespace ICSharpCode.ILSpy { var dis = new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken); dis.DisassembleMethod(method); - OnDecompilationFinished(new DecompileEventArgs { CodeMappings = dis.CodeMappings }); + OnDecompilationFinished(new DecompileEventArgs { CodeMappings = dis.CodeMappings, DecompiledMemberReferences = dis.DecompiledMemberReferences }); } public override void DecompileField(FieldDefinition field, ITextOutput output, DecompilationOptions options) { - new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken).DisassembleField(field); - OnDecompilationFinished(null); + var dis = new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken); + dis.DisassembleField(field); + OnDecompilationFinished(new DecompileEventArgs { DecompiledMemberReferences = dis.DecompiledMemberReferences }); } public override void DecompileProperty(PropertyDefinition property, ITextOutput output, DecompilationOptions options) { var dis = new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken); dis.DisassembleProperty(property); - OnDecompilationFinished(new DecompileEventArgs { CodeMappings = dis.CodeMappings }); + OnDecompilationFinished(new DecompileEventArgs { CodeMappings = dis.CodeMappings, DecompiledMemberReferences = dis.DecompiledMemberReferences }); } public override void DecompileEvent(EventDefinition ev, ITextOutput output, DecompilationOptions options) { var dis = new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken); dis.DisassembleEvent(ev); - OnDecompilationFinished(new DecompileEventArgs { CodeMappings = dis.CodeMappings }); + OnDecompilationFinished(new DecompileEventArgs { CodeMappings = dis.CodeMappings, DecompiledMemberReferences = dis.DecompiledMemberReferences }); } public override void DecompileType(TypeDefinition type, ITextOutput output, DecompilationOptions options) { var dis = new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken); dis.DisassembleType(type); - - OnDecompilationFinished(new DecompileEventArgs { CodeMappings = dis.CodeMappings }); + OnDecompilationFinished(new DecompileEventArgs { CodeMappings = dis.CodeMappings , DecompiledMemberReferences = dis.DecompiledMemberReferences}); } public override void DecompileNamespace(string nameSpace, IEnumerable types, ITextOutput output, DecompilationOptions options) diff --git a/ILSpy/Language.cs b/ILSpy/Language.cs index dfa663a2d..338a0dbf0 100644 --- a/ILSpy/Language.cs +++ b/ILSpy/Language.cs @@ -37,12 +37,17 @@ namespace ICSharpCode.ILSpy /// /// Gets ot sets the code mappings /// - public Tuple> CodeMappings { get; set; } + public Dictionary> CodeMappings { get; set; } /// /// Gets or sets the local variables. /// public ConcurrentDictionary> LocalVariables { get; set; } + + /// + /// Gets the list of MembeReferences that are decompiled (TypeDefinitions, MethodDefinitions, etc) + /// + public Dictionary DecompiledMemberReferences { get; set; } } /// diff --git a/ILSpy/Options/DebuggerSettingsPanel.xaml b/ILSpy/Options/DebuggerSettingsPanel.xaml index 27c24d0b6..9b130bb38 100644 --- a/ILSpy/Options/DebuggerSettingsPanel.xaml +++ b/ILSpy/Options/DebuggerSettingsPanel.xaml @@ -5,6 +5,9 @@ Show warning messages + Debug whole types only \ No newline at end of file diff --git a/ILSpy/Options/DebuggerSettingsPanel.xaml.cs b/ILSpy/Options/DebuggerSettingsPanel.xaml.cs index 0455dfe14..cd5b7a7d8 100644 --- a/ILSpy/Options/DebuggerSettingsPanel.xaml.cs +++ b/ILSpy/Options/DebuggerSettingsPanel.xaml.cs @@ -19,6 +19,10 @@ namespace ICSharpCode.ILSpy.Options [ExportOptionPage(Title = "Debugger", Order = 1)] partial class DebuggerSettingsPanel : UserControl, IOptionPage { + private const string DEBUGGER_SETTINGS = "DebuggerSettings"; + private const string SHOW_WARNINGS = "showWarnings"; + private const string DEBUG_WHOLE_TYPES_ONLY = "debugWholeTypesOnly"; + public DebuggerSettingsPanel() { InitializeComponent(); @@ -39,19 +43,21 @@ namespace ICSharpCode.ILSpy.Options public static DebuggerSettings LoadDebuggerSettings(ILSpySettings settings) { - XElement e = settings["DebuggerSettings"]; + XElement e = settings[DEBUGGER_SETTINGS]; DebuggerSettings s = new DebuggerSettings(); - s.ShowWarnings = (bool?)e.Attribute("showWarnings") ?? s.ShowWarnings; + s.ShowWarnings = (bool?)e.Attribute(SHOW_WARNINGS) ?? s.ShowWarnings; + s.DebugWholeTypesOnly = (bool?)e.Attribute(DEBUG_WHOLE_TYPES_ONLY) ?? s.DebugWholeTypesOnly; return s; } public void Save(XElement root) { var s = (DebuggerSettings)this.DataContext; - XElement section = new XElement("DebuggerSettings"); - section.SetAttributeValue("showWarnings", s.ShowWarnings); + XElement section = new XElement(DEBUGGER_SETTINGS); + section.SetAttributeValue(SHOW_WARNINGS, s.ShowWarnings); + section.SetAttributeValue(DEBUG_WHOLE_TYPES_ONLY, s.DebugWholeTypesOnly); - XElement existingElement = root.Element("DebuggerSettings"); + XElement existingElement = root.Element(DEBUGGER_SETTINGS); if (existingElement != null) existingElement.ReplaceWith(section); else diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index c07d657a8..e1e836feb 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -44,6 +44,7 @@ using ICSharpCode.ILSpy.Debugger; using ICSharpCode.ILSpy.Debugger.AvalonEdit; using ICSharpCode.ILSpy.Debugger.Bookmarks; using ICSharpCode.ILSpy.Debugger.Tooltips; +using ICSharpCode.ILSpy.Options; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.XmlDoc; using ICSharpCode.NRefactory.Documentation; @@ -335,9 +336,9 @@ namespace ICSharpCode.ILSpy.TextView void DoDecompile(DecompilationContext context, int outputLengthLimit) { // reset type - DebugData.CurrentMemberReference = null; DebugData.OldCodeMappings = DebugData.CodeMappings; TextEditorListener.Instance.ClosePopup(); + bool isDecompilationOk = true; RunWithCancellation( delegate (CancellationToken ct) { // creation of the background task @@ -363,39 +364,34 @@ namespace ICSharpCode.ILSpy.TextView output.WriteLine(ex.ToString()); } ShowOutput(output); - - // reset type - DebugData.CurrentMemberReference = null; + isDecompilationOk = false; } finally { // set the language DebugData.Language = MainWindow.Instance.sessionSettings.FilterSettings.Language.Name.StartsWith("IL") ? DecompiledLanguages.IL : DecompiledLanguages.CSharp; + bool debugOnlyTypes = DebuggerSettingsPanel.CurrentDebuggerSettings.DebugWholeTypesOnly; + DebugData.DebugWholeTypesOnly = debugOnlyTypes; - if (DebugData.CurrentMemberReference != null) { - // TODO: show margin for single methods and properties - if (context.TreeNodes.Count() == 1 && DebugData.IsCurrentMemberReferenceType) { - iconMargin.Visibility = Visibility.Visible; + if (isDecompilationOk) { + if (DebugData.DecompiledMemberReferences != null && DebugData.DecompiledMemberReferences.Count > 0) { + // repaint bookmarks iconMargin.InvalidateVisual(); // show the currentline marker var bm = CurrentLineBookmark.Instance; - if (bm != null && DebugData.CurrentMemberReference != null) { - if (DebugData.CurrentMemberReference == bm.Member) { + if (bm != null) { + if (DebugData.DecompiledMemberReferences.ContainsKey(bm.MemberReference.FullName)) { DocumentLine line = textEditor.Document.GetLineByNumber(bm.LineNumber); bm.Marker = bm.CreateMarker(textMarkerService, line.Offset, line.Length); } UnfoldAndScroll(bm.LineNumber); } - } else { - // hide the margin - iconMargin.Visibility = Visibility.Collapsed; } } else { // remove currentline marker CurrentLineBookmark.Remove(); - iconMargin.Visibility = Visibility.Collapsed; } } }); @@ -454,9 +450,6 @@ namespace ICSharpCode.ILSpy.TextView if (i > 0) textOutput.WriteLine(); - if (nodes[i] is IMemberTreeNode) { - DebugData.CurrentMemberReference = (nodes[i] as IMemberTreeNode).Member; - } context.Options.CancellationToken.ThrowIfCancellationRequested(); nodes[i].Decompile(context.Language, textOutput, context.Options); } @@ -466,8 +459,13 @@ namespace ICSharpCode.ILSpy.TextView static void Language_DecompileFinished(object sender, DecompileEventArgs e) { if (e != null) { - DebugData.CodeMappings = e.CodeMappings; + DebugData.CodeMappings = e.CodeMappings; DebugData.LocalVariables = e.LocalVariables; + DebugData.DecompiledMemberReferences = e.DecompiledMemberReferences; + } else { + DebugData.CodeMappings = null; + DebugData.LocalVariables = null; + DebugData.DecompiledMemberReferences = null; } } #endregion