diff --git a/Debugger/ILSpy.Debugger/Commands/BreakpointCommand.cs b/Debugger/ILSpy.Debugger/Commands/BreakpointCommand.cs index 31e7023c1..31d1c0560 100644 --- a/Debugger/ILSpy.Debugger/Commands/BreakpointCommand.cs +++ b/Debugger/ILSpy.Debugger/Commands/BreakpointCommand.cs @@ -28,8 +28,8 @@ namespace ICSharpCode.ILSpy.Debugger.Commands // check if the codemappings exists for this line var storage = DebugInformation.CodeMappings; int token = 0; - foreach (var storageEntry in storage) { - var instruction = storageEntry.Value.GetInstructionByLineNumber(line, out token); + foreach (var storageEntry in storage.Values) { + var instruction = storageEntry.GetInstructionByLineNumber(line, out token); if (instruction == null) { continue; @@ -40,8 +40,7 @@ namespace ICSharpCode.ILSpy.Debugger.Commands instruction.MemberMapping.MemberReference, line, token, - instruction.ILInstructionOffset, - DebugInformation.Language); + instruction.ILInstructionOffset); break; } diff --git a/Debugger/ILSpy.Debugger/Models/TreeModel/ExpressionNode.cs b/Debugger/ILSpy.Debugger/Models/TreeModel/ExpressionNode.cs index e1f69771b..1a5ec7d6c 100644 --- a/Debugger/ILSpy.Debugger/Models/TreeModel/ExpressionNode.cs +++ b/Debugger/ILSpy.Debugger/Models/TreeModel/ExpressionNode.cs @@ -11,6 +11,7 @@ using System.Windows.Media; using Debugger; using Debugger.MetaData; +using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Ast; using ICSharpCode.Decompiler.ILAst; using ICSharpCode.ILSpy.Debugger.Services; @@ -161,9 +162,9 @@ namespace ICSharpCode.ILSpy.Debugger.Models.TreeModel } // get local variable index - IEnumerable list; - if (DebugInformation.LocalVariables.TryGetValue(token, out list)) { - var variable = list.FirstOrDefault(v => v.Name == targetName); + MemberMapping mapping; + if (DebugInformation.CodeMappings != null && DebugInformation.CodeMappings.TryGetValue(token, out mapping)) { + var variable = mapping.LocalVariables.FirstOrDefault(v => v.Name == targetName); if (variable != null && variable.OriginalVariable != null) { if (expression is MemberReferenceExpression) { var memberExpression = (MemberReferenceExpression)expression; diff --git a/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs b/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs index 92bc652e6..31bd7766b 100644 --- a/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs +++ b/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs @@ -288,7 +288,7 @@ namespace ICSharpCode.ILSpy.Debugger.Services if (DebugInformation.CodeMappings == null || !DebugInformation.CodeMappings.ContainsKey(key)) return null; - return DebugInformation.CodeMappings[key].GetInstructionByTokenAndOffset(key, frame.IP, out isMatch); + return DebugInformation.CodeMappings[key].GetInstructionByTokenAndOffset(frame.IP, out isMatch); } StackFrame GetStackFrame() @@ -301,7 +301,7 @@ namespace ICSharpCode.ILSpy.Debugger.Services frame.ILRanges = new [] { 0, 1 }; } else { //var frame = debuggedProcess.SelectedThread.MostRecentStackFrame; - frame.SourceCodeLine = map.SourceCodeLine; + frame.SourceCodeLine = map.StartLocation.Line; frame.ILRanges = map.ToArray(isMatch); } @@ -800,7 +800,7 @@ namespace ICSharpCode.ILSpy.Debugger.Services if (DebugInformation.CodeMappings != null && DebugInformation.CodeMappings.ContainsKey(token) && - DebugInformation.CodeMappings[token].GetInstructionByTokenAndOffset(token, ilOffset, out memberReference, out line)) { + DebugInformation.CodeMappings[token].GetInstructionByTokenAndOffset(ilOffset, out memberReference, out line)) { DebugInformation.DebugStepInformation = null; // we do not need to step into/out DebuggerService.RemoveCurrentLineMarker(); DebuggerService.JumpToCurrentLine(memberReference, line, 0, line, 0, ilOffset); diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index f40d5d154..db7dddf7c 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -125,7 +125,7 @@ namespace ICSharpCode.Decompiler.Ast astBlock.Statements.InsertBefore(insertionPoint, newVarDecl); } - astBlock.AddAnnotation(new MemberMapping(methodDef)); + astBlock.AddAnnotation(new MemberMapping(methodDef) { LocalVariables = localVariables }); return astBlock; } diff --git a/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs b/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs index 2d21e643a..965965de9 100644 --- a/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs +++ b/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs @@ -46,7 +46,7 @@ namespace ICSharpCode.Decompiler.Ast object memberRef = GetCurrentMemberReference(); if (memberRef != null) { - output.WriteReference(identifier, memberRef); + output.WriteReference(identifier, memberRef, isIconMapping: IsIconMapping()); return; } @@ -121,8 +121,9 @@ namespace ICSharpCode.Decompiler.Ast { // Attach member reference to token only if there's no identifier in the current node. MemberReference memberRef = GetCurrentMemberReference(); - if (memberRef != null && nodeStack.Peek().GetChildByRole(AstNode.Roles.Identifier).IsNull) - output.WriteReference(token, memberRef); + var node = nodeStack.Peek(); + if (memberRef != null && node.GetChildByRole(AstNode.Roles.Identifier).IsNull) + output.WriteReference(token, memberRef, isIconMapping: IsIconMapping()); else output.Write(token); } @@ -244,5 +245,23 @@ namespace ICSharpCode.Decompiler.Ast currentMemberMapping = parentMemberMappings.Pop(); } } + + private bool IsIconMapping() + { + if (nodeStack == null || nodeStack.Count == 0) + return false; + + var node = nodeStack.Peek(); + + return + node is FieldDeclaration || + node is ConstructorDeclaration || + node is EventDeclaration || + node is DestructorDeclaration || + node is DelegateDeclaration || + node is OperatorDeclaration|| + node is MemberDeclaration || + node is TypeDeclaration; + } } } diff --git a/ICSharpCode.Decompiler/CodeMappings.cs b/ICSharpCode.Decompiler/CodeMappings.cs index 83c4272b2..3fef8970b 100644 --- a/ICSharpCode.Decompiler/CodeMappings.cs +++ b/ICSharpCode.Decompiler/CodeMappings.cs @@ -29,25 +29,11 @@ using Mono.Cecil; namespace ICSharpCode.Decompiler { - [Obsolete] - public enum DecompiledLanguages - { - IL, - CSharp - } - /// /// Maps the source code to IL. /// public sealed class SourceCodeMapping { - [Obsolete("Use StartLocation instead - there might be multiple statements per line (e.g. for loops)")] - public int SourceCodeLine { - get { - return this.StartLocation.Line; - } - } - /// /// Gets or sets the start location of the instruction. /// @@ -78,7 +64,7 @@ namespace ICSharpCode.Decompiler // add list for the current source code line currentList.AddRange(ILRange.OrderAndJoint(MemberMapping.MemberCodeMappings - .FindAll(m => m.SourceCodeLine == this.SourceCodeLine) + .FindAll(m => m.StartLocation.Line == this.StartLocation.Line) .ConvertAll(m => m.ILInstructionOffset))); if (!isMatch) { @@ -103,7 +89,7 @@ namespace ICSharpCode.Decompiler } /// - /// Stores the method information and its source code mappings. + /// Stores the member information and its source code mappings. /// public sealed class MemberMapping { @@ -141,6 +127,11 @@ namespace ICSharpCode.Decompiler /// public List MemberCodeMappings { get; internal set; } + /// + /// Gets or sets the local variables. + /// + public IEnumerable LocalVariables { get; internal set; } + /// /// Gets the inverted IL Ranges.
/// E.g.: for (0-9, 11-14, 14-18, 21-25) => (9-11,18-21). @@ -164,40 +155,6 @@ namespace ICSharpCode.Decompiler ///
public static class CodeMappings { - /// - /// Create code mapping for a method. - /// - /// Method to create the mapping for. - /// Source code mapping storage. - /// The actual member reference. - internal static MemberMapping CreateCodeMapping( - this MethodDefinition member, - List codeMappings, - MemberReference actualMemberReference = null) - { - if (member == null || !member.HasBody) - return null; - - if (codeMappings == null) - return null; - - // create IL/CSharp code mappings - used in debugger - MemberMapping currentMemberMapping = null; - - if (codeMappings.Find(map => map.MetadataToken == member.MetadataToken.ToInt32()) == null) { - currentMemberMapping = new MemberMapping() { - MetadataToken = member.MetadataToken.ToInt32(), - MemberCodeMappings = new List(), - MemberReference = actualMemberReference ?? member, - CodeSize = member.Body.CodeSize - }; - - codeMappings.Add(currentMemberMapping); - } - - return currentMemberMapping; - } - /// /// Gets source code mapping and metadata token based on type name and line number. /// @@ -207,19 +164,17 @@ namespace ICSharpCode.Decompiler /// Metadata token. /// public static SourceCodeMapping GetInstructionByLineNumber( - this List codeMappings, + this MemberMapping codeMapping, int lineNumber, out int metadataToken) { - if (codeMappings == null) + if (codeMapping == null) throw new ArgumentException("CodeMappings storage must be valid!"); - foreach (var maping in codeMappings) { - var map = maping.MemberCodeMappings.Find(m => m.SourceCodeLine == lineNumber); - if (map != null) { - metadataToken = maping.MetadataToken; - return map; - } + var map = codeMapping.MemberCodeMappings.Find(m => m.StartLocation.Line == lineNumber); + if (map != null) { + metadataToken = codeMapping.MetadataToken; + return map; } metadataToken = 0; @@ -235,30 +190,24 @@ namespace ICSharpCode.Decompiler /// True, if perfect match. /// A code mapping. public static SourceCodeMapping GetInstructionByTokenAndOffset( - this List codeMappings, - int token, + this MemberMapping codeMapping, int ilOffset, out bool isMatch) { isMatch = false; - if (codeMappings == null) + if (codeMapping == null) throw new ArgumentNullException("CodeMappings storage must be valid!"); - var maping = codeMappings.Find(m => m.MetadataToken == token); - - if (maping == null) - return null; - // try find an exact match - var map = maping.MemberCodeMappings.Find(m => m.ILInstructionOffset.From <= ilOffset && ilOffset < m.ILInstructionOffset.To); + var map = codeMapping.MemberCodeMappings.Find(m => m.ILInstructionOffset.From <= ilOffset && ilOffset < m.ILInstructionOffset.To); if (map == null) { // get the immediate next one - map = maping.MemberCodeMappings.Find(m => m.ILInstructionOffset.From > ilOffset); + map = codeMapping.MemberCodeMappings.Find(m => m.ILInstructionOffset.From > ilOffset); isMatch = false; if (map == null) - map = maping.MemberCodeMappings.LastOrDefault(); // get the last + map = codeMapping.MemberCodeMappings.LastOrDefault(); // get the last return map; } @@ -270,15 +219,14 @@ namespace ICSharpCode.Decompiler /// /// Gets the source code and type name from metadata token and offset. /// - /// Code mappings storage. + /// Code mapping storage. /// 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 GetInstructionByTokenAndOffset( - this List codeMappings, - int token, + this MemberMapping mapping, int ilOffset, out MemberReference member, out int line) @@ -286,13 +234,9 @@ namespace ICSharpCode.Decompiler member = null; line = 0; - if (codeMappings == null) - throw new ArgumentException("CodeMappings storage must be valid!"); - - var mapping = codeMappings.Find(m => m.MetadataToken == token); if (mapping == null) - return false; - + throw new ArgumentException("CodeMappings storage must be valid!"); + var codeMapping = mapping.MemberCodeMappings.Find( cm => cm.ILInstructionOffset.From <= ilOffset && ilOffset <= cm.ILInstructionOffset.To - 1); if (codeMapping == null) { @@ -305,7 +249,7 @@ namespace ICSharpCode.Decompiler } member = mapping.MemberReference; - line = codeMapping.SourceCodeLine; + line = codeMapping.StartLocation.Line; return true; } } diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs index 4dc6728f3..8ffb68b16 100644 --- a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs @@ -179,6 +179,7 @@ namespace ICSharpCode.Decompiler.Disassembler output.Write(DisassemblerHelpers.Escape(method.Name + "$PST" + method.MetadataToken.ToInt32().ToString("X8"))); } else { output.Write(DisassemblerHelpers.Escape(method.Name)); + output.WriteReference(DisassemblerHelpers.Escape(method.Name), method, isIconMapping: true); } WriteTypeParameters(output, method); @@ -674,6 +675,7 @@ namespace ICSharpCode.Decompiler.Disassembler public void DisassembleField(FieldDefinition field) { output.WriteDefinition(".field ", field); + output.WriteReference(DisassemblerHelpers.Escape(field.Name), field, isIconMapping: true); WriteEnum(field.Attributes & FieldAttributes.FieldAccessMask, fieldVisibility); const FieldAttributes hasXAttributes = FieldAttributes.HasDefault | FieldAttributes.HasFieldMarshal | FieldAttributes.HasFieldRVA; WriteFlags(field.Attributes & ~(FieldAttributes.FieldAccessMask | hasXAttributes), fieldAttributes); @@ -712,6 +714,7 @@ namespace ICSharpCode.Decompiler.Disassembler currentMember = property; output.WriteDefinition(".property ", property); + output.WriteReference(DisassemblerHelpers.Escape(property.Name), property, isIconMapping: true); WriteFlags(property.Attributes, propertyAttributes); if (property.HasThis) output.Write("instance "); @@ -743,6 +746,8 @@ namespace ICSharpCode.Decompiler.Disassembler { if (method == null) return; + + output.WriteReference(DisassemblerHelpers.Escape(method.Name), method, isIconMapping: true); output.Write(keyword); output.Write(' '); method.WriteTo(output); @@ -762,6 +767,7 @@ namespace ICSharpCode.Decompiler.Disassembler currentMember = ev; output.WriteDefinition(".event ", ev); + output.WriteReference(DisassemblerHelpers.Escape(ev.Name), ev, isIconMapping: true); WriteFlags(ev.Attributes, eventAttributes); ev.EventType.WriteTo(output, ILNameSyntax.TypeName); output.Write(' '); @@ -816,6 +822,7 @@ namespace ICSharpCode.Decompiler.Disassembler { // start writing IL output.WriteDefinition(".class ", type); + output.WriteReference(DisassemblerHelpers.Escape(type.Name), type, isIconMapping: true); if ((type.Attributes & TypeAttributes.ClassSemanticMask) == TypeAttributes.Interface) output.Write("interface "); diff --git a/ICSharpCode.Decompiler/ITextOutput.cs b/ICSharpCode.Decompiler/ITextOutput.cs index b13c0d905..b98ad6960 100644 --- a/ICSharpCode.Decompiler/ITextOutput.cs +++ b/ICSharpCode.Decompiler/ITextOutput.cs @@ -19,6 +19,7 @@ using System; using System.IO; using ICSharpCode.NRefactory; +using Mono.Cecil; namespace ICSharpCode.Decompiler { @@ -32,7 +33,7 @@ namespace ICSharpCode.Decompiler void Write(string text); void WriteLine(); void WriteDefinition(string text, object definition); - void WriteReference(string text, object reference, bool isLocal = false); + void WriteReference(string text, object reference, bool isLocal = false, bool isIconMapping = false); void AddDebuggerMemberMapping(MemberMapping memberMapping); diff --git a/ICSharpCode.Decompiler/PlainTextOutput.cs b/ICSharpCode.Decompiler/PlainTextOutput.cs index 560016724..e7610d8a2 100644 --- a/ICSharpCode.Decompiler/PlainTextOutput.cs +++ b/ICSharpCode.Decompiler/PlainTextOutput.cs @@ -17,12 +17,57 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Collections.Generic; using System.IO; + using ICSharpCode.NRefactory; +using Mono.Cecil; namespace ICSharpCode.Decompiler { - public sealed class PlainTextOutput : ITextOutput + /// + /// Base text output. + /// Provides access to code mappings. + /// + public abstract class BaseTextOutput : ITextOutput + { + #region Code mappings + Dictionary codeMappings = new Dictionary(); + + public Dictionary CodeMappings { + get { return codeMappings; } + } + + public virtual void AddDebuggerMemberMapping(MemberMapping memberMapping) + { + if (memberMapping == null) + throw new ArgumentNullException("memberMapping"); + + int token = memberMapping.MetadataToken; + codeMappings.Add(token, memberMapping); + } + + #endregion + + #region ITextOutput members + public abstract TextLocation Location { get; } + public abstract void Indent(); + public abstract void Unindent(); + public abstract void Write(char ch); + public abstract void Write(string text); + public abstract void WriteLine(); + public abstract void WriteDefinition(string text, object definition); + public abstract void WriteReference(string text, object reference, bool isLocal, bool isIconMapping); + public abstract void MarkFoldStart(string collapsedText, bool defaultCollapsed); + public abstract void MarkFoldEnd(); + #endregion + } + + /// + /// Plain text output. + /// Can be used when there's no UI. + /// + public sealed class PlainTextOutput : BaseTextOutput { readonly TextWriter writer; int indent; @@ -43,7 +88,7 @@ namespace ICSharpCode.Decompiler this.writer = new StringWriter(); } - public TextLocation Location { + public override TextLocation Location { get { return new TextLocation(line, column + (needsIndent ? indent : 0)); } @@ -54,12 +99,12 @@ namespace ICSharpCode.Decompiler return writer.ToString(); } - public void Indent() + public override void Indent() { indent++; } - public void Unindent() + public override void Unindent() { indent--; } @@ -75,21 +120,21 @@ namespace ICSharpCode.Decompiler } } - public void Write(char ch) + public override void Write(char ch) { WriteIndent(); writer.Write(ch); column++; } - public void Write(string text) + public override void Write(string text) { WriteIndent(); writer.Write(text); column += text.Length; } - public void WriteLine() + public override void WriteLine() { writer.WriteLine(); needsIndent = true; @@ -97,25 +142,21 @@ namespace ICSharpCode.Decompiler column = 1; } - public void WriteDefinition(string text, object definition) + public override void WriteDefinition(string text, object definition) { Write(text); } - public void WriteReference(string text, object reference, bool isLocal) + public override void WriteReference(string text, object reference, bool isLocal, bool isIconMapping) { Write(text); } - void ITextOutput.MarkFoldStart(string collapsedText, bool defaultCollapsed) - { - } - - void ITextOutput.MarkFoldEnd() + public override void MarkFoldStart(string collapsedText, bool defaultCollapsed) { } - void ITextOutput.AddDebuggerMemberMapping(MemberMapping memberMapping) + public override void MarkFoldEnd() { } } diff --git a/ILSpy.SharpDevelop.LGPL/AvalonEdit/IconBarManager.cs b/ILSpy.SharpDevelop.LGPL/AvalonEdit/IconBarManager.cs index b39a0ebd8..12926b26f 100644 --- a/ILSpy.SharpDevelop.LGPL/AvalonEdit/IconBarManager.cs +++ b/ILSpy.SharpDevelop.LGPL/AvalonEdit/IconBarManager.cs @@ -8,7 +8,9 @@ using System.Collections.Specialized; using System.Linq; using ICSharpCode.ILSpy.Bookmarks; +using ICSharpCode.ILSpy.Debugger; using ICSharpCode.NRefactory.CSharp; +using Mono.Cecil; namespace ICSharpCode.ILSpy.AvalonEdit { @@ -42,25 +44,18 @@ namespace ICSharpCode.ILSpy.AvalonEdit public event EventHandler RedrawRequested; - public void UpdateClassMemberBookmarks(IEnumerable nodes, Type bookmarkType, Type memberType) + public void UpdateClassMemberBookmarks(Dictionary iconMappings, Type bookmarkType, Type memberType) { this.bookmarks.Clear(); - if (nodes == null || nodes.Count() == 0) + if (iconMappings == null || iconMappings.Count == 0) return; - foreach (var n in nodes) { - switch (n.NodeType) { - case NodeType.TypeDeclaration: - case NodeType.TypeReference: - this.bookmarks.Add(Activator.CreateInstance(bookmarkType, n) as IBookmark); - break; - case NodeType.Member: - this.bookmarks.Add(Activator.CreateInstance(memberType, n) as IBookmark); - break; - default: - // do nothing - break; + foreach (var n in iconMappings) { + if (n.Key is TypeDefinition) { + this.bookmarks.Add(Activator.CreateInstance(bookmarkType, n.Key, n.Value) as IBookmark); + } else { + this.bookmarks.Add(Activator.CreateInstance(memberType, n.Key, n.Value) as IBookmark); } } } diff --git a/ILSpy.SharpDevelop.LGPL/AvalonEdit/IconBarMargin.cs b/ILSpy.SharpDevelop.LGPL/AvalonEdit/IconBarMargin.cs index 63cc35dff..41e1d2fb8 100644 --- a/ILSpy.SharpDevelop.LGPL/AvalonEdit/IconBarMargin.cs +++ b/ILSpy.SharpDevelop.LGPL/AvalonEdit/IconBarMargin.cs @@ -7,6 +7,7 @@ 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; @@ -16,7 +17,6 @@ using ICSharpCode.ILSpy.Debugger; using ICSharpCode.ILSpy.Debugger.Bookmarks; using ICSharpCode.ILSpy.Debugger.Services; using ICSharpCode.NRefactory; -using ICSharpCode.NRefactory.CSharp; using Mono.Cecil; namespace ICSharpCode.ILSpy.AvalonEdit @@ -37,8 +37,6 @@ namespace ICSharpCode.ILSpy.AvalonEdit get { return manager; } } - public IList DecompiledMembers { get; set; } - public virtual void Dispose() { this.TextView = null; // detach from TextView (will also detach from manager) @@ -75,10 +73,6 @@ namespace ICSharpCode.ILSpy.AvalonEdit if (DebugInformation.CodeMappings == null || DebugInformation.CodeMappings.Count == 0 || !DebugInformation.CodeMappings.ContainsKey(((BreakpointBookmark)bm).FunctionToken)) continue; - } else { - if (DebugInformation.DecompiledMemberReferences == null || DebugInformation.DecompiledMemberReferences.Count == 0 || - !DebugInformation.DecompiledMemberReferences.ContainsKey(((MarkerBookmark)bm).MemberReference.MetadataToken.ToInt32())) - continue; } int line = bm.LineNumber; IBookmark existingBookmark; @@ -143,11 +137,14 @@ namespace ICSharpCode.ILSpy.AvalonEdit { BookmarkBase result = null; foreach (BookmarkBase bm in BookmarkManager.Bookmarks) { - if (bm.LineNumber == line && - this.DecompiledMembers != null && this.DecompiledMembers.Contains(bm.MemberReference)) { - if (result == null || bm.ZOrder > result.ZOrder) - return result; - } + if (bm.LineNumber != line) + continue; + if (DebugInformation.CodeMappings == null || DebugInformation.CodeMappings.Count == 0 || + !DebugInformation.CodeMappings.ContainsKey(((BreakpointBookmark)bm).FunctionToken)) + continue; + + if (result == null || bm.ZOrder > result.ZOrder) + return result; } return manager.Bookmarks.FirstOrDefault(b => b.LineNumber == line); @@ -275,7 +272,7 @@ namespace ICSharpCode.ILSpy.AvalonEdit return; var key = breakpoint.MemberReference.MetadataToken.ToInt32(); if (storage.ContainsKey(key)) - { + { breakpoint.ImageChanged -= delegate { InvalidateVisual(); }; InvalidateVisual(); } @@ -299,22 +296,22 @@ namespace ICSharpCode.ILSpy.AvalonEdit var key = breakpoint.FunctionToken; if (!storage.ContainsKey(key)) - { - continue; - } + continue; bool isMatch; - SourceCodeMapping map = storage[key].GetInstructionByTokenAndOffset(key, breakpoint.ILRange.From, out isMatch); + SourceCodeMapping map = storage[key].GetInstructionByTokenAndOffset(breakpoint.ILRange.From, out isMatch); if (map != null) { - BreakpointBookmark newBookmark = new BreakpointBookmark( - breakpoint.MemberReference, new TextLocation(map.SourceCodeLine, 0), breakpoint.FunctionToken, - map.ILInstructionOffset, BreakpointAction.Break, DebugInformation.Language); - newBookmark.IsEnabled = breakpoint.IsEnabled; - - newBookmarks.Add(newBookmark); + BreakpointBookmark newBookmark = new BreakpointBookmark(breakpoint.MemberReference, + new TextLocation(map.StartLocation.Line, 0), + breakpoint.FunctionToken, + map.ILInstructionOffset, + BreakpointAction.Break); + newBookmark.IsEnabled = breakpoint.IsEnabled; + + newBookmarks.Add(newBookmark); - BookmarkManager.RemoveMark(breakpoint); + BookmarkManager.RemoveMark(breakpoint); } } newBookmarks.ForEach(m => BookmarkManager.AddMark(m)); @@ -343,7 +340,7 @@ namespace ICSharpCode.ILSpy.AvalonEdit // 2. map the marker line MemberReference memberReference; int newline; - if (codeMappings[token].GetInstructionByTokenAndOffset(token, offset, out memberReference, out newline)) { + if (codeMappings[token].GetInstructionByTokenAndOffset(offset, out memberReference, out newline)) { // 3. create breakpoint for new languages DebuggerService.JumpToCurrentLine(memberReference, newline, 0, newline, 0, offset); } diff --git a/ILSpy.SharpDevelop.LGPL/Bookmarks/BreakpointBookmark.cs b/ILSpy.SharpDevelop.LGPL/Bookmarks/BreakpointBookmark.cs index 0b41404ed..ec2add4a0 100644 --- a/ILSpy.SharpDevelop.LGPL/Bookmarks/BreakpointBookmark.cs +++ b/ILSpy.SharpDevelop.LGPL/Bookmarks/BreakpointBookmark.cs @@ -27,8 +27,6 @@ namespace ICSharpCode.ILSpy.Debugger.Bookmarks bool isEnabled = true; BreakpointAction action = BreakpointAction.Break; - public DecompiledLanguages Language { get; private set; } - public BreakpointAction Action { get { return action; @@ -84,14 +82,13 @@ namespace ICSharpCode.ILSpy.Debugger.Bookmarks public string Tooltip { get; private set; } - public BreakpointBookmark(MemberReference member, TextLocation location, int functionToken, ILRange range, BreakpointAction action, DecompiledLanguages language) + public BreakpointBookmark(MemberReference member, TextLocation location, int functionToken, ILRange range, BreakpointAction action) : base(member, location) { this.action = action; this.FunctionToken = functionToken; this.ILRange = range; - this.Tooltip = string.Format("Language:{0}, Line:{1}, IL range:{2}-{3}", language.ToString(), location.Line, range.From, range.To); - this.Language = language; + this.Tooltip = string.Format("Line:{0}, IL range:{1}-{2}", location.Line, range.From, range.To); } public override ImageSource Image { diff --git a/ILSpy.SharpDevelop.LGPL/Bookmarks/CurrentLineBookmark.cs b/ILSpy.SharpDevelop.LGPL/Bookmarks/CurrentLineBookmark.cs index 8fdf6952e..3bd9d2e46 100644 --- a/ILSpy.SharpDevelop.LGPL/Bookmarks/CurrentLineBookmark.cs +++ b/ILSpy.SharpDevelop.LGPL/Bookmarks/CurrentLineBookmark.cs @@ -84,8 +84,8 @@ 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 && DebugInformation.DecompiledMemberReferences != null && - DebugInformation.DecompiledMemberReferences.ContainsKey(((MarkerBookmark)b).MemberReference.MetadataToken.ToInt32()); + marker.IsVisible = b => b is MarkerBookmark && DebugInformation.CodeMappings != null && + DebugInformation.CodeMappings.ContainsKey(((MarkerBookmark)b).MemberReference.MetadataToken.ToInt32()); marker.Bookmark = this; this.Marker = marker; return marker; diff --git a/ILSpy.SharpDevelop.LGPL/DebugInformation.cs b/ILSpy.SharpDevelop.LGPL/DebugInformation.cs index adeac55b3..8124a77b7 100644 --- a/ILSpy.SharpDevelop.LGPL/DebugInformation.cs +++ b/ILSpy.SharpDevelop.LGPL/DebugInformation.cs @@ -16,22 +16,6 @@ namespace ICSharpCode.ILSpy.Debugger /// public static class DebugInformation { - static DecompiledLanguages language; - - /// - /// Gets or sets the decompiled language. - /// - public static DecompiledLanguages Language { - get { return language; } - set { - var oldLanguage = language; - if (value != language) { - language = value; - OnLanguageChanged(new LanguageEventArgs(oldLanguage, language)); - } - } - } - /// /// List of loaded assemblies. /// @@ -40,17 +24,7 @@ namespace ICSharpCode.ILSpy.Debugger /// /// Gets or sets the current code mappings. /// - public static Dictionary> CodeMappings { get; set; } - - /// - /// Gets or sets the local variables of the current decompiled type, method, etc. - /// - public static ConcurrentDictionary> LocalVariables { get; set; } - - /// - /// Gets or sets the MembeReference that was decompiled (a TypeDefinition, MethodDefinition, etc) - /// - public static Dictionary DecompiledMemberReferences { get; set; } + public static Dictionary CodeMappings { get; set; } /// /// Gets or sets the current token, IL offset and member reference. Used for step in/out. @@ -61,31 +35,5 @@ namespace ICSharpCode.ILSpy.Debugger /// Gets or sets whether the debugger is loaded. /// public static bool IsDebuggerLoaded { get; set; } - - /// - /// Occures when the language is changed. - /// - public static event EventHandler LanguageChanged; - - private static void OnLanguageChanged(LanguageEventArgs e) - { - var handler = LanguageChanged; - if (handler != null) { - handler(null, e); - } - } - } - - public class LanguageEventArgs : EventArgs - { - public DecompiledLanguages OldLanguage { get; private set; } - - public DecompiledLanguages NewLanguage { get; private set; } - - public LanguageEventArgs(DecompiledLanguages oldLanguage, DecompiledLanguages newLanguage) - { - this.OldLanguage = oldLanguage; - this.NewLanguage = newLanguage; - } } } diff --git a/ILSpy.SharpDevelop.LGPL/Services/DebuggerService.cs b/ILSpy.SharpDevelop.LGPL/Services/DebuggerService.cs index 246ad47c4..d8394a769 100644 --- a/ILSpy.SharpDevelop.LGPL/Services/DebuggerService.cs +++ b/ILSpy.SharpDevelop.LGPL/Services/DebuggerService.cs @@ -169,12 +169,12 @@ namespace ICSharpCode.ILSpy.Debugger.Services } } - public static void ToggleBreakpointAt(MemberReference member, int lineNumber, int functionToken, ILRange range, DecompiledLanguages language) + public static void ToggleBreakpointAt(MemberReference member, int lineNumber, int functionToken, ILRange range) { BookmarkManager.ToggleBookmark( member.FullName, lineNumber, b => b.CanToggle && b is BreakpointBookmark, - location => new BreakpointBookmark(member, location, functionToken, range, BreakpointAction.Break, language)); + location => new BreakpointBookmark(member, location, functionToken, range, BreakpointAction.Break)); } /* TODO: reimplement this stuff diff --git a/ILSpy/Bookmarks/Commands.cs b/ILSpy/Bookmarks/Commands.cs index 021daee1b..451b29cb0 100644 --- a/ILSpy/Bookmarks/Commands.cs +++ b/ILSpy/Bookmarks/Commands.cs @@ -42,7 +42,7 @@ namespace ICSharpCode.ILSpy.Bookmarks if (!(mark is MemberBookmark)) continue; - var member = (mark as MemberBookmark).Node.Annotation(); + var member = (mark as MemberBookmark).Member; AnalyzeContextMenuEntry.Analyze(member); } } diff --git a/ILSpy/Bookmarks/MemberBookmark.cs b/ILSpy/Bookmarks/MemberBookmark.cs index f50da023a..c228ba35e 100644 --- a/ILSpy/Bookmarks/MemberBookmark.cs +++ b/ILSpy/Bookmarks/MemberBookmark.cs @@ -35,68 +35,51 @@ namespace ICSharpCode.ILSpy.Bookmarks /// public class MemberBookmark : IBookmark { - AstNode node; + MemberReference member; - public AstNode Node { + public MemberReference Member { get { - return node; + return member; } } - public MemberBookmark(AstNode node) + public MemberBookmark(MemberReference member, int line) { - this.node = node; + this.member = member; + LineNumber = line; } public virtual ImageSource Image { get { - var attrNode = (AttributedNode)node; - if (node is EnumMemberDeclaration) - return GetMemberOverlayedImage(attrNode, MemberIcon.EnumValue); + if (member is FieldDefinition) + return GetMemberOverlayedImage(member, MemberIcon.Field); - if (node is FieldDeclaration) - return GetMemberOverlayedImage(attrNode, MemberIcon.Field); + if (member is PropertyDefinition) + return GetMemberOverlayedImage(member, MemberIcon.Property); - if (node is PropertyDeclaration) - return GetMemberOverlayedImage(attrNode, MemberIcon.Property); + if (member is EventDefinition) + return GetMemberOverlayedImage(member, MemberIcon.Event); - if (node is EventDeclaration || node is CustomEventDeclaration) - return GetMemberOverlayedImage(attrNode, MemberIcon.Event); - - if (node is IndexerDeclaration) - return GetMemberOverlayedImage(attrNode, MemberIcon.Indexer); - - if (node is OperatorDeclaration) - return GetMemberOverlayedImage(attrNode, MemberIcon.Operator); - - if (node is ConstructorDeclaration || node is DestructorDeclaration) - return GetMemberOverlayedImage(attrNode, MemberIcon.Constructor); - - return GetMemberOverlayedImage(attrNode, MemberIcon.Method); + return GetMemberOverlayedImage(member, MemberIcon.Method); } } - ImageSource GetMemberOverlayedImage(AttributedNode attrNode, MemberIcon icon) + ImageSource GetMemberOverlayedImage(MemberReference member, MemberIcon icon) { - switch (attrNode.Modifiers & Modifiers.VisibilityMask) { - case Modifiers.Protected: - return Images.GetIcon(icon, AccessOverlayIcon.Protected, (attrNode.Modifiers & Modifiers.Static) == Modifiers.Static); - case Modifiers.Private: - return Images.GetIcon(icon, AccessOverlayIcon.Private, (attrNode.Modifiers & Modifiers.Static) == Modifiers.Static); - case Modifiers.Internal: - return Images.GetIcon(icon, AccessOverlayIcon.Internal, (attrNode.Modifiers & Modifiers.Static) == Modifiers.Static); - } + if (member is FieldDefinition) + return Images.GetIcon(icon, ((FieldDefinition)member).IsPublic ? AccessOverlayIcon.Public : AccessOverlayIcon.Private, false); + + if (member is PropertyDefinition) + return Images.GetIcon(icon, AccessOverlayIcon.Public, false); + + if (member is EventDefinition) + return Images.GetIcon(icon, AccessOverlayIcon.Public, false); - return Images.GetIcon(icon, AccessOverlayIcon.Public, (attrNode.Modifiers & Modifiers.Static) == Modifiers.Static); + return Images.GetIcon(icon, ((MethodDefinition)member).IsPublic ? AccessOverlayIcon.Public : AccessOverlayIcon.Private, false); } public int LineNumber { - get { - //var t = node.Annotation(); - //if (t != null) - // return t.Line; - return 0; - } + get; private set; } public virtual void MouseDown(MouseButtonEventArgs e) @@ -123,47 +106,32 @@ namespace ICSharpCode.ILSpy.Bookmarks public class TypeBookmark : MemberBookmark { - public TypeBookmark(AstNode node) : base (node) + public TypeBookmark(MemberReference member, int line) : base (member, line) { } public override ImageSource Image { get { - var attrNode = (AttributedNode)Node; - - if (Node is DelegateDeclaration) - return GetTypeOverlayedImage(attrNode, TypeIcon.Delegate); - - if (Node is TypeDeclaration) { - var n = Node as TypeDeclaration; - switch (n.ClassType) - { - case ClassType.Enum: - return GetTypeOverlayedImage(attrNode, TypeIcon.Enum); - case ClassType.Struct: - return GetTypeOverlayedImage(attrNode, TypeIcon.Struct); - case ClassType.Interface: - return GetTypeOverlayedImage(attrNode, TypeIcon.Interface); - } + if (Member is TypeDefinition) { + var type = Member as TypeDefinition; + if (type.IsEnum) + return GetTypeOverlayedImage(type, TypeIcon.Enum); + if (type.IsValueType) + return GetTypeOverlayedImage(type, TypeIcon.Struct); + if (type.IsInterface) + return GetTypeOverlayedImage(type, TypeIcon.Interface); + + return GetTypeOverlayedImage(type, TypeIcon.Class); } - if ((attrNode.Modifiers & Modifiers.Static) == Modifiers.Static) - return GetTypeOverlayedImage(attrNode, TypeIcon.StaticClass); - - return GetTypeOverlayedImage(attrNode, TypeIcon.Class); + return null; } } - ImageSource GetTypeOverlayedImage(AttributedNode attrNode, TypeIcon icon) + ImageSource GetTypeOverlayedImage(TypeDefinition type, TypeIcon icon) { - switch (attrNode.Modifiers & Modifiers.VisibilityMask) { - case Modifiers.Protected: - return Images.GetIcon(icon, AccessOverlayIcon.Protected); - case Modifiers.Private: - return Images.GetIcon(icon, AccessOverlayIcon.Private); - case Modifiers.Internal: - return Images.GetIcon(icon, AccessOverlayIcon.Internal); - } + if (type.IsNotPublic) + return Images.GetIcon(icon, AccessOverlayIcon.Private); return Images.GetIcon(icon, AccessOverlayIcon.Public); } diff --git a/ILSpy/Languages/Language.cs b/ILSpy/Languages/Language.cs index 91565dc50..4333b69e1 100644 --- a/ILSpy/Languages/Language.cs +++ b/ILSpy/Languages/Language.cs @@ -59,12 +59,6 @@ namespace ICSharpCode.ILSpy /// public abstract class Language { - /// - /// Decompile finished event. - /// - [Obsolete] - public event EventHandler DecompileFinished { add {} remove {} } - /// /// Gets the name of the language (as shown in the UI) /// diff --git a/ILSpy/TextView/AvalonEditTextOutput.cs b/ILSpy/TextView/AvalonEditTextOutput.cs index 11b08cefd..a5d71edea 100644 --- a/ILSpy/TextView/AvalonEditTextOutput.cs +++ b/ILSpy/TextView/AvalonEditTextOutput.cs @@ -22,11 +22,13 @@ using System.Diagnostics; using System.Linq; using System.Text; using System.Windows; + using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Folding; using ICSharpCode.AvalonEdit.Rendering; using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.Decompiler; +using Mono.Cecil; namespace ICSharpCode.ILSpy.TextView { @@ -65,7 +67,7 @@ namespace ICSharpCode.ILSpy.TextView /// /// Text output implementation for AvalonEdit. /// - public sealed class AvalonEditTextOutput : ISmartTextOutput + public sealed class AvalonEditTextOutput : BaseTextOutput, ISmartTextOutput { int lastLineStart = 0; int lineNumber = 1; @@ -92,7 +94,8 @@ namespace ICSharpCode.ILSpy.TextView /// Embedded UIElements, see . internal readonly List>> UIElements = new List>>(); - List memberMappings = new List(); + /// Icon mappings. + internal readonly Dictionary IconMappings = new Dictionary(); public AvalonEditTextOutput() { @@ -121,16 +124,12 @@ namespace ICSharpCode.ILSpy.TextView get { return b.Length; } } - public ICSharpCode.NRefactory.TextLocation Location { - get { + public override ICSharpCode.NRefactory.TextLocation Location { + get { return new ICSharpCode.NRefactory.TextLocation(lineNumber, b.Length - lastLineStart + 1 + (needsIndent ? indent : 0)); } } - public IList MemberMappings { - get { return memberMappings; } - } - #region Text Document TextDocument textDocument; @@ -163,12 +162,12 @@ namespace ICSharpCode.ILSpy.TextView } #endregion - public void Indent() + public override void Indent() { indent++; } - public void Unindent() + public override void Unindent() { indent--; } @@ -184,19 +183,19 @@ namespace ICSharpCode.ILSpy.TextView } } - public void Write(char ch) + public override void Write(char ch) { WriteIndent(); b.Append(ch); } - public void Write(string text) + public override void Write(string text) { WriteIndent(); b.Append(text); } - public void WriteLine() + public override void WriteLine() { Debug.Assert(textDocument == null); b.AppendLine(); @@ -208,7 +207,7 @@ namespace ICSharpCode.ILSpy.TextView } } - public void WriteDefinition(string text, object definition) + public override void WriteDefinition(string text, object definition) { WriteIndent(); int start = this.TextLength; @@ -218,16 +217,20 @@ namespace ICSharpCode.ILSpy.TextView references.Add(new ReferenceSegment { StartOffset = start, EndOffset = end, Reference = definition, IsLocal = true, IsLocalTarget = true }); } - public void WriteReference(string text, object reference, bool isLocal) + public override void WriteReference(string text, object reference, bool isLocal, bool isIconMapping) { WriteIndent(); int start = this.TextLength; b.Append(text); int end = this.TextLength; references.Add(new ReferenceSegment { StartOffset = start, EndOffset = end, Reference = reference, IsLocal = isLocal }); + + if (isIconMapping && reference is MemberReference && !this.IconMappings.ContainsKey((MemberReference)reference)) { + this.IconMappings.Add((MemberReference)reference, this.Location.Line); + } } - public void MarkFoldStart(string collapsedText, bool defaultCollapsed) + public override void MarkFoldStart(string collapsedText, bool defaultCollapsed) { WriteIndent(); openFoldings.Push( @@ -238,7 +241,7 @@ namespace ICSharpCode.ILSpy.TextView }); } - public void MarkFoldEnd() + public override void MarkFoldEnd() { NewFolding f = openFoldings.Pop(); f.EndOffset = this.TextLength; @@ -253,10 +256,5 @@ namespace ICSharpCode.ILSpy.TextView this.UIElements.Add(new KeyValuePair>(this.TextLength, new Lazy(element))); } } - - public void AddDebuggerMemberMapping(MemberMapping memberMapping) - { - memberMappings.Add(memberMapping); - } } } diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 39eb1e59a..08e834311 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -465,7 +465,7 @@ namespace ICSharpCode.ILSpy.TextView if (DebugInformation.CodeMappings == null || !DebugInformation.CodeMappings.ContainsKey(token)) return; - DebugInformation.CodeMappings[token].GetInstructionByTokenAndOffset(token, ilOffset, out member, out line); + DebugInformation.CodeMappings[token].GetInstructionByTokenAndOffset(ilOffset, out member, out line); // update marker DebuggerService.JumpToCurrentLine(member, line, 0, line, 0, ilOffset); @@ -529,15 +529,10 @@ namespace ICSharpCode.ILSpy.TextView void DecompileNodes(DecompilationContext context, ITextOutput textOutput) { - // reset data + // reset debug information DebugInformation.CodeMappings = null; - DebugInformation.LocalVariables = null; - DebugInformation.DecompiledMemberReferences = null; - // set the language - DebugInformation.Language = MainWindow.Instance.sessionSettings.FilterSettings.Language.Name.StartsWith("IL") ? DecompiledLanguages.IL : DecompiledLanguages.CSharp; var nodes = context.TreeNodes; - context.Language.DecompileFinished += Language_DecompileFinished; for (int i = 0; i < nodes.Length; i++) { if (i > 0) textOutput.WriteLine(); @@ -545,32 +540,22 @@ namespace ICSharpCode.ILSpy.TextView context.Options.CancellationToken.ThrowIfCancellationRequested(); nodes[i].Decompile(context.Language, textOutput, context.Options); } - context.Language.DecompileFinished -= Language_DecompileFinished; + + OnDecompilationFinished(textOutput); } - void Language_DecompileFinished(object sender, DecompileEventArgs e) + void OnDecompilationFinished(ITextOutput textOutput) { - if (e != null) { - manager.UpdateClassMemberBookmarks(e.AstNodes, typeof(TypeBookmark), typeof(MemberBookmark)); - if (iconMargin.DecompiledMembers == null) { - iconMargin.DecompiledMembers = new List(); - } - iconMargin.DecompiledMembers.AddRange(e.DecompiledMemberReferences.Values.AsEnumerable()); - - // debugger info - if (DebugInformation.CodeMappings == null) { - DebugInformation.CodeMappings = e.CodeMappings; - DebugInformation.LocalVariables = e.LocalVariables; - DebugInformation.DecompiledMemberReferences = e.DecompiledMemberReferences; - } else { - DebugInformation.CodeMappings.AddRange(e.CodeMappings); - DebugInformation.DecompiledMemberReferences.AddRange(e.DecompiledMemberReferences); - if (e.LocalVariables != null) - DebugInformation.LocalVariables.AddRange(e.LocalVariables); - } - } else { - manager.UpdateClassMemberBookmarks(null, typeof(TypeBookmark), typeof(MemberBookmark)); - } + if (!(textOutput is AvalonEditTextOutput)) + return; + + var output = textOutput as AvalonEditTextOutput; + + // update class bookmarks + manager.UpdateClassMemberBookmarks(output.IconMappings, typeof(TypeBookmark), typeof(MemberBookmark)); + + // update debug inforomation + DebugInformation.CodeMappings = output.CodeMappings; } #endregion diff --git a/ILSpy/VB/VBTextOutputFormatter.cs b/ILSpy/VB/VBTextOutputFormatter.cs index 0217a10bf..d4fbce3ed 100644 --- a/ILSpy/VB/VBTextOutputFormatter.cs +++ b/ILSpy/VB/VBTextOutputFormatter.cs @@ -81,7 +81,7 @@ namespace ICSharpCode.ILSpy.VB object memberRef = GetCurrentMemberReference(); if (memberRef != null) { - output.WriteReference(identifier, memberRef); + output.WriteReference(identifier, memberRef, isIconMapping: IsIconMapping()); return; } @@ -157,7 +157,7 @@ namespace ICSharpCode.ILSpy.VB // Attach member reference to token only if there's no identifier in the current node. MemberReference memberRef = GetCurrentMemberReference(); if (memberRef != null && nodeStack.Peek().GetChildByRole(AstNode.Roles.Identifier).IsNull) - output.WriteReference(token, memberRef); + output.WriteReference(token, memberRef, isIconMapping: IsIconMapping()); else output.Write(token); } @@ -200,5 +200,22 @@ namespace ICSharpCode.ILSpy.VB { output.MarkFoldEnd(); } + + private bool IsIconMapping() + { + if (nodeStack == null || nodeStack.Count == 0) + return false; + + var node = nodeStack.Peek(); + + return + node is FieldDeclaration || + node is ConstructorDeclaration || + node is EventDeclaration || + node is DelegateDeclaration || + node is OperatorDeclaration|| + node is MemberDeclaration || + node is TypeDeclaration; + } } }