Browse Source

PDB generator now can emit "method stepping information" for async functions.

pull/1440/head
Daniel Grunwald 6 years ago
parent
commit
f1c8142ce2
  1. 43
      ICSharpCode.Decompiler/DebugInfo/AsyncDebugInfo.cs
  2. 192
      ICSharpCode.Decompiler/DebugInfo/DebugInfoGenerator.cs
  3. 1
      ICSharpCode.Decompiler/DebugInfo/ImportScopeInfo.cs
  4. 100
      ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs
  5. 199
      ICSharpCode.Decompiler/DebugInfo/ScopesGenerator.cs
  6. 3
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  7. 21
      ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs
  8. 13
      ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
  9. 5
      ICSharpCode.Decompiler/Util/KeyComparer.cs
  10. 2
      ILSpy/Languages/CSharpILMixedLanguage.cs
  11. 2
      ILSpy/MainWindow.xaml

43
ICSharpCode.Decompiler/DebugInfo/AsyncDebugInfo.cs

@ -0,0 +1,43 @@ @@ -0,0 +1,43 @@
using System;
using System.Collections.Immutable;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
namespace ICSharpCode.Decompiler.DebugInfo
{
readonly struct AsyncDebugInfo
{
public readonly int CatchHandlerOffset;
public readonly ImmutableArray<Await> Awaits;
public AsyncDebugInfo(int catchHandlerOffset, ImmutableArray<Await> awaits)
{
this.CatchHandlerOffset = catchHandlerOffset;
this.Awaits = awaits;
}
public readonly struct Await
{
public readonly int YieldOffset;
public readonly int ResumeOffset;
public Await(int yieldOffset, int resumeOffset)
{
this.YieldOffset = yieldOffset;
this.ResumeOffset = resumeOffset;
}
}
internal BlobBuilder BuildBlob(MethodDefinitionHandle moveNext)
{
BlobBuilder blob = new BlobBuilder();
blob.WriteUInt32((uint)CatchHandlerOffset);
foreach (var await in Awaits) {
blob.WriteUInt32((uint)await.YieldOffset);
blob.WriteUInt32((uint)await.ResumeOffset);
blob.WriteCompressedInteger(MetadataTokens.GetToken(moveNext));
}
return blob;
}
}
}

192
ICSharpCode.Decompiler/DebugInfo/DebugInfoGenerator.cs

@ -0,0 +1,192 @@ @@ -0,0 +1,192 @@
// Copyright (c) 2018 Siegfried Pammer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Text;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.DebugInfo
{
/// <summary>
/// Visitor that generates debug information.
///
/// The intended usage is to create a new instance for each source file,
/// and call syntaxTree.AcceptVisitor(debugInfoGenerator) to fill the internal debug info tables.
/// This can happen concurrently for multiple source files.
/// Then the main thread calls Generate() to write out the results into the PDB.
/// </summary>
class DebugInfoGenerator : DepthFirstAstVisitor
{
static readonly KeyComparer<ILVariable, int> ILVariableKeyComparer = new KeyComparer<ILVariable, int>(l => l.Index.Value, Comparer<int>.Default, EqualityComparer<int>.Default);
IDecompilerTypeSystem typeSystem;
readonly ImportScopeInfo globalImportScope = new ImportScopeInfo();
ImportScopeInfo currentImportScope;
List<ImportScopeInfo> importScopes = new List<ImportScopeInfo>();
List<(MethodDefinitionHandle Method, ImportScopeInfo Import, int Offset, int Length, HashSet<ILVariable> Locals)> localScopes = new List<(MethodDefinitionHandle Method, ImportScopeInfo Import, int Offset, int Length, HashSet<ILVariable> Locals)>();
List<ILFunction> functions = new List<ILFunction>();
/// <summary>
/// Gets all functions with bodies that were seen by the visitor so far.
/// </summary>
public IReadOnlyList<ILFunction> Functions {
get => functions;
}
public DebugInfoGenerator(IDecompilerTypeSystem typeSystem)
{
this.typeSystem = typeSystem ?? throw new ArgumentNullException("typeSystem");
this.currentImportScope = globalImportScope;
}
public void Generate(MetadataBuilder metadata, ImportScopeHandle globalImportScope)
{
foreach (var scope in importScopes) {
var blob = EncodeImports(metadata, scope);
scope.Handle = metadata.AddImportScope(scope.Parent == null ? globalImportScope : scope.Parent.Handle, blob);
}
foreach (var localScope in localScopes) {
int nextRow = metadata.GetRowCount(TableIndex.LocalVariable) + 1;
var firstLocalVariable = MetadataTokens.LocalVariableHandle(nextRow);
foreach (var local in localScope.Locals.OrderBy(l => l.Index)) {
var name = local.Name != null ? metadata.GetOrAddString(local.Name) : default;
metadata.AddLocalVariable(LocalVariableAttributes.None, local.Index.Value, name);
}
metadata.AddLocalScope(localScope.Method, localScope.Import.Handle, firstLocalVariable,
default, localScope.Offset, localScope.Length);
}
}
static BlobHandle EncodeImports(MetadataBuilder metadata, ImportScopeInfo scope)
{
var writer = new BlobBuilder();
foreach (var import in scope.Imports) {
writer.WriteByte((byte)ImportDefinitionKind.ImportNamespace);
writer.WriteCompressedInteger(MetadataTokens.GetHeapOffset(metadata.GetOrAddBlobUTF8(import)));
}
return metadata.GetOrAddBlob(writer);
}
public override void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration)
{
var parentImportScope = currentImportScope;
currentImportScope = new ImportScopeInfo(parentImportScope);
importScopes.Add(currentImportScope);
base.VisitNamespaceDeclaration(namespaceDeclaration);
currentImportScope = parentImportScope;
}
public override void VisitUsingDeclaration(UsingDeclaration usingDeclaration)
{
currentImportScope.Imports.Add(usingDeclaration.Namespace);
}
public override void VisitMethodDeclaration(MethodDeclaration methodDeclaration)
{
HandleMethod(methodDeclaration);
}
public override void VisitAccessor(Accessor accessor)
{
HandleMethod(accessor);
}
public override void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration)
{
HandleMethod(constructorDeclaration);
}
public override void VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration)
{
HandleMethod(destructorDeclaration);
}
public override void VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration)
{
HandleMethod(operatorDeclaration);
}
public override void VisitLambdaExpression(LambdaExpression lambdaExpression)
{
HandleMethod(lambdaExpression);
}
public override void VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression)
{
HandleMethod(anonymousMethodExpression);
}
void HandleMethod(AstNode node)
{
// Look into method body, e.g. in order to find lambdas
VisitChildren(node);
var function = node.Annotation<ILFunction>();
if (function == null || function.Method == null || function.Method.MetadataToken.IsNil)
return;
this.functions.Add(function);
var method = function.MoveNextMethod ?? function.Method;
MethodDefinitionHandle handle = (MethodDefinitionHandle)method.MetadataToken;
var file = typeSystem.MainModule.PEFile;
MethodDefinition md = file.Metadata.GetMethodDefinition(handle);
if (md.HasBody()) {
HandleMethodBody(function, file.Reader.GetMethodBody(md.RelativeVirtualAddress));
}
}
void HandleMethodBody(ILFunction function, MethodBodyBlock methodBody)
{
var method = function.MoveNextMethod ?? function.Method;
var localVariables = new HashSet<ILVariable>(ILVariableKeyComparer);
if (!methodBody.LocalSignature.IsNil) {
#if DEBUG
var types = typeSystem.MainModule.DecodeLocalSignature(methodBody.LocalSignature,
new TypeSystem.GenericContext(method));
#endif
foreach (var v in function.Variables) {
if (v.Index != null && v.Kind.IsLocal()) {
#if DEBUG
Debug.Assert(v.Index < types.Length && v.Type.Equals(types[v.Index.Value]));
#endif
localVariables.Add(v);
}
}
}
localScopes.Add(((MethodDefinitionHandle)method.MetadataToken, currentImportScope,
0, methodBody.GetCodeSize(), localVariables));
}
}
}

1
ICSharpCode.Decompiler/DebugInfo/ImportScopeInfo.cs

@ -25,7 +25,6 @@ namespace ICSharpCode.Decompiler.DebugInfo @@ -25,7 +25,6 @@ namespace ICSharpCode.Decompiler.DebugInfo
{
public readonly ImportScopeInfo Parent;
public ImportScopeHandle Handle;
public readonly HashSet<MethodDefinitionHandle> MethodDefinitions = new HashSet<MethodDefinitionHandle>();
public readonly HashSet<string> Imports = new HashSet<string>();
public ImportScopeInfo()

100
ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs

@ -41,12 +41,13 @@ namespace ICSharpCode.Decompiler.DebugInfo @@ -41,12 +41,13 @@ namespace ICSharpCode.Decompiler.DebugInfo
{
public class PortablePdbWriter
{
public static readonly Guid CSharpLanguageGuid = new Guid("3f5162f8-07c6-11d3-9053-00c04fa302a1");
static readonly Guid CSharpLanguageGuid = new Guid("3f5162f8-07c6-11d3-9053-00c04fa302a1");
public static readonly Guid DebugInfoEmbeddedSource = new Guid("0e8a571b-6926-466e-b4ad-8ab04611f5fe");
static readonly Guid DebugInfoEmbeddedSource = new Guid("0e8a571b-6926-466e-b4ad-8ab04611f5fe");
static readonly Guid MethodSteppingInformationBlobId = new Guid("54FD2AC5-E925-401A-9C2A-F94F171072F8");
public static readonly Guid HashAlgorithmSHA1 = new Guid("ff1816ec-aa5e-4d10-87f7-6f4963833460");
public static readonly Guid HashAlgorithmSHA256 = new Guid("8829d00f-11b8-4213-878b-770e8597ac16");
static readonly Guid HashAlgorithmSHA1 = new Guid("ff1816ec-aa5e-4d10-87f7-6f4963833460");
static readonly Guid HashAlgorithmSHA256 = new Guid("8829d00f-11b8-4213-878b-770e8597ac16");
static readonly FileVersionInfo decompilerVersion = FileVersionInfo.GetVersionInfo(typeof(CSharpDecompiler).Assembly.Location);
static readonly SHA256 hasher = SHA256.Create();
@ -63,6 +64,9 @@ namespace ICSharpCode.Decompiler.DebugInfo @@ -63,6 +64,9 @@ namespace ICSharpCode.Decompiler.DebugInfo
var sequencePointBlobs = new Dictionary<MethodDefinitionHandle, (DocumentHandle Document, BlobHandle SequencePoints)>();
var emptyList = new List<SequencePoint>();
var stateMachineMethods = new List<(MethodDefinitionHandle MoveNextMethod, MethodDefinitionHandle KickoffMethod)>();
var customDocumentDebugInfo = new List<(DocumentHandle Parent, GuidHandle Guid, BlobHandle Blob)>();
var customMethodDebugInfo = new List<(MethodDefinitionHandle Parent, GuidHandle Guid, BlobHandle Blob)>();
var globalImportScope = metadata.AddImportScope(default, default);
foreach (var handle in reader.GetTopLevelTypeDefinitions()) {
@ -74,36 +78,50 @@ namespace ICSharpCode.Decompiler.DebugInfo @@ -74,36 +78,50 @@ namespace ICSharpCode.Decompiler.DebugInfo
continue;
// Generate source and checksum
var name = metadata.GetOrAddDocumentName(type.GetFullTypeName(reader).ReflectionName.Replace('.', Path.DirectorySeparatorChar) + ".cs");
if (!noLogo)
syntaxTree.InsertChildAfter(null, new Comment(" PDB and source generated by ICSharpCode.Decompiler " + decompilerVersion.FileVersion), Roles.Comment);
var sourceText = SyntaxTreeToString(syntaxTree, settings);
var sourceBlob = WriteSourceToBlob(metadata, sourceText, out var sourceCheckSum);
// Create Document(Handle)
var document = metadata.AddDocument(name,
hashAlgorithm: metadata.GetOrAddGuid(HashAlgorithmSHA256),
hash: metadata.GetOrAddBlob(sourceCheckSum),
language: metadata.GetOrAddGuid(CSharpLanguageGuid));
// Add embedded source to the PDB
metadata.AddCustomDebugInformation(document, metadata.GetOrAddGuid(DebugInfoEmbeddedSource), sourceBlob);
ScopesGenerator.Generate(decompiler.TypeSystem, metadata, globalImportScope, syntaxTree);
// Generate sequence points for the syntax tree
var sequencePoints = decompiler.CreateSequencePoints(syntaxTree).ToDictionary(sp => (MethodDefinitionHandle)sp.Key.Method.MetadataToken, sp => sp.Value);
foreach (var method in type.GetMethods()) {
ProcessMethod(method, document, sequencePoints, syntaxTree);
}
foreach (var nestedTypeHandle in type.GetNestedTypes()) {
var nestedType = reader.GetTypeDefinition(nestedTypeHandle);
foreach (var method in nestedType.GetMethods()) {
ProcessMethod(method, document, sequencePoints, syntaxTree);
var sequencePoints = decompiler.CreateSequencePoints(syntaxTree);
// Generate other debug information
var debugInfoGen = new DebugInfoGenerator(decompiler.TypeSystem);
syntaxTree.AcceptVisitor(debugInfoGen);
lock (metadata) {
var sourceBlob = WriteSourceToBlob(metadata, sourceText, out var sourceCheckSum);
var name = metadata.GetOrAddDocumentName(type.GetFullTypeName(reader).ReflectionName.Replace('.', Path.DirectorySeparatorChar) + ".cs");
// Create Document(Handle)
var document = metadata.AddDocument(name,
hashAlgorithm: metadata.GetOrAddGuid(HashAlgorithmSHA256),
hash: metadata.GetOrAddBlob(sourceCheckSum),
language: metadata.GetOrAddGuid(CSharpLanguageGuid));
// Add embedded source to the PDB
customDocumentDebugInfo.Add((document,
metadata.GetOrAddGuid(DebugInfoEmbeddedSource),
sourceBlob));
debugInfoGen.Generate(metadata, globalImportScope);
foreach (var function in debugInfoGen.Functions) {
var method = function.MoveNextMethod ?? function.Method;
var methodHandle = (MethodDefinitionHandle)method.MetadataToken;
sequencePoints.TryGetValue(function, out var points);
ProcessMethod(methodHandle, document, points, syntaxTree);
if (function.MoveNextMethod != null) {
stateMachineMethods.Add((
(MethodDefinitionHandle)function.MoveNextMethod.MetadataToken,
(MethodDefinitionHandle)function.Method.MetadataToken
));
}
if (function.IsAsync) {
customMethodDebugInfo.Add((methodHandle,
metadata.GetOrAddGuid(MethodSteppingInformationBlobId),
metadata.GetOrAddBlob(function.AsyncDebugInfo.BuildBlob(methodHandle))));
}
}
}
}
@ -118,6 +136,19 @@ namespace ICSharpCode.Decompiler.DebugInfo @@ -118,6 +136,19 @@ namespace ICSharpCode.Decompiler.DebugInfo
}
}
stateMachineMethods.SortBy(row => row.MoveNextMethod);
foreach (var row in stateMachineMethods) {
metadata.AddStateMachineMethod(row.MoveNextMethod, row.KickoffMethod);
}
customMethodDebugInfo.SortBy(row => row.Parent);
foreach (var row in customMethodDebugInfo) {
metadata.AddCustomDebugInformation(row.Parent, row.Guid, row.Blob);
}
customDocumentDebugInfo.SortBy(row => row.Parent);
foreach (var row in customDocumentDebugInfo) {
metadata.AddCustomDebugInformation(row.Parent, row.Guid, row.Blob);
}
var debugDir = file.Reader.ReadDebugDirectory().FirstOrDefault(dir => dir.Type == DebugDirectoryEntryType.CodeView);
var portable = file.Reader.ReadCodeViewDebugDirectoryData(debugDir);
var contentId = new BlobContentId(portable.Guid, debugDir.Stamp);
@ -127,7 +158,7 @@ namespace ICSharpCode.Decompiler.DebugInfo @@ -127,7 +158,7 @@ namespace ICSharpCode.Decompiler.DebugInfo
blobBuilder.WriteContentTo(targetStream);
void ProcessMethod(MethodDefinitionHandle method, DocumentHandle document,
Dictionary<MethodDefinitionHandle, List<SequencePoint>> sequencePoints, SyntaxTree syntaxTree)
List<SequencePoint> sequencePoints, SyntaxTree syntaxTree)
{
var methodDef = reader.GetMethodDefinition(method);
int localSignatureRowId;
@ -139,13 +170,10 @@ namespace ICSharpCode.Decompiler.DebugInfo @@ -139,13 +170,10 @@ namespace ICSharpCode.Decompiler.DebugInfo
methodBody = null;
localSignatureRowId = 0;
}
if (!sequencePoints.TryGetValue(method, out var points))
points = emptyList;
if (points.Count == 0)
sequencePointBlobs.Add(method, (default, default));
if (sequencePoints?.Count > 0)
sequencePointBlobs.Add(method, (document, EncodeSequencePoints(metadata, localSignatureRowId, sequencePoints)));
else
sequencePointBlobs.Add(method, (document, EncodeSequencePoints(metadata, localSignatureRowId, points)));
sequencePointBlobs.Add(method, (default, default));
}
}

199
ICSharpCode.Decompiler/DebugInfo/ScopesGenerator.cs

@ -1,199 +0,0 @@ @@ -1,199 +0,0 @@
// Copyright (c) 2018 Siegfried Pammer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Text;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.DebugInfo
{
class ScopesGenerator : DepthFirstAstVisitor<ImportScopeInfo, ImportScopeInfo>
{
static readonly KeyComparer<ILVariable, int> ILVariableKeyComparer = new KeyComparer<ILVariable, int>(l => l.Index.Value, Comparer<int>.Default, EqualityComparer<int>.Default);
IDecompilerTypeSystem typeSystem;
ImportScopeInfo currentImportScope;
List<ImportScopeInfo> importScopes = new List<ImportScopeInfo>();
List<(MethodDefinitionHandle Method, ImportScopeInfo Import, int Offset, int Length, HashSet<ILVariable> Locals)> localScopes = new List<(MethodDefinitionHandle Method, ImportScopeInfo Import, int Offset, int Length, HashSet<ILVariable> Locals)>();
private ScopesGenerator()
{
}
public static void Generate(IDecompilerTypeSystem typeSystem, MetadataBuilder metadata, ImportScopeHandle globalImportScope, SyntaxTree syntaxTree)
{
var generator = new ScopesGenerator();
generator.typeSystem = typeSystem;
syntaxTree.AcceptVisitor(generator, new ImportScopeInfo());
foreach (var scope in generator.importScopes) {
var blob = EncodeImports(metadata, scope);
scope.Handle = metadata.AddImportScope(scope.Parent == null ? globalImportScope : scope.Parent.Handle, blob);
}
foreach (var localScope in generator.localScopes) {
int nextRow = metadata.GetRowCount(TableIndex.LocalVariable) + 1;
var firstLocalVariable = MetadataTokens.LocalVariableHandle(nextRow);
foreach (var local in localScope.Locals.OrderBy(l => l.Index)) {
var name = local.Name != null ? metadata.GetOrAddString(local.Name) : default;
metadata.AddLocalVariable(LocalVariableAttributes.None, local.Index.Value, name);
}
metadata.AddLocalScope(localScope.Method, localScope.Import.Handle, firstLocalVariable,
default, localScope.Offset, localScope.Length);
}
}
static BlobHandle EncodeImports(MetadataBuilder metadata, ImportScopeInfo scope)
{
var writer = new BlobBuilder();
foreach (var import in scope.Imports) {
writer.WriteByte((byte)ImportDefinitionKind.ImportNamespace);
writer.WriteCompressedInteger(MetadataTokens.GetHeapOffset(metadata.GetOrAddBlobUTF8(import)));
}
return metadata.GetOrAddBlob(writer);
}
public override ImportScopeInfo VisitSyntaxTree(SyntaxTree unit, ImportScopeInfo data)
{
importScopes.Add(data);
currentImportScope = data;
base.VisitSyntaxTree(unit, data);
currentImportScope = data;
return data;
}
public override ImportScopeInfo VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, ImportScopeInfo data)
{
ImportScopeInfo scope = new ImportScopeInfo(data);
importScopes.Add(scope);
currentImportScope = scope;
base.VisitNamespaceDeclaration(namespaceDeclaration, scope);
currentImportScope = data;
return data;
}
public override ImportScopeInfo VisitUsingDeclaration(UsingDeclaration usingDeclaration, ImportScopeInfo data)
{
data.Imports.Add(usingDeclaration.Namespace);
return data;
}
public override ImportScopeInfo VisitMethodDeclaration(MethodDeclaration methodDeclaration, ImportScopeInfo data)
{
HandleMethod(methodDeclaration, data);
return data;
}
public override ImportScopeInfo VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration, ImportScopeInfo data)
{
var symbol = propertyDeclaration.GetSymbol();
if (symbol is IProperty property && !property.MetadataToken.IsNil) {
if (property.CanGet && !property.Getter.MetadataToken.IsNil)
data.MethodDefinitions.Add((MethodDefinitionHandle)property.Getter.MetadataToken);
if (property.CanSet && !property.Setter.MetadataToken.IsNil)
data.MethodDefinitions.Add((MethodDefinitionHandle)property.Setter.MetadataToken);
}
return data;
}
public override ImportScopeInfo VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration, ImportScopeInfo data)
{
HandleEvent(eventDeclaration, data);
return data;
}
void HandleEvent(AstNode node, ImportScopeInfo data)
{
var symbol = node.GetSymbol();
if (symbol is IEvent @event && !@event.MetadataToken.IsNil) {
if (@event.CanAdd && !@event.AddAccessor.MetadataToken.IsNil)
data.MethodDefinitions.Add((MethodDefinitionHandle)@event.AddAccessor.MetadataToken);
if (@event.CanRemove && !@event.RemoveAccessor.MetadataToken.IsNil)
data.MethodDefinitions.Add((MethodDefinitionHandle)@event.RemoveAccessor.MetadataToken);
if (@event.CanInvoke && !@event.InvokeAccessor.MetadataToken.IsNil)
data.MethodDefinitions.Add((MethodDefinitionHandle)@event.InvokeAccessor.MetadataToken);
}
}
public override ImportScopeInfo VisitEventDeclaration(EventDeclaration eventDeclaration, ImportScopeInfo data)
{
HandleEvent(eventDeclaration, data);
return data;
}
public override ImportScopeInfo VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, ImportScopeInfo data)
{
HandleMethod(constructorDeclaration, data);
return data;
}
public override ImportScopeInfo VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration, ImportScopeInfo data)
{
HandleMethod(destructorDeclaration, data);
return data;
}
void HandleMethod(AstNode node, ImportScopeInfo data)
{
var symbol = node.GetSymbol();
var function = node.Annotations.OfType<ILFunction>().FirstOrDefault();
if (function != null && symbol is IMethod method && !method.MetadataToken.IsNil) {
MethodDefinitionHandle handle = (MethodDefinitionHandle)method.MetadataToken;
data.MethodDefinitions.Add(handle);
var file = typeSystem.MainModule.PEFile;
MethodDefinition md = file.Metadata.GetMethodDefinition(handle);
if (md.HasBody()) {
HandleMethodBody(function, file.Reader.GetMethodBody(md.RelativeVirtualAddress));
}
}
}
void HandleMethodBody(ILFunction function, MethodBodyBlock methodBody)
{
var localVariables = new HashSet<ILVariable>(ILVariableKeyComparer);
if (!methodBody.LocalSignature.IsNil) {
var types = typeSystem.MainModule.DecodeLocalSignature(methodBody.LocalSignature,
new TypeSystem.GenericContext(function.Method));
foreach (var v in function.Variables) {
if (v.Index != null && v.Kind.IsLocal()) {
Debug.Assert(v.Index < types.Length && v.Type.Equals(types[v.Index.Value]));
localVariables.Add(v);
}
}
}
localScopes.Add(((MethodDefinitionHandle)function.Method.MetadataToken, currentImportScope,
0, methodBody.GetCodeSize(), localVariables));
}
}
}

3
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -240,8 +240,9 @@ @@ -240,8 +240,9 @@
<Compile Include="CSharp\TypeSystem\SimpleTypeOrNamespaceReference.cs" />
<Compile Include="CSharp\TypeSystem\TypeOrNamespaceReference.cs" />
<Compile Include="CSharp\TypeSystem\UsingScope.cs" />
<Compile Include="DebugInfo\AsyncDebugInfo.cs" />
<Compile Include="DebugInfo\ImportScopeInfo.cs" />
<Compile Include="DebugInfo\ScopesGenerator.cs" />
<Compile Include="DebugInfo\DebugInfoGenerator.cs" />
<Compile Include="DecompilerException.cs" />
<Compile Include="DecompilerSettings.cs" />
<Compile Include="CSharp\Transforms\ContextTrackingVisitor.cs" />

21
ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs

@ -17,11 +17,13 @@ @@ -17,11 +17,13 @@
// DEALINGS IN THE SOFTWARE.
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.Decompiler.IL.Transforms;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Reflection.Metadata;
@ -94,6 +96,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -94,6 +96,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// across the yield point.
Dictionary<Block, (ILVariable awaiterVar, IField awaiterField)> awaitBlocks = new Dictionary<Block, (ILVariable awaiterVar, IField awaiterField)>();
int catchHandlerOffset;
List<AsyncDebugInfo.Await> awaitDebugInfos = new List<AsyncDebugInfo.Await>();
public void Run(ILFunction function, ILTransformContext context)
{
if (!context.Settings.AsyncAwait)
@ -102,6 +107,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -102,6 +107,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
fieldToParameterMap.Clear();
cachedFieldToParameterMap.Clear();
awaitBlocks.Clear();
awaitDebugInfos.Clear();
moveNextLeaves.Clear();
if (!MatchTaskCreationPattern(function))
return;
@ -135,6 +141,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -135,6 +141,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
AwaitInCatchTransform.Run(function, context);
AwaitInFinallyTransform.Run(function, context);
awaitDebugInfos.SortBy(row => row.YieldOffset);
function.AsyncDebugInfo = new AsyncDebugInfo(catchHandlerOffset, awaitDebugInfos.ToImmutableArray());
}
private void CleanUpBodyOfMoveNext(ILFunction function)
@ -427,6 +436,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -427,6 +436,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
if (!handler.Filter.MatchLdcI4(1))
throw new SymbolicAnalysisFailedException();
var catchBlock = YieldReturnDecompiler.SingleBlock(handler.Body);
catchHandlerOffset = catchBlock.ILRange.Start;
if (catchBlock?.Instructions.Count != 4)
throw new SymbolicAnalysisFailedException();
// stloc exception(ldloc E_143)
@ -488,6 +498,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -488,6 +498,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
context.Step("Inline body of MoveNext()", function);
function.Body = mainTryCatch.TryBlock;
function.AsyncReturnType = underlyingReturnType;
function.MoveNextMethod = moveNextFunction.Method;
function.CodeSize = moveNextFunction.CodeSize;
moveNextFunction.Variables.Clear();
moveNextFunction.ReleaseRef();
foreach (var branch in function.Descendants.OfType<Branch>()) {
@ -555,10 +567,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -555,10 +567,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
context.CancellationToken.ThrowIfCancellationRequested();
if (block.Instructions.Last() is Leave leave && moveNextLeaves.Contains(leave)) {
// This is likely an 'await' block
if (AnalyzeAwaitBlock(block, out var awaiterVar, out var awaiterField, out var state)) {
if (AnalyzeAwaitBlock(block, out var awaiterVar, out var awaiterField, out int state, out int yieldOffset)) {
block.Instructions.Add(new Await(new LdLoca(awaiterVar)));
Block targetBlock = stateToBlockMap.GetOrDefault(state);
if (targetBlock != null) {
awaitDebugInfos.Add(new AsyncDebugInfo.Await(yieldOffset, targetBlock.ILRange.Start));
block.Instructions.Add(new Branch(targetBlock));
} else {
block.Instructions.Add(new InvalidBranch("Could not find block for state " + state));
@ -583,11 +596,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -583,11 +596,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
}
bool AnalyzeAwaitBlock(Block block, out ILVariable awaiter, out IField awaiterField, out int state)
bool AnalyzeAwaitBlock(Block block, out ILVariable awaiter, out IField awaiterField, out int state, out int yieldOffset)
{
awaiter = null;
awaiterField = null;
state = 0;
yieldOffset = -1;
int pos = block.Instructions.Count - 2;
if (pos >= 0 && doFinallyBodies != null && block.Instructions[pos] is StLoc storeDoFinallyBodies) {
if (!(storeDoFinallyBodies.Variable.Kind == VariableKind.Local
@ -638,6 +652,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -638,6 +652,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
if (!value.MatchLdLoc(awaiter))
return false;
pos--;
// Store IL offset for debug info:
yieldOffset = block.Instructions[pos].ILRange.End;
// stloc S_10(ldloc this)
// stloc S_11(ldc.i4 0)
// stloc cachedStateVar(ldloc S_11)

13
ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs

@ -30,7 +30,11 @@ namespace ICSharpCode.Decompiler.IL @@ -30,7 +30,11 @@ namespace ICSharpCode.Decompiler.IL
{
public readonly IMethod Method;
public readonly GenericContext GenericContext;
public readonly int CodeSize;
/// <summary>
/// Size of the IL code in this function.
/// Note: after async/await transform, this is the code size of the MoveNext function.
/// </summary>
public int CodeSize;
public readonly ILVariableCollection Variables;
/// <summary>
@ -61,6 +65,13 @@ namespace ICSharpCode.Decompiler.IL @@ -61,6 +65,13 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
public IType AsyncReturnType;
/// <summary>
/// If this function is an iterator/async, this field stores the compiler-generated MoveNext() method.
/// </summary>
public IMethod MoveNextMethod;
internal DebugInfo.AsyncDebugInfo AsyncDebugInfo;
/// <summary>
/// If this is an expression tree or delegate, returns the expression tree type Expression{T} or T.
/// T is the delegate type that matches the signature of this method.

5
ICSharpCode.Decompiler/Util/KeyComparer.cs

@ -42,6 +42,11 @@ namespace ICSharpCode.Decompiler.Util @@ -42,6 +42,11 @@ namespace ICSharpCode.Decompiler.Util
{
return new KeyComparer<TElement, TKey>(keySelector, Comparer<TKey>.Default, equalityComparer);
}
public static void SortBy<TElement, TKey>(this List<TElement> list, Func<TElement, TKey> keySelector)
{
list.Sort(Create(keySelector));
}
}
public class KeyComparer<TElement, TKey> : IComparer<TElement>, IEqualityComparer<TElement>

2
ILSpy/Languages/CSharpILMixedLanguage.cs

@ -91,7 +91,7 @@ namespace ICSharpCode.ILSpy @@ -91,7 +91,7 @@ namespace ICSharpCode.ILSpy
CSharpDecompiler decompiler = CreateDecompiler(module, options);
var st = decompiler.Decompile(handle);
WriteCode(csharpOutput, options.DecompilerSettings, st, decompiler.TypeSystem);
var mapping = decompiler.CreateSequencePoints(st).FirstOrDefault(kvp => kvp.Key.Method.MetadataToken == handle);
var mapping = decompiler.CreateSequencePoints(st).FirstOrDefault(kvp => (kvp.Key.MoveNextMethod ?? kvp.Key.Method).MetadataToken == handle);
this.sequencePoints = mapping.Value ?? (IList<SequencePoint>)EmptyList<SequencePoint>.Instance;
this.codeLines = csharpOutput.ToString().Split(new[] { Environment.NewLine }, StringSplitOptions.None);
base.Disassemble(module, handle);

2
ILSpy/MainWindow.xaml

@ -92,7 +92,7 @@ @@ -92,7 +92,7 @@
<ComboBox Name="languageComboBox" DisplayMemberPath="Name" Width="100" MaxDropDownHeight="Auto"
ItemsSource="{x:Static local:Languages.AllLanguages}"
SelectedItem="{Binding FilterSettings.Language}"/>
<ComboBox Name="languageVersionComboBox" DisplayMemberPath="DisplayName" Width="100" MaxDropDownHeight="Auto"
<ComboBox Name="languageVersionComboBox" DisplayMemberPath="DisplayName" Width="120" MaxDropDownHeight="Auto"
Visibility="{Binding SelectedItem.HasLanguageVersions, ElementName=languageComboBox, Converter={StaticResource BooleanToVisibilityConverter}}"
ItemsSource="{Binding SelectedItem.LanguageVersions, ElementName=languageComboBox, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding FilterSettings.LanguageVersion, UpdateSourceTrigger=PropertyChanged}"/>

Loading…
Cancel
Save