Browse Source

First draft of support for ImportScope and LocalScope tables.

pull/1420/head
Siegfried Pammer 7 years ago
parent
commit
6926651791
  1. 41
      ICSharpCode.Decompiler/DebugInfo/ImportScopeInfo.cs
  2. 125
      ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs
  3. 215
      ICSharpCode.Decompiler/DebugInfo/ScopesGenerator.cs
  4. 2
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

41
ICSharpCode.Decompiler/DebugInfo/ImportScopeInfo.cs

@ -0,0 +1,41 @@
// 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.Collections.Generic;
using System.Reflection.Metadata;
namespace ICSharpCode.Decompiler.DebugInfo
{
class ImportScopeInfo
{
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()
{
Parent = null;
}
public ImportScopeInfo(ImportScopeInfo parent)
{
Parent = parent;
}
}
}

125
ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs

@ -1,4 +1,22 @@
using System; // 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.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics; using System.Diagnostics;
@ -43,67 +61,55 @@ namespace ICSharpCode.Decompiler.DebugInfo
var hasher = SHA256.Create(); var hasher = SHA256.Create();
var sequencePointBlobs = new Dictionary<MethodDefinitionHandle, (DocumentHandle Document, BlobHandle SequencePoints)>(); var sequencePointBlobs = new Dictionary<MethodDefinitionHandle, (DocumentHandle Document, BlobHandle SequencePoints)>();
var importScopeBlobs = new Dictionary<MethodDefinitionHandle, (DocumentHandle Document, BlobHandle ImportScope)>();
var emptyList = new List<SequencePoint>(); var emptyList = new List<SequencePoint>();
var globalImportScope = metadata.AddImportScope(default, default);
foreach (var handle in reader.GetTopLevelTypeDefinitions()) { foreach (var handle in reader.GetTopLevelTypeDefinitions()) {
var type = reader.GetTypeDefinition(handle); var type = reader.GetTypeDefinition(handle);
// Generate syntax tree, source and checksum
var name = metadata.GetOrAddDocumentName("ILSpy_Generated_" + type.GetFullTypeName(reader) + "_" + Guid.NewGuid() + ".cs"); var name = metadata.GetOrAddDocumentName("ILSpy_Generated_" + type.GetFullTypeName(reader) + "_" + Guid.NewGuid() + ".cs");
var ast = decompiler.DecompileTypes(new[] { handle }); var syntaxTree = decompiler.DecompileTypes(new[] { handle });
ast.InsertChildAfter(null, new Comment(" PDB and source generated by ICSharpCode.Decompiler " + decompilerVersion.FileVersion), Roles.Comment); syntaxTree.InsertChildAfter(null, new Comment(" PDB and source generated by ICSharpCode.Decompiler " + decompilerVersion.FileVersion), Roles.Comment);
var sourceText = SyntaxTreeToString(ast, settings); var sourceText = SyntaxTreeToString(syntaxTree, settings);
var sequencePoints = decompiler.CreateSequencePoints(ast).ToDictionary(sp => (MethodDefinitionHandle)sp.Key.Method.MetadataToken, sp => sp.Value);
var sourceCheckSum = hasher.ComputeHash(Encoding.UTF8.GetBytes(sourceText)); var sourceCheckSum = hasher.ComputeHash(Encoding.UTF8.GetBytes(sourceText));
var sourceBlob = WriteSourceToBlob(metadata, sourceText); var sourceBlob = WriteSourceToBlob(metadata, sourceText);
// Create Document(Handle)
var document = metadata.AddDocument(name, var document = metadata.AddDocument(name,
hashAlgorithm: metadata.GetOrAddGuid(HashAlgorithmSHA256), hashAlgorithm: metadata.GetOrAddGuid(HashAlgorithmSHA256),
hash: metadata.GetOrAddBlob(sourceCheckSum), hash: metadata.GetOrAddBlob(sourceCheckSum),
language: metadata.GetOrAddGuid(CSharpLanguageGuid)); language: metadata.GetOrAddGuid(CSharpLanguageGuid));
// Add embedded source to the PDB
metadata.AddCustomDebugInformation(document, metadata.GetOrAddGuid(DebugInfoEmbeddedSource), sourceBlob); 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()) { foreach (var method in type.GetMethods()) {
var methodDef = reader.GetMethodDefinition(method); ProcessMethod(method, document, sequencePoints, syntaxTree);
if (!sequencePoints.TryGetValue(method, out var points))
points = emptyList;
int localSignatureRowId;
MethodBodyBlock methodBody;
if (methodDef.RelativeVirtualAddress != 0) {
methodBody = file.Reader.GetMethodBody(methodDef.RelativeVirtualAddress);
localSignatureRowId = methodBody.LocalSignature.IsNil ? 0 : MetadataTokens.GetRowNumber(methodBody.LocalSignature);
} else {
methodBody = null;
localSignatureRowId = 0;
}
if (points.Count == 0)
sequencePointBlobs.Add(method, (default, default));
else
sequencePointBlobs.Add(method, (document, EncodeSequencePoints(metadata, localSignatureRowId, points)));
importScopeBlobs.Add(method, (document, EncodeImportScope(metadata, reader, ast, decompiler.TypeSystem)));
} }
foreach (var nestedTypeHandle in type.GetNestedTypes()) { foreach (var nestedTypeHandle in type.GetNestedTypes()) {
var nestedType = reader.GetTypeDefinition(nestedTypeHandle); var nestedType = reader.GetTypeDefinition(nestedTypeHandle);
foreach (var method in nestedType.GetMethods()) { foreach (var method in nestedType.GetMethods()) {
ProcessMethod(method, document, sequencePoints, syntaxTree);
} }
} }
} }
foreach (var method in reader.MethodDefinitions) { foreach (var method in reader.MethodDefinitions) {
var md = reader.GetMethodDefinition(method);
if (sequencePointBlobs.TryGetValue(method, out var info)) { if (sequencePointBlobs.TryGetValue(method, out var info)) {
metadata.AddMethodDebugInformation(info.Document, info.SequencePoints); metadata.AddMethodDebugInformation(info.Document, info.SequencePoints);
} else { } else {
metadata.AddMethodDebugInformation(default, default); metadata.AddMethodDebugInformation(default, default);
} }
/*if (importScopeBlobs.TryGetValue(method, out var scopeInfo)) {
//metadata.AddImportScope(default, scopeInfo.ImportScope);
metadata.AddImportScope(default, default);
} else {
metadata.AddImportScope(default, default);
}*/
} }
var debugDir = file.Reader.ReadDebugDirectory().FirstOrDefault(dir => dir.Type == DebugDirectoryEntryType.CodeView); var debugDir = file.Reader.ReadDebugDirectory().FirstOrDefault(dir => dir.Type == DebugDirectoryEntryType.CodeView);
@ -113,6 +119,28 @@ namespace ICSharpCode.Decompiler.DebugInfo
BlobBuilder blobBuilder = new BlobBuilder(); BlobBuilder blobBuilder = new BlobBuilder();
serializer.Serialize(blobBuilder); serializer.Serialize(blobBuilder);
blobBuilder.WriteContentTo(targetStream); blobBuilder.WriteContentTo(targetStream);
void ProcessMethod(MethodDefinitionHandle method, DocumentHandle document,
Dictionary<MethodDefinitionHandle, List<SequencePoint>> sequencePoints, SyntaxTree syntaxTree)
{
var methodDef = reader.GetMethodDefinition(method);
int localSignatureRowId;
MethodBodyBlock methodBody;
if (methodDef.RelativeVirtualAddress != 0) {
methodBody = file.Reader.GetMethodBody(methodDef.RelativeVirtualAddress);
localSignatureRowId = methodBody.LocalSignature.IsNil ? 0 : MetadataTokens.GetRowNumber(methodBody.LocalSignature);
} else {
methodBody = null;
localSignatureRowId = 0;
}
if (!sequencePoints.TryGetValue(method, out var points))
points = emptyList;
if (points.Count == 0)
sequencePointBlobs.Add(method, (default, default));
else
sequencePointBlobs.Add(method, (document, EncodeSequencePoints(metadata, localSignatureRowId, points)));
}
} }
static BlobHandle WriteSourceToBlob(MetadataBuilder metadata, string sourceText) static BlobHandle WriteSourceToBlob(MetadataBuilder metadata, string sourceText)
@ -124,43 +152,6 @@ namespace ICSharpCode.Decompiler.DebugInfo
return metadata.GetOrAddBlob(builder); return metadata.GetOrAddBlob(builder);
} }
static BlobHandle EncodeImportScope(MetadataBuilder metadata, MetadataReader reader, SyntaxTree ast, ICompilation compilation)
{
var scope = ast.Annotation<UsingScope>()?.Resolve(compilation);
if (scope == null)
return default;
Dictionary<IModule, AssemblyReferenceHandle> assemblyReferences = new Dictionary<IModule, AssemblyReferenceHandle>();
var writer = new BlobBuilder();
foreach (var import in scope.Usings) {
foreach (var asm in import.ContributingModules) {
if (asm == compilation.MainModule) {
writer.WriteByte(1);
writer.WriteCompressedInteger(MetadataTokens.GetHeapOffset(metadata.GetOrAddBlobUTF8(import.FullName)));
} else {
writer.WriteByte(2);
if (!assemblyReferences.TryGetValue(asm, out var referenceHandle)) {
foreach (var h in reader.AssemblyReferences) {
var reference = reader.GetAssemblyReference(h);
string asmName = reader.GetString(reference.Name);
if (asmName == asm.AssemblyName) {
assemblyReferences.Add(asm, referenceHandle = h);
}
}
}
Debug.Assert(!referenceHandle.IsNil);
writer.WriteCompressedInteger(MetadataTokens.GetRowNumber(referenceHandle));
writer.WriteCompressedInteger(MetadataTokens.GetHeapOffset(metadata.GetOrAddBlobUTF8(import.FullName)));
}
}
}
return metadata.GetOrAddBlob(writer);
}
static BlobHandle EncodeSequencePoints(MetadataBuilder metadata, int localSignatureRowId, List<SequencePoint> sequencePoints) static BlobHandle EncodeSequencePoints(MetadataBuilder metadata, int localSignatureRowId, List<SequencePoint> sequencePoints)
{ {
if (sequencePoints.Count == 0) if (sequencePoints.Count == 0)

215
ICSharpCode.Decompiler/DebugInfo/ScopesGenerator.cs

@ -0,0 +1,215 @@
// 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.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, 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);
var importScope = scope.Handle = metadata.AddImportScope(scope.Parent == null ? globalImportScope : scope.Parent.Handle, blob);
}
foreach (var localScope in generator.localScopes) {
LocalVariableHandle firstLocalVariable = default;
foreach (var local in localScope.Locals.OrderBy(l => l.Index)) {
var h = metadata.AddLocalVariable(LocalVariableAttributes.None, local.Index, metadata.GetOrAddString(local.Name));
if (firstLocalVariable.IsNil)
firstLocalVariable = h;
}
metadata.AddLocalScope(localScope.Method, localScope.Import.Handle, firstLocalVariable,
default, localScope.Offset, localScope.Length);
}
}
private static bool IsSupported(VariableKind kind)
{
return kind == VariableKind.Local
|| kind == VariableKind.PinnedLocal
|| kind == VariableKind.UsingLocal
|| kind == VariableKind.ForeachLocal;
}
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) {
switch (v.Kind) {
case VariableKind.Local:
case VariableKind.PinnedLocal:
case VariableKind.UsingLocal:
case VariableKind.ForeachLocal:
case VariableKind.Exception:
if (v.Index < types.Length && v.Type.Equals(types[v.Index])) {
localVariables.Add(v);
}
break;
default:
continue;
}
}
}
localScopes.Add(((MethodDefinitionHandle)function.Method.MetadataToken, currentImportScope,
0, methodBody.GetCodeSize(), localVariables));
}
}
}

2
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

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

Loading…
Cancel
Save