Skip to content

Commit e43bd76

Browse files
authored
Merge pull request #485 from ElektroKill/new-roslyn-cdi
Add support for new Roslyn CustomDebugInfo
2 parents 44ee7c8 + 5ac12c3 commit e43bd76

12 files changed

+270
-8
lines changed

src/DotNet/Pdb/CustomDebugInfoGuids.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ public static class CustomDebugInfoGuids {
2020
public static readonly Guid TupleElementNames = new Guid("ED9FDF71-8879-4747-8ED3-FE5EDE3CE710");
2121
public static readonly Guid CompilationMetadataReferences = new Guid("7E4D4708-096E-4C5C-AEDA-CB10BA6A740D");
2222
public static readonly Guid CompilationOptions = new Guid("B5FEEC05-8CD0-4A83-96DA-466284BB4BD8");
23+
public static readonly Guid TypeDefinitionDocuments = new Guid("932E74BC-DBA9-4478-8D46-0F32A7BAB3D3");
24+
public static readonly Guid EncStateMachineStateMap = new Guid("8B78CD68-2EDE-420B-980B-E15884B8AAA3");
2325
#pragma warning restore 1591 // Missing XML comment for publicly visible type or member
2426
}
2527
}

src/DotNet/Pdb/Dss/SymbolDocumentImpl.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,5 +89,7 @@ public override PdbCustomDebugInfo[] CustomDebugInfos {
8989
}
9090
}
9191
PdbCustomDebugInfo[] customDebugInfos;
92+
93+
public override MDToken? MDToken => null;
9294
}
9395
}

src/DotNet/Pdb/Managed/DbiDocument.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ public override PdbCustomDebugInfo[] CustomDebugInfos {
3838
}
3939
PdbCustomDebugInfo[] customDebugInfos;
4040

41+
public override MDToken? MDToken => null;
42+
4143
public DbiDocument(string url) {
4244
this.url = url;
4345
documentType = SymDocumentType.Text;

src/DotNet/Pdb/PdbCustomDebugInfo.cs

Lines changed: 171 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
using System;
44
using System.Collections.Generic;
5+
using System.Threading;
56
using dnlib.DotNet.Emit;
67

78
namespace dnlib.DotNet.Pdb {
@@ -111,6 +112,16 @@ public enum PdbCustomDebugInfoKind {
111112
/// <see cref="PdbCompilationOptionsCustomDebugInfo"/>
112113
/// </summary>
113114
CompilationOptions,
115+
116+
/// <summary>
117+
/// <see cref="PdbTypeDefinitionDocumentsDebugInfo"/>
118+
/// </summary>
119+
TypeDefinitionDocuments,
120+
121+
/// <summary>
122+
/// <see cref="PdbEditAndContinueStateMachineStateMapDebugInfo"/>
123+
/// </summary>
124+
EditAndContinueStateMachineStateMap,
114125
}
115126

116127
/// <summary>
@@ -665,7 +676,7 @@ public sealed class PortablePdbTupleElementNamesCustomDebugInfo : PdbCustomDebug
665676

666677
/// <summary>
667678
/// Async method stepping info
668-
///
679+
///
669680
/// It's internal and translated to a <see cref="PdbAsyncMethodCustomDebugInfo"/>
670681
/// </summary>
671682
sealed class PdbAsyncMethodSteppingInformationCustomDebugInfo : PdbCustomDebugInfo {
@@ -777,9 +788,9 @@ public sealed class PdbEmbeddedSourceCustomDebugInfo : PdbCustomDebugInfo {
777788

778789
/// <summary>
779790
/// Gets the source code blob.
780-
///
791+
///
781792
/// It's not decompressed and converted to a string because the encoding isn't specified.
782-
///
793+
///
783794
/// https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PortablePdb-Metadata.md#embedded-source-c-and-vb-compilers
784795
/// </summary>
785796
public byte[] SourceCodeBlob { get; set; }
@@ -1101,4 +1112,161 @@ public sealed class PdbCompilationOptionsCustomDebugInfo : PdbCustomDebugInfo {
11011112
/// </summary>
11021113
public PdbCompilationOptionsCustomDebugInfo() => Options = new List<KeyValuePair<string, string>>();
11031114
}
1115+
1116+
/// <summary>
1117+
/// Links a TypeDef with no method IL with a PDB document.
1118+
/// </summary>
1119+
public class PdbTypeDefinitionDocumentsDebugInfo : PdbCustomDebugInfo {
1120+
/// <summary>
1121+
/// Returns <see cref="PdbCustomDebugInfoKind.TypeDefinitionDocuments"/>
1122+
/// </summary>
1123+
public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.TypeDefinitionDocuments;
1124+
1125+
/// <summary>
1126+
/// Gets the custom debug info guid, see <see cref="CustomDebugInfoGuids"/>
1127+
/// </summary>
1128+
public override Guid Guid => CustomDebugInfoGuids.TypeDefinitionDocuments;
1129+
1130+
/// <summary>
1131+
/// List of documents associated with the type
1132+
/// </summary>
1133+
public IList<PdbDocument> Documents {
1134+
get {
1135+
if (documents is null)
1136+
InitializeDocuments();
1137+
return documents;
1138+
}
1139+
}
1140+
/// <summary/>
1141+
protected IList<PdbDocument> documents;
1142+
/// <summary>Initializes <see cref="documents"/></summary>
1143+
protected virtual void InitializeDocuments() =>
1144+
Interlocked.CompareExchange(ref documents, new List<PdbDocument>(), null);
1145+
}
1146+
1147+
sealed class PdbTypeDefinitionDocumentsDebugInfoMD : PdbTypeDefinitionDocumentsDebugInfo {
1148+
readonly ModuleDef readerModule;
1149+
readonly IList<MDToken> documentTokens;
1150+
1151+
protected override void InitializeDocuments() {
1152+
var list = new List<PdbDocument>(documentTokens.Count);
1153+
if (readerModule.PdbState is not null) {
1154+
for (var i = 0; i < documentTokens.Count; i++) {
1155+
if (readerModule.PdbState.tokenToDocument.TryGetValue(documentTokens[i], out var document))
1156+
list.Add(document);
1157+
}
1158+
}
1159+
Interlocked.CompareExchange(ref documents, list, null);
1160+
}
1161+
1162+
public PdbTypeDefinitionDocumentsDebugInfoMD(ModuleDef readerModule, IList<MDToken> documentTokens) {
1163+
this.readerModule = readerModule;
1164+
this.documentTokens = documentTokens;
1165+
}
1166+
}
1167+
1168+
/// <summary>
1169+
/// Contains the EnC state machine state mapping
1170+
/// </summary>
1171+
public sealed class PdbEditAndContinueStateMachineStateMapDebugInfo : PdbCustomDebugInfo {
1172+
/// <summary>
1173+
/// Returns <see cref="PdbCustomDebugInfoKind.TypeDefinitionDocuments"/>
1174+
/// </summary>
1175+
public override PdbCustomDebugInfoKind Kind => PdbCustomDebugInfoKind.EditAndContinueStateMachineStateMap;
1176+
1177+
/// <summary>
1178+
/// Gets the custom debug info guid, see <see cref="CustomDebugInfoGuids"/>
1179+
/// </summary>
1180+
public override Guid Guid => CustomDebugInfoGuids.EncStateMachineStateMap;
1181+
1182+
/// <summary>
1183+
/// State machine states
1184+
/// </summary>
1185+
public List<StateMachineStateInfo> StateMachineStates { get; }
1186+
1187+
/// <summary>
1188+
/// Constructor
1189+
/// </summary>
1190+
public PdbEditAndContinueStateMachineStateMapDebugInfo() => StateMachineStates = new List<StateMachineStateInfo>();
1191+
}
1192+
1193+
/// <summary>
1194+
/// State machine state information used by debuggers
1195+
/// </summary>
1196+
public struct StateMachineStateInfo {
1197+
/// <summary>
1198+
/// Syntax offset
1199+
/// </summary>
1200+
public readonly int SyntaxOffset;
1201+
1202+
/// <summary>
1203+
/// State machine state
1204+
/// </summary>
1205+
public readonly StateMachineState State;
1206+
1207+
/// <summary>
1208+
/// Constructor
1209+
/// </summary>
1210+
/// <param name="syntaxOffset">Syntax offset</param>
1211+
/// <param name="state">State machine state</param>
1212+
public StateMachineStateInfo(int syntaxOffset, StateMachineState state) {
1213+
SyntaxOffset = syntaxOffset;
1214+
State = state;
1215+
}
1216+
}
1217+
1218+
/// <summary>
1219+
/// State machine state
1220+
/// from Roslyn: StateMachineState.cs
1221+
/// </summary>
1222+
public enum StateMachineState {
1223+
/// <summary>
1224+
/// First state of an async iterator state machine that is used to resume the machine after yield return.
1225+
/// Initial state is not used to resume state machine that yielded. State numbers decrease as the iterator makes progress.
1226+
/// </summary>
1227+
FirstResumableAsyncIteratorState = InitialAsyncIteratorState - 1,
1228+
1229+
/// <summary>
1230+
/// Initial iterator state of an async iterator.
1231+
/// Distinct from <see cref="NotStartedOrRunningState"/> so that DisposeAsync can throw in latter case.
1232+
/// </summary>
1233+
InitialAsyncIteratorState = -3,
1234+
1235+
/// <summary>
1236+
/// First state of an iterator state machine. State numbers decrease for subsequent finalize states.
1237+
/// </summary>
1238+
FirstIteratorFinalizeState = -3,
1239+
1240+
/// <summary>
1241+
/// The last state of a state machine.
1242+
/// </summary>
1243+
FinishedState = -2,
1244+
1245+
/// <summary>
1246+
/// State machine not started or is running
1247+
/// </summary>
1248+
NotStartedOrRunningState = -1,
1249+
1250+
/// <summary>
1251+
/// First unused state
1252+
/// </summary>
1253+
FirstUnusedState = 0,
1254+
1255+
/// <summary>
1256+
/// First state in async state machine that is used to resume the machine after await.
1257+
/// State numbers increase as the async computation makes progress.
1258+
/// </summary>
1259+
FirstResumableAsyncState = 0,
1260+
1261+
/// <summary>
1262+
/// Initial iterator state of an iterator.
1263+
/// </summary>
1264+
InitialIteratorState = 0,
1265+
1266+
/// <summary>
1267+
/// First state in iterator state machine that is used to resume the machine after yield return.
1268+
/// Initial state is not used to resume state machine that yielded. State numbers increase as the iterator makes progress.
1269+
/// </summary>
1270+
FirstResumableIteratorState = InitialIteratorState + 1,
1271+
}
11041272
}

src/DotNet/Pdb/PdbDocument.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ public sealed class PdbDocument : IHasCustomDebugInformation {
5353
public IList<PdbCustomDebugInfo> CustomDebugInfos => customDebugInfos;
5454
IList<PdbCustomDebugInfo> customDebugInfos;
5555

56+
/// <summary>
57+
/// Gets the Metadata token of the document if available.
58+
/// </summary>
59+
public MDToken? MDToken { get; internal set; }
60+
5661
/// <summary>
5762
/// Default constructor
5863
/// </summary>
@@ -86,6 +91,7 @@ internal void Initialize(SymbolDocument symDoc) {
8691
customDebugInfos = new List<PdbCustomDebugInfo>();
8792
foreach (var cdi in symDoc.CustomDebugInfos)
8893
customDebugInfos.Add(cdi);
94+
MDToken = symDoc.MDToken;
8995
}
9096

9197
/// <summary>

src/DotNet/Pdb/PdbState.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ namespace dnlib.DotNet.Pdb {
1515
public sealed class PdbState {
1616
readonly SymbolReader reader;
1717
readonly Dictionary<PdbDocument, PdbDocument> docDict = new Dictionary<PdbDocument, PdbDocument>();
18+
internal readonly Dictionary<MDToken, PdbDocument> tokenToDocument = new Dictionary<MDToken, PdbDocument>();
1819
MethodDef userEntryPoint;
1920
readonly Compiler compiler;
2021
readonly PdbFileKind originalPdbFileKind;
@@ -64,7 +65,7 @@ public bool HasDocuments {
6465
#if THREAD_SAFE
6566
} finally { theLock.ExitWriteLock(); }
6667
#endif
67-
68+
6869
}
6970
}
7071

@@ -123,6 +124,8 @@ PdbDocument Add_NoLock(PdbDocument doc) {
123124
if (docDict.TryGetValue(doc, out var orig))
124125
return orig;
125126
docDict.Add(doc, doc);
127+
if (doc.MDToken.HasValue)
128+
tokenToDocument.Add(doc.MDToken.Value, doc);
126129
return doc;
127130
}
128131

@@ -133,6 +136,8 @@ PdbDocument Add_NoLock(SymbolDocument symDoc) {
133136
// Expensive part, can read source code etc
134137
doc.Initialize(symDoc);
135138
docDict.Add(doc, doc);
139+
if (symDoc.MDToken.HasValue)
140+
tokenToDocument.Add(symDoc.MDToken.Value, doc);
136141
return doc;
137142
}
138143

@@ -145,6 +150,8 @@ public bool Remove(PdbDocument doc) {
145150
#if THREAD_SAFE
146151
theLock.EnterWriteLock(); try {
147152
#endif
153+
if (doc.MDToken.HasValue)
154+
tokenToDocument.Remove(doc.MDToken.Value);
148155
return docDict.Remove(doc);
149156
#if THREAD_SAFE
150157
} finally { theLock.ExitWriteLock(); }
@@ -186,6 +193,7 @@ public List<PdbDocument> RemoveAllDocuments(bool returnDocs) {
186193
theLock.EnterWriteLock(); try {
187194
#endif
188195
var docs = returnDocs ? new List<PdbDocument>(docDict.Values) : null;
196+
tokenToDocument.Clear();
189197
docDict.Clear();
190198
return docs;
191199
#if THREAD_SAFE

src/DotNet/Pdb/Portable/PortablePdbCustomDebugInfoReader.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ PdbCustomDebugInfo Read(Guid kind) {
6565
return ReadCompilationMetadataReferences();
6666
if (kind == CustomDebugInfoGuids.CompilationOptions)
6767
return ReadCompilationOptions();
68+
if (kind == CustomDebugInfoGuids.TypeDefinitionDocuments)
69+
return ReadTypeDefinitionDocuments();
70+
if (kind == CustomDebugInfoGuids.EncStateMachineStateMap)
71+
return ReadEncStateMachineStateMap();
6872
Debug.Fail("Unknown custom debug info guid: " + kind.ToString());
6973
return new PdbUnknownCustomDebugInfo(kind, reader.ReadRemainingBytes());
7074
}
@@ -228,6 +232,34 @@ PdbCustomDebugInfo ReadCompilationOptions() {
228232
return cdi;
229233
}
230234

235+
PdbCustomDebugInfo ReadTypeDefinitionDocuments() {
236+
var docList = new List<MDToken>();
237+
while (reader.BytesLeft > 0)
238+
docList.Add(new MDToken(Table.Document, reader.ReadCompressedUInt32()));
239+
240+
return new PdbTypeDefinitionDocumentsDebugInfoMD(module, docList);
241+
}
242+
243+
PdbCustomDebugInfo ReadEncStateMachineStateMap() {
244+
var cdi = new PdbEditAndContinueStateMachineStateMapDebugInfo();
245+
246+
var count = reader.ReadCompressedUInt32();
247+
if (count > 0) {
248+
long syntaxOffsetBaseline = -reader.ReadCompressedUInt32();
249+
250+
while (count > 0) {
251+
int stateNumber = reader.ReadCompressedInt32();
252+
int syntaxOffset = (int)(syntaxOffsetBaseline + reader.ReadCompressedUInt32());
253+
254+
cdi.StateMachineStates.Add(new StateMachineStateInfo(syntaxOffset, (StateMachineState)stateNumber));
255+
256+
count--;
257+
}
258+
}
259+
260+
return cdi;
261+
}
262+
231263
Instruction GetInstruction(uint offset) {
232264
var instructions = bodyOpt.Instructions;
233265
int lo = 0, hi = instructions.Count - 1;

0 commit comments

Comments
 (0)