diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index be98d8dcf..58d4ee66c 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; @@ -7,6 +8,7 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; + using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Ast.Transforms; using ICSharpCode.Decompiler.ILAst; @@ -29,7 +31,7 @@ namespace ICSharpCode.Decompiler.Ast IncludeTypeParameterDefinitions = 2 } - public class AstBuilder + public class AstBuilder : ICodeMappings { DecompilerContext context; CompilationUnit astCompileUnit = new CompilationUnit(); @@ -42,6 +44,8 @@ namespace ICSharpCode.Decompiler.Ast throw new ArgumentNullException("context"); this.context = context; this.DecompileMethodBodies = true; + + this.LocalVariables = new ConcurrentDictionary>(); } public static bool MemberIsHidden(MemberReference member, DecompilerSettings settings) @@ -192,6 +196,11 @@ 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; TypeDeclaration astType = new TypeDeclaration(); @@ -615,6 +624,9 @@ namespace ICSharpCode.Decompiler.Ast AttributedNode CreateMethod(MethodDefinition methodDef) { + // Create mapping - used in debugger + MemberMapping methodMapping = methodDef.CreateCodeMapping(this.CodeMappings); + MethodDeclaration astMethod = new MethodDeclaration(); astMethod.AddAnnotation(methodDef); astMethod.ReturnType = ConvertType(methodDef.ReturnType, methodDef.MethodReturnType); @@ -657,6 +669,7 @@ namespace ICSharpCode.Decompiler.Ast return op; } } + astMethod.WithAnnotation(methodMapping); return astMethod; } @@ -700,6 +713,9 @@ namespace ICSharpCode.Decompiler.Ast ConstructorDeclaration CreateConstructor(MethodDefinition methodDef) { + // Create mapping - used in debugger + MemberMapping methodMapping = methodDef.CreateCodeMapping(this.CodeMappings); + ConstructorDeclaration astMethod = new ConstructorDeclaration(); astMethod.AddAnnotation(methodDef); astMethod.Modifiers = ConvertModifiers(methodDef); @@ -711,6 +727,7 @@ namespace ICSharpCode.Decompiler.Ast astMethod.Parameters.AddRange(MakeParameters(methodDef)); astMethod.Body = CreateMethodBody(methodDef, astMethod.Parameters); ConvertAttributes(astMethod, methodDef); + astMethod.WithAnnotation(methodMapping); return astMethod; } @@ -759,6 +776,9 @@ namespace ICSharpCode.Decompiler.Ast astProp.Name = CleanName(propDef.Name); astProp.ReturnType = ConvertType(propDef.PropertyType, propDef); if (propDef.GetMethod != null) { + // Create mapping - used in debugger + MemberMapping methodMapping = propDef.GetMethod.CreateCodeMapping(this.CodeMappings); + astProp.Getter = new Accessor(); astProp.Getter.Body = CreateMethodBody(propDef.GetMethod); astProp.AddAnnotation(propDef.GetMethod); @@ -766,8 +786,13 @@ namespace ICSharpCode.Decompiler.Ast if ((getterModifiers & Modifiers.VisibilityMask) != (astProp.Modifiers & Modifiers.VisibilityMask)) astProp.Getter.Modifiers = getterModifiers & Modifiers.VisibilityMask; + + astProp.Getter.WithAnnotation(methodMapping); } if (propDef.SetMethod != null) { + // Create mapping - used in debugger + MemberMapping methodMapping = propDef.SetMethod.CreateCodeMapping(this.CodeMappings); + astProp.Setter = new Accessor(); astProp.Setter.Body = CreateMethodBody(propDef.SetMethod); astProp.Setter.AddAnnotation(propDef.SetMethod); @@ -776,6 +801,8 @@ namespace ICSharpCode.Decompiler.Ast if ((setterModifiers & Modifiers.VisibilityMask) != (astProp.Modifiers & Modifiers.VisibilityMask)) astProp.Setter.Modifiers = setterModifiers & Modifiers.VisibilityMask; + + astProp.Setter.WithAnnotation(methodMapping); } ConvertCustomAttributes(astProp, propDef); @@ -823,16 +850,26 @@ namespace ICSharpCode.Decompiler.Ast else astEvent.PrivateImplementationType = ConvertType(eventDef.AddMethod.Overrides.First().DeclaringType); if (eventDef.AddMethod != null) { + // Create mapping - used in debugger + MemberMapping methodMapping = eventDef.AddMethod.CreateCodeMapping(this.CodeMappings); + astEvent.AddAccessor = new Accessor { Body = CreateMethodBody(eventDef.AddMethod) }.WithAnnotation(eventDef.AddMethod); ConvertAttributes(astEvent.AddAccessor, eventDef.AddMethod); + + astEvent.AddAccessor.WithAnnotation(methodMapping); } if (eventDef.RemoveMethod != null) { + // Create mapping - used in debugger + MemberMapping methodMapping = eventDef.RemoveMethod.CreateCodeMapping(this.CodeMappings); + astEvent.RemoveAccessor = new Accessor { Body = CreateMethodBody(eventDef.RemoveMethod) }.WithAnnotation(eventDef.RemoveMethod); ConvertAttributes(astEvent.RemoveAccessor, eventDef.RemoveMethod); + + astEvent.RemoveAccessor.WithAnnotation(methodMapping); } return astEvent; } @@ -843,7 +880,7 @@ namespace ICSharpCode.Decompiler.Ast BlockStatement CreateMethodBody(MethodDefinition method, IEnumerable parameters = null) { if (DecompileMethodBodies) - return AstMethodBodyBuilder.CreateMethodBody(method, context, parameters); + return AstMethodBodyBuilder.CreateMethodBody(method, context, parameters, LocalVariables); else return null; } @@ -1315,5 +1352,16 @@ namespace ICSharpCode.Decompiler.Ast return type.CustomAttributes.Any(attr => attr.AttributeType.FullName == "System.FlagsAttribute"); } + + /// + /// + /// + public Tuple> 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; } } } diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 368dadefd..888a9e98d 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -32,9 +32,16 @@ namespace ICSharpCode.Decompiler.Ast /// Decompilation context. /// Parameter declarations of the method being decompiled. /// These are used to update the parameter names when the decompiler generates names for the parameters. + /// Local variables storage that will be filled/updated with the local variables. /// Block for the method body - public static BlockStatement CreateMethodBody(MethodDefinition methodDef, DecompilerContext context, IEnumerable parameters = null) + public static BlockStatement CreateMethodBody(MethodDefinition methodDef, + DecompilerContext context, + IEnumerable parameters = null, + ConcurrentDictionary> localVariables = null) { + if (localVariables == null) + localVariables = new ConcurrentDictionary>(); + MethodDefinition oldCurrentMethod = context.CurrentMethod; Debug.Assert(oldCurrentMethod == null || oldCurrentMethod == methodDef); context.CurrentMethod = methodDef; @@ -44,10 +51,10 @@ namespace ICSharpCode.Decompiler.Ast builder.context = context; builder.typeSystem = methodDef.Module.TypeSystem; if (Debugger.IsAttached) { - return builder.CreateMethodBody(parameters); + return builder.CreateMethodBody(parameters, localVariables); } else { try { - return builder.CreateMethodBody(parameters); + return builder.CreateMethodBody(parameters, localVariables); } catch (OperationCanceledException) { throw; } catch (Exception ex) { @@ -59,10 +66,14 @@ namespace ICSharpCode.Decompiler.Ast } } - public BlockStatement CreateMethodBody(IEnumerable parameters) + public BlockStatement CreateMethodBody(IEnumerable parameters, + ConcurrentDictionary> localVariables) { if (methodDef.Body == null) return null; + if (localVariables == null) + throw new ArgumentException("localVariables must be instantiated"); + context.CancellationToken.ThrowIfCancellationRequested(); ILBlock ilMethod = new ILBlock(); ILAstBuilder astBuilder = new ILAstBuilder(); @@ -102,6 +113,10 @@ namespace ICSharpCode.Decompiler.Ast astBlock.Statements.InsertBefore(insertionPoint, newVarDecl); } + // store the variables - used for debugger + int token = methodDef.MetadataToken.ToInt32(); + localVariables.AddOrUpdate(token, allVariables, (key, oldValue) => allVariables); + return astBlock; } @@ -215,10 +230,20 @@ namespace ICSharpCode.Decompiler.Ast { AstNode node = TransformByteCode(expr); Expression astExpr = node as Expression; + + // get IL ranges - used in debugger + List ilRanges = ILRange.OrderAndJoint(expr.GetSelfAndChildrenRecursive().SelectMany(e => e.ILRanges)); + AstNode result; + if (astExpr != null) - return Convert(astExpr, expr.InferredType, expr.ExpectedType); + result = Convert(astExpr, expr.InferredType, expr.ExpectedType); else - return node; + result = node; + + if (result != null) + return result.WithAnnotation(ilRanges); + + return result; } AstNode TransformByteCode(ILExpression byteCode) diff --git a/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs b/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs index 101371535..1e6915872 100644 --- a/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs +++ b/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs @@ -4,7 +4,9 @@ using System; using System.Collections.Generic; using System.Linq; + using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.ILAst; using ICSharpCode.NRefactory.CSharp; using Mono.Cecil; @@ -121,6 +123,28 @@ namespace ICSharpCode.Decompiler.Ast public void StartNode(AstNode node) { + var ranges = node.Annotation>(); + if (ranges != null && ranges.Count > 0) + { + // find the ancestor that has method mapping as annotation + if (node.Ancestors != null && node.Ancestors.Count() > 0) + { + var n = node.Ancestors.FirstOrDefault(a => a.Annotation() != null); + if (n != null) { + MemberMapping mapping = n.Annotation(); + + // add all ranges + foreach (var range in ranges) { + mapping.MemberCodeMappings.Add(new SourceCodeMapping { + ILInstructionOffset = range, + SourceCodeLine = output.CurrentLine, + MemberMapping = mapping + }); + } + } + } + } + nodeStack.Push(node); } diff --git a/ICSharpCode.Decompiler/CodeMappings.cs b/ICSharpCode.Decompiler/CodeMappings.cs new file mode 100644 index 000000000..c169d0e6f --- /dev/null +++ b/ICSharpCode.Decompiler/CodeMappings.cs @@ -0,0 +1,307 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; + +using ICSharpCode.Decompiler.Ast; +using ICSharpCode.Decompiler.Disassembler; +using ICSharpCode.Decompiler.ILAst; +using Mono.Cecil; + +namespace ICSharpCode.Decompiler +{ + public enum DecompiledLanguages + { + IL, + CSharp + } + + /// + /// Interface for decompliler classes : AstBuilder & ReflectionDisassembler. + /// + public interface ICodeMappings + { + /// + /// Gets the code mappings. + /// + Tuple> CodeMappings { get; } + } + + /// + /// Maps the source code to IL. + /// + public sealed class SourceCodeMapping + { + /// + /// Gets or sets the source code line number in the output. + /// + public int SourceCodeLine { get; internal set; } + + /// + /// Gets or sets IL Range offset for the source code line. E.g.: 13-19 <-> 135. + /// + public ILRange ILInstructionOffset { get; internal set; } + + /// + /// Gets or sets the member mapping this source code mapping belongs to. + /// + public MemberMapping MemberMapping { get; internal set; } + + /// + /// Retrieves the array that contains the IL range and the missing gaps between ranges. + /// + /// The array representation of the step aranges. + public int[] ToArray(bool isMatch) + { + var currentList = new List(); + + // add list for the current source code line + currentList.AddRange(ILRange.OrderAndJoint(MemberMapping.MemberCodeMappings + .FindAll(m => m.SourceCodeLine == this.SourceCodeLine) + .ConvertAll(m => m.ILInstructionOffset))); + + if (!isMatch) { + // add inverted + currentList.AddRange(MemberMapping.InvertedList); + } else { + // if the current list contains the last mapping, add also the last gap + var lastInverted = MemberMapping.InvertedList.LastOrDefault(); + if (lastInverted != null && lastInverted.From == currentList[currentList.Count - 1].To) + currentList.Add(lastInverted); + } + + // set the output + var resultList = new List(); + foreach (var element in ILRange.OrderAndJoint(currentList)) { + resultList.Add(element.From); + resultList.Add(element.To); + } + + return resultList.ToArray(); + } + } + + /// + /// Stores the method information and its source code mappings. + /// + public sealed class MemberMapping + { + IEnumerable invertedList; + + /// + /// Gets or sets the type of the mapping. + /// + public MemberReference MemberReference { get; internal set; } + + /// + /// Metadata token of the method. + /// + public uint MetadataToken { get; internal set; } + + /// + /// Gets or sets the code size for the member mapping. + /// + public int CodeSize { get; internal set; } + + /// + /// Gets or sets the source code mappings. + /// + public List MemberCodeMappings { get; internal set; } + + /// + /// Gets the inverted IL Ranges.
+ /// E.g.: for (0-9, 11-14, 14-18, 21-25) => (9-11,18-21). + ///
+ /// IL Range inverted list. + public IEnumerable InvertedList + { + get { + if (invertedList == null) { + var list = MemberCodeMappings.ConvertAll( + s => new ILRange { From = s.ILInstructionOffset.From, To = s.ILInstructionOffset.To }); + invertedList = ILRange.OrderAndJoint(ILRange.Invert(list, CodeSize)); + } + return invertedList; + } + } + } + + /// + /// Code mappings helper class. + /// + public static class CodeMappings + { + /// + /// Create code mapping for a method. + /// + /// Method to create the mapping for. + /// Source code mapping storage. + internal static MemberMapping CreateCodeMapping( + this MethodDefinition member, + Tuple> codeMappings) + { + 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.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); + } + } + + return currentMemberMapping; + } + + /// + /// Gets source code mapping and metadata token based on type name and line number. + /// + /// Code mappings storage. + /// Type name. + /// Line number. + /// Metadata token. + /// + public static SourceCodeMapping GetInstructionByTypeAndLine( + this Tuple> 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; + } + + var methodMappings = codeMappings.Item2; + foreach (var maping in methodMappings) { + var map = maping.MemberCodeMappings.Find(m => m.SourceCodeLine == lineNumber); + if (map != null) { + metadataToken = maping.MetadataToken; + return map; + } + } + + metadataToken = 0; + return null; + } + + /// + /// Gets a mapping given a type, a token and an IL offset. + /// + /// Code mappings storage. + /// Type name. + /// Token. + /// IL offset. + /// True, if perfect match. + /// A code mapping. + public static SourceCodeMapping GetInstructionByTypeTokenAndOffset( + this Tuple> codeMappings, + string memberReferenceName, + uint token, + 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); + + if (maping == null) { + return null; + } + + // try find an exact match + var map = maping.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); + isMatch = false; + if (map == null) + map = maping.MemberCodeMappings.LastOrDefault(); // get the last + + return map; + } + + isMatch = true; + return map; + } + + /// + /// 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, + uint token, + int ilOffset, + out MemberReference type, + out int line) + { + type = null; + line = 0; + + if (codeMappings == null) + throw new ArgumentNullException("CodeMappings storage must be valid!"); + + memberReferenceName = memberReferenceName.Replace("+", "/"); + if (codeMappings.Item1 != memberReferenceName) + return false; + + var mapping = codeMappings.Item2.Find(m => m.MetadataToken == token); + if (mapping == null) + return false; + + var codeMapping = mapping.MemberCodeMappings.Find( + cm => cm.ILInstructionOffset.From <= ilOffset && ilOffset <= cm.ILInstructionOffset.To - 1); + if (codeMapping == null) { + codeMapping = mapping.MemberCodeMappings.Find(cm => (cm.ILInstructionOffset.From >= ilOffset)); + if (codeMapping == null) { + codeMapping = mapping.MemberCodeMappings.LastOrDefault(); + if (codeMapping == null) + return false; + } + } + + type = mapping.MemberReference; + line = codeMapping.SourceCodeLine; + return true; + } + } +} diff --git a/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs index 32161a3f8..ef2f8a980 100644 --- a/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs @@ -20,8 +20,10 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading; + using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.FlowAnalysis; +using ICSharpCode.Decompiler.ILAst; using Mono.Cecil; using Mono.Cecil.Cil; @@ -45,8 +47,9 @@ namespace ICSharpCode.Decompiler.Disassembler this.cancellationToken = cancellationToken; } - public void Disassemble(MethodBody body) + public void Disassemble(MethodBody body, MemberMapping methodMapping) { + // start writing IL code MethodDefinition method = body.Method; output.WriteLine("// Method begins at RVA 0x{0:x4}", method.RVA); if (method.HasOverrides) @@ -77,10 +80,19 @@ 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, methodMapping, method.Body.CodeSize); } else { foreach (var inst in method.Body.Instructions) { inst.WriteTo(output); + + // add IL code mappings - used in debugger + methodMapping.MemberCodeMappings.Add( + new SourceCodeMapping() { + SourceCodeLine = output.CurrentLine, + ILInstructionOffset = new ILRange { From = inst.Offset, To = inst.Next == null ? method.Body.CodeSize : inst.Next.Offset }, + MemberMapping = methodMapping + }); + output.WriteLine(); } output.WriteLine(); @@ -136,7 +148,7 @@ namespace ICSharpCode.Decompiler.Disassembler output.Indent(); } - void WriteStructureBody(ILStructure s, ref Instruction inst) + void WriteStructureBody(ILStructure s, ref Instruction inst, MemberMapping currentMethodMapping, int codeSize) { int childIndex = 0; while (inst != null && inst.Offset < s.EndOffset) { @@ -144,10 +156,21 @@ namespace ICSharpCode.Decompiler.Disassembler 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, codeSize); WriteStructureFooter(child); } else { inst.WriteTo(output); + + // add IL code mappings - used in debugger + if (currentMethodMapping != null) { + currentMethodMapping.MemberCodeMappings.Add( + new SourceCodeMapping() { + SourceCodeLine = output.CurrentLine, + ILInstructionOffset = new ILRange { From = inst.Offset, To = inst.Next == null ? codeSize : inst.Next.Offset }, + MemberMapping = currentMethodMapping + }); + } + output.WriteLine(); inst = inst.Next; } diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs index db4699562..3ae34ffb3 100644 --- a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs @@ -28,7 +28,7 @@ namespace ICSharpCode.Decompiler.Disassembler /// /// Disassembles type and member definitions. /// - public sealed class ReflectionDisassembler + public sealed class ReflectionDisassembler : ICodeMappings { ITextOutput output; CancellationToken cancellationToken; @@ -146,8 +146,11 @@ namespace ICSharpCode.Decompiler.Disassembler OpenBlock(defaultCollapsed: isInType); WriteAttributes(method.CustomAttributes); - if (method.HasBody) - methodBodyDisassembler.Disassemble(method.Body); + if (method.HasBody) { + // create IL code mappings - used in debugger + MemberMapping methodMapping = method.CreateCodeMapping(this.CodeMappings); + methodBodyDisassembler.Disassemble(method.Body, methodMapping); + } CloseBlock("End of method " + method.DeclaringType.Name + "." + method.Name); } else { @@ -312,6 +315,11 @@ 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()); + + // start writing IL output.WriteDefinition(".class ", type); if ((type.Attributes & TypeAttributes.ClassSemanticMask) == TypeAttributes.Interface) @@ -558,5 +566,11 @@ namespace ICSharpCode.Decompiler.Disassembler WriteAttributes(asm.CustomAttributes); CloseBlock(); } + + /// + public Tuple> CodeMappings { + get; + private set; + } } } diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 6e8d0eae8..ee48b4734 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -73,6 +73,7 @@ + diff --git a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs index f9394fbce..233928531 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs @@ -1,7 +1,9 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; + using Mono.Cecil; using Mono.Cecil.Cil; using Cecil = Mono.Cecil; diff --git a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs index 721458348..7979e5f15 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs @@ -210,7 +210,10 @@ namespace ICSharpCode.Decompiler.ILAst public static List OrderAndJoint(IEnumerable input) { - List ranges = input.OrderBy(r => r.From).ToList(); + if (input == null) + throw new ArgumentNullException("Input is null!"); + + List ranges = input.Where(r => r != null).OrderBy(r => r.From).ToList(); for (int i = 0; i < ranges.Count - 1;) { ILRange curr = ranges[i]; ILRange next = ranges[i + 1]; @@ -227,6 +230,12 @@ namespace ICSharpCode.Decompiler.ILAst public static IEnumerable Invert(IEnumerable input, int codeSize) { + if (input == null) + throw new ArgumentNullException("Input is null!"); + + if (codeSize <= 0) + throw new ArgumentException("Code size must be grater than 0"); + var ordered = OrderAndJoint(input); if (ordered.Count == 0) { yield return new ILRange() { From = 0, To = codeSize }; diff --git a/ICSharpCode.Decompiler/ITextOutput.cs b/ICSharpCode.Decompiler/ITextOutput.cs index b4374d50b..1cced95f2 100644 --- a/ICSharpCode.Decompiler/ITextOutput.cs +++ b/ICSharpCode.Decompiler/ITextOutput.cs @@ -23,6 +23,8 @@ namespace ICSharpCode.Decompiler { public interface ITextOutput { + int CurrentLine { get; } + void Indent(); void Unindent(); void Write(char ch); diff --git a/ICSharpCode.Decompiler/PlainTextOutput.cs b/ICSharpCode.Decompiler/PlainTextOutput.cs index 76721e666..3e167b01b 100644 --- a/ICSharpCode.Decompiler/PlainTextOutput.cs +++ b/ICSharpCode.Decompiler/PlainTextOutput.cs @@ -32,13 +32,17 @@ namespace ICSharpCode.Decompiler if (writer == null) throw new ArgumentNullException("writer"); this.writer = writer; + CurrentLine = 1; } public PlainTextOutput() { this.writer = new StringWriter(); + CurrentLine = 1; } + public int CurrentLine { get; set; } + public override string ToString() { return writer.ToString(); @@ -80,6 +84,7 @@ namespace ICSharpCode.Decompiler { writer.WriteLine(); needsIndent = true; + ++CurrentLine; } public void WriteDefinition(string text, object definition) diff --git a/ILSpy/TextView/AvalonEditTextOutput.cs b/ILSpy/TextView/AvalonEditTextOutput.cs index 0bcdb7bd5..30d03e967 100644 --- a/ILSpy/TextView/AvalonEditTextOutput.cs +++ b/ILSpy/TextView/AvalonEditTextOutput.cs @@ -64,6 +64,8 @@ namespace ICSharpCode.ILSpy.TextView /// public sealed class AvalonEditTextOutput : ISmartTextOutput { + int lineNumber = 1; + int lastLineStart = 0; readonly StringBuilder b = new StringBuilder(); /// Current indentation level @@ -173,6 +175,8 @@ namespace ICSharpCode.ILSpy.TextView Debug.Assert(textDocument == null); b.AppendLine(); needsIndent = true; + lastLineStart = b.Length; + lineNumber++; if (this.TextLength > LengthLimit) { throw new OutputLengthExceededException(); } @@ -220,5 +224,17 @@ namespace ICSharpCode.ILSpy.TextView this.UIElements.Add(new KeyValuePair>(this.TextLength, new Lazy(element))); } } + + public int CurrentLine { + get { + return lineNumber; + } + } + + public int CurrentColumn { + get { + return b.Length - lastLineStart + 1; + } + } } }