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.
227 lines
9.3 KiB
227 lines
9.3 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.Collections.Immutable; |
|
using System.Diagnostics; |
|
using System.IO; |
|
using System.Linq; |
|
using System.Reflection.Metadata; |
|
using System.Reflection.Metadata.Ecma335; |
|
using System.Reflection.PortableExecutable; |
|
using System.Security.Cryptography; |
|
using System.Text; |
|
using ICSharpCode.Decompiler.CSharp; |
|
using ICSharpCode.Decompiler.CSharp.OutputVisitor; |
|
using ICSharpCode.Decompiler.CSharp.Syntax; |
|
using ICSharpCode.Decompiler.CSharp.TypeSystem; |
|
using ICSharpCode.Decompiler.IL; |
|
using ICSharpCode.Decompiler.Metadata; |
|
using ICSharpCode.Decompiler.TypeSystem; |
|
using ICSharpCode.Decompiler.Util; |
|
|
|
namespace ICSharpCode.Decompiler.DebugInfo |
|
{ |
|
public class PortablePdbWriter |
|
{ |
|
public static readonly Guid CSharpLanguageGuid = new Guid("3f5162f8-07c6-11d3-9053-00c04fa302a1"); |
|
|
|
public static readonly Guid DebugInfoEmbeddedSource = new Guid("0e8a571b-6926-466e-b4ad-8ab04611f5fe"); |
|
|
|
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 FileVersionInfo decompilerVersion = FileVersionInfo.GetVersionInfo(typeof(CSharpDecompiler).Assembly.Location); |
|
|
|
public static bool HasCodeViewDebugDirectoryEntry(PEFile file) |
|
{ |
|
return file.Reader.ReadDebugDirectory().Any(entry => entry.Type == DebugDirectoryEntryType.CodeView); |
|
} |
|
|
|
public static void WritePdb(PEFile file, CSharpDecompiler decompiler, DecompilerSettings settings, Stream targetStream) |
|
{ |
|
MetadataBuilder metadata = new MetadataBuilder(); |
|
MetadataReader reader = file.Metadata; |
|
var entrypointHandle = MetadataTokens.MethodDefinitionHandle(file.Reader.PEHeaders.CorHeader.EntryPointTokenOrRelativeVirtualAddress); |
|
|
|
var hasher = SHA256.Create(); |
|
var sequencePointBlobs = new Dictionary<MethodDefinitionHandle, (DocumentHandle Document, BlobHandle SequencePoints)>(); |
|
var emptyList = new List<SequencePoint>(); |
|
var globalImportScope = metadata.AddImportScope(default, default); |
|
|
|
foreach (var handle in reader.GetTopLevelTypeDefinitions()) { |
|
var type = reader.GetTypeDefinition(handle); |
|
|
|
// Generate syntax tree, source and checksum |
|
var name = metadata.GetOrAddDocumentName("ILSpy_Generated_" + type.GetFullTypeName(reader) + "_" + Guid.NewGuid() + ".cs"); |
|
var syntaxTree = decompiler.DecompileTypes(new[] { handle }); |
|
syntaxTree.InsertChildAfter(null, new Comment(" PDB and source generated by ICSharpCode.Decompiler " + decompilerVersion.FileVersion), Roles.Comment); |
|
var sourceText = SyntaxTreeToString(syntaxTree, settings); |
|
var sourceCheckSum = hasher.ComputeHash(Encoding.UTF8.GetBytes(sourceText)); |
|
var sourceBlob = WriteSourceToBlob(metadata, sourceText); |
|
|
|
// 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); |
|
} |
|
} |
|
} |
|
|
|
foreach (var method in reader.MethodDefinitions) { |
|
var md = reader.GetMethodDefinition(method); |
|
|
|
if (sequencePointBlobs.TryGetValue(method, out var info)) { |
|
metadata.AddMethodDebugInformation(info.Document, info.SequencePoints); |
|
} else { |
|
metadata.AddMethodDebugInformation(default, default); |
|
} |
|
} |
|
|
|
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); |
|
PortablePdbBuilder serializer = new PortablePdbBuilder(metadata, GetRowCounts(reader), entrypointHandle, blobs => contentId); |
|
BlobBuilder blobBuilder = new BlobBuilder(); |
|
serializer.Serialize(blobBuilder); |
|
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) |
|
{ |
|
var builder = new BlobBuilder(); |
|
builder.WriteInt32(0); // uncompressed |
|
builder.WriteUTF8(sourceText); |
|
|
|
return metadata.GetOrAddBlob(builder); |
|
} |
|
|
|
static BlobHandle EncodeSequencePoints(MetadataBuilder metadata, int localSignatureRowId, List<SequencePoint> sequencePoints) |
|
{ |
|
if (sequencePoints.Count == 0) |
|
return default; |
|
var writer = new BlobBuilder(); |
|
// header: |
|
writer.WriteCompressedInteger(localSignatureRowId); |
|
|
|
int previousOffset = -1; |
|
int previousStartLine = -1; |
|
int previousStartColumn = -1; |
|
|
|
for (int i = 0; i < sequencePoints.Count; i++) { |
|
var sequencePoint = sequencePoints[i]; |
|
// delta IL offset: |
|
if (i > 0) |
|
writer.WriteCompressedInteger(sequencePoint.Offset - previousOffset); |
|
else |
|
writer.WriteCompressedInteger(sequencePoint.Offset); |
|
previousOffset = sequencePoint.Offset; |
|
|
|
if (sequencePoint.IsHidden) { |
|
writer.WriteInt16(0); |
|
continue; |
|
} |
|
|
|
int lineDelta = sequencePoint.EndLine - sequencePoint.StartLine; |
|
int columnDelta = sequencePoint.EndColumn - sequencePoint.StartColumn; |
|
|
|
writer.WriteCompressedInteger(lineDelta); |
|
|
|
if (lineDelta == 0) { |
|
writer.WriteCompressedInteger(columnDelta); |
|
} else { |
|
writer.WriteCompressedSignedInteger(columnDelta); |
|
} |
|
|
|
if (previousStartLine < 0) { |
|
writer.WriteCompressedInteger(sequencePoint.StartLine); |
|
writer.WriteCompressedInteger(sequencePoint.StartColumn); |
|
} else { |
|
writer.WriteCompressedSignedInteger(sequencePoint.StartLine - previousStartLine); |
|
writer.WriteCompressedSignedInteger(sequencePoint.StartColumn - previousStartColumn); |
|
} |
|
|
|
previousStartLine = sequencePoint.StartLine; |
|
previousStartColumn = sequencePoint.StartColumn; |
|
} |
|
|
|
return metadata.GetOrAddBlob(writer); |
|
} |
|
|
|
static ImmutableArray<int> GetRowCounts(MetadataReader reader) |
|
{ |
|
var builder = ImmutableArray.CreateBuilder<int>(MetadataTokens.TableCount); |
|
for (int i = 0; i < MetadataTokens.TableCount; i++) { |
|
builder.Add(reader.GetTableRowCount((TableIndex)i)); |
|
} |
|
|
|
return builder.MoveToImmutable(); |
|
} |
|
|
|
static string SyntaxTreeToString(SyntaxTree syntaxTree, DecompilerSettings settings) |
|
{ |
|
StringWriter w = new StringWriter(); |
|
TokenWriter tokenWriter = new TextWriterTokenWriter(w); |
|
syntaxTree.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }); |
|
tokenWriter = TokenWriter.WrapInWriterThatSetsLocationsInAST(tokenWriter); |
|
syntaxTree.AcceptVisitor(new CSharpOutputVisitor(tokenWriter, settings.CSharpFormattingOptions)); |
|
return w.ToString(); |
|
} |
|
} |
|
}
|
|
|