Browse Source

Merged ICSharpCode.Decompiler changes from debugger branch.

(from commit e184c73f2d)
pull/150/head
Eusebiu Marcu 14 years ago committed by Daniel Grunwald
parent
commit
e4513b947b
  1. 52
      ICSharpCode.Decompiler/Ast/AstBuilder.cs
  2. 37
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  3. 24
      ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs
  4. 307
      ICSharpCode.Decompiler/CodeMappings.cs
  5. 31
      ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs
  6. 20
      ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs
  7. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  8. 2
      ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs
  9. 11
      ICSharpCode.Decompiler/ILAst/ILAstTypes.cs
  10. 2
      ICSharpCode.Decompiler/ITextOutput.cs
  11. 5
      ICSharpCode.Decompiler/PlainTextOutput.cs
  12. 16
      ILSpy/TextView/AvalonEditTextOutput.cs

52
ICSharpCode.Decompiler/Ast/AstBuilder.cs

@ -1,4 +1,5 @@ @@ -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; @@ -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 @@ -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 @@ -42,6 +44,8 @@ namespace ICSharpCode.Decompiler.Ast
throw new ArgumentNullException("context");
this.context = context;
this.DecompileMethodBodies = true;
this.LocalVariables = new ConcurrentDictionary<int, IEnumerable<ILVariable>>();
}
public static bool MemberIsHidden(MemberReference member, DecompilerSettings settings)
@ -192,6 +196,11 @@ namespace ICSharpCode.Decompiler.Ast @@ -192,6 +196,11 @@ namespace ICSharpCode.Decompiler.Ast
/// <returns>TypeDeclaration or DelegateDeclaration.</returns>
public AttributedNode CreateType(TypeDefinition typeDef)
{
// create CSharp code mappings - used for debugger
if (this.CodeMappings == null)
this.CodeMappings = new Tuple<string, List<MemberMapping>>(typeDef.FullName, new List<MemberMapping>());
// create type
TypeDefinition oldCurrentType = context.CurrentType;
context.CurrentType = typeDef;
TypeDeclaration astType = new TypeDeclaration();
@ -615,6 +624,9 @@ namespace ICSharpCode.Decompiler.Ast @@ -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 @@ -657,6 +669,7 @@ namespace ICSharpCode.Decompiler.Ast
return op;
}
}
astMethod.WithAnnotation(methodMapping);
return astMethod;
}
@ -700,6 +713,9 @@ namespace ICSharpCode.Decompiler.Ast @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -843,7 +880,7 @@ namespace ICSharpCode.Decompiler.Ast
BlockStatement CreateMethodBody(MethodDefinition method, IEnumerable<ParameterDeclaration> 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 @@ -1315,5 +1352,16 @@ namespace ICSharpCode.Decompiler.Ast
return type.CustomAttributes.Any(attr => attr.AttributeType.FullName == "System.FlagsAttribute");
}
/// <summary>
/// <inheritdoc/>
/// </summary>
public Tuple<string, List<MemberMapping>> CodeMappings { get; private set; }
/// <summary>
/// Gets the local variables for the current decompiled type, method, etc.
/// <remarks>The key is the metadata token.</remarks>
/// </summary>
public ConcurrentDictionary<int, IEnumerable<ILVariable>> LocalVariables { get; private set; }
}
}

37
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -32,9 +32,16 @@ namespace ICSharpCode.Decompiler.Ast @@ -32,9 +32,16 @@ namespace ICSharpCode.Decompiler.Ast
/// <param name="context">Decompilation context.</param>
/// <param name="parameters">Parameter declarations of the method being decompiled.
/// These are used to update the parameter names when the decompiler generates names for the parameters.</param>
/// <param name="localVariables">Local variables storage that will be filled/updated with the local variables.</param>
/// <returns>Block for the method body</returns>
public static BlockStatement CreateMethodBody(MethodDefinition methodDef, DecompilerContext context, IEnumerable<ParameterDeclaration> parameters = null)
public static BlockStatement CreateMethodBody(MethodDefinition methodDef,
DecompilerContext context,
IEnumerable<ParameterDeclaration> parameters = null,
ConcurrentDictionary<int, IEnumerable<ILVariable>> localVariables = null)
{
if (localVariables == null)
localVariables = new ConcurrentDictionary<int, IEnumerable<ILVariable>>();
MethodDefinition oldCurrentMethod = context.CurrentMethod;
Debug.Assert(oldCurrentMethod == null || oldCurrentMethod == methodDef);
context.CurrentMethod = methodDef;
@ -44,10 +51,10 @@ namespace ICSharpCode.Decompiler.Ast @@ -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 @@ -59,10 +66,14 @@ namespace ICSharpCode.Decompiler.Ast
}
}
public BlockStatement CreateMethodBody(IEnumerable<ParameterDeclaration> parameters)
public BlockStatement CreateMethodBody(IEnumerable<ParameterDeclaration> parameters,
ConcurrentDictionary<int, IEnumerable<ILVariable>> 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 @@ -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 @@ -215,10 +230,20 @@ namespace ICSharpCode.Decompiler.Ast
{
AstNode node = TransformByteCode(expr);
Expression astExpr = node as Expression;
// get IL ranges - used in debugger
List<ILRange> ilRanges = ILRange.OrderAndJoint(expr.GetSelfAndChildrenRecursive<ILExpression>().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)

24
ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs

@ -4,7 +4,9 @@ @@ -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 @@ -121,6 +123,28 @@ namespace ICSharpCode.Decompiler.Ast
public void StartNode(AstNode node)
{
var ranges = node.Annotation<List<ILRange>>();
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<MemberMapping>() != null);
if (n != null) {
MemberMapping mapping = n.Annotation<MemberMapping>();
// add all ranges
foreach (var range in ranges) {
mapping.MemberCodeMappings.Add(new SourceCodeMapping {
ILInstructionOffset = range,
SourceCodeLine = output.CurrentLine,
MemberMapping = mapping
});
}
}
}
}
nodeStack.Push(node);
}

307
ICSharpCode.Decompiler/CodeMappings.cs

@ -0,0 +1,307 @@ @@ -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
}
/// <summary>
/// Interface for decompliler classes : AstBuilder & ReflectionDisassembler.
/// </summary>
public interface ICodeMappings
{
/// <summary>
/// Gets the code mappings.
/// </summary>
Tuple<string, List<MemberMapping>> CodeMappings { get; }
}
/// <summary>
/// Maps the source code to IL.
/// </summary>
public sealed class SourceCodeMapping
{
/// <summary>
/// Gets or sets the source code line number in the output.
/// </summary>
public int SourceCodeLine { get; internal set; }
/// <summary>
/// Gets or sets IL Range offset for the source code line. E.g.: 13-19 &lt;-&gt; 135.
/// </summary>
public ILRange ILInstructionOffset { get; internal set; }
/// <summary>
/// Gets or sets the member mapping this source code mapping belongs to.
/// </summary>
public MemberMapping MemberMapping { get; internal set; }
/// <summary>
/// Retrieves the array that contains the IL range and the missing gaps between ranges.
/// </summary>
/// <returns>The array representation of the step aranges.</returns>
public int[] ToArray(bool isMatch)
{
var currentList = new List<ILRange>();
// add list for the current source code line
currentList.AddRange(ILRange.OrderAndJoint(MemberMapping.MemberCodeMappings
.FindAll(m => m.SourceCodeLine == this.SourceCodeLine)
.ConvertAll<ILRange>(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<int>();
foreach (var element in ILRange.OrderAndJoint(currentList)) {
resultList.Add(element.From);
resultList.Add(element.To);
}
return resultList.ToArray();
}
}
/// <summary>
/// Stores the method information and its source code mappings.
/// </summary>
public sealed class MemberMapping
{
IEnumerable<ILRange> invertedList;
/// <summary>
/// Gets or sets the type of the mapping.
/// </summary>
public MemberReference MemberReference { get; internal set; }
/// <summary>
/// Metadata token of the method.
/// </summary>
public uint MetadataToken { get; internal set; }
/// <summary>
/// Gets or sets the code size for the member mapping.
/// </summary>
public int CodeSize { get; internal set; }
/// <summary>
/// Gets or sets the source code mappings.
/// </summary>
public List<SourceCodeMapping> MemberCodeMappings { get; internal set; }
/// <summary>
/// Gets the inverted IL Ranges.<br/>
/// E.g.: for (0-9, 11-14, 14-18, 21-25) => (9-11,18-21).
/// </summary>
/// <returns>IL Range inverted list.</returns>
public IEnumerable<ILRange> InvertedList
{
get {
if (invertedList == null) {
var list = MemberCodeMappings.ConvertAll<ILRange>(
s => new ILRange { From = s.ILInstructionOffset.From, To = s.ILInstructionOffset.To });
invertedList = ILRange.OrderAndJoint(ILRange.Invert(list, CodeSize));
}
return invertedList;
}
}
}
/// <summary>
/// Code mappings helper class.
/// </summary>
public static class CodeMappings
{
/// <summary>
/// Create code mapping for a method.
/// </summary>
/// <param name="method">Method to create the mapping for.</param>
/// <param name="sourceCodeMappings">Source code mapping storage.</param>
internal static MemberMapping CreateCodeMapping(
this MethodDefinition member,
Tuple<string, List<MemberMapping>> 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<SourceCodeMapping>(),
CodeSize = member.Body.CodeSize
};
mapping.Add(currentMemberMapping);
}
}
return currentMemberMapping;
}
/// <summary>
/// Gets source code mapping and metadata token based on type name and line number.
/// </summary>
/// <param name="codeMappings">Code mappings storage.</param>
/// <param name="typeName">Type name.</param>
/// <param name="lineNumber">Line number.</param>
/// <param name="metadataToken">Metadata token.</param>
/// <returns></returns>
public static SourceCodeMapping GetInstructionByTypeAndLine(
this Tuple<string, List<MemberMapping>> 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;
}
/// <summary>
/// Gets a mapping given a type, a token and an IL offset.
/// </summary>
/// <param name="codeMappings">Code mappings storage.</param>
/// <param name="typeName">Type name.</param>
/// <param name="token">Token.</param>
/// <param name="ilOffset">IL offset.</param>
/// <param name="isMatch">True, if perfect match.</param>
/// <returns>A code mapping.</returns>
public static SourceCodeMapping GetInstructionByTypeTokenAndOffset(
this Tuple<string, List<MemberMapping>> 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;
}
/// <summary>
/// Gets the source code and type name from metadata token and offset.
/// </summary>
/// <param name="codeMappings">Code mappings storage.</param>
/// <param name="typeName">Current type name.</param>
/// <param name="token">Metadata token.</param>
/// <param name="ilOffset">IL offset.</param>
/// <param name="typeName">Type definition.</param>
/// <param name="line">Line number.</param>
/// <remarks>It is possible to exist to different types from different assemblies with the same metadata token.</remarks>
public static bool GetSourceCodeFromMetadataTokenAndOffset(
this Tuple<string, List<MemberMapping>> 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;
}
}
}

31
ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs

@ -20,8 +20,10 @@ using System; @@ -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 @@ -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 @@ -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 @@ -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 @@ -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;
}

20
ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs

@ -28,7 +28,7 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -28,7 +28,7 @@ namespace ICSharpCode.Decompiler.Disassembler
/// <summary>
/// Disassembles type and member definitions.
/// </summary>
public sealed class ReflectionDisassembler
public sealed class ReflectionDisassembler : ICodeMappings
{
ITextOutput output;
CancellationToken cancellationToken;
@ -146,8 +146,11 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -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 @@ -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<string, List<MemberMapping>>(type.FullName, new List<MemberMapping>());
// start writing IL
output.WriteDefinition(".class ", type);
if ((type.Attributes & TypeAttributes.ClassSemanticMask) == TypeAttributes.Interface)
@ -558,5 +566,11 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -558,5 +566,11 @@ namespace ICSharpCode.Decompiler.Disassembler
WriteAttributes(asm.CustomAttributes);
CloseBlock();
}
/// <inheritdoc/>
public Tuple<string, List<MemberMapping>> CodeMappings {
get;
private set;
}
}
}

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -73,6 +73,7 @@ @@ -73,6 +73,7 @@
<Compile Include="Ast\Transforms\PatternStatementTransform.cs" />
<Compile Include="Ast\TypesHierarchyHelpers.cs" />
<Compile Include="CecilExtensions.cs" />
<Compile Include="CodeMappings.cs" />
<Compile Include="DecompilerException.cs" />
<Compile Include="DecompilerSettings.cs" />
<Compile Include="Disassembler\DisassemblerHelpers.cs" />

2
ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs

@ -1,7 +1,9 @@ @@ -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;

11
ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

@ -210,7 +210,10 @@ namespace ICSharpCode.Decompiler.ILAst @@ -210,7 +210,10 @@ namespace ICSharpCode.Decompiler.ILAst
public static List<ILRange> OrderAndJoint(IEnumerable<ILRange> input)
{
List<ILRange> ranges = input.OrderBy(r => r.From).ToList();
if (input == null)
throw new ArgumentNullException("Input is null!");
List<ILRange> 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 @@ -227,6 +230,12 @@ namespace ICSharpCode.Decompiler.ILAst
public static IEnumerable<ILRange> Invert(IEnumerable<ILRange> 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 };

2
ICSharpCode.Decompiler/ITextOutput.cs

@ -23,6 +23,8 @@ namespace ICSharpCode.Decompiler @@ -23,6 +23,8 @@ namespace ICSharpCode.Decompiler
{
public interface ITextOutput
{
int CurrentLine { get; }
void Indent();
void Unindent();
void Write(char ch);

5
ICSharpCode.Decompiler/PlainTextOutput.cs

@ -32,13 +32,17 @@ namespace ICSharpCode.Decompiler @@ -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 @@ -80,6 +84,7 @@ namespace ICSharpCode.Decompiler
{
writer.WriteLine();
needsIndent = true;
++CurrentLine;
}
public void WriteDefinition(string text, object definition)

16
ILSpy/TextView/AvalonEditTextOutput.cs

@ -64,6 +64,8 @@ namespace ICSharpCode.ILSpy.TextView @@ -64,6 +64,8 @@ namespace ICSharpCode.ILSpy.TextView
/// </summary>
public sealed class AvalonEditTextOutput : ISmartTextOutput
{
int lineNumber = 1;
int lastLineStart = 0;
readonly StringBuilder b = new StringBuilder();
/// <summary>Current indentation level</summary>
@ -173,6 +175,8 @@ namespace ICSharpCode.ILSpy.TextView @@ -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 @@ -220,5 +224,17 @@ namespace ICSharpCode.ILSpy.TextView
this.UIElements.Add(new KeyValuePair<int, Lazy<UIElement>>(this.TextLength, new Lazy<UIElement>(element)));
}
}
public int CurrentLine {
get {
return lineNumber;
}
}
public int CurrentColumn {
get {
return b.Length - lastLineStart + 1;
}
}
}
}

Loading…
Cancel
Save