2using System.Collections.Generic;
3using System.Collections.Immutable;
4using System.Diagnostics;
5using System.Diagnostics.SymbolStore;
7using System.Reflection.Metadata;
8using System.Reflection.Metadata.Ecma335;
18 internal class PortablePdbSymbolWriter : IMetadataSymbolWriter
21 protected sealed class Document : ISymbolDocumentWriter
25 readonly Guid language;
26 readonly Guid languageVendor;
27 readonly Guid documentType;
40 internal Document(
string url, Guid language, Guid languageVendor, Guid documentType)
43 this.language = language;
44 this.languageVendor = languageVendor;
45 this.documentType = documentType;
55 this.algorithmId = algorithmId;
56 this.checksum = checksum;
72 public string Url => url;
192 internal int startOffset;
193 internal int endOffset;
194 internal List<Scope> scopes;
195 internal Dictionary<string, LocalVar> locals;
196 internal List<string> namespaces;
202 internal Scope(
int startOffset)
204 this.startOffset = startOffset;
220 public IReadOnlyList<Scope>
Scopes => (IReadOnlyList<Scope>)scopes ?? Array.Empty<
Scope>();
225 public IReadOnlyDictionary<string, LocalVar>
Locals => (IReadOnlyDictionary<string, LocalVar>)locals ?? ImmutableDictionary<string, LocalVar>.Empty;
230 public IReadOnlyList<string>
Namespaces => (IReadOnlyList<string>)namespaces ?? Array.Empty<
string>();
241 readonly StandaloneSignatureHandle localSignatureHandle;
242 internal List<SequencePoint> sequencePoints;
243 internal List<Scope> scopes;
244 internal Stack<Scope> scopeStack;
251 internal Method(
int token, StandaloneSignatureHandle localSignatureHandle)
254 this.localSignatureHandle = localSignatureHandle;
275 public IReadOnlyList<Scope>
Scopes => (IReadOnlyList<Scope>)scopes ?? Array.Empty<
Scope>();
342 readonly ModuleBuilder module;
343 Dictionary<string, Document> documents;
344 List<Method> methods;
352 public PortablePdbSymbolWriter(ModuleBuilder module)
354 this.module = module ??
throw new ArgumentNullException(nameof(module));
358 public virtual void Initialize(IntPtr emitter,
string filename,
bool fFullBuild)
360 throw new NotImplementedException();
364 public virtual ISymbolDocumentWriter DefineDocument(
string url, Guid language, Guid languageVendor, Guid documentType)
367 if (documents.TryGetValue(url, out var doc) ==
false)
368 documents.Add(url, doc =
new Document(url, language, languageVendor, documentType));
374 public virtual void OpenNamespace(
string name)
376 throw new NotImplementedException();
380 public virtual void OpenMethod(SymbolToken method)
382 Debug.Assert(ModuleBuilder.IsPseudoToken(method.GetToken()) ==
false);
383 currentMethod =
new Method(method.GetToken(),
default);
391 public virtual void OpenMethod(SymbolToken method, StandaloneSignatureHandle localSignatureHandle)
393 Debug.Assert(ModuleBuilder.IsPseudoToken(method.GetToken()) ==
false);
394 currentMethod =
new Method(method.GetToken(), localSignatureHandle);
398 public virtual void DefineSequencePoints(ISymbolDocumentWriter document,
int[] offsets,
int[] lines,
int[] columns,
int[] endLines,
int[] endColumns)
400 if (currentMethod ==
null)
401 throw new InvalidOperationException(
"No open method.");
403 if (document is not Document doc)
404 throw new InvalidOperationException(
"Document not obtained from symbol writer.");
406 var sequencePoint =
new SequencePoint(doc, offsets, lines, columns, endLines, endColumns);
407 currentMethod.sequencePoints ??=
new();
408 currentMethod.sequencePoints.Add(sequencePoint);
412 public virtual int OpenScope(
int startOffset)
414 if (currentMethod ==
null)
415 throw new InvalidOperationException(
"No open method.");
417 var scope =
new Scope(startOffset);
419 if (currentMethod.scopeStack ==
null || currentMethod.scopeStack.Count == 0)
421 currentMethod.scopes ??=
new();
422 currentMethod.scopes.Add(scope);
426 var thisScope = currentMethod.scopeStack !=
null ? currentMethod.scopeStack.Peek() :
null;
427 thisScope.scopes ??=
new();
428 thisScope.scopes.Add(scope);
431 currentMethod.scopeStack ??=
new();
432 currentMethod.scopeStack.Push(scope);
438 public virtual void UsingNamespace(
string fullName)
440 if (currentMethod ==
null)
441 throw new InvalidOperationException(
"No open method.");
443 var scope = currentMethod.scopeStack !=
null ? currentMethod.scopeStack.Peek() :
null;
445 throw new InvalidOperationException(
"No open scope.");
447 scope.namespaces ??=
new();
448 scope.namespaces.Add(fullName);
452 public virtual void DefineLocalVariable(
string name,
System.Reflection.FieldAttributes attributes,
byte[] signature, SymAddressKind addrKind,
int addr1,
int addr2,
int addr3,
int startOffset,
int endOffset)
454 if (currentMethod ==
null)
455 throw new InvalidOperationException(
"No open method.");
457 var scope = currentMethod.scopeStack !=
null ? currentMethod.scopeStack.Peek() :
null;
459 throw new InvalidOperationException(
"No open scope.");
461 scope.locals ??=
new();
462 scope.locals[name] =
new LocalVar(attributes, signature, addrKind, addr1, addr2, addr3, startOffset, endOffset);
466 public virtual void CloseScope(
int endOffset)
468 if (currentMethod ==
null)
469 throw new InvalidOperationException(
"No open method.");
471 var scope = currentMethod.scopeStack !=
null ? currentMethod.scopeStack.Pop() :
null;
473 throw new InvalidOperationException(
"No open scope.");
475 scope.endOffset = endOffset;
479 public virtual void CloseMethod()
481 if (currentMethod ==
null)
482 throw new InvalidOperationException(
"No open method.");
485 methods.Add(currentMethod);
486 currentMethod =
null;
490 public virtual void CloseNamespace()
492 throw new NotImplementedException();
496 public virtual void Close()
502 public virtual void WriteTo(MetadataBuilder metadata, out
int userEntryPoint)
504 metadata.GetOrAddString(
"");
505 metadata.GetOrAddUserString(
"");
509 var documentCache =
new Dictionary<Document, DocumentHandle>();
510 foreach (var method
in methods)
511 WriteMethod(metadata, method, documentCache);
514 userEntryPoint = this.userEntryPoint;
523 void WriteMethod(MetadataBuilder metadata, Method method, Dictionary<Document, DocumentHandle> documentCache)
526 var initialDocument = GetSingleDocument(method.SequencePoints);
527 var initialDocumentHandle = initialDocument !=
null ? GetOrWriteDocument(metadata, initialDocument, documentCache) : default;
528 var currentDocumentHandle = initialDocumentHandle;
531 WriteSequencePoints(metadata, method, documentCache, out var sequencePointsHandle,
ref currentDocumentHandle);
532 WriteScopes(metadata, method);
535 var methodDebugHandle = metadata.AddMethodDebugInformation(initialDocumentHandle, sequencePointsHandle);
536 Debug.Assert(MetadataTokens.GetRowNumber(methodDebugHandle) == MetadataTokens.GetRowNumber(MetadataTokens.EntityHandle(method.Token)));
544 Document GetSingleDocument(IEnumerable<SequencePoint> sequencePoints)
546 var s = sequencePoints.Select(i => i.Document).Distinct().Take(2).ToList();
547 return s.Count == 1 ? s[0] :
null;
559 void WriteSequencePoints(MetadataBuilder metadata, Method method, Dictionary<Document, DocumentHandle> documentCache, out BlobHandle sequencePointHandle,
ref DocumentHandle previousDocument)
562 sequencePointHandle =
default;
563 if (method.SequencePoints.Count == 0)
566 var buf =
new BlobBuilder();
570 if (method.LocalSignatureHandle.IsNil)
571 throw new InvalidOperationException(
"MethodBuilder missing local signature.");
574 enc.LocalSignature(
default);
577 foreach (var (document, offset, startLine, endLine, startColumn, endColumn) in ExpandSequencePoints(method.SequencePoints).OrderBy(i => i.Offset))
579 var doc = GetOrWriteDocument(metadata, document, documentCache);
580 enc.SequencePoint(doc, offset, startLine, startColumn, endLine, endColumn,
ref previousDocument);
584 sequencePointHandle = metadata.GetOrAddBlob(buf);
592 IEnumerable<(Document Document,
int Offset,
int StartLine,
int EndLine,
int StartColumn,
int EndColumn)> ExpandSequencePoints(IReadOnlyList<SequencePoint> sequencePoints)
594 for (
int i = 0; i < sequencePoints.Count; i++)
595 for (
int j = 0; j < sequencePoints[i].Offsets.Length; j++)
596 yield
return (sequencePoints[i].Document, sequencePoints[i].Offsets[j], sequencePoints[i].Lines[j], sequencePoints[i].EndLines[j], sequencePoints[i].Columns[j], sequencePoints[i].EndColumns[j]);
605 DocumentHandle GetOrWriteDocument(MetadataBuilder metadata, Document document, Dictionary<Document, DocumentHandle> documentCache)
607 if (document ==
null)
610 if (documentCache.TryGetValue(document, out var handle) ==
false)
611 documentCache.Add(document, handle = WriteDocument(metadata, document));
622 DocumentHandle WriteDocument(MetadataBuilder metadata, Document document)
624 return metadata.AddDocument(
625 metadata.GetOrAddDocumentName(document.Url ??
""),
626 document.AlgorithmId != Guid.Empty ? metadata.GetOrAddGuid(document.AlgorithmId) :
default,
627 document.Checksum !=
null ? metadata.GetOrAddBlob(document.Checksum) :
default,
628 document.Language != Guid.Empty ? metadata.GetOrAddGuid(document.Language) :
default);
636 void WriteScopes(MetadataBuilder metadata, Method method)
638 foreach (var scope
in method.Scopes.OrderBy(i => i.StartOffset).ThenByDescending(i => i.EndOffset - i.StartOffset))
639 WriteScope(metadata, method, scope);
648 void WriteScope(MetadataBuilder metadata, Method method, Scope scope)
650 WriteScope(metadata, method, scope,
default);
660 void WriteScope(MetadataBuilder metadata, Method method, Scope scope, ImportScopeHandle parentImportScope)
663 var importScope =
default(ImportScopeHandle);
664 if (scope.Namespaces.Count > 0)
666 var buf =
new BlobBuilder();
670 foreach (var ns
in scope.Namespaces)
671 enc.ImportNamespace(metadata.GetOrAddBlobUTF8(ns));
674 importScope = metadata.AddImportScope(parentImportScope, metadata.GetOrAddBlob(buf));
678 var variableList =
default(LocalVariableHandle);
679 foreach (var kvp
in scope.Locals.OrderBy(i => i.Value.Address1))
681 var h = metadata.AddLocalVariable(LocalVariableAttributes.None, kvp.Value.Address1, metadata.GetOrAddString(kvp.Key));
682 if (variableList.IsNil)
687 metadata.AddLocalScope(
688 (MethodDefinitionHandle)MetadataTokens.EntityHandle(method.Token),
693 scope.EndOffset - scope.StartOffset);
696 foreach (var nestedScope
in scope.Scopes.OrderBy(i => i.StartOffset).ThenByDescending(i => i.EndOffset - i.StartOffset))
697 WriteScope(metadata, method, nestedScope, importScope);
700 #region Not Implemented
703 public void DefineField(SymbolToken parent,
string name,
System.Reflection.FieldAttributes attributes,
byte[] signature, SymAddressKind addrKind,
int addr1,
int addr2,
int addr3)
705 throw new NotImplementedException();
709 public void DefineGlobalVariable(
string name,
System.Reflection.FieldAttributes attributes,
byte[] signature, SymAddressKind addrKind,
int addr1,
int addr2,
int addr3)
711 throw new NotImplementedException();
715 public void DefineParameter(
string name,
System.Reflection.ParameterAttributes attributes,
int sequence, SymAddressKind addrKind,
int addr1,
int addr2,
int addr3)
717 throw new NotImplementedException();
721 public void SetMethodSourceRange(ISymbolDocumentWriter startDoc,
int startLine,
int startColumn, ISymbolDocumentWriter endDoc,
int endLine,
int endColumn)
723 throw new NotImplementedException();
727 public void SetScopeRange(
int scopeID,
int startOffset,
int endOffset)
729 throw new NotImplementedException();
733 public void SetSymAttribute(SymbolToken parent,
string name,
byte[] data)
735 throw new NotImplementedException();
739 public void SetUnderlyingWriter(IntPtr underlyingWriter)
741 throw new NotImplementedException();
745 public void SetUserEntryPoint(SymbolToken entryMethod)
747 this.userEntryPoint = entryMethod.GetToken();
string Url
Gets the URL of the document.
void SetSource(byte[] source)
Sets the source.
Guid Language
Gets the language of the document.
byte[] Source
Gets the source.
Guid LanguageVendor
Gets the vendor of the document.
Guid DocumentType
Gets the document type.
byte[] Checksum
Gets the checksum.
Guid AlgorithmId
Gets the checksum algorithm ID.
void SetCheckSum(Guid algorithmId, byte[] checksum)
Sets the checksum.
Tracks method information.
int Token
Gets the token of the method.
IReadOnlyList< Scope > Scopes
Gets the scopes.
StandaloneSignatureHandle LocalSignatureHandle
Gets the local variable signature.
IReadOnlyList< SequencePoint > SequencePoints
Gets the sequence points.
Tracks method scope information.
int StartOffset
Gets the start offset.
IReadOnlyDictionary< string, LocalVar > Locals
Gets the local variables.
int EndOffset
Gets the end offset.
IReadOnlyList< Scope > Scopes
Gets the nested scopes.
IReadOnlyList< string > Namespaces
Gets the namespaces.
Track local variable information.
System.Reflection.FieldAttributes Attributes
Gets the attributes of the variable.
int Address3
Gets the third address.
int StartOffset
Gets the start offset in IL of the variable.
int Address2
Gets the second address.
int Address1
Gets the first address.
readonly byte[] signature
SymAddressKind AddressKind
Gets the address kind.
int EndOffset
Gets the end offset in IL of the variable.
readonly System.Reflection.FieldAttributes attributes
readonly SymAddressKind addrKind
Describes a sequence point on a method.
int[] Lines
Gets the lines.
int[] Columns
Gets the columns.
readonly Document document
int[] EndLines
Gets the line endings.
int[] Offsets
Gets the offsets.
readonly int[] endColumns
int[] EndColumns
Gets the column endings.
Helper to encode sequence points.