Files
2026-08-27 10:56:38 -06:00

578 lines
20 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Security.Cryptography;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.DiaSymReader;
using Roslyn.Utilities;
namespace Microsoft.Cci;
internal sealed class PdbWriter : IDisposable
{
internal const uint Age = 1u;
private readonly HashAlgorithmName _hashAlgorithmNameOpt;
private readonly string _fileName;
private readonly Func<ISymWriterMetadataProvider, SymUnmanagedWriter> _symWriterFactory;
private readonly Dictionary<DebugSourceDocument, int> _documentIndex;
private MetadataWriter _metadataWriter;
private SymUnmanagedWriter _symWriter;
private SymUnmanagedSequencePointsWriter _sequencePointsWriter;
private readonly Dictionary<object, string> _qualifiedNameCache;
private bool IsDeterministic
{
get
{
HashAlgorithmName hashAlgorithmNameOpt = _hashAlgorithmNameOpt;
return hashAlgorithmNameOpt.Name != null;
}
}
private CommonPEModuleBuilder Module => Context.Module;
private EmitContext Context => _metadataWriter.Context;
public PdbWriter(string fileName, Func<ISymWriterMetadataProvider, SymUnmanagedWriter> symWriterFactory, HashAlgorithmName hashAlgorithmNameOpt)
{
_fileName = fileName;
_symWriterFactory = symWriterFactory;
_hashAlgorithmNameOpt = hashAlgorithmNameOpt;
_documentIndex = new Dictionary<DebugSourceDocument, int>();
_qualifiedNameCache = new Dictionary<object, string>(Roslyn.Utilities.ReferenceEqualityComparer.Instance);
}
public void WriteTo(Stream stream)
{
_symWriter.WriteTo(stream);
}
public void Dispose()
{
_symWriter?.Dispose();
}
public void SerializeDebugInfo(IMethodBody methodBody, StandaloneSignatureHandle localSignatureHandleOpt, CustomDebugInfoWriter customDebugInfoWriter)
{
MethodDefinitionHandle methodDefinitionHandle = (MethodDefinitionHandle)_metadataWriter.GetMethodHandle(methodBody.MethodDefinition);
bool flag = methodBody.StateMachineTypeName != null;
bool flag2 = flag || !methodBody.SequencePoints.IsEmpty || methodBody.MethodDefinition == (Context.Module.DebugEntryPoint ?? Context.Module.PEEntryPoint);
CompilationOptions options = Context.Module.CommonCompilation.Options;
bool flag3 = options.OutputKind == OutputKind.WindowsRuntimeMetadata;
bool emitDynamicAndTupleInfo = flag2 && !flag3;
bool emitEncInfo = options.EnableEditAndContinue && _metadataWriter.IsFullMetadata && !flag3;
bool emitExternNamespaces;
byte[] array = customDebugInfoWriter.SerializeMethodDebugInfo(Context, methodBody, methodDefinitionHandle, flag2, emitEncInfo, emitDynamicAndTupleInfo, out emitExternNamespaces);
if (!flag2 && array.Length == 0)
{
return;
}
int token = MetadataTokens.GetToken(methodDefinitionHandle);
OpenMethod(token);
if (flag2)
{
ImmutableArray<LocalScope> localScopes = methodBody.LocalScopes;
if (localScopes.Length > 0)
{
DefineScopeLocals(localScopes[0], localSignatureHandleOpt);
}
if (!flag && methodBody.ImportScope != null)
{
if (customDebugInfoWriter.ShouldForwardNamespaceScopes(Context, methodBody, methodDefinitionHandle, out var forwardToMethod))
{
if (forwardToMethod != null)
{
UsingNamespace("@" + MetadataTokens.GetToken(_metadataWriter.GetMethodHandle(forwardToMethod)), methodBody.MethodDefinition);
}
}
else
{
DefineNamespaceScopes(methodBody);
}
}
DefineLocalScopes(localScopes, localSignatureHandleOpt);
EmitSequencePoints(methodBody.SequencePoints);
if (methodBody.MoveNextBodyInfo is AsyncMoveNextBodyDebugInfo asyncMoveNextBodyDebugInfo)
{
_symWriter.SetAsyncInfo(token, MetadataTokens.GetToken(_metadataWriter.GetMethodHandle(asyncMoveNextBodyDebugInfo.KickoffMethod)), asyncMoveNextBodyDebugInfo.CatchHandlerOffset, asyncMoveNextBodyDebugInfo.YieldOffsets.AsSpan(), asyncMoveNextBodyDebugInfo.ResumeOffsets.AsSpan());
}
if (emitExternNamespaces)
{
DefineAssemblyReferenceAliases();
}
}
if (array.Length != 0)
{
_symWriter.DefineCustomMetadata(array);
}
CloseMethod(methodBody.IL.Length);
}
private void DefineNamespaceScopes(IMethodBody methodBody)
{
CommonPEModuleBuilder module = Module;
bool generateVisualBasicStylePdb = module.GenerateVisualBasicStylePdb;
IMethodDefinition methodDefinition = methodBody.MethodDefinition;
IImportScope importScope = methodBody.ImportScope;
PooledHashSet<string> pooledHashSet = null;
ImmutableArray<UsedNamespaceOrType>.Enumerator enumerator;
if (!generateVisualBasicStylePdb)
{
for (IImportScope importScope2 = importScope; importScope2 != null; importScope2 = importScope2.Parent)
{
enumerator = importScope2.GetUsedNamespaces(Context).GetEnumerator();
while (enumerator.MoveNext())
{
UsedNamespaceOrType current = enumerator.Current;
if (current.TargetNamespaceOpt == null && current.TargetTypeOpt == null)
{
if (pooledHashSet == null)
{
pooledHashSet = PooledHashSet<string>.GetInstance();
}
pooledHashSet.Add(current.AliasOpt);
}
}
}
}
for (IImportScope importScope3 = importScope; importScope3 != null; importScope3 = importScope3.Parent)
{
enumerator = importScope3.GetUsedNamespaces(Context).GetEnumerator();
while (enumerator.MoveNext())
{
UsedNamespaceOrType current2 = enumerator.Current;
string text = TryEncodeImport(current2, pooledHashSet, isProjectLevel: false);
if (text != null)
{
UsingNamespace(text, methodDefinition);
}
}
}
pooledHashSet?.Free();
if (!generateVisualBasicStylePdb)
{
return;
}
string defaultNamespace = module.DefaultNamespace;
if (!string.IsNullOrEmpty(defaultNamespace))
{
UsingNamespace("*" + defaultNamespace, module);
}
foreach (string item in module.LinkedAssembliesDebugInfo)
{
UsingNamespace("&" + item, module);
}
enumerator = module.GetImports().GetEnumerator();
while (enumerator.MoveNext())
{
UsedNamespaceOrType current4 = enumerator.Current;
string text2 = TryEncodeImport(current4, null, isProjectLevel: true);
if (text2 != null)
{
UsingNamespace(text2, methodDefinition);
}
}
UsingNamespace(GetOrCreateSerializedNamespaceName(methodDefinition.ContainingNamespace), methodDefinition);
}
private void DefineAssemblyReferenceAliases()
{
ImmutableArray<AssemblyReferenceAlias>.Enumerator enumerator = Module.GetAssemblyReferenceAliases(Context).GetEnumerator();
while (enumerator.MoveNext())
{
AssemblyReferenceAlias current = enumerator.Current;
UsingNamespace("Z" + current.Name + " " + current.Assembly.Identity.GetDisplayName(), Module);
}
}
private string TryEncodeImport(UsedNamespaceOrType import, HashSet<string> declaredExternAliasesOpt, bool isProjectLevel)
{
if (Module.GenerateVisualBasicStylePdb)
{
if (import.TargetTypeOpt != null)
{
if (import.TargetTypeOpt.IsTypeSpecification())
{
return null;
}
string orCreateSerializedTypeName = GetOrCreateSerializedTypeName(import.TargetTypeOpt);
if (import.AliasOpt != null)
{
return (isProjectLevel ? "@PA:" : "@FA:") + import.AliasOpt + "=" + orCreateSerializedTypeName;
}
return (isProjectLevel ? "@PT:" : "@FT:") + orCreateSerializedTypeName;
}
if (import.TargetNamespaceOpt != null)
{
string orCreateSerializedNamespaceName = GetOrCreateSerializedNamespaceName(import.TargetNamespaceOpt);
if (import.AliasOpt == null)
{
return (isProjectLevel ? "@P:" : "@F:") + orCreateSerializedNamespaceName;
}
return (isProjectLevel ? "@PA:" : "@FA:") + import.AliasOpt + "=" + orCreateSerializedNamespaceName;
}
return (isProjectLevel ? "@PX:" : "@FX:") + import.AliasOpt + "=" + import.TargetXmlNamespaceOpt;
}
if (import.TargetTypeOpt != null)
{
string orCreateSerializedTypeName2 = GetOrCreateSerializedTypeName(import.TargetTypeOpt);
if (import.AliasOpt == null)
{
return "T" + orCreateSerializedTypeName2;
}
return "A" + import.AliasOpt + " T" + orCreateSerializedTypeName2;
}
if (import.TargetNamespaceOpt != null)
{
string orCreateSerializedNamespaceName2 = GetOrCreateSerializedNamespaceName(import.TargetNamespaceOpt);
if (import.AliasOpt != null)
{
if (import.TargetAssemblyOpt == null)
{
return "A" + import.AliasOpt + " U" + orCreateSerializedNamespaceName2;
}
return "A" + import.AliasOpt + " E" + orCreateSerializedNamespaceName2 + " " + GetAssemblyReferenceAlias(import.TargetAssemblyOpt, declaredExternAliasesOpt);
}
if (import.TargetAssemblyOpt == null)
{
return "U" + orCreateSerializedNamespaceName2;
}
return "E" + orCreateSerializedNamespaceName2 + " " + GetAssemblyReferenceAlias(import.TargetAssemblyOpt, declaredExternAliasesOpt);
}
return "X" + import.AliasOpt;
}
internal string GetOrCreateSerializedNamespaceName(INamespace @namespace)
{
if (!_qualifiedNameCache.TryGetValue(@namespace, out var value))
{
value = TypeNameSerializer.BuildQualifiedNamespaceName(@namespace);
_qualifiedNameCache.Add(@namespace, value);
}
return value;
}
internal string GetOrCreateSerializedTypeName(ITypeReference typeReference)
{
if (!_qualifiedNameCache.TryGetValue(typeReference, out var value))
{
value = ((!Module.GenerateVisualBasicStylePdb) ? typeReference.GetSerializedTypeName(Context) : SerializeVisualBasicImportTypeReference(typeReference));
_qualifiedNameCache.Add(typeReference, value);
}
return value;
}
private string SerializeVisualBasicImportTypeReference(ITypeReference typeReference)
{
PooledStringBuilder instance = PooledStringBuilder.GetInstance();
INestedTypeReference asNestedTypeReference = typeReference.AsNestedTypeReference;
ArrayBuilder<string> arrayBuilder;
if (asNestedTypeReference != null)
{
arrayBuilder = ArrayBuilder<string>.GetInstance();
while (asNestedTypeReference != null)
{
arrayBuilder.Add(asNestedTypeReference.Name);
typeReference = asNestedTypeReference.GetContainingType(_metadataWriter.Context);
asNestedTypeReference = typeReference.AsNestedTypeReference;
}
}
else
{
arrayBuilder = null;
}
INamespaceTypeReference asNamespaceTypeReference = typeReference.AsNamespaceTypeReference;
string namespaceName = asNamespaceTypeReference.NamespaceName;
if (namespaceName.Length != 0)
{
instance.Builder.Append(namespaceName);
instance.Builder.Append('.');
}
instance.Builder.Append(asNamespaceTypeReference.Name);
if (arrayBuilder != null)
{
for (int num = arrayBuilder.Count - 1; num >= 0; num--)
{
instance.Builder.Append('.');
instance.Builder.Append(arrayBuilder[num]);
}
arrayBuilder.Free();
}
return instance.ToStringAndFree();
}
private string GetAssemblyReferenceAlias(IAssemblyReference assembly, HashSet<string> declaredExternAliases)
{
ImmutableArray<AssemblyReferenceAlias>.Enumerator enumerator = _metadataWriter.Context.Module.GetAssemblyReferenceAliases(_metadataWriter.Context).GetEnumerator();
while (enumerator.MoveNext())
{
AssemblyReferenceAlias current = enumerator.Current;
if (assembly == current.Assembly && declaredExternAliases.Contains(current.Name))
{
return current.Name;
}
}
throw ExceptionUtilities.Unreachable("/_/src/Compilers/Core/Portable/NativePdbWriter/PdbWriter.cs", 444);
}
private void DefineLocalScopes(ImmutableArray<LocalScope> scopes, StandaloneSignatureHandle localSignatureHandleOpt)
{
bool generateVisualBasicStylePdb = Module.GenerateVisualBasicStylePdb;
ArrayBuilder<LocalScope> instance = ArrayBuilder<LocalScope>.GetInstance();
for (int i = 1; i < scopes.Length; i++)
{
LocalScope localScope = scopes[i];
while (instance.Count > 0)
{
LocalScope localScope2 = instance.Last();
if (localScope.StartOffset < localScope2.StartOffset + localScope2.Length)
{
break;
}
instance.RemoveLast();
_symWriter.CloseScope(generateVisualBasicStylePdb ? (localScope2.EndOffset - 1) : localScope2.EndOffset);
}
instance.Add(localScope);
_symWriter.OpenScope(localScope.StartOffset);
DefineScopeLocals(localScope, localSignatureHandleOpt);
}
for (int num = instance.Count - 1; num >= 0; num--)
{
LocalScope localScope3 = instance[num];
_symWriter.CloseScope(generateVisualBasicStylePdb ? (localScope3.EndOffset - 1) : localScope3.EndOffset);
}
instance.Free();
}
private void DefineScopeLocals(LocalScope currentScope, StandaloneSignatureHandle localSignatureHandleOpt)
{
ImmutableArray<ILocalDefinition>.Enumerator enumerator = currentScope.Constants.GetEnumerator();
while (enumerator.MoveNext())
{
ILocalDefinition current = enumerator.Current;
StandaloneSignatureHandle standaloneSignatureHandle = _metadataWriter.SerializeLocalConstantStandAloneSignature(current);
if (!_metadataWriter.IsLocalNameTooLong(current))
{
_symWriter.DefineLocalConstant(current.Name, current.CompileTimeValue.Value, MetadataTokens.GetToken(standaloneSignatureHandle));
}
}
enumerator = currentScope.Variables.GetEnumerator();
while (enumerator.MoveNext())
{
ILocalDefinition current2 = enumerator.Current;
if (!_metadataWriter.IsLocalNameTooLong(current2))
{
_symWriter.DefineLocalVariable(current2.SlotIndex, current2.Name, (int)current2.PdbAttributes, (!localSignatureHandleOpt.IsNil) ? MetadataTokens.GetToken(localSignatureHandleOpt) : 0);
}
}
}
public void SetMetadataEmitter(MetadataWriter metadataWriter)
{
SymUnmanagedWriterCreationOptions options = (SymUnmanagedWriterCreationOptions)((IsDeterministic ? 8 : 4) | 2);
SymWriterMetadataProvider symWriterMetadataProvider = new SymWriterMetadataProvider(metadataWriter);
SymUnmanagedWriter symUnmanagedWriter;
try
{
symUnmanagedWriter = ((_symWriterFactory != null) ? _symWriterFactory(symWriterMetadataProvider) : SymUnmanagedWriterFactory.CreateWriter(symWriterMetadataProvider, options));
}
catch (DllNotFoundException ex)
{
throw new SymUnmanagedWriterException(ex.Message);
}
catch (SymUnmanagedWriterException ex2) when (ex2.InnerException is NotSupportedException)
{
throw new SymUnmanagedWriterException(string.Format(IsDeterministic ? CodeAnalysisResources.SymWriterNotDeterministic : CodeAnalysisResources.SymWriterOlderVersionThanRequired, ex2.ImplementationModuleName));
}
_metadataWriter = metadataWriter;
_symWriter = symUnmanagedWriter;
_sequencePointsWriter = new SymUnmanagedSequencePointsWriter(symUnmanagedWriter);
}
public BlobContentId GetContentId()
{
BlobContentId result;
if (IsDeterministic)
{
result = BlobContentId.FromHash(CryptographicHashProvider.ComputeHash(_hashAlgorithmNameOpt, _symWriter.GetUnderlyingData()));
_symWriter.UpdateSignature(result.Guid, result.Stamp, 1);
}
else
{
_symWriter.GetSignature(out var guid, out var stamp, out var _);
result = new BlobContentId(guid, stamp);
}
_symWriter.Dispose();
return result;
}
public void SetEntryPoint(int entryMethodToken)
{
_symWriter.SetEntryPoint(entryMethodToken);
}
private int GetDocumentIndex(DebugSourceDocument document)
{
if (_documentIndex.TryGetValue(document, out var value))
{
return value;
}
return AddDocumentIndex(document);
}
private int AddDocumentIndex(DebugSourceDocument document)
{
DebugSourceInfo sourceInfo = document.GetSourceInfo();
Guid algorithmId;
ReadOnlySpan<byte> checksum;
if (!sourceInfo.Checksum.IsDefault)
{
algorithmId = sourceInfo.ChecksumAlgorithmId;
checksum = sourceInfo.Checksum.AsSpan();
}
else
{
algorithmId = default(Guid);
checksum = null;
}
ReadOnlySpan<byte> source = (sourceInfo.EmbeddedTextBlob.IsDefault ? ((ReadOnlySpan<byte>)null) : sourceInfo.EmbeddedTextBlob.AsSpan());
int num = _symWriter.DefineDocument(document.Location, document.Language, document.LanguageVendor, document.DocumentType, algorithmId, checksum, source);
_documentIndex.Add(document, num);
return num;
}
private void OpenMethod(int methodToken)
{
_symWriter.OpenMethod(methodToken);
_symWriter.OpenScope(0);
}
private void CloseMethod(int ilLength)
{
_symWriter.CloseScope(ilLength);
_symWriter.CloseMethod();
}
private void UsingNamespace(string fullName, INamedEntity errorEntity)
{
if (!_metadataWriter.IsUsingStringTooLong(fullName, errorEntity))
{
_symWriter.UsingNamespace(fullName);
}
}
private void EmitSequencePoints(ImmutableArray<SequencePoint> sequencePoints)
{
int num = -1;
DebugSourceDocument debugSourceDocument = null;
ImmutableArray<SequencePoint>.Enumerator enumerator = sequencePoints.GetEnumerator();
while (enumerator.MoveNext())
{
SequencePoint current = enumerator.Current;
DebugSourceDocument document = current.Document;
int documentIndex;
if (debugSourceDocument == document)
{
documentIndex = num;
}
else
{
debugSourceDocument = document;
documentIndex = (num = GetDocumentIndex(debugSourceDocument));
}
_sequencePointsWriter.Add(documentIndex, current.Offset, current.StartLine, current.StartColumn, current.EndLine, current.EndColumn);
}
_sequencePointsWriter.Flush();
}
[Conditional("DEBUG")]
public void AssertAllDefinitionsHaveTokens(MultiDictionary<DebugSourceDocument, DefinitionWithLocation> file2definitions)
{
foreach (KeyValuePair<DebugSourceDocument, MultiDictionary<DebugSourceDocument, DefinitionWithLocation>.ValueSet> file2definition in file2definitions)
{
foreach (DefinitionWithLocation item in file2definition.Value)
{
_metadataWriter.GetDefinitionHandle(item.Definition);
}
}
}
public void WriteDefinitionLocations(MultiDictionary<DebugSourceDocument, DefinitionWithLocation> file2definitions)
{
bool flag = false;
foreach (KeyValuePair<DebugSourceDocument, MultiDictionary<DebugSourceDocument, DefinitionWithLocation>.ValueSet> file2definition in file2definitions)
{
foreach (DefinitionWithLocation item in file2definition.Value)
{
if (!flag)
{
_symWriter.OpenTokensToSourceSpansMap();
flag = true;
}
int token = MetadataTokens.GetToken(_metadataWriter.GetDefinitionHandle(item.Definition));
_symWriter.MapTokenToSourceSpan(token, GetDocumentIndex(file2definition.Key), item.StartLine + 1, item.StartColumn + 1, item.EndLine + 1, item.EndColumn + 1);
}
}
if (flag)
{
_symWriter.CloseTokensToSourceSpansMap();
}
}
public void EmbedSourceLink(Stream stream)
{
byte[] sourceLinkData;
try
{
sourceLinkData = stream.ReadAllBytes();
}
catch (Exception ex)
{
throw new SymUnmanagedWriterException(ex.Message, ex);
}
try
{
_symWriter.SetSourceLinkData(sourceLinkData);
}
catch (SymUnmanagedWriterException ex2) when (ex2.InnerException is NotSupportedException)
{
throw new SymUnmanagedWriterException(string.Format(CodeAnalysisResources.SymWriterDoesNotSupportSourceLink, ex2.ImplementationModuleName));
}
}
public void WriteRemainingDebugDocuments(IReadOnlyDictionary<string, DebugSourceDocument> documents)
{
foreach (KeyValuePair<string, DebugSourceDocument> item in from kvp in documents
where !_documentIndex.ContainsKey(kvp.Value)
orderby kvp.Key
select kvp)
{
AddDocumentIndex(item.Value);
}
}
public void WriteCompilerVersion(string language)
{
Assembly assembly = typeof(Compilation).Assembly;
Version version = Version.Parse(assembly.GetCustomAttribute<AssemblyFileVersionAttribute>().Version);
string informationalVersion = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
_symWriter.AddCompilerInfo((ushort)version.Major, (ushort)version.Minor, (ushort)version.Build, (ushort)version.Revision, language + " - " + informationalVersion);
}
}