diff --git a/src/DotNet/Pdb/CustomDebugInfoGuids.cs b/src/DotNet/Pdb/CustomDebugInfoGuids.cs
index 95acf760b..1002f6877 100644
--- a/src/DotNet/Pdb/CustomDebugInfoGuids.cs
+++ b/src/DotNet/Pdb/CustomDebugInfoGuids.cs
@@ -20,6 +20,8 @@ public static class CustomDebugInfoGuids {
public static readonly Guid TupleElementNames = new Guid("ED9FDF71-8879-4747-8ED3-FE5EDE3CE710");
public static readonly Guid CompilationMetadataReferences = new Guid("7E4D4708-096E-4C5C-AEDA-CB10BA6A740D");
public static readonly Guid CompilationOptions = new Guid("B5FEEC05-8CD0-4A83-96DA-466284BB4BD8");
+ public static readonly Guid TypeDefinitionDocuments = new Guid("932E74BC-DBA9-4478-8D46-0F32A7BAB3D3");
+ public static readonly Guid EncStateMachineStateMap = new Guid("8B78CD68-2EDE-420B-980B-E15884B8AAA3");
#pragma warning restore 1591 // Missing XML comment for publicly visible type or member
}
}
diff --git a/src/DotNet/Pdb/Dss/SymbolDocumentImpl.cs b/src/DotNet/Pdb/Dss/SymbolDocumentImpl.cs
index 062f6820d..650797db3 100644
--- a/src/DotNet/Pdb/Dss/SymbolDocumentImpl.cs
+++ b/src/DotNet/Pdb/Dss/SymbolDocumentImpl.cs
@@ -89,5 +89,7 @@ public override PdbCustomDebugInfo[] CustomDebugInfos {
}
}
PdbCustomDebugInfo[] customDebugInfos;
+
+ public override MDToken? MDToken => null;
}
}
diff --git a/src/DotNet/Pdb/Managed/DbiDocument.cs b/src/DotNet/Pdb/Managed/DbiDocument.cs
index b13f70d6f..fde9e87a3 100644
--- a/src/DotNet/Pdb/Managed/DbiDocument.cs
+++ b/src/DotNet/Pdb/Managed/DbiDocument.cs
@@ -38,6 +38,8 @@ public override PdbCustomDebugInfo[] CustomDebugInfos {
}
PdbCustomDebugInfo[] customDebugInfos;
+ public override MDToken? MDToken => null;
+
public DbiDocument(string url) {
this.url = url;
documentType = SymDocumentType.Text;
diff --git a/src/DotNet/Pdb/PdbCustomDebugInfo.cs b/src/DotNet/Pdb/PdbCustomDebugInfo.cs
index 55909f8c6..39f5853cf 100644
--- a/src/DotNet/Pdb/PdbCustomDebugInfo.cs
+++ b/src/DotNet/Pdb/PdbCustomDebugInfo.cs
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
+using System.Threading;
using dnlib.DotNet.Emit;
namespace dnlib.DotNet.Pdb {
@@ -111,6 +112,16 @@ public enum PdbCustomDebugInfoKind {
///
///
CompilationOptions,
+
+ ///
+ ///
+ ///
+ TypeDefinitionDocuments,
+
+ ///
+ ///
+ ///
+ EditAndContinueStateMachineStateMap,
}
///
@@ -665,7 +676,7 @@ public sealed class PortablePdbTupleElementNamesCustomDebugInfo : PdbCustomDebug
///
/// Async method stepping info
- ///
+ ///
/// It's internal and translated to a
///
sealed class PdbAsyncMethodSteppingInformationCustomDebugInfo : PdbCustomDebugInfo {
@@ -777,9 +788,9 @@ public sealed class PdbEmbeddedSourceCustomDebugInfo : PdbCustomDebugInfo {
///
/// Gets the source code blob.
- ///
+ ///
/// It's not decompressed and converted to a string because the encoding isn't specified.
- ///
+ ///
/// https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PortablePdb-Metadata.md#embedded-source-c-and-vb-compilers
///
public byte[] SourceCodeBlob { get; set; }
@@ -1101,4 +1112,161 @@ public sealed class PdbCompilationOptionsCustomDebugInfo : PdbCustomDebugInfo {
///
public PdbCompilationOptionsCustomDebugInfo() => Options = new List>();
}
+
+ ///
+ /// Links a TypeDef with no method IL with a PDB document.
+ ///
+ public class PdbTypeDefinitionDocumentsDebugInfo : PdbCustomDebugInfo {
+ ///
+ /// Returns
+ ///
+ public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.TypeDefinitionDocuments;
+
+ ///
+ /// Gets the custom debug info guid, see
+ ///
+ public override Guid Guid => CustomDebugInfoGuids.TypeDefinitionDocuments;
+
+ ///
+ /// List of documents associated with the type
+ ///
+ public IList Documents {
+ get {
+ if (documents is null)
+ InitializeDocuments();
+ return documents;
+ }
+ }
+ ///
+ protected IList documents;
+ /// Initializes
+ protected virtual void InitializeDocuments() =>
+ Interlocked.CompareExchange(ref documents, new List(), null);
+ }
+
+ sealed class PdbTypeDefinitionDocumentsDebugInfoMD : PdbTypeDefinitionDocumentsDebugInfo {
+ readonly ModuleDef readerModule;
+ readonly IList documentTokens;
+
+ protected override void InitializeDocuments() {
+ var list = new List(documentTokens.Count);
+ if (readerModule.PdbState is not null) {
+ for (var i = 0; i < documentTokens.Count; i++) {
+ if (readerModule.PdbState.tokenToDocument.TryGetValue(documentTokens[i], out var document))
+ list.Add(document);
+ }
+ }
+ Interlocked.CompareExchange(ref documents, list, null);
+ }
+
+ public PdbTypeDefinitionDocumentsDebugInfoMD(ModuleDef readerModule, IList documentTokens) {
+ this.readerModule = readerModule;
+ this.documentTokens = documentTokens;
+ }
+ }
+
+ ///
+ /// Contains the EnC state machine state mapping
+ ///
+ public sealed class PdbEditAndContinueStateMachineStateMapDebugInfo : PdbCustomDebugInfo {
+ ///
+ /// Returns
+ ///
+ public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.EditAndContinueStateMachineStateMap;
+
+ ///
+ /// Gets the custom debug info guid, see
+ ///
+ public override Guid Guid => CustomDebugInfoGuids.EncStateMachineStateMap;
+
+ ///
+ /// State machine states
+ ///
+ public List StateMachineStates { get; }
+
+ ///
+ /// Constructor
+ ///
+ public PdbEditAndContinueStateMachineStateMapDebugInfo() => StateMachineStates = new List();
+ }
+
+ ///
+ /// State machine state information used by debuggers
+ ///
+ public struct StateMachineStateInfo {
+ ///
+ /// Syntax offset
+ ///
+ public readonly int SyntaxOffset;
+
+ ///
+ /// State machine state
+ ///
+ public readonly StateMachineState State;
+
+ ///
+ /// Constructor
+ ///
+ /// Syntax offset
+ /// State machine state
+ public StateMachineStateInfo(int syntaxOffset, StateMachineState state) {
+ SyntaxOffset = syntaxOffset;
+ State = state;
+ }
+ }
+
+ ///
+ /// State machine state
+ /// from Roslyn: StateMachineState.cs
+ ///
+ public enum StateMachineState {
+ ///
+ /// First state of an async iterator state machine that is used to resume the machine after yield return.
+ /// Initial state is not used to resume state machine that yielded. State numbers decrease as the iterator makes progress.
+ ///
+ FirstResumableAsyncIteratorState = InitialAsyncIteratorState - 1,
+
+ ///
+ /// Initial iterator state of an async iterator.
+ /// Distinct from so that DisposeAsync can throw in latter case.
+ ///
+ InitialAsyncIteratorState = -3,
+
+ ///
+ /// First state of an iterator state machine. State numbers decrease for subsequent finalize states.
+ ///
+ FirstIteratorFinalizeState = -3,
+
+ ///
+ /// The last state of a state machine.
+ ///
+ FinishedState = -2,
+
+ ///
+ /// State machine not started or is running
+ ///
+ NotStartedOrRunningState = -1,
+
+ ///
+ /// First unused state
+ ///
+ FirstUnusedState = 0,
+
+ ///
+ /// First state in async state machine that is used to resume the machine after await.
+ /// State numbers increase as the async computation makes progress.
+ ///
+ FirstResumableAsyncState = 0,
+
+ ///
+ /// Initial iterator state of an iterator.
+ ///
+ InitialIteratorState = 0,
+
+ ///
+ /// First state in iterator state machine that is used to resume the machine after yield return.
+ /// Initial state is not used to resume state machine that yielded. State numbers increase as the iterator makes progress.
+ ///
+ FirstResumableIteratorState = InitialIteratorState + 1,
+ }
}
diff --git a/src/DotNet/Pdb/PdbDocument.cs b/src/DotNet/Pdb/PdbDocument.cs
index b38fe311c..6f25bbe8c 100644
--- a/src/DotNet/Pdb/PdbDocument.cs
+++ b/src/DotNet/Pdb/PdbDocument.cs
@@ -53,6 +53,11 @@ public sealed class PdbDocument : IHasCustomDebugInformation {
public IList CustomDebugInfos => customDebugInfos;
IList customDebugInfos;
+ ///
+ /// Gets the Metadata token of the document if available.
+ ///
+ public MDToken? MDToken { get; internal set; }
+
///
/// Default constructor
///
@@ -86,6 +91,7 @@ internal void Initialize(SymbolDocument symDoc) {
customDebugInfos = new List();
foreach (var cdi in symDoc.CustomDebugInfos)
customDebugInfos.Add(cdi);
+ MDToken = symDoc.MDToken;
}
///
diff --git a/src/DotNet/Pdb/PdbState.cs b/src/DotNet/Pdb/PdbState.cs
index 455c24d11..39146563f 100644
--- a/src/DotNet/Pdb/PdbState.cs
+++ b/src/DotNet/Pdb/PdbState.cs
@@ -15,6 +15,7 @@ namespace dnlib.DotNet.Pdb {
public sealed class PdbState {
readonly SymbolReader reader;
readonly Dictionary docDict = new Dictionary();
+ internal readonly Dictionary tokenToDocument = new Dictionary();
MethodDef userEntryPoint;
readonly Compiler compiler;
readonly PdbFileKind originalPdbFileKind;
@@ -64,7 +65,7 @@ public bool HasDocuments {
#if THREAD_SAFE
} finally { theLock.ExitWriteLock(); }
#endif
-
+
}
}
@@ -123,6 +124,8 @@ PdbDocument Add_NoLock(PdbDocument doc) {
if (docDict.TryGetValue(doc, out var orig))
return orig;
docDict.Add(doc, doc);
+ if (doc.MDToken.HasValue)
+ tokenToDocument.Add(doc.MDToken.Value, doc);
return doc;
}
@@ -133,6 +136,8 @@ PdbDocument Add_NoLock(SymbolDocument symDoc) {
// Expensive part, can read source code etc
doc.Initialize(symDoc);
docDict.Add(doc, doc);
+ if (symDoc.MDToken.HasValue)
+ tokenToDocument.Add(symDoc.MDToken.Value, doc);
return doc;
}
@@ -145,6 +150,8 @@ public bool Remove(PdbDocument doc) {
#if THREAD_SAFE
theLock.EnterWriteLock(); try {
#endif
+ if (doc.MDToken.HasValue)
+ tokenToDocument.Remove(doc.MDToken.Value);
return docDict.Remove(doc);
#if THREAD_SAFE
} finally { theLock.ExitWriteLock(); }
@@ -186,6 +193,7 @@ public List RemoveAllDocuments(bool returnDocs) {
theLock.EnterWriteLock(); try {
#endif
var docs = returnDocs ? new List(docDict.Values) : null;
+ tokenToDocument.Clear();
docDict.Clear();
return docs;
#if THREAD_SAFE
diff --git a/src/DotNet/Pdb/Portable/PortablePdbCustomDebugInfoReader.cs b/src/DotNet/Pdb/Portable/PortablePdbCustomDebugInfoReader.cs
index 68b61ee80..5a1e1705d 100644
--- a/src/DotNet/Pdb/Portable/PortablePdbCustomDebugInfoReader.cs
+++ b/src/DotNet/Pdb/Portable/PortablePdbCustomDebugInfoReader.cs
@@ -65,6 +65,10 @@ PdbCustomDebugInfo Read(Guid kind) {
return ReadCompilationMetadataReferences();
if (kind == CustomDebugInfoGuids.CompilationOptions)
return ReadCompilationOptions();
+ if (kind == CustomDebugInfoGuids.TypeDefinitionDocuments)
+ return ReadTypeDefinitionDocuments();
+ if (kind == CustomDebugInfoGuids.EncStateMachineStateMap)
+ return ReadEncStateMachineStateMap();
Debug.Fail("Unknown custom debug info guid: " + kind.ToString());
return new PdbUnknownCustomDebugInfo(kind, reader.ReadRemainingBytes());
}
@@ -228,6 +232,34 @@ PdbCustomDebugInfo ReadCompilationOptions() {
return cdi;
}
+ PdbCustomDebugInfo ReadTypeDefinitionDocuments() {
+ var docList = new List();
+ while (reader.BytesLeft > 0)
+ docList.Add(new MDToken(Table.Document, reader.ReadCompressedUInt32()));
+
+ return new PdbTypeDefinitionDocumentsDebugInfoMD(module, docList);
+ }
+
+ PdbCustomDebugInfo ReadEncStateMachineStateMap() {
+ var cdi = new PdbEditAndContinueStateMachineStateMapDebugInfo();
+
+ var count = reader.ReadCompressedUInt32();
+ if (count > 0) {
+ long syntaxOffsetBaseline = -reader.ReadCompressedUInt32();
+
+ while (count > 0) {
+ int stateNumber = reader.ReadCompressedInt32();
+ int syntaxOffset = (int)(syntaxOffsetBaseline + reader.ReadCompressedUInt32());
+
+ cdi.StateMachineStates.Add(new StateMachineStateInfo(syntaxOffset, (StateMachineState)stateNumber));
+
+ count--;
+ }
+ }
+
+ return cdi;
+ }
+
Instruction GetInstruction(uint offset) {
var instructions = bodyOpt.Instructions;
int lo = 0, hi = instructions.Count - 1;
diff --git a/src/DotNet/Pdb/Portable/PortablePdbCustomDebugInfoWriter.cs b/src/DotNet/Pdb/Portable/PortablePdbCustomDebugInfoWriter.cs
index 0729c602e..789d3ee4a 100644
--- a/src/DotNet/Pdb/Portable/PortablePdbCustomDebugInfoWriter.cs
+++ b/src/DotNet/Pdb/Portable/PortablePdbCustomDebugInfoWriter.cs
@@ -1,6 +1,8 @@
// dnlib: See LICENSE.txt for more info
+using System;
using System.IO;
+using System.Linq;
using System.Text;
using dnlib.DotNet.Emit;
using dnlib.DotNet.Writer;
@@ -92,6 +94,14 @@ byte[] Write(PdbCustomDebugInfo cdi) {
case PdbCustomDebugInfoKind.CompilationOptions:
WriteCompilationOptions((PdbCompilationOptionsCustomDebugInfo)cdi);
break;
+
+ case PdbCustomDebugInfoKind.TypeDefinitionDocuments:
+ WriteTypeDefinitionDocuments((PdbTypeDefinitionDocumentsDebugInfo)cdi);
+ break;
+
+ case PdbCustomDebugInfoKind.EditAndContinueStateMachineStateMap:
+ WriteEditAndContinueStateMachineStateMap((PdbEditAndContinueStateMachineStateMapDebugInfo)cdi);
+ break;
}
return outStream.ToArray();
}
@@ -317,5 +327,25 @@ void WriteCompilationOptions(PdbCompilationOptionsCustomDebugInfo cdi) {
WriteUTF8Z(kv.Value);
}
}
+
+ void WriteTypeDefinitionDocuments(PdbTypeDefinitionDocumentsDebugInfo cdi) {
+ foreach (var document in cdi.Documents)
+ writer.WriteCompressedUInt32(systemMetadata.GetRid(document));
+ }
+
+ void WriteEditAndContinueStateMachineStateMap(PdbEditAndContinueStateMachineStateMapDebugInfo cdi) {
+ writer.WriteCompressedUInt32((uint)cdi.StateMachineStates.Count);
+
+ if (cdi.StateMachineStates.Count <= 0)
+ return;
+
+ int syntaxOffsetBaseline = Math.Min(cdi.StateMachineStates.Min(state => state.SyntaxOffset), 0);
+ writer.WriteCompressedUInt32((uint)-syntaxOffsetBaseline);
+
+ foreach (var state in cdi.StateMachineStates) {
+ writer.WriteCompressedInt32((int)state.State);
+ writer.WriteCompressedUInt32((uint)(state.SyntaxOffset - syntaxOffsetBaseline));
+ }
+ }
}
}
diff --git a/src/DotNet/Pdb/Portable/PortablePdbReader.cs b/src/DotNet/Pdb/Portable/PortablePdbReader.cs
index fc8fdec37..bae2ae621 100644
--- a/src/DotNet/Pdb/Portable/PortablePdbReader.cs
+++ b/src/DotNet/Pdb/Portable/PortablePdbReader.cs
@@ -61,7 +61,8 @@ SymbolDocument[] ReadDocuments() {
var custInfos = ListCache.AllocList();
var gpContext = new GenericParamContext();
for (int i = 0; i < docs.Length; i++) {
- bool b = pdbMetadata.TablesStream.TryReadDocumentRow((uint)i + 1, out var row);
+ uint rid = (uint)i + 1;
+ bool b = pdbMetadata.TablesStream.TryReadDocumentRow(rid, out var row);
Debug.Assert(b);
var url = nameReader.ReadDocumentName(row.Name);
var language = pdbMetadata.GuidStream.Read(row.Language) ?? Guid.Empty;
@@ -70,12 +71,13 @@ SymbolDocument[] ReadDocuments() {
var checkSumAlgorithmId = pdbMetadata.GuidStream.Read(row.HashAlgorithm) ?? Guid.Empty;
var checkSum = pdbMetadata.BlobStream.ReadNoNull(row.Hash);
- var token = new MDToken(Table.Document, i + 1).ToInt32();
+ var mdToken = new MDToken(Table.Document, rid);
+ var token = mdToken.ToInt32();
custInfos.Clear();
GetCustomDebugInfos(token, gpContext, custInfos);
var custInfosArray = custInfos.Count == 0 ? Array2.Empty() : custInfos.ToArray();
- docs[i] = new SymbolDocumentImpl(url, language, languageVendor, documentType, checkSumAlgorithmId, checkSum, custInfosArray);
+ docs[i] = new SymbolDocumentImpl(url, language, languageVendor, documentType, checkSumAlgorithmId, checkSum, custInfosArray, mdToken);
}
ListCache.Free(ref custInfos);
return docs;
diff --git a/src/DotNet/Pdb/Portable/SymbolDocumentImpl.cs b/src/DotNet/Pdb/Portable/SymbolDocumentImpl.cs
index 7168d3c85..dce64fb4a 100644
--- a/src/DotNet/Pdb/Portable/SymbolDocumentImpl.cs
+++ b/src/DotNet/Pdb/Portable/SymbolDocumentImpl.cs
@@ -15,6 +15,7 @@ sealed class SymbolDocumentImpl : SymbolDocument {
/*readonly*/ Guid checkSumAlgorithmId;
readonly byte[] checkSum;
readonly PdbCustomDebugInfo[] customDebugInfos;
+ MDToken mdToken;
string GetDebuggerString() {
var sb = new StringBuilder();
@@ -45,8 +46,9 @@ string GetDebuggerString() {
public override Guid CheckSumAlgorithmId => checkSumAlgorithmId;
public override byte[] CheckSum => checkSum;
public override PdbCustomDebugInfo[] CustomDebugInfos => customDebugInfos;
+ public override MDToken? MDToken => mdToken;
- public SymbolDocumentImpl(string url, Guid language, Guid languageVendor, Guid documentType, Guid checkSumAlgorithmId, byte[] checkSum, PdbCustomDebugInfo[] customDebugInfos) {
+ public SymbolDocumentImpl(string url, Guid language, Guid languageVendor, Guid documentType, Guid checkSumAlgorithmId, byte[] checkSum, PdbCustomDebugInfo[] customDebugInfos, MDToken mdToken) {
this.url = url;
this.language = language;
this.languageVendor = languageVendor;
@@ -54,6 +56,7 @@ public SymbolDocumentImpl(string url, Guid language, Guid languageVendor, Guid d
this.checkSumAlgorithmId = checkSumAlgorithmId;
this.checkSum = checkSum;
this.customDebugInfos = customDebugInfos;
+ this.mdToken = mdToken;
}
}
}
diff --git a/src/DotNet/Pdb/Symbols/SymbolDocument.cs b/src/DotNet/Pdb/Symbols/SymbolDocument.cs
index ec12ded6b..58509969e 100644
--- a/src/DotNet/Pdb/Symbols/SymbolDocument.cs
+++ b/src/DotNet/Pdb/Symbols/SymbolDocument.cs
@@ -41,5 +41,10 @@ public abstract class SymbolDocument {
/// Gets the custom debug infos
///
public abstract PdbCustomDebugInfo[] CustomDebugInfos { get; }
+
+ ///
+ /// Gets the Metadata token of the document if available.
+ ///
+ public abstract MDToken? MDToken { get; }
}
}
diff --git a/src/DotNet/Writer/Metadata.cs b/src/DotNet/Writer/Metadata.cs
index df93b83e1..37bcc490a 100644
--- a/src/DotNet/Writer/Metadata.cs
+++ b/src/DotNet/Writer/Metadata.cs
@@ -3384,6 +3384,8 @@ void AddCustomDebugInformation(SerializerMethodContext serializerMethodContext,
case PdbCustomDebugInfoKind.SourceLink:
case PdbCustomDebugInfoKind.CompilationMetadataReferences:
case PdbCustomDebugInfoKind.CompilationOptions:
+ case PdbCustomDebugInfoKind.TypeDefinitionDocuments:
+ case PdbCustomDebugInfoKind.EditAndContinueStateMachineStateMap:
AddCustomDebugInformationCore(serializerMethodContext, encodedToken, cdi, cdi.Guid);
break;