Browse Source

Refactored handling of debug mapping in the decompiler: we now avoid using a side-channel for the debug info and instead return it via the ITextOutput.

pull/205/merge
Daniel Grunwald 14 years ago
parent
commit
56c75e09b2
  1. 51
      ICSharpCode.Decompiler/Ast/AstBuilder.cs
  2. 29
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  3. 59
      ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs
  4. 88
      ICSharpCode.Decompiler/CodeMappings.cs
  5. 8
      ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs
  6. 12
      ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs
  7. 11
      ICSharpCode.Decompiler/ITextOutput.cs
  8. 27
      ICSharpCode.Decompiler/PlainTextOutput.cs
  9. 6
      ILSpy/Bookmarks/MemberBookmark.cs
  10. 6
      ILSpy/Languages/CSharpLanguage.cs
  11. 7
      ILSpy/Languages/ILLanguage.cs
  12. 43
      ILSpy/Languages/Language.cs
  13. 20
      ILSpy/TextView/AvalonEditTextOutput.cs
  14. 1
      ILSpy/VB/VBLanguage.cs

51
ICSharpCode.Decompiler/Ast/AstBuilder.cs

@ -49,7 +49,7 @@ namespace ICSharpCode.Decompiler.Ast
DoNotUsePrimitiveTypeNames = 4 DoNotUsePrimitiveTypeNames = 4
} }
public class AstBuilder : BaseCodeMappings public class AstBuilder
{ {
DecompilerContext context; DecompilerContext context;
CompilationUnit astCompileUnit = new CompilationUnit(); CompilationUnit astCompileUnit = new CompilationUnit();
@ -62,10 +62,6 @@ namespace ICSharpCode.Decompiler.Ast
throw new ArgumentNullException("context"); throw new ArgumentNullException("context");
this.context = context; this.context = context;
this.DecompileMethodBodies = true; this.DecompileMethodBodies = true;
this.LocalVariables = new ConcurrentDictionary<int, IEnumerable<ILVariable>>();
this.CodeMappings = new Dictionary<int, List<MemberMapping>>();
this.DecompiledMemberReferences = new Dictionary<int, MemberReference>();
} }
public static bool MemberIsHidden(MemberReference member, DecompilerSettings settings) public static bool MemberIsHidden(MemberReference member, DecompilerSettings settings)
@ -729,11 +725,7 @@ namespace ICSharpCode.Decompiler.Ast
AttributedNode CreateMethod(MethodDefinition methodDef) AttributedNode CreateMethod(MethodDefinition methodDef)
{ {
// Create mapping - used in debugger MethodDeclaration astMethod = new MethodDeclaration();
CreateCodeMappings(methodDef.MetadataToken.ToInt32(), methodDef);
MemberMapping methodMapping = methodDef.CreateCodeMapping(this.CodeMappings[methodDef.MetadataToken.ToInt32()]);
MethodDeclaration astMethod = new MethodDeclaration().WithAnnotation(methodMapping);
astMethod.AddAnnotation(methodDef); astMethod.AddAnnotation(methodDef);
astMethod.ReturnType = ConvertType(methodDef.ReturnType, methodDef.MethodReturnType); astMethod.ReturnType = ConvertType(methodDef.ReturnType, methodDef.MethodReturnType);
astMethod.Name = CleanName(methodDef.Name); astMethod.Name = CleanName(methodDef.Name);
@ -823,10 +815,6 @@ namespace ICSharpCode.Decompiler.Ast
ConstructorDeclaration CreateConstructor(MethodDefinition methodDef) ConstructorDeclaration CreateConstructor(MethodDefinition methodDef)
{ {
// Create mapping - used in debugger
CreateCodeMappings(methodDef.MetadataToken.ToInt32(), methodDef);
MemberMapping methodMapping = methodDef.CreateCodeMapping(this.CodeMappings[methodDef.MetadataToken.ToInt32()]);
ConstructorDeclaration astMethod = new ConstructorDeclaration(); ConstructorDeclaration astMethod = new ConstructorDeclaration();
astMethod.AddAnnotation(methodDef); astMethod.AddAnnotation(methodDef);
astMethod.Modifiers = ConvertModifiers(methodDef); astMethod.Modifiers = ConvertModifiers(methodDef);
@ -838,7 +826,6 @@ namespace ICSharpCode.Decompiler.Ast
astMethod.Parameters.AddRange(MakeParameters(methodDef)); astMethod.Parameters.AddRange(MakeParameters(methodDef));
astMethod.Body = CreateMethodBody(methodDef, astMethod.Parameters); astMethod.Body = CreateMethodBody(methodDef, astMethod.Parameters);
ConvertAttributes(astMethod, methodDef); ConvertAttributes(astMethod, methodDef);
astMethod.WithAnnotation(methodMapping);
if (methodDef.IsStatic && methodDef.DeclaringType.IsBeforeFieldInit && !astMethod.Body.IsNull) { if (methodDef.IsStatic && methodDef.DeclaringType.IsBeforeFieldInit && !astMethod.Body.IsNull) {
astMethod.Body.InsertChildAfter(null, new Comment(" Note: this type is marked as 'beforefieldinit'."), AstNode.Roles.Comment); astMethod.Body.InsertChildAfter(null, new Comment(" Note: this type is marked as 'beforefieldinit'."), AstNode.Roles.Comment);
} }
@ -891,10 +878,6 @@ namespace ICSharpCode.Decompiler.Ast
astProp.ReturnType = ConvertType(propDef.PropertyType, propDef); astProp.ReturnType = ConvertType(propDef.PropertyType, propDef);
if (propDef.GetMethod != null) { if (propDef.GetMethod != null) {
// Create mapping - used in debugger
CreateCodeMappings(propDef.GetMethod.MetadataToken.ToInt32(), propDef);
MemberMapping methodMapping = propDef.GetMethod.CreateCodeMapping(this.CodeMappings[propDef.GetMethod.MetadataToken.ToInt32()], propDef);
astProp.Getter = new Accessor(); astProp.Getter = new Accessor();
astProp.Getter.Body = CreateMethodBody(propDef.GetMethod); astProp.Getter.Body = CreateMethodBody(propDef.GetMethod);
astProp.Getter.AddAnnotation(propDef.GetMethod); astProp.Getter.AddAnnotation(propDef.GetMethod);
@ -902,14 +885,8 @@ namespace ICSharpCode.Decompiler.Ast
if ((getterModifiers & Modifiers.VisibilityMask) != (astProp.Modifiers & Modifiers.VisibilityMask)) if ((getterModifiers & Modifiers.VisibilityMask) != (astProp.Modifiers & Modifiers.VisibilityMask))
astProp.Getter.Modifiers = getterModifiers & Modifiers.VisibilityMask; astProp.Getter.Modifiers = getterModifiers & Modifiers.VisibilityMask;
astProp.Getter.WithAnnotation(methodMapping);
} }
if (propDef.SetMethod != null) { if (propDef.SetMethod != null) {
// Create mapping - used in debugger
CreateCodeMappings(propDef.SetMethod.MetadataToken.ToInt32(), propDef);
MemberMapping methodMapping = propDef.SetMethod.CreateCodeMapping(this.CodeMappings[propDef.SetMethod.MetadataToken.ToInt32()], propDef);
astProp.Setter = new Accessor(); astProp.Setter = new Accessor();
astProp.Setter.Body = CreateMethodBody(propDef.SetMethod); astProp.Setter.Body = CreateMethodBody(propDef.SetMethod);
astProp.Setter.AddAnnotation(propDef.SetMethod); astProp.Setter.AddAnnotation(propDef.SetMethod);
@ -924,8 +901,6 @@ namespace ICSharpCode.Decompiler.Ast
if ((setterModifiers & Modifiers.VisibilityMask) != (astProp.Modifiers & Modifiers.VisibilityMask)) if ((setterModifiers & Modifiers.VisibilityMask) != (astProp.Modifiers & Modifiers.VisibilityMask))
astProp.Setter.Modifiers = setterModifiers & Modifiers.VisibilityMask; astProp.Setter.Modifiers = setterModifiers & Modifiers.VisibilityMask;
astProp.Setter.WithAnnotation(methodMapping);
} }
ConvertCustomAttributes(astProp, propDef); ConvertCustomAttributes(astProp, propDef);
@ -977,28 +952,16 @@ namespace ICSharpCode.Decompiler.Ast
astEvent.PrivateImplementationType = ConvertType(eventDef.AddMethod.Overrides.First().DeclaringType); astEvent.PrivateImplementationType = ConvertType(eventDef.AddMethod.Overrides.First().DeclaringType);
if (eventDef.AddMethod != null) { if (eventDef.AddMethod != null) {
// Create mapping - used in debugger
CreateCodeMappings(eventDef.AddMethod.MetadataToken.ToInt32(), eventDef);
MemberMapping methodMapping = eventDef.AddMethod.CreateCodeMapping(this.CodeMappings[eventDef.AddMethod.MetadataToken.ToInt32()], eventDef);
astEvent.AddAccessor = new Accessor { astEvent.AddAccessor = new Accessor {
Body = CreateMethodBody(eventDef.AddMethod) Body = CreateMethodBody(eventDef.AddMethod)
}.WithAnnotation(eventDef.AddMethod); }.WithAnnotation(eventDef.AddMethod);
ConvertAttributes(astEvent.AddAccessor, eventDef.AddMethod); ConvertAttributes(astEvent.AddAccessor, eventDef.AddMethod);
astEvent.AddAccessor.WithAnnotation(methodMapping);
} }
if (eventDef.RemoveMethod != null) { if (eventDef.RemoveMethod != null) {
// Create mapping - used in debugger
CreateCodeMappings(eventDef.RemoveMethod.MetadataToken.ToInt32(), eventDef);
MemberMapping methodMapping = eventDef.RemoveMethod.CreateCodeMapping(this.CodeMappings[eventDef.RemoveMethod.MetadataToken.ToInt32()], eventDef);
astEvent.RemoveAccessor = new Accessor { astEvent.RemoveAccessor = new Accessor {
Body = CreateMethodBody(eventDef.RemoveMethod) Body = CreateMethodBody(eventDef.RemoveMethod)
}.WithAnnotation(eventDef.RemoveMethod); }.WithAnnotation(eventDef.RemoveMethod);
ConvertAttributes(astEvent.RemoveAccessor, eventDef.RemoveMethod); ConvertAttributes(astEvent.RemoveAccessor, eventDef.RemoveMethod);
astEvent.RemoveAccessor.WithAnnotation(methodMapping);
} }
MethodDefinition accessor = eventDef.AddMethod ?? eventDef.RemoveMethod; MethodDefinition accessor = eventDef.AddMethod ?? eventDef.RemoveMethod;
if (accessor.IsVirtual == accessor.IsNewSlot) { if (accessor.IsVirtual == accessor.IsNewSlot) {
@ -1013,15 +976,13 @@ namespace ICSharpCode.Decompiler.Ast
BlockStatement CreateMethodBody(MethodDefinition method, IEnumerable<ParameterDeclaration> parameters = null) BlockStatement CreateMethodBody(MethodDefinition method, IEnumerable<ParameterDeclaration> parameters = null)
{ {
if (DecompileMethodBodies) if (DecompileMethodBodies)
return AstMethodBodyBuilder.CreateMethodBody(method, context, parameters, LocalVariables); return AstMethodBodyBuilder.CreateMethodBody(method, context, parameters);
else else
return null; return null;
} }
FieldDeclaration CreateField(FieldDefinition fieldDef) FieldDeclaration CreateField(FieldDefinition fieldDef)
{ {
this.DecompiledMemberReferences.Add(fieldDef.MetadataToken.ToInt32(), fieldDef);
FieldDeclaration astField = new FieldDeclaration(); FieldDeclaration astField = new FieldDeclaration();
astField.AddAnnotation(fieldDef); astField.AddAnnotation(fieldDef);
VariableInitializer initializer = new VariableInitializer(CleanName(fieldDef.Name)); VariableInitializer initializer = new VariableInitializer(CleanName(fieldDef.Name));
@ -1673,11 +1634,5 @@ namespace ICSharpCode.Decompiler.Ast
&& (condition == null || condition(m)) && (condition == null || condition(m))
&& TypesHierarchyHelpers.IsVisibleFromDerived(m, derived.DeclaringType)); && TypesHierarchyHelpers.IsVisibleFromDerived(m, derived.DeclaringType));
} }
/// <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; }
} }
} }

29
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -50,16 +50,11 @@ namespace ICSharpCode.Decompiler.Ast
/// <param name="context">Decompilation context.</param> /// <param name="context">Decompilation context.</param>
/// <param name="parameters">Parameter declarations of the method being decompiled. /// <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> /// 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> /// <returns>Block for the method body</returns>
public static BlockStatement CreateMethodBody(MethodDefinition methodDef, public static BlockStatement CreateMethodBody(MethodDefinition methodDef,
DecompilerContext context, DecompilerContext context,
IEnumerable<ParameterDeclaration> parameters = null, IEnumerable<ParameterDeclaration> parameters = null)
ConcurrentDictionary<int, IEnumerable<ILVariable>> localVariables = null)
{ {
if (localVariables == null)
localVariables = new ConcurrentDictionary<int, IEnumerable<ILVariable>>();
MethodDefinition oldCurrentMethod = context.CurrentMethod; MethodDefinition oldCurrentMethod = context.CurrentMethod;
Debug.Assert(oldCurrentMethod == null || oldCurrentMethod == methodDef); Debug.Assert(oldCurrentMethod == null || oldCurrentMethod == methodDef);
context.CurrentMethod = methodDef; context.CurrentMethod = methodDef;
@ -69,10 +64,10 @@ namespace ICSharpCode.Decompiler.Ast
builder.context = context; builder.context = context;
builder.typeSystem = methodDef.Module.TypeSystem; builder.typeSystem = methodDef.Module.TypeSystem;
if (Debugger.IsAttached) { if (Debugger.IsAttached) {
return builder.CreateMethodBody(parameters, localVariables); return builder.CreateMethodBody(parameters);
} else { } else {
try { try {
return builder.CreateMethodBody(parameters, localVariables); return builder.CreateMethodBody(parameters);
} catch (OperationCanceledException) { } catch (OperationCanceledException) {
throw; throw;
} catch (Exception ex) { } catch (Exception ex) {
@ -84,13 +79,11 @@ 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 (methodDef.Body == null) {
return null;
if (localVariables == null) }
throw new ArgumentException("localVariables must be instantiated");
context.CancellationToken.ThrowIfCancellationRequested(); context.CancellationToken.ThrowIfCancellationRequested();
ILBlock ilMethod = new ILBlock(); ILBlock ilMethod = new ILBlock();
@ -102,10 +95,10 @@ namespace ICSharpCode.Decompiler.Ast
bodyGraph.Optimize(context, ilMethod); bodyGraph.Optimize(context, ilMethod);
context.CancellationToken.ThrowIfCancellationRequested(); context.CancellationToken.ThrowIfCancellationRequested();
var allVariables = ilMethod.GetSelfAndChildrenRecursive<ILExpression>().Select(e => e.Operand as ILVariable) var localVariables = ilMethod.GetSelfAndChildrenRecursive<ILExpression>().Select(e => e.Operand as ILVariable)
.Where(v => v != null && !v.IsParameter).Distinct(); .Where(v => v != null && !v.IsParameter).Distinct();
Debug.Assert(context.CurrentMethod == methodDef); Debug.Assert(context.CurrentMethod == methodDef);
NameVariables.AssignNamesToVariables(context, astBuilder.Parameters, allVariables, ilMethod); NameVariables.AssignNamesToVariables(context, astBuilder.Parameters, localVariables, ilMethod);
if (parameters != null) { if (parameters != null) {
foreach (var pair in (from p in parameters foreach (var pair in (from p in parameters
@ -132,9 +125,7 @@ namespace ICSharpCode.Decompiler.Ast
astBlock.Statements.InsertBefore(insertionPoint, newVarDecl); astBlock.Statements.InsertBefore(insertionPoint, newVarDecl);
} }
// store the variables - used for debugger astBlock.AddAnnotation(new MemberMapping(methodDef));
int token = methodDef.MetadataToken.ToInt32();
localVariables.AddOrUpdate(token, allVariables, (key, oldValue) => allVariables);
return astBlock; return astBlock;
} }

59
ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs

@ -19,9 +19,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.ILAst; using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp;
using Mono.Cecil; using Mono.Cecil;
@ -198,48 +198,51 @@ namespace ICSharpCode.Decompiler.Ast
} }
} }
Stack<TextLocation> startLocations = new Stack<TextLocation>();
MemberMapping currentMemberMapping;
Stack<MemberMapping> parentMemberMappings = new Stack<MemberMapping>();
public void StartNode(AstNode node) public void StartNode(AstNode node)
{ {
nodeStack.Push(node);
startLocations.Push(output.Location);
MemberMapping mapping = node.Annotation<MemberMapping>();
if (mapping != null) {
parentMemberMappings.Push(currentMemberMapping);
currentMemberMapping = mapping;
}
}
public void EndNode(AstNode node)
{
if (nodeStack.Pop() != node)
throw new InvalidOperationException();
var startLocation = startLocations.Pop();
// code mappings // code mappings
if (currentMemberMapping != null) {
var ranges = node.Annotation<List<ILRange>>(); var ranges = node.Annotation<List<ILRange>>();
if (ranges != null && ranges.Count > 0) { if (ranges != null && ranges.Count > 0) {
// find the ancestor that has method mapping as annotation
if (node.Parent != null)
{
var n = node.Ancestors.FirstOrDefault(a => a.Annotation<MemberMapping>() != null);
if (n != null) {
MemberMapping mapping = n.Annotation<MemberMapping>();
// add all ranges // add all ranges
foreach (var range in ranges) { foreach (var range in ranges) {
mapping.MemberCodeMappings.Add(new SourceCodeMapping { currentMemberMapping.MemberCodeMappings.Add(
new SourceCodeMapping {
ILInstructionOffset = range, ILInstructionOffset = range,
SourceCodeLine = output.Location.Line, StartLocation = startLocation,
MemberMapping = mapping EndLocation = output.Location,
MemberMapping = currentMemberMapping
}); });
} }
} }
} }
}
// definitions of types and their members
Predicate<AstNode> predicate = n => n is AttributedNode;
if (predicate(node)) {
var n = node as AttributedNode;
int attributesCount = 0;
if (n != null)
attributesCount = n.Attributes.Count;
node.AddAnnotation(new TextOutputLocation { Line = output.Location.Line + attributesCount, Column = output.Location.Column});
}
nodeStack.Push(node); if (node.Annotation<MemberMapping>() != null) {
output.AddDebuggerMemberMapping(currentMemberMapping);
currentMemberMapping = parentMemberMappings.Pop();
} }
public void EndNode(AstNode node)
{
if (nodeStack.Pop() != node)
throw new InvalidOperationException();
} }
} }
} }

88
ICSharpCode.Decompiler/CodeMappings.cs

@ -20,15 +20,16 @@ using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using ICSharpCode.Decompiler.Ast; using ICSharpCode.Decompiler.Ast;
using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Disassembler;
using ICSharpCode.Decompiler.ILAst; using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp;
using Mono.Cecil; using Mono.Cecil;
namespace ICSharpCode.Decompiler namespace ICSharpCode.Decompiler
{ {
[Obsolete]
public enum DecompiledLanguages public enum DecompiledLanguages
{ {
IL, IL,
@ -36,57 +37,36 @@ namespace ICSharpCode.Decompiler
} }
/// <summary> /// <summary>
/// Base class for decompliler classes : AstBuilder & ReflectionDisassembler. /// Maps the source code to IL.
/// </summary>
public abstract class BaseCodeMappings
{
/// <summary>
/// Gets the code mappings.
/// <remarks>Key is the metadata token.</remarks>
/// </summary>
public Dictionary<int, List<MemberMapping>> CodeMappings { get; protected set; }
/// <summary>
/// Gets the MembeReference that is decompiled (a MethodDefinition, PropertyDefinition etc.)
/// <remarks>Key is the metadata token.</remarks>
/// </summary>
public Dictionary<int, MemberReference> DecompiledMemberReferences { get; protected set; }
/// <summary>
/// Create data in the CodeMappings and DecompiledMemberReferences.
/// </summary> /// </summary>
/// <param name="token">Token of the current method.</param> public sealed class SourceCodeMapping
/// <param name="member">Current member (MethodDefinition, PropertyDefinition, EventDefinition).</param>
/// <remarks>The token is used in CodeMappings; member (and its token) is used in DecompiledMemberReferences.</remarks>
protected virtual void CreateCodeMappings(int token, MemberReference member)
{ {
this.CodeMappings.Add(token, new List<MemberMapping>()); [Obsolete("Use StartLocation instead - there might be multiple statements per line (e.g. for loops)")]
public int SourceCodeLine {
int t = member.MetadataToken.ToInt32(); get {
if (!this.DecompiledMemberReferences.ContainsKey(t)) return this.StartLocation.Line;
this.DecompiledMemberReferences.Add(t, member);
} }
} }
/// <summary> /// <summary>
/// Maps the source code to IL. /// Gets or sets the start location of the instruction.
/// </summary> /// </summary>
public sealed class SourceCodeMapping public TextLocation StartLocation { get; set; }
{
/// <summary> /// <summary>
/// Gets or sets the source code line number in the output. /// Gets or sets the end location of the instruction.
/// </summary> /// </summary>
public int SourceCodeLine { get; internal set; } public TextLocation EndLocation { get; set; }
/// <summary> /// <summary>
/// Gets or sets IL Range offset for the source code line. E.g.: 13-19 &lt;-&gt; 135. /// Gets or sets IL Range offset for the source code line. E.g.: 13-19 &lt;-&gt; 135.
/// </summary> /// </summary>
public ILRange ILInstructionOffset { get; internal set; } public ILRange ILInstructionOffset { get; set; }
/// <summary> /// <summary>
/// Gets or sets the member mapping this source code mapping belongs to. /// Gets or sets the member mapping this source code mapping belongs to.
/// </summary> /// </summary>
public MemberMapping MemberMapping { get; internal set; } public MemberMapping MemberMapping { get; set; }
/// <summary> /// <summary>
/// Retrieves the array that contains the IL range and the missing gaps between ranges. /// Retrieves the array that contains the IL range and the missing gaps between ranges.
@ -129,6 +109,18 @@ namespace ICSharpCode.Decompiler
{ {
IEnumerable<ILRange> invertedList; IEnumerable<ILRange> invertedList;
internal MemberMapping()
{
}
public MemberMapping(MethodDefinition method)
{
this.MetadataToken = method.MetadataToken.ToInt32();
this.MemberCodeMappings = new List<SourceCodeMapping>();
this.MemberReference = method;
this.CodeSize = method.Body.CodeSize;
}
/// <summary> /// <summary>
/// Gets or sets the type of the mapping. /// Gets or sets the type of the mapping.
/// </summary> /// </summary>
@ -317,30 +309,4 @@ namespace ICSharpCode.Decompiler
return true; return true;
} }
} }
/// <summary>
/// Decompilation data. Can be used by other applications to store the decompilation data.
/// </summary>
public class DecompileInformation
{
/// <summary>
/// Gets ot sets the code mappings
/// </summary>
public Dictionary<int, List<MemberMapping>> CodeMappings { get; set; }
/// <summary>
/// Gets or sets the local variables.
/// </summary>
public ConcurrentDictionary<int, IEnumerable<ILVariable>> LocalVariables { get; set; }
/// <summary>
/// Gets the list of MembeReferences that are decompiled (TypeDefinitions, MethodDefinitions, etc)
/// </summary>
public Dictionary<int, MemberReference> DecompiledMemberReferences { get; set; }
/// <summary>
/// Gets (or internal sets) the AST nodes.
/// </summary>
public IEnumerable<AstNode> AstNodes { get; set; }
}
} }

8
ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs

@ -85,13 +85,15 @@ namespace ICSharpCode.Decompiler.Disassembler
WriteStructureBody(new ILStructure(body), branchTargets, ref inst, methodMapping, method.Body.CodeSize); WriteStructureBody(new ILStructure(body), branchTargets, ref inst, methodMapping, method.Body.CodeSize);
} else { } else {
foreach (var inst in method.Body.Instructions) { foreach (var inst in method.Body.Instructions) {
var startLocation = output.Location;
inst.WriteTo(output); inst.WriteTo(output);
if (methodMapping != null) { if (methodMapping != null) {
// add IL code mappings - used in debugger // add IL code mappings - used in debugger
methodMapping.MemberCodeMappings.Add( methodMapping.MemberCodeMappings.Add(
new SourceCodeMapping() { new SourceCodeMapping() {
SourceCodeLine = output.Location.Line, StartLocation = output.Location,
EndLocation = output.Location,
ILInstructionOffset = new ILRange { From = inst.Offset, To = inst.Next == null ? method.Body.CodeSize : inst.Next.Offset }, ILInstructionOffset = new ILRange { From = inst.Offset, To = inst.Next == null ? method.Body.CodeSize : inst.Next.Offset },
MemberMapping = methodMapping MemberMapping = methodMapping
}); });
@ -188,13 +190,15 @@ namespace ICSharpCode.Decompiler.Disassembler
if (!isFirstInstructionInStructure && (prevInstructionWasBranch || branchTargets.Contains(offset))) { if (!isFirstInstructionInStructure && (prevInstructionWasBranch || branchTargets.Contains(offset))) {
output.WriteLine(); // put an empty line after branches, and in front of branch targets output.WriteLine(); // put an empty line after branches, and in front of branch targets
} }
var startLocation = output.Location;
inst.WriteTo(output); inst.WriteTo(output);
// add IL code mappings - used in debugger // add IL code mappings - used in debugger
if (currentMethodMapping != null) { if (currentMethodMapping != null) {
currentMethodMapping.MemberCodeMappings.Add( currentMethodMapping.MemberCodeMappings.Add(
new SourceCodeMapping() { new SourceCodeMapping() {
SourceCodeLine = output.Location.Line, StartLocation = startLocation,
EndLocation = output.Location,
ILInstructionOffset = new ILRange { From = inst.Offset, To = inst.Next == null ? codeSize : inst.Next.Offset }, ILInstructionOffset = new ILRange { From = inst.Offset, To = inst.Next == null ? codeSize : inst.Next.Offset },
MemberMapping = currentMethodMapping MemberMapping = currentMethodMapping
}); });

12
ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs

@ -30,7 +30,7 @@ namespace ICSharpCode.Decompiler.Disassembler
/// <summary> /// <summary>
/// Disassembles type and member definitions. /// Disassembles type and member definitions.
/// </summary> /// </summary>
public sealed class ReflectionDisassembler : BaseCodeMappings public sealed class ReflectionDisassembler
{ {
ITextOutput output; ITextOutput output;
CancellationToken cancellationToken; CancellationToken cancellationToken;
@ -45,9 +45,6 @@ namespace ICSharpCode.Decompiler.Disassembler
this.output = output; this.output = output;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
this.methodBodyDisassembler = new MethodBodyDisassembler(output, detectControlStructure, cancellationToken); this.methodBodyDisassembler = new MethodBodyDisassembler(output, detectControlStructure, cancellationToken);
this.CodeMappings = new Dictionary<int, List<MemberMapping>>();
this.DecompiledMemberReferences = new Dictionary<int, MemberReference>();
} }
#region Disassemble Method #region Disassemble Method
@ -220,9 +217,9 @@ namespace ICSharpCode.Decompiler.Disassembler
if (method.HasBody) { if (method.HasBody) {
// create IL code mappings - used in debugger // create IL code mappings - used in debugger
CreateCodeMappings(method.MetadataToken.ToInt32(), currentMember); MemberMapping methodMapping = new MemberMapping(method);
MemberMapping methodMapping = method.CreateCodeMapping(this.CodeMappings[method.MetadataToken.ToInt32()], currentMember);
methodBodyDisassembler.Disassemble(method.Body, methodMapping); methodBodyDisassembler.Disassemble(method.Body, methodMapping);
output.AddDebuggerMemberMapping(methodMapping);
} }
CloseBlock("end of method " + DisassemblerHelpers.Escape(method.DeclaringType.Name) + "::" + DisassemblerHelpers.Escape(method.Name)); CloseBlock("end of method " + DisassemblerHelpers.Escape(method.DeclaringType.Name) + "::" + DisassemblerHelpers.Escape(method.Name));
@ -676,9 +673,6 @@ namespace ICSharpCode.Decompiler.Disassembler
public void DisassembleField(FieldDefinition field) public void DisassembleField(FieldDefinition field)
{ {
// create mappings for decompiled fields only
this.DecompiledMemberReferences.Add(field.MetadataToken.ToInt32(), field);
output.WriteDefinition(".field ", field); output.WriteDefinition(".field ", field);
WriteEnum(field.Attributes & FieldAttributes.FieldAccessMask, fieldVisibility); WriteEnum(field.Attributes & FieldAttributes.FieldAccessMask, fieldVisibility);
const FieldAttributes hasXAttributes = FieldAttributes.HasDefault | FieldAttributes.HasFieldMarshal | FieldAttributes.HasFieldRVA; const FieldAttributes hasXAttributes = FieldAttributes.HasDefault | FieldAttributes.HasFieldMarshal | FieldAttributes.HasFieldRVA;

11
ICSharpCode.Decompiler/ITextOutput.cs

@ -18,12 +18,13 @@
using System; using System;
using System.IO; using System.IO;
using ICSharpCode.NRefactory;
namespace ICSharpCode.Decompiler namespace ICSharpCode.Decompiler
{ {
public interface ITextOutput public interface ITextOutput
{ {
TextOutputLocation Location { get; } TextLocation Location { get; }
void Indent(); void Indent();
void Unindent(); void Unindent();
@ -33,16 +34,12 @@ namespace ICSharpCode.Decompiler
void WriteDefinition(string text, object definition); void WriteDefinition(string text, object definition);
void WriteReference(string text, object reference, bool isLocal = false); void WriteReference(string text, object reference, bool isLocal = false);
void AddDebuggerMemberMapping(MemberMapping memberMapping);
void MarkFoldStart(string collapsedText = "...", bool defaultCollapsed = false); void MarkFoldStart(string collapsedText = "...", bool defaultCollapsed = false);
void MarkFoldEnd(); void MarkFoldEnd();
} }
public sealed class TextOutputLocation
{
public int Line { get; set; }
public int Column { get; set; }
}
public static class TextOutputExtensions public static class TextOutputExtensions
{ {
public static void Write(this ITextOutput output, string format, params object[] args) public static void Write(this ITextOutput output, string format, params object[] args)

27
ICSharpCode.Decompiler/PlainTextOutput.cs

@ -18,17 +18,18 @@
using System; using System;
using System.IO; using System.IO;
using ICSharpCode.NRefactory;
namespace ICSharpCode.Decompiler namespace ICSharpCode.Decompiler
{ {
public sealed class PlainTextOutput : ITextOutput public sealed class PlainTextOutput : ITextOutput
{ {
const int TAB_SIZE = 4;
readonly TextWriter writer; readonly TextWriter writer;
int indent; int indent;
bool needsIndent; bool needsIndent;
TextOutputLocation location = new TextOutputLocation { Line = 1, Column = 1};
int line = 1;
int column = 1;
public PlainTextOutput(TextWriter writer) public PlainTextOutput(TextWriter writer)
{ {
@ -42,8 +43,10 @@ namespace ICSharpCode.Decompiler
this.writer = new StringWriter(); this.writer = new StringWriter();
} }
public TextOutputLocation Location { public TextLocation Location {
get { return location; } get {
return new TextLocation(line, column + (needsIndent ? indent : 0));
}
} }
public override string ToString() public override string ToString()
@ -67,8 +70,8 @@ namespace ICSharpCode.Decompiler
needsIndent = false; needsIndent = false;
for (int i = 0; i < indent; i++) { for (int i = 0; i < indent; i++) {
writer.Write('\t'); writer.Write('\t');
location.Column += TAB_SIZE - 1;
} }
column += indent;
} }
} }
@ -76,22 +79,22 @@ namespace ICSharpCode.Decompiler
{ {
WriteIndent(); WriteIndent();
writer.Write(ch); writer.Write(ch);
location.Column++; column++;
} }
public void Write(string text) public void Write(string text)
{ {
WriteIndent(); WriteIndent();
writer.Write(text); writer.Write(text);
location.Column += text.Length; column += text.Length;
} }
public void WriteLine() public void WriteLine()
{ {
location.Line++;
writer.WriteLine(); writer.WriteLine();
needsIndent = true; needsIndent = true;
location.Column = TAB_SIZE * indent; line++;
column = 1;
} }
public void WriteDefinition(string text, object definition) public void WriteDefinition(string text, object definition)
@ -111,5 +114,9 @@ namespace ICSharpCode.Decompiler
void ITextOutput.MarkFoldEnd() void ITextOutput.MarkFoldEnd()
{ {
} }
void ITextOutput.AddDebuggerMemberMapping(MemberMapping memberMapping)
{
}
} }
} }

6
ILSpy/Bookmarks/MemberBookmark.cs

@ -92,9 +92,9 @@ namespace ICSharpCode.ILSpy.Bookmarks
public int LineNumber { public int LineNumber {
get { get {
var t = node.Annotation<TextOutputLocation>(); //var t = node.Annotation<TextOutputLocation>();
if (t != null) //if (t != null)
return t.Line; // return t.Line;
return 0; return 0;
} }
} }

6
ILSpy/Languages/CSharpLanguage.cs

@ -100,7 +100,6 @@ namespace ICSharpCode.ILSpy
codeDomBuilder.AddMethod(method); codeDomBuilder.AddMethod(method);
RunTransformsAndGenerateCode(codeDomBuilder, output, options); RunTransformsAndGenerateCode(codeDomBuilder, output, options);
} }
NotifyDecompilationFinished(codeDomBuilder);
} }
class SelectCtorTransform : IAstTransform class SelectCtorTransform : IAstTransform
@ -145,7 +144,6 @@ namespace ICSharpCode.ILSpy
AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: property.DeclaringType, isSingleMember: true); AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: property.DeclaringType, isSingleMember: true);
codeDomBuilder.AddProperty(property); codeDomBuilder.AddProperty(property);
RunTransformsAndGenerateCode(codeDomBuilder, output, options); RunTransformsAndGenerateCode(codeDomBuilder, output, options);
NotifyDecompilationFinished(codeDomBuilder);
} }
public override void DecompileField(FieldDefinition field, ITextOutput output, DecompilationOptions options) public override void DecompileField(FieldDefinition field, ITextOutput output, DecompilationOptions options)
@ -159,7 +157,6 @@ namespace ICSharpCode.ILSpy
AddFieldsAndCtors(codeDomBuilder, field.DeclaringType, field.IsStatic); AddFieldsAndCtors(codeDomBuilder, field.DeclaringType, field.IsStatic);
} }
RunTransformsAndGenerateCode(codeDomBuilder, output, options, new SelectFieldTransform(field)); RunTransformsAndGenerateCode(codeDomBuilder, output, options, new SelectFieldTransform(field));
NotifyDecompilationFinished(codeDomBuilder);
} }
/// <summary> /// <summary>
@ -203,7 +200,6 @@ namespace ICSharpCode.ILSpy
AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: ev.DeclaringType, isSingleMember: true); AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: ev.DeclaringType, isSingleMember: true);
codeDomBuilder.AddEvent(ev); codeDomBuilder.AddEvent(ev);
RunTransformsAndGenerateCode(codeDomBuilder, output, options); RunTransformsAndGenerateCode(codeDomBuilder, output, options);
NotifyDecompilationFinished(codeDomBuilder);
} }
public override void DecompileType(TypeDefinition type, ITextOutput output, DecompilationOptions options) public override void DecompileType(TypeDefinition type, ITextOutput output, DecompilationOptions options)
@ -211,7 +207,6 @@ namespace ICSharpCode.ILSpy
AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: type); AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: type);
codeDomBuilder.AddType(type); codeDomBuilder.AddType(type);
RunTransformsAndGenerateCode(codeDomBuilder, output, options); RunTransformsAndGenerateCode(codeDomBuilder, output, options);
NotifyDecompilationFinished(codeDomBuilder);
} }
void RunTransformsAndGenerateCode(AstBuilder astBuilder, ITextOutput output, DecompilationOptions options, IAstTransform additionalTransform = null) void RunTransformsAndGenerateCode(AstBuilder astBuilder, ITextOutput output, DecompilationOptions options, IAstTransform additionalTransform = null)
@ -283,7 +278,6 @@ namespace ICSharpCode.ILSpy
codeDomBuilder.GenerateCode(output); codeDomBuilder.GenerateCode(output);
} }
} }
OnDecompilationFinished(null);
} }
#region WriteProjectFile #region WriteProjectFile

7
ILSpy/Languages/ILLanguage.cs

@ -52,14 +52,12 @@ namespace ICSharpCode.ILSpy
{ {
var dis = new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken); var dis = new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken);
dis.DisassembleMethod(method); dis.DisassembleMethod(method);
NotifyDecompilationFinished(dis);
} }
public override void DecompileField(FieldDefinition field, ITextOutput output, DecompilationOptions options) public override void DecompileField(FieldDefinition field, ITextOutput output, DecompilationOptions options)
{ {
var dis = new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken); var dis = new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken);
dis.DisassembleField(field); dis.DisassembleField(field);
NotifyDecompilationFinished(dis);
} }
public override void DecompileProperty(PropertyDefinition property, ITextOutput output, DecompilationOptions options) public override void DecompileProperty(PropertyDefinition property, ITextOutput output, DecompilationOptions options)
@ -78,7 +76,6 @@ namespace ICSharpCode.ILSpy
output.WriteLine(); output.WriteLine();
rd.DisassembleMethod(m); rd.DisassembleMethod(m);
} }
NotifyDecompilationFinished(rd);
} }
public override void DecompileEvent(EventDefinition ev, ITextOutput output, DecompilationOptions options) public override void DecompileEvent(EventDefinition ev, ITextOutput output, DecompilationOptions options)
@ -97,20 +94,17 @@ namespace ICSharpCode.ILSpy
output.WriteLine(); output.WriteLine();
rd.DisassembleMethod(m); rd.DisassembleMethod(m);
} }
NotifyDecompilationFinished(rd);
} }
public override void DecompileType(TypeDefinition type, ITextOutput output, DecompilationOptions options) public override void DecompileType(TypeDefinition type, ITextOutput output, DecompilationOptions options)
{ {
var dis = new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken); var dis = new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken);
dis.DisassembleType(type); dis.DisassembleType(type);
NotifyDecompilationFinished(dis);
} }
public override void DecompileNamespace(string nameSpace, IEnumerable<TypeDefinition> types, ITextOutput output, DecompilationOptions options) public override void DecompileNamespace(string nameSpace, IEnumerable<TypeDefinition> types, ITextOutput output, DecompilationOptions options)
{ {
new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken).DisassembleNamespace(nameSpace, types); new ReflectionDisassembler(output, detectControlStructure, options.CancellationToken).DisassembleNamespace(nameSpace, types);
OnDecompilationFinished(null);
} }
public override void DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) public override void DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options)
@ -129,7 +123,6 @@ namespace ICSharpCode.ILSpy
output.WriteLine(); output.WriteLine();
rd.WriteModuleContents(assembly.AssemblyDefinition.MainModule); rd.WriteModuleContents(assembly.AssemblyDefinition.MainModule);
} }
OnDecompilationFinished(null);
} }
public override string TypeToString(TypeReference t, bool includeNamespace, ICustomAttributeProvider attributeProvider) public override string TypeToString(TypeReference t, bool includeNamespace, ICustomAttributeProvider attributeProvider)

43
ILSpy/Languages/Language.cs

@ -19,14 +19,10 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Ast;
using ICSharpCode.Decompiler.Disassembler;
using ICSharpCode.Decompiler.ILAst; using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.Utils;
using Mono.Cecil; using Mono.Cecil;
namespace ICSharpCode.ILSpy namespace ICSharpCode.ILSpy
@ -34,6 +30,7 @@ namespace ICSharpCode.ILSpy
/// <summary> /// <summary>
/// Decompilation event arguments. /// Decompilation event arguments.
/// </summary> /// </summary>
[Obsolete]
public sealed class DecompileEventArgs : EventArgs public sealed class DecompileEventArgs : EventArgs
{ {
/// <summary> /// <summary>
@ -65,7 +62,8 @@ namespace ICSharpCode.ILSpy
/// <summary> /// <summary>
/// Decompile finished event. /// Decompile finished event.
/// </summary> /// </summary>
public event EventHandler<DecompileEventArgs> DecompileFinished; [Obsolete]
public event EventHandler<DecompileEventArgs> DecompileFinished { add {} remove {} }
/// <summary> /// <summary>
/// Gets the name of the language (as shown in the UI) /// Gets the name of the language (as shown in the UI)
@ -121,7 +119,6 @@ namespace ICSharpCode.ILSpy
public virtual void DecompileNamespace(string nameSpace, IEnumerable<TypeDefinition> types, ITextOutput output, DecompilationOptions options) public virtual void DecompileNamespace(string nameSpace, IEnumerable<TypeDefinition> types, ITextOutput output, DecompilationOptions options)
{ {
WriteCommentLine(output, nameSpace); WriteCommentLine(output, nameSpace);
OnDecompilationFinished(null);
} }
public virtual void DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) public virtual void DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options)
@ -197,39 +194,5 @@ namespace ICSharpCode.ILSpy
{ {
return member; return member;
} }
protected virtual void OnDecompilationFinished(DecompileEventArgs e)
{
if (DecompileFinished != null) {
DecompileFinished(this, e);
}
}
protected void NotifyDecompilationFinished(BaseCodeMappings b)
{
if (b is AstBuilder) {
var builder = b as AstBuilder;
var nodes = TreeTraversal
.PreOrder((AstNode)builder.CompilationUnit, n => n.Children)
.Where(n => n is AttributedNode && n.Annotation<TextOutputLocation>() != null);
OnDecompilationFinished(new DecompileEventArgs {
CodeMappings = builder.CodeMappings,
LocalVariables = builder.LocalVariables,
DecompiledMemberReferences = builder.DecompiledMemberReferences,
AstNodes = nodes
});
}
if (b is ReflectionDisassembler) {
var dis = b as ReflectionDisassembler;
OnDecompilationFinished(new DecompileEventArgs {
CodeMappings = dis.CodeMappings,
DecompiledMemberReferences = dis.DecompiledMemberReferences,
AstNodes = null // TODO: how can I find the nodes with line numbers from dis?
});
}
}
} }
} }

20
ILSpy/TextView/AvalonEditTextOutput.cs

@ -67,8 +67,8 @@ namespace ICSharpCode.ILSpy.TextView
/// </summary> /// </summary>
public sealed class AvalonEditTextOutput : ISmartTextOutput public sealed class AvalonEditTextOutput : ISmartTextOutput
{ {
TextOutputLocation location = new TextOutputLocation { Line = 1, Column = 1 };
int lastLineStart = 0; int lastLineStart = 0;
int lineNumber = 1;
readonly StringBuilder b = new StringBuilder(); readonly StringBuilder b = new StringBuilder();
/// <summary>Current indentation level</summary> /// <summary>Current indentation level</summary>
@ -92,6 +92,8 @@ namespace ICSharpCode.ILSpy.TextView
/// <summary>Embedded UIElements, see <see cref="UIElementGenerator"/>.</summary> /// <summary>Embedded UIElements, see <see cref="UIElementGenerator"/>.</summary>
internal readonly List<KeyValuePair<int, Lazy<UIElement>>> UIElements = new List<KeyValuePair<int, Lazy<UIElement>>>(); internal readonly List<KeyValuePair<int, Lazy<UIElement>>> UIElements = new List<KeyValuePair<int, Lazy<UIElement>>>();
List<MemberMapping> memberMappings = new List<MemberMapping>();
public AvalonEditTextOutput() public AvalonEditTextOutput()
{ {
} }
@ -119,11 +121,14 @@ namespace ICSharpCode.ILSpy.TextView
get { return b.Length; } get { return b.Length; }
} }
public TextOutputLocation Location { public ICSharpCode.NRefactory.TextLocation Location {
get { get {
location.Column = b.Length - lastLineStart + 1; return new ICSharpCode.NRefactory.TextLocation(lineNumber, b.Length - lastLineStart + 1 + (needsIndent ? indent : 0));
return location; }
} }
public IList<MemberMapping> MemberMappings {
get { return memberMappings; }
} }
#region Text Document #region Text Document
@ -197,7 +202,7 @@ namespace ICSharpCode.ILSpy.TextView
b.AppendLine(); b.AppendLine();
needsIndent = true; needsIndent = true;
lastLineStart = b.Length; lastLineStart = b.Length;
location.Line++; lineNumber++;
if (this.TextLength > LengthLimit) { if (this.TextLength > LengthLimit) {
throw new OutputLengthExceededException(); throw new OutputLengthExceededException();
} }
@ -248,5 +253,10 @@ namespace ICSharpCode.ILSpy.TextView
this.UIElements.Add(new KeyValuePair<int, Lazy<UIElement>>(this.TextLength, new Lazy<UIElement>(element))); this.UIElements.Add(new KeyValuePair<int, Lazy<UIElement>>(this.TextLength, new Lazy<UIElement>(element)));
} }
} }
public void AddDebuggerMemberMapping(MemberMapping memberMapping)
{
memberMappings.Add(memberMapping);
}
} }
} }

1
ILSpy/VB/VBLanguage.cs

@ -125,7 +125,6 @@ namespace ICSharpCode.ILSpy.VB
RunTransformsAndGenerateCode(codeDomBuilder, output, options, assembly.AssemblyDefinition.MainModule); RunTransformsAndGenerateCode(codeDomBuilder, output, options, assembly.AssemblyDefinition.MainModule);
} }
} }
OnDecompilationFinished(null);
} }
static readonly string[] projectImports = new[] { static readonly string[] projectImports = new[] {

Loading…
Cancel
Save