mirror of https://github.com/icsharpcode/ILSpy.git
11 changed files with 339 additions and 242 deletions
@ -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; |
||||
} |
||||
} |
||||
} |
@ -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,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)); |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue