mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
267 lines
8.8 KiB
267 lines
8.8 KiB
// 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.Reflection.Metadata; |
|
using System.Reflection.Metadata.Ecma335; |
|
|
|
using ICSharpCode.Decompiler.CSharp; |
|
using ICSharpCode.Decompiler.CSharp.Syntax; |
|
using ICSharpCode.Decompiler.IL; |
|
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>(); |
|
internal List<(MethodDefinitionHandle Method, ImportScopeInfo Import, int Offset, int Length, HashSet<ILVariable> Locals)> LocalScopes { get; } = 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(nameof(typeSystem)); |
|
this.currentImportScope = globalImportScope; |
|
} |
|
|
|
public void GenerateImportScopes(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); |
|
} |
|
} |
|
|
|
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); |
|
} |
|
|
|
public override void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) |
|
{ |
|
if (!propertyDeclaration.ExpressionBody.IsNull) |
|
{ |
|
HandleMethod(propertyDeclaration.ExpressionBody, propertyDeclaration.Annotation<ILFunction>()); |
|
} |
|
else |
|
{ |
|
base.VisitPropertyDeclaration(propertyDeclaration); |
|
} |
|
} |
|
|
|
public override void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) |
|
{ |
|
if (!indexerDeclaration.ExpressionBody.IsNull) |
|
{ |
|
HandleMethod(indexerDeclaration.ExpressionBody, indexerDeclaration.Annotation<ILFunction>()); |
|
} |
|
else |
|
{ |
|
base.VisitIndexerDeclaration(indexerDeclaration); |
|
} |
|
} |
|
|
|
public override void VisitQueryFromClause(QueryFromClause queryFromClause) |
|
{ |
|
if (queryFromClause.Parent.FirstChild != queryFromClause) |
|
{ |
|
HandleMethod(queryFromClause); |
|
} |
|
else |
|
{ |
|
base.VisitQueryFromClause(queryFromClause); |
|
} |
|
} |
|
|
|
public override void VisitQueryGroupClause(QueryGroupClause queryGroupClause) |
|
{ |
|
var annotation = queryGroupClause.Annotation<QueryGroupClauseAnnotation>(); |
|
if (annotation == null) |
|
{ |
|
base.VisitQueryGroupClause(queryGroupClause); |
|
return; |
|
} |
|
HandleMethod(queryGroupClause.Projection, annotation.ProjectionLambda); |
|
HandleMethod(queryGroupClause.Key, annotation.KeyLambda); |
|
} |
|
|
|
public override void VisitQueryJoinClause(QueryJoinClause queryJoinClause) |
|
{ |
|
var annotation = queryJoinClause.Annotation<QueryJoinClauseAnnotation>(); |
|
if (annotation == null) |
|
{ |
|
base.VisitQueryJoinClause(queryJoinClause); |
|
return; |
|
} |
|
HandleMethod(queryJoinClause.OnExpression, annotation.OnLambda); |
|
HandleMethod(queryJoinClause.EqualsExpression, annotation.EqualsLambda); |
|
} |
|
|
|
public override void VisitQueryLetClause(QueryLetClause queryLetClause) |
|
{ |
|
HandleMethod(queryLetClause); |
|
} |
|
|
|
public override void VisitQueryOrdering(QueryOrdering queryOrdering) |
|
{ |
|
HandleMethod(queryOrdering); |
|
} |
|
|
|
public override void VisitQuerySelectClause(QuerySelectClause querySelectClause) |
|
{ |
|
HandleMethod(querySelectClause); |
|
} |
|
|
|
public override void VisitQueryWhereClause(QueryWhereClause queryWhereClause) |
|
{ |
|
HandleMethod(queryWhereClause); |
|
} |
|
|
|
void HandleMethod(AstNode node) |
|
{ |
|
HandleMethod(node, node.Annotation<ILFunction>()); |
|
} |
|
|
|
void HandleMethod(AstNode node, ILFunction function) |
|
{ |
|
// Look into method body, e.g. in order to find lambdas |
|
VisitChildren(node); |
|
|
|
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.MetadataFile; |
|
MethodDefinition md = file.Metadata.GetMethodDefinition(handle); |
|
if (md.HasBody()) |
|
{ |
|
HandleMethodBody(function, file.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 && NormalizeTypeVisitor.TypeErasure.EquivalentTypes(v.Type, types[v.Index.Value])); |
|
#endif |
|
localVariables.Add(v); |
|
} |
|
} |
|
} |
|
|
|
LocalScopes.Add(((MethodDefinitionHandle)method.MetadataToken, currentImportScope, |
|
0, methodBody.GetCodeSize(), localVariables)); |
|
} |
|
} |
|
}
|
|
|