IKVM11  11
Java SE 11 Virtual Machine for .NET
Loading...
Searching...
No Matches
PortablePdbSymbolWriter.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.Collections.Immutable;
4using System.Diagnostics;
5using System.Diagnostics.SymbolStore;
6using System.Linq;
7using System.Reflection.Metadata;
8using System.Reflection.Metadata.Ecma335;
9
11
13{
14
18 internal class PortablePdbSymbolWriter : IMetadataSymbolWriter
19 {
20
21 protected sealed class Document : ISymbolDocumentWriter
22 {
23
24 readonly string url;
25 readonly Guid language;
26 readonly Guid languageVendor;
27 readonly Guid documentType;
28
29 Guid algorithmId;
30 byte[] checksum;
31 byte[] source;
32
40 internal Document(string url, Guid language, Guid languageVendor, Guid documentType)
41 {
42 this.url = url;
43 this.language = language;
44 this.languageVendor = languageVendor;
45 this.documentType = documentType;
46 }
47
53 public void SetCheckSum(Guid algorithmId, byte[] checksum)
54 {
55 this.algorithmId = algorithmId;
56 this.checksum = checksum;
57 }
58
63 public void SetSource(byte[] source)
64 {
65 this.source = source;
66 }
67
71
72 public string Url => url;
73
77 public Guid Language => language;
78
82 public Guid LanguageVendor => languageVendor;
83
87 public Guid DocumentType => documentType;
88
92 public Guid AlgorithmId => algorithmId;
93
97 public byte[] Checksum => checksum;
98
102 public byte[] Source => source;
103
104 }
105
109 protected readonly struct LocalVar
110 {
111
112 readonly System.Reflection.FieldAttributes attributes;
113 readonly byte[] signature;
114 readonly SymAddressKind addrKind;
115 readonly int addr1;
116 readonly int addr2;
117 readonly int addr3;
118 readonly int startOffset;
119 readonly int endOffset;
120
132 internal LocalVar(System.Reflection.FieldAttributes attributes, byte[] signature, SymAddressKind addrKind, int addr1, int addr2, int addr3, int startOffset, int endOffset)
133 {
134 this.attributes = attributes;
135 this.signature = signature;
136 this.addrKind = addrKind;
137 this.addr1 = addr1;
138 this.addr2 = addr2;
139 this.addr3 = addr3;
140 this.startOffset = startOffset;
141 this.endOffset = endOffset;
142 }
143
147 public System.Reflection.FieldAttributes Attributes => attributes;
148
152 public byte[] Signature => signature;
153
157 public SymAddressKind AddressKind => addrKind;
158
162 public int Address1 => addr1;
163
167 public int Address2 => addr2;
168
172 public int Address3 => addr3;
173
178
182 public int EndOffset => endOffset;
183
184 }
185
189 protected sealed class Scope
190 {
191
192 internal int startOffset;
193 internal int endOffset;
194 internal List<Scope> scopes;
195 internal Dictionary<string, LocalVar> locals;
196 internal List<string> namespaces;
197
202 internal Scope(int startOffset)
203 {
204 this.startOffset = startOffset;
205 }
206
210 public int StartOffset => startOffset;
211
215 public int EndOffset => endOffset;
216
220 public IReadOnlyList<Scope> Scopes => (IReadOnlyList<Scope>)scopes ?? Array.Empty<Scope>();
221
225 public IReadOnlyDictionary<string, LocalVar> Locals => (IReadOnlyDictionary<string, LocalVar>)locals ?? ImmutableDictionary<string, LocalVar>.Empty;
226
230 public IReadOnlyList<string> Namespaces => (IReadOnlyList<string>)namespaces ?? Array.Empty<string>();
231
232 }
233
237 protected sealed class Method
238 {
239
240 readonly int token;
241 readonly StandaloneSignatureHandle localSignatureHandle;
242 internal List<SequencePoint> sequencePoints;
243 internal List<Scope> scopes;
244 internal Stack<Scope> scopeStack;
245
251 internal Method(int token, StandaloneSignatureHandle localSignatureHandle)
252 {
253 this.token = token;
254 this.localSignatureHandle = localSignatureHandle;
255 }
256
260 public int Token => token;
261
265 public StandaloneSignatureHandle LocalSignatureHandle => localSignatureHandle;
266
270 public IReadOnlyList<SequencePoint> SequencePoints => (IReadOnlyList<SequencePoint>)sequencePoints ?? Array.Empty<SequencePoint>();
271
275 public IReadOnlyList<Scope> Scopes => (IReadOnlyList<Scope>)scopes ?? Array.Empty<Scope>();
276
277 }
278
282 protected readonly struct SequencePoint
283 {
284
286 readonly int[] offsets;
287 readonly int[] lines;
288 readonly int[] columns;
289 readonly int[] endLines;
290 readonly int[] endColumns;
291
300 internal SequencePoint(Document document, int[] offsets, int[] lines, int[] columns, int[] endLines, int[] endColumns)
301 {
302 this.document = document;
303 this.offsets = offsets;
304 this.lines = lines;
305 this.columns = columns;
306 this.endLines = endLines;
307 this.endColumns = endColumns;
308 }
309
314
318 public int[] Offsets => offsets;
319
323 public int[] Lines => lines;
324
328 public int[] Columns => columns;
329
333 public int[] EndLines => endLines;
334
338 public int[] EndColumns => endColumns;
339
340 }
341
342 readonly ModuleBuilder module;
343 Dictionary<string, Document> documents;
344 List<Method> methods;
345 Method currentMethod;
346 int userEntryPoint;
347
352 public PortablePdbSymbolWriter(ModuleBuilder module)
353 {
354 this.module = module ?? throw new ArgumentNullException(nameof(module));
355 }
356
358 public virtual void Initialize(IntPtr emitter, string filename, bool fFullBuild)
359 {
360 throw new NotImplementedException();
361 }
362
364 public virtual ISymbolDocumentWriter DefineDocument(string url, Guid language, Guid languageVendor, Guid documentType)
365 {
366 documents ??= new();
367 if (documents.TryGetValue(url, out var doc) == false)
368 documents.Add(url, doc = new Document(url, language, languageVendor, documentType));
369
370 return doc;
371 }
372
374 public virtual void OpenNamespace(string name)
375 {
376 throw new NotImplementedException();
377 }
378
380 public virtual void OpenMethod(SymbolToken method)
381 {
382 Debug.Assert(ModuleBuilder.IsPseudoToken(method.GetToken()) == false);
383 currentMethod = new Method(method.GetToken(), default);
384 }
385
391 public virtual void OpenMethod(SymbolToken method, StandaloneSignatureHandle localSignatureHandle)
392 {
393 Debug.Assert(ModuleBuilder.IsPseudoToken(method.GetToken()) == false);
394 currentMethod = new Method(method.GetToken(), localSignatureHandle);
395 }
396
398 public virtual void DefineSequencePoints(ISymbolDocumentWriter document, int[] offsets, int[] lines, int[] columns, int[] endLines, int[] endColumns)
399 {
400 if (currentMethod == null)
401 throw new InvalidOperationException("No open method.");
402
403 if (document is not Document doc)
404 throw new InvalidOperationException("Document not obtained from symbol writer.");
405
406 var sequencePoint = new SequencePoint(doc, offsets, lines, columns, endLines, endColumns);
407 currentMethod.sequencePoints ??= new();
408 currentMethod.sequencePoints.Add(sequencePoint);
409 }
410
412 public virtual int OpenScope(int startOffset)
413 {
414 if (currentMethod == null)
415 throw new InvalidOperationException("No open method.");
416
417 var scope = new Scope(startOffset);
418
419 if (currentMethod.scopeStack == null || currentMethod.scopeStack.Count == 0)
420 {
421 currentMethod.scopes ??= new();
422 currentMethod.scopes.Add(scope);
423 }
424 else
425 {
426 var thisScope = currentMethod.scopeStack != null ? currentMethod.scopeStack.Peek() : null;
427 thisScope.scopes ??= new();
428 thisScope.scopes.Add(scope);
429 }
430
431 currentMethod.scopeStack ??= new();
432 currentMethod.scopeStack.Push(scope);
433
434 return 0;
435 }
436
438 public virtual void UsingNamespace(string fullName)
439 {
440 if (currentMethod == null)
441 throw new InvalidOperationException("No open method.");
442
443 var scope = currentMethod.scopeStack != null ? currentMethod.scopeStack.Peek() : null;
444 if (scope == null)
445 throw new InvalidOperationException("No open scope.");
446
447 scope.namespaces ??= new();
448 scope.namespaces.Add(fullName);
449 }
450
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)
453 {
454 if (currentMethod == null)
455 throw new InvalidOperationException("No open method.");
456
457 var scope = currentMethod.scopeStack != null ? currentMethod.scopeStack.Peek() : null;
458 if (scope == null)
459 throw new InvalidOperationException("No open scope.");
460
461 scope.locals ??= new();
462 scope.locals[name] = new LocalVar(attributes, signature, addrKind, addr1, addr2, addr3, startOffset, endOffset);
463 }
464
466 public virtual void CloseScope(int endOffset)
467 {
468 if (currentMethod == null)
469 throw new InvalidOperationException("No open method.");
470
471 var scope = currentMethod.scopeStack != null ? currentMethod.scopeStack.Pop() : null;
472 if (scope == null)
473 throw new InvalidOperationException("No open scope.");
474
475 scope.endOffset = endOffset;
476 }
477
479 public virtual void CloseMethod()
480 {
481 if (currentMethod == null)
482 throw new InvalidOperationException("No open method.");
483
484 methods ??= new();
485 methods.Add(currentMethod);
486 currentMethod = null;
487 }
488
490 public virtual void CloseNamespace()
491 {
492 throw new NotImplementedException();
493 }
494
496 public virtual void Close()
497 {
498
499 }
500
502 public virtual void WriteTo(MetadataBuilder metadata, out int userEntryPoint)
503 {
504 metadata.GetOrAddString("");
505 metadata.GetOrAddUserString("");
506
507 if (methods != null)
508 {
509 var documentCache = new Dictionary<Document, DocumentHandle>();
510 foreach (var method in methods)
511 WriteMethod(metadata, method, documentCache);
512 }
513
514 userEntryPoint = this.userEntryPoint;
515 }
516
523 void WriteMethod(MetadataBuilder metadata, Method method, Dictionary<Document, DocumentHandle> documentCache)
524 {
525 // find first document and set as initial, as it will be directly on method debug information
526 var initialDocument = GetSingleDocument(method.SequencePoints);
527 var initialDocumentHandle = initialDocument != null ? GetOrWriteDocument(metadata, initialDocument, documentCache) : default;
528 var currentDocumentHandle = initialDocumentHandle;
529
530 // write sequence points and scopes
531 WriteSequencePoints(metadata, method, documentCache, out var sequencePointsHandle, ref currentDocumentHandle);
532 WriteScopes(metadata, method);
533
534 // final debug information, containing initial document
535 var methodDebugHandle = metadata.AddMethodDebugInformation(initialDocumentHandle, sequencePointsHandle);
536 Debug.Assert(MetadataTokens.GetRowNumber(methodDebugHandle) == MetadataTokens.GetRowNumber(MetadataTokens.EntityHandle(method.Token)));
537 }
538
544 Document GetSingleDocument(IEnumerable<SequencePoint> sequencePoints)
545 {
546 var s = sequencePoints.Select(i => i.Document).Distinct().Take(2).ToList();
547 return s.Count == 1 ? s[0] : null;
548 }
549
559 void WriteSequencePoints(MetadataBuilder metadata, Method method, Dictionary<Document, DocumentHandle> documentCache, out BlobHandle sequencePointHandle, ref DocumentHandle previousDocument)
560 {
561 // no sequence points?
562 sequencePointHandle = default;
563 if (method.SequencePoints.Count == 0)
564 return;
565
566 var buf = new BlobBuilder();
567 var enc = new SequencePointEncoder(buf);
568
569 // obtain local signature from method builder directly
570 if (method.LocalSignatureHandle.IsNil)
571 throw new InvalidOperationException("MethodBuilder missing local signature.");
572
573 // define the local signature, default seems to work fine
574 enc.LocalSignature(default);
575
576 // add the sequence points recorded on the method
577 foreach (var (document, offset, startLine, endLine, startColumn, endColumn) in ExpandSequencePoints(method.SequencePoints).OrderBy(i => i.Offset))
578 {
579 var doc = GetOrWriteDocument(metadata, document, documentCache);
580 enc.SequencePoint(doc, offset, startLine, startColumn, endLine, endColumn, ref previousDocument);
581 }
582
583 // add sequence points blob
584 sequencePointHandle = metadata.GetOrAddBlob(buf);
585 }
586
592 IEnumerable<(Document Document, int Offset, int StartLine, int EndLine, int StartColumn, int EndColumn)> ExpandSequencePoints(IReadOnlyList<SequencePoint> sequencePoints)
593 {
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]);
597 }
598
605 DocumentHandle GetOrWriteDocument(MetadataBuilder metadata, Document document, Dictionary<Document, DocumentHandle> documentCache)
606 {
607 if (document == null)
608 return default;
609
610 if (documentCache.TryGetValue(document, out var handle) == false)
611 documentCache.Add(document, handle = WriteDocument(metadata, document));
612
613 return handle;
614 }
615
622 DocumentHandle WriteDocument(MetadataBuilder metadata, Document document)
623 {
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);
629 }
630
636 void WriteScopes(MetadataBuilder metadata, Method method)
637 {
638 foreach (var scope in method.Scopes.OrderBy(i => i.StartOffset).ThenByDescending(i => i.EndOffset - i.StartOffset))
639 WriteScope(metadata, method, scope);
640 }
641
648 void WriteScope(MetadataBuilder metadata, Method method, Scope scope)
649 {
650 WriteScope(metadata, method, scope, default);
651 }
652
660 void WriteScope(MetadataBuilder metadata, Method method, Scope scope, ImportScopeHandle parentImportScope)
661 {
662 // insert import scope for this scope
663 var importScope = default(ImportScopeHandle);
664 if (scope.Namespaces.Count > 0)
665 {
666 var buf = new BlobBuilder();
667 var enc = new ImportsEncoder(buf);
668
669 // add namespace imports
670 foreach (var ns in scope.Namespaces)
671 enc.ImportNamespace(metadata.GetOrAddBlobUTF8(ns));
672
673 // write nested scope imports
674 importScope = metadata.AddImportScope(parentImportScope, metadata.GetOrAddBlob(buf));
675 }
676
677 // insert variables for this scope, keeping first entry
678 var variableList = default(LocalVariableHandle);
679 foreach (var kvp in scope.Locals.OrderBy(i => i.Value.Address1))
680 {
681 var h = metadata.AddLocalVariable(LocalVariableAttributes.None, kvp.Value.Address1, metadata.GetOrAddString(kvp.Key));
682 if (variableList.IsNil)
683 variableList = h;
684 }
685
686 // add scope
687 metadata.AddLocalScope(
688 (MethodDefinitionHandle)MetadataTokens.EntityHandle(method.Token),
689 importScope,
690 variableList,
691 default,
692 scope.StartOffset,
693 scope.EndOffset - scope.StartOffset);
694
695 // repeat for children scopes
696 foreach (var nestedScope in scope.Scopes.OrderBy(i => i.StartOffset).ThenByDescending(i => i.EndOffset - i.StartOffset))
697 WriteScope(metadata, method, nestedScope, importScope);
698 }
699
700 #region Not Implemented
701
703 public void DefineField(SymbolToken parent, string name, System.Reflection.FieldAttributes attributes, byte[] signature, SymAddressKind addrKind, int addr1, int addr2, int addr3)
704 {
705 throw new NotImplementedException();
706 }
707
709 public void DefineGlobalVariable(string name, System.Reflection.FieldAttributes attributes, byte[] signature, SymAddressKind addrKind, int addr1, int addr2, int addr3)
710 {
711 throw new NotImplementedException();
712 }
713
715 public void DefineParameter(string name, System.Reflection.ParameterAttributes attributes, int sequence, SymAddressKind addrKind, int addr1, int addr2, int addr3)
716 {
717 throw new NotImplementedException();
718 }
719
721 public void SetMethodSourceRange(ISymbolDocumentWriter startDoc, int startLine, int startColumn, ISymbolDocumentWriter endDoc, int endLine, int endColumn)
722 {
723 throw new NotImplementedException();
724 }
725
727 public void SetScopeRange(int scopeID, int startOffset, int endOffset)
728 {
729 throw new NotImplementedException();
730 }
731
733 public void SetSymAttribute(SymbolToken parent, string name, byte[] data)
734 {
735 throw new NotImplementedException();
736 }
737
739 public void SetUnderlyingWriter(IntPtr underlyingWriter)
740 {
741 throw new NotImplementedException();
742 }
743
745 public void SetUserEntryPoint(SymbolToken entryMethod)
746 {
747 this.userEntryPoint = entryMethod.GetToken();
748 }
749
750 #endregion
751
752 }
753
754}
void SetCheckSum(Guid algorithmId, byte[] checksum)
Sets the checksum.
StandaloneSignatureHandle LocalSignatureHandle
Gets the local variable signature.
IReadOnlyList< SequencePoint > SequencePoints
Gets the sequence points.
IReadOnlyDictionary< string, LocalVar > Locals
Gets the local variables.
IReadOnlyList< Scope > Scopes
Gets the nested scopes.
System.Reflection.FieldAttributes Attributes
Gets the attributes of the variable.
int StartOffset
Gets the start offset in IL of the variable.