From 556fb79e8c96191f9cb9f6db421fe80f8d1aeb39 Mon Sep 17 00:00:00 2001 From: Eusebiu Marcu Date: Sun, 13 Feb 2011 13:30:20 +0200 Subject: [PATCH] Create IL <-> editor code mappings --- Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj | 1 - .../Services/Debugger/DebuggerService.cs | 2 +- .../Disassembler/CodeMappings.cs | 66 +++++++++++++++++++ .../Disassembler/MethodBodyDisassembler.cs | 34 +++++++++- .../Disassembler/ReflectionDisassembler.cs | 15 +++++ .../ICSharpCode.Decompiler.csproj | 1 + ICSharpCode.Decompiler/ITextOutput.cs | 2 + ICSharpCode.Decompiler/PlainTextOutput.cs | 4 ++ ILSpy.sln | 28 ++++---- ILSpy/TextView/AvalonEditTextOutput.cs | 8 +++ 10 files changed, 142 insertions(+), 19 deletions(-) create mode 100644 ICSharpCode.Decompiler/Disassembler/CodeMappings.cs diff --git a/Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj b/Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj index 742048f58..cfaa007cc 100644 --- a/Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj +++ b/Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj @@ -78,7 +78,6 @@ - diff --git a/Debugger/ILSpy.Debugger/Services/Debugger/DebuggerService.cs b/Debugger/ILSpy.Debugger/Services/Debugger/DebuggerService.cs index 35f85fb58..75906b6ce 100644 --- a/Debugger/ILSpy.Debugger/Services/Debugger/DebuggerService.cs +++ b/Debugger/ILSpy.Debugger/Services/Debugger/DebuggerService.cs @@ -22,7 +22,7 @@ namespace ILSpy.Debugger.Services static IDebugger GetCompatibleDebugger() { - return new DefaultDebugger(); + return new WindowsDebugger(); } /// diff --git a/ICSharpCode.Decompiler/Disassembler/CodeMappings.cs b/ICSharpCode.Decompiler/Disassembler/CodeMappings.cs new file mode 100644 index 000000000..ff4e710b2 --- /dev/null +++ b/ICSharpCode.Decompiler/Disassembler/CodeMappings.cs @@ -0,0 +1,66 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) +using System; +using System.Collections.Generic; +using Mono.Cecil.Cil; + +namespace ICSharpCode.Decompiler.Disassembler +{ + public class ILCodeMapping + { + public int SourceCodeLine { get; set; } + + public Instruction ILInstruction { get; set; } + } + + public class MethodMapping + { + public string TypeName { get; set; } + + public int MetadataToken { get; set; } + + public List MethodCodeMappings { get; set; } + + /// + /// Finds the IL instruction given a source code line number. + /// + /// Source code line number. + /// IL Instruction or null, if the instruction was not found. + public Instruction FindByLine(int sourceCodeLine) + { + if (sourceCodeLine <= 0) + throw new ArgumentException("The source line must be greater thatn 0."); + + if (MethodCodeMappings == null || MethodCodeMappings.Count == 0) + return null; + + foreach (var codeMapping in MethodCodeMappings) { + if (codeMapping.SourceCodeLine == sourceCodeLine) + return codeMapping.ILInstruction; + } + + return null; + } + + /// + /// Finds the source code line given an IL instruction offset. + /// + /// IL Instruction offset. + /// Source code line, if it is found, -1 otherwise. + public int FindByInstruction(int instructionOffset) + { + if (instructionOffset <= 0) + throw new ArgumentNullException("The instruction offset cannot be lower than 0."); + + if (MethodCodeMappings == null || MethodCodeMappings.Count == 0) + return -1; + + foreach (var codeMapping in MethodCodeMappings) { + if (codeMapping.ILInstruction.Offset == instructionOffset) + return codeMapping.SourceCodeLine; + } + + return -1; + } + } +} diff --git a/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs index 55ff87df0..fc36bcf41 100644 --- a/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs @@ -47,6 +47,20 @@ namespace ICSharpCode.Decompiler.Disassembler public void Disassemble(MethodBody body) { + // create mappings + MethodMapping currentMethodMapping = null; + if (ReflectionDisassembler.ILSourceCodeMappings.ContainsKey(body.Method.DeclaringType.FullName)) { + var mapping = ReflectionDisassembler.ILSourceCodeMappings[body.Method.DeclaringType.FullName]; + if (mapping.Find(map => map.MetadataToken == body.Method.MetadataToken.ToInt32()) == null) { + currentMethodMapping = new MethodMapping() { + MetadataToken = body.Method.MetadataToken.ToInt32(), + TypeName = body.Method.DeclaringType.FullName, + MethodCodeMappings = new List() + }; + mapping.Add(currentMethodMapping); + } + } + MethodDefinition method = body.Method; output.WriteLine("// Method begins at RVA 0x{0:x4}", method.RVA); output.WriteLine("// Code size {0} (0x{0:x})", body.CodeSize); @@ -74,9 +88,15 @@ namespace ICSharpCode.Decompiler.Disassembler if (detectControlStructure && body.Instructions.Count > 0) { Instruction inst = body.Instructions[0]; - WriteStructureBody(new ILStructure(body), ref inst); + WriteStructureBody(new ILStructure(body), ref inst, currentMethodMapping); } else { foreach (var inst in method.Body.Instructions) { + // add IL code mappings + currentMethodMapping.MethodCodeMappings.Add(new ILCodeMapping() { + SourceCodeLine = output.CurrentLine, + ILInstruction = inst + }); + inst.WriteTo(output); output.WriteLine(); } @@ -133,15 +153,23 @@ namespace ICSharpCode.Decompiler.Disassembler output.Indent(); } - void WriteStructureBody(ILStructure s, ref Instruction inst) + void WriteStructureBody(ILStructure s, ref Instruction inst, MethodMapping currentMethodMapping) { int childIndex = 0; while (inst != null && inst.Offset < s.EndOffset) { + // add IL code mappings + if (currentMethodMapping != null) { + currentMethodMapping.MethodCodeMappings.Add(new ILCodeMapping() { + SourceCodeLine = output.CurrentLine, + ILInstruction = inst + }); + } + int offset = inst.Offset; if (childIndex < s.Children.Count && s.Children[childIndex].StartOffset <= offset && offset < s.Children[childIndex].EndOffset) { ILStructure child = s.Children[childIndex++]; WriteStructureHeader(child); - WriteStructureBody(child, ref inst); + WriteStructureBody(child, ref inst, currentMethodMapping); WriteStructureFooter(child); } else { inst.WriteTo(output); diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs index fb0d9d1e2..60e88fda8 100644 --- a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs @@ -36,6 +36,16 @@ namespace ICSharpCode.Decompiler.Disassembler bool isInType; // whether we are currently disassembling a whole type (-> defaultCollapsed for foldings) MethodBodyDisassembler methodBodyDisassembler; + static Dictionary> ilCodeMappings = new Dictionary>(); + + /// + /// Stores the source codes mappings: IL <-> editor lines + /// + public static Dictionary> ILSourceCodeMappings { + get { return ilCodeMappings; } + set { ilCodeMappings = value; } + } + public ReflectionDisassembler(ITextOutput output, bool detectControlStructure, CancellationToken cancellationToken) { if (output == null) @@ -101,6 +111,11 @@ namespace ICSharpCode.Decompiler.Disassembler void DisassembleMethodInternal(MethodDefinition method) { + // create mappings for types that were not disassebled + if (!ilCodeMappings.ContainsKey(method.DeclaringType.FullName)) { + ilCodeMappings.Add(method.DeclaringType.FullName, new List()); + } + // .method public hidebysig specialname // instance default class [mscorlib]System.IO.TextWriter get_BaseWriter () cil managed // diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index af57a944b..320db9c11 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -60,6 +60,7 @@ + diff --git a/ICSharpCode.Decompiler/ITextOutput.cs b/ICSharpCode.Decompiler/ITextOutput.cs index f862d5f0a..fdee0a682 100644 --- a/ICSharpCode.Decompiler/ITextOutput.cs +++ b/ICSharpCode.Decompiler/ITextOutput.cs @@ -22,6 +22,8 @@ namespace ICSharpCode.Decompiler { public interface ITextOutput { + int CurrentLine { get; set; } + void Indent(); void Unindent(); void Write(char ch); diff --git a/ICSharpCode.Decompiler/PlainTextOutput.cs b/ICSharpCode.Decompiler/PlainTextOutput.cs index 76721e666..8464c22d5 100644 --- a/ICSharpCode.Decompiler/PlainTextOutput.cs +++ b/ICSharpCode.Decompiler/PlainTextOutput.cs @@ -32,6 +32,7 @@ namespace ICSharpCode.Decompiler if (writer == null) throw new ArgumentNullException("writer"); this.writer = writer; + CurrentLine = 1; } public PlainTextOutput() @@ -39,6 +40,8 @@ namespace ICSharpCode.Decompiler this.writer = new StringWriter(); } + public int CurrentLine { get; set; } + public override string ToString() { return writer.ToString(); @@ -80,6 +83,7 @@ namespace ICSharpCode.Decompiler { writer.WriteLine(); needsIndent = true; + ++CurrentLine; } public void WriteDefinition(string text, object definition) diff --git a/ILSpy.sln b/ILSpy.sln index 40407ff69..279b1b8c8 100644 --- a/ILSpy.sln +++ b/ILSpy.sln @@ -6,25 +6,25 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Debugger", "Debugger", "{BF ProjectSection(SolutionItems) = postProject EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Debugger.Core", "Debugger\Debugger.Core\Debugger.Core.csproj", "{1D18D788-F7EE-4585-A23B-34DC8EC63CB8}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpy.Debugger", "Debugger\ILSpy.Debugger\ILSpy.Debugger.csproj", "{6D3D0F0D-348D-456A-A6ED-E9BD5EFABB6A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Debugger.Core", "Debugger\Debugger.Core\Debugger.Core.csproj", "{1D18D788-F7EE-4585-A23B-34DC8EC63CB8}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{4E076A9B-159A-45C4-9E34-AE1D20D83E42}" ProjectSection(SolutionItems) = postProject EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Cecil", "Mono.Cecil\Mono.Cecil.csproj", "{D68133BD-1E63-496E-9EDE-4FBDBF77B486}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.AvalonEdit", "AvalonEdit\ICSharpCode.AvalonEdit\ICSharpCode.AvalonEdit.csproj", "{6C55B776-26D4-4DB3-A6AB-87E783B2F3D1}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NRefactory", "Decompiler\lib\NRefactory\Project\NRefactory.csproj", "{3A9AE6AA-BC07-4A2F-972C-581E3AE2F195}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop.Dom", "Libraries\ICSharpCode.SharpDevelop.Dom\Project\ICSharpCode.SharpDevelop.Dom.csproj", "{924EE450-603D-49C1-A8E5-4AFAA31CE6F3}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.NRefactory", "NRefactory\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj", "{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.TreeView", "SharpTreeView\ICSharpCode.TreeView.csproj", "{DDE2A481-8271-4EAC-A330-8FA6A38D13D1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.NRefactory", "NRefactory\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj", "{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop.Dom", "Libraries\ICSharpCode.SharpDevelop.Dom\Project\ICSharpCode.SharpDevelop.Dom.csproj", "{924EE450-603D-49C1-A8E5-4AFAA31CE6F3}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NRefactory", "Decompiler\lib\NRefactory\Project\NRefactory.csproj", "{3A9AE6AA-BC07-4A2F-972C-581E3AE2F195}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.AvalonEdit", "AvalonEdit\ICSharpCode.AvalonEdit\ICSharpCode.AvalonEdit.csproj", "{6C55B776-26D4-4DB3-A6AB-87E783B2F3D1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Cecil", "Mono.Cecil\Mono.Cecil.csproj", "{D68133BD-1E63-496E-9EDE-4FBDBF77B486}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpy", "ILSpy\ILSpy.csproj", "{1E85EFF9-E370-4683-83E4-8A3D063FF791}" EndProject @@ -117,13 +117,13 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {6D3D0F0D-348D-456A-A6ED-E9BD5EFABB6A} = {BF79A230-0918-47DF-8A36-776779A331DE} {1D18D788-F7EE-4585-A23B-34DC8EC63CB8} = {BF79A230-0918-47DF-8A36-776779A331DE} - {3A9AE6AA-BC07-4A2F-972C-581E3AE2F195} = {4E076A9B-159A-45C4-9E34-AE1D20D83E42} - {3B2A5653-EC97-4001-BB9B-D90F1AF2C371} = {4E076A9B-159A-45C4-9E34-AE1D20D83E42} - {DDE2A481-8271-4EAC-A330-8FA6A38D13D1} = {4E076A9B-159A-45C4-9E34-AE1D20D83E42} - {924EE450-603D-49C1-A8E5-4AFAA31CE6F3} = {4E076A9B-159A-45C4-9E34-AE1D20D83E42} - {6C55B776-26D4-4DB3-A6AB-87E783B2F3D1} = {4E076A9B-159A-45C4-9E34-AE1D20D83E42} + {6D3D0F0D-348D-456A-A6ED-E9BD5EFABB6A} = {BF79A230-0918-47DF-8A36-776779A331DE} {D68133BD-1E63-496E-9EDE-4FBDBF77B486} = {4E076A9B-159A-45C4-9E34-AE1D20D83E42} + {6C55B776-26D4-4DB3-A6AB-87E783B2F3D1} = {4E076A9B-159A-45C4-9E34-AE1D20D83E42} + {924EE450-603D-49C1-A8E5-4AFAA31CE6F3} = {4E076A9B-159A-45C4-9E34-AE1D20D83E42} + {DDE2A481-8271-4EAC-A330-8FA6A38D13D1} = {4E076A9B-159A-45C4-9E34-AE1D20D83E42} + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371} = {4E076A9B-159A-45C4-9E34-AE1D20D83E42} + {3A9AE6AA-BC07-4A2F-972C-581E3AE2F195} = {4E076A9B-159A-45C4-9E34-AE1D20D83E42} EndGlobalSection EndGlobal diff --git a/ILSpy/TextView/AvalonEditTextOutput.cs b/ILSpy/TextView/AvalonEditTextOutput.cs index e3934a886..500501a06 100644 --- a/ILSpy/TextView/AvalonEditTextOutput.cs +++ b/ILSpy/TextView/AvalonEditTextOutput.cs @@ -86,6 +86,11 @@ namespace ICSharpCode.ILSpy.TextView /// Embedded UIElements, see . public readonly List>> UIElements = new List>>(); + public AvalonEditTextOutput() + { + CurrentLine = 1; + } + /// /// Gets the list of references (hyperlinks). /// @@ -104,6 +109,8 @@ namespace ICSharpCode.ILSpy.TextView get { return b.Length; } } + public int CurrentLine { get; set; } + #region Text Document TextDocument textDocument; @@ -173,6 +180,7 @@ namespace ICSharpCode.ILSpy.TextView { Debug.Assert(textDocument == null); b.AppendLine(); + ++CurrentLine; needsIndent = true; if (this.TextLength > LengthLimit) { throw new OutputLengthExceededException();