private void SerializeNamespaceScopeMetadata(ref CustomDebugInfoEncoder encoder, EmitContext context, IMethodBody methodBody) { if (context.Module.GenerateVisualBasicStylePdb) { return; } if (ShouldForwardToPreviousMethodWithUsingInfo(context, methodBody)) { Debug.Assert(!ReferenceEquals(_previousMethodBodyWithUsingInfo, methodBody)); encoder.AddForwardMethodInfo(_previousMethodWithUsingInfo); return; } var usingCounts = ArrayBuilder <int> .GetInstance(); for (IImportScope scope = methodBody.ImportScope; scope != null; scope = scope.Parent) { usingCounts.Add(scope.GetUsedNamespaces().Length); } encoder.AddUsingGroups(usingCounts); usingCounts.Free(); if (_methodBodyWithModuleInfo != null && !ReferenceEquals(_methodBodyWithModuleInfo, methodBody)) { encoder.AddForwardModuleInfo(_methodWithModuleInfo); } }
public void UsingInfo3() { var builder = new BlobBuilder(); var cdiEncoder = new CustomDebugInfoEncoder(builder); cdiEncoder.AddUsingGroups(new[] { 1, 2, 3 }); var cdi = cdiEncoder.ToArray(); Assert.Equal(1, cdiEncoder.RecordCount); AssertEx.Equal(new byte[] { 0x04, // version 0x01, // record count 0x00, 0x00, // alignment 0x04, // version 0x00, // record kind 0x00, 0x00, // aligned record size 0x10, 0x00, 0x00, 0x00, // payload (4B aligned) 0x03, 0x00, // bucket count 0x01, 0x00, // using count #1 0x02, 0x00, // using count #2 0x03, 0x00, // using count #3 }, cdi); }
public void UsingInfo1() { var builder = new BlobBuilder(); var cdiEncoder = new CustomDebugInfoEncoder(builder); cdiEncoder.AddUsingGroups(new int[0]); var cdi = cdiEncoder.ToArray(); Assert.Equal(0, cdiEncoder.RecordCount); Assert.Null(cdi); }
internal void Convert(PEReader peReader, MetadataReader pdbReader, PdbWriter <TDocumentWriter> pdbWriter, PdbConversionOptions options) { if (!SymReaderHelpers.TryReadPdbId(peReader, out var pePdbId, out int peAge)) { throw new InvalidDataException(ConverterResources.SpecifiedPEFileHasNoAssociatedPdb); } if (!new BlobContentId(pdbReader.DebugMetadataHeader.Id).Equals(pePdbId)) { throw new InvalidDataException(ConverterResources.PdbNotMatchingDebugDirectory); } string vbDefaultNamespace = MetadataUtilities.GetVisualBasicDefaultNamespace(pdbReader); bool vbSemantics = vbDefaultNamespace != null; string vbDefaultNamespaceImportString = string.IsNullOrEmpty(vbDefaultNamespace) ? null : "*" + vbDefaultNamespace; var metadataReader = peReader.GetMetadataReader(); var metadataModel = new MetadataModel(metadataReader, vbSemantics); var documentWriters = new ArrayBuilder <TDocumentWriter>(pdbReader.Documents.Count); var documentNames = new ArrayBuilder <string>(pdbReader.Documents.Count); var symSequencePointBuilder = new SequencePointsBuilder(capacity: 64); var declaredExternAliases = new HashSet <string>(); var importStringsBuilder = new List <string>(); var importGroups = new List <int>(); var cdiBuilder = new BlobBuilder(); var dynamicLocals = new List <(string LocalName, byte[] Flags, int Count, int SlotIndex)>(); var tupleLocals = new List <(string LocalName, int SlotIndex, int ScopeStart, int ScopeEnd, ImmutableArray <string> Names)>(); var openScopeEndOffsets = new Stack <int>(); // state for calculating import string forwarding: var lastImportScopeHandle = default(ImportScopeHandle); var vbLastImportScopeNamespace = default(string); var lastImportScopeMethodDefHandle = default(MethodDefinitionHandle); var importStringsMap = new Dictionary <ImmutableArray <string>, MethodDefinitionHandle>(SequenceComparer <string> .Instance); var aliasedAssemblyRefs = GetAliasedAssemblyRefs(pdbReader); foreach (var documentHandle in pdbReader.Documents) { var document = pdbReader.GetDocument(documentHandle); var languageGuid = pdbReader.GetGuid(document.Language); var name = pdbReader.GetString(document.Name); documentNames.Add(name); documentWriters.Add(pdbWriter.DefineDocument( name: name, language: languageGuid, type: s_documentTypeText, vendor: GetLanguageVendorGuid(languageGuid), algorithmId: pdbReader.GetGuid(document.HashAlgorithm), checksum: pdbReader.GetBlobBytes(document.Hash))); } var localScopeEnumerator = pdbReader.LocalScopes.GetEnumerator(); LocalScope?currentLocalScope = NextLocalScope(); LocalScope?NextLocalScope() => localScopeEnumerator.MoveNext() ? pdbReader.GetLocalScope(localScopeEnumerator.Current) : default(LocalScope?); // Handle of the method that is gonna contain list of AssemblyRef aliases. // Other methods will forward to it. var methodDefHandleWithAssemblyRefAliases = default(MethodDefinitionHandle); foreach (var methodDebugInfoHandle in pdbReader.MethodDebugInformation) { var methodDebugInfo = pdbReader.GetMethodDebugInformation(methodDebugInfoHandle); var methodDefHandle = methodDebugInfoHandle.ToDefinitionHandle(); int methodToken = MetadataTokens.GetToken(methodDefHandle); var methodDef = metadataReader.GetMethodDefinition(methodDefHandle); #if DEBUG var declaringTypeDef = metadataReader.GetTypeDefinition(methodDef.GetDeclaringType()); var typeName = metadataReader.GetString(declaringTypeDef.Name); var methodName = metadataReader.GetString(methodDef.Name); #endif bool methodOpened = false; var methodBodyOpt = (methodDef.RelativeVirtualAddress != 0 && (methodDef.ImplAttributes & MethodImplAttributes.CodeTypeMask) == MethodImplAttributes.Managed) ? peReader.GetMethodBody(methodDef.RelativeVirtualAddress) : null; var vbCurrentMethodNamespace = vbSemantics ? GetMethodNamespace(metadataReader, methodDef) : null; var moveNextHandle = metadataModel.FindStateMachineMoveNextMethod(methodDefHandle, vbSemantics); bool isKickOffMethod = !moveNextHandle.IsNil; var forwardImportScopesToMethodDef = default(MethodDefinitionHandle); Debug.Assert(dynamicLocals.Count == 0); Debug.Assert(tupleLocals.Count == 0); Debug.Assert(openScopeEndOffsets.Count == 0); void LazyOpenMethod() { if (!methodOpened) { #if DEBUG Debug.WriteLine($"Open Method '{typeName}::{methodName}' {methodToken:X8}"); #endif pdbWriter.OpenMethod(methodToken); methodOpened = true; } } void CloseOpenScopes(int currentScopeStartOffset) { // close all open scopes that end before this scope starts: while (openScopeEndOffsets.Count > 0 && currentScopeStartOffset >= openScopeEndOffsets.Peek()) { int scopeEnd = openScopeEndOffsets.Pop(); Debug.WriteLine($"Close Scope [.., {scopeEnd})"); // Note that the root scope end is not end-inclusive in VB: pdbWriter.CloseScope(AdjustEndScopeOffset(scopeEnd, isEndInclusive: vbSemantics && openScopeEndOffsets.Count > 0)); } } bool isFirstMethodScope = true; while (currentLocalScope.HasValue && currentLocalScope.Value.Method == methodDefHandle) { // kickoff methods don't have any scopes emitted to Windows PDBs if (methodBodyOpt == null) { ReportDiagnostic(PdbDiagnosticId.MethodAssociatedWithLocalScopeHasNoBody, MetadataTokens.GetToken(localScopeEnumerator.Current)); } else if (!isKickOffMethod) { LazyOpenMethod(); var localScope = currentLocalScope.Value; CloseOpenScopes(localScope.StartOffset); Debug.WriteLine($"Open Scope [{localScope.StartOffset}, {localScope.EndOffset})"); pdbWriter.OpenScope(localScope.StartOffset); openScopeEndOffsets.Push(localScope.EndOffset); if (isFirstMethodScope) { if (lastImportScopeHandle == localScope.ImportScope && vbLastImportScopeNamespace == vbCurrentMethodNamespace) { // forward to a method that has the same imports: forwardImportScopesToMethodDef = lastImportScopeMethodDefHandle; } else { Debug.Assert(importStringsBuilder.Count == 0); Debug.Assert(declaredExternAliases.Count == 0); Debug.Assert(importGroups.Count == 0); AddImportStrings(importStringsBuilder, importGroups, declaredExternAliases, pdbReader, metadataModel, localScope.ImportScope, aliasedAssemblyRefs, vbDefaultNamespaceImportString, vbCurrentMethodNamespace, vbSemantics); var importStrings = importStringsBuilder.ToImmutableArray(); importStringsBuilder.Clear(); if (importStringsMap.TryGetValue(importStrings, out forwardImportScopesToMethodDef)) { // forward to a method that has the same imports: lastImportScopeMethodDefHandle = forwardImportScopesToMethodDef; } else { // attach import strings to the current method: WriteImports(pdbWriter, importStrings); lastImportScopeMethodDefHandle = methodDefHandle; importStringsMap[importStrings] = methodDefHandle; } lastImportScopeHandle = localScope.ImportScope; vbLastImportScopeNamespace = vbCurrentMethodNamespace; } if (vbSemantics && !forwardImportScopesToMethodDef.IsNil) { pdbWriter.UsingNamespace("@" + MetadataTokens.GetToken(forwardImportScopesToMethodDef)); } // This is the method that's gonna have AssemblyRef aliases attached: if (methodDefHandleWithAssemblyRefAliases.IsNil) { foreach (var(assemblyRefHandle, alias) in aliasedAssemblyRefs) { var assemblyRef = metadataReader.GetAssemblyReference(assemblyRefHandle); pdbWriter.UsingNamespace("Z" + alias + " " + AssemblyDisplayNameBuilder.GetAssemblyDisplayName(metadataReader, assemblyRef)); } } } foreach (var localVariableHandle in localScope.GetLocalVariables()) { var variable = pdbReader.GetLocalVariable(localVariableHandle); string name = pdbReader.GetString(variable.Name); if (name.Length > MaxEntityNameLength) { ReportDiagnostic(PdbDiagnosticId.LocalConstantNameTooLong, MetadataTokens.GetToken(localVariableHandle)); continue; } if (methodBodyOpt.LocalSignature.IsNil) { ReportDiagnostic(PdbDiagnosticId.MethodContainingLocalVariablesHasNoLocalSignature, methodToken); continue; } // TODO: translate hoisted variable scopes to dummy VB hoisted state machine locals (https://github.com/dotnet/roslyn/issues/8473) pdbWriter.DefineLocalVariable(variable.Index, name, variable.Attributes, MetadataTokens.GetToken(methodBodyOpt.LocalSignature)); var dynamicFlags = MetadataUtilities.ReadDynamicCustomDebugInformation(pdbReader, localVariableHandle); if (TryGetDynamicLocal(name, variable.Index, dynamicFlags, out var dynamicLocal)) { dynamicLocals.Add(dynamicLocal); } var tupleElementNames = MetadataUtilities.ReadTupleCustomDebugInformation(pdbReader, localVariableHandle); if (!tupleElementNames.IsDefaultOrEmpty) { tupleLocals.Add((name, SlotIndex: variable.Index, ScopeStart: 0, ScopeEnd: 0, Names: tupleElementNames)); } } foreach (var localConstantHandle in localScope.GetLocalConstants()) { var constant = pdbReader.GetLocalConstant(localConstantHandle); string name = pdbReader.GetString(constant.Name); if (name.Length > MaxEntityNameLength) { ReportDiagnostic(PdbDiagnosticId.LocalConstantNameTooLong, MetadataTokens.GetToken(localConstantHandle)); continue; } var(value, signature) = PortableConstantSignature.GetConstantValueAndSignature(pdbReader, localConstantHandle, metadataReader.GetQualifiedTypeName); if (!metadataModel.TryGetStandaloneSignatureHandle(signature, out var constantSignatureHandle)) { // Signature will be unspecified. At least we store the name and the value. constantSignatureHandle = default(StandaloneSignatureHandle); } pdbWriter.DefineLocalConstant(name, value, MetadataTokens.GetToken(constantSignatureHandle)); var dynamicFlags = MetadataUtilities.ReadDynamicCustomDebugInformation(pdbReader, localConstantHandle); if (TryGetDynamicLocal(name, 0, dynamicFlags, out var dynamicLocal)) { dynamicLocals.Add(dynamicLocal); } var tupleElementNames = MetadataUtilities.ReadTupleCustomDebugInformation(pdbReader, localConstantHandle); if (!tupleElementNames.IsDefaultOrEmpty) { // Note that the end offset of tuple locals is always end-exclusive, regardless of whether the PDB uses VB semantics or not. tupleLocals.Add((name, SlotIndex: -1, ScopeStart: localScope.StartOffset, ScopeEnd: localScope.EndOffset, Names: tupleElementNames)); } } } currentLocalScope = NextLocalScope(); isFirstMethodScope = false; } bool hasAnyScopes = !isFirstMethodScope; CloseOpenScopes(int.MaxValue); if (openScopeEndOffsets.Count > 0) { ReportDiagnostic(PdbDiagnosticId.LocalScopeRangesNestingIsInvalid, methodToken); openScopeEndOffsets.Clear(); } if (!methodDebugInfo.SequencePointsBlob.IsNil) { LazyOpenMethod(); WriteSequencePoints(pdbWriter, documentWriters, symSequencePointBuilder, methodDebugInfo.GetSequencePoints(), methodToken); } // async method data: var asyncData = MetadataUtilities.ReadAsyncMethodData(pdbReader, methodDebugInfoHandle); if (!asyncData.IsNone) { LazyOpenMethod(); pdbWriter.SetAsyncInfo( moveNextMethodToken: methodToken, kickoffMethodToken: MetadataTokens.GetToken(asyncData.KickoffMethod), catchHandlerOffset: asyncData.CatchHandlerOffset, yieldOffsets: asyncData.YieldOffsets.ToArray(), resumeOffsets: asyncData.ResumeOffsets.ToArray()); } // custom debug information: var cdiEncoder = new CustomDebugInfoEncoder(cdiBuilder); if (isKickOffMethod) { cdiEncoder.AddStateMachineTypeName(GetIteratorTypeName(metadataReader, moveNextHandle)); } else { if (!vbSemantics && hasAnyScopes) { if (forwardImportScopesToMethodDef.IsNil) { // record the number of import strings in each scope: cdiEncoder.AddUsingGroups(importGroups); if (!methodDefHandleWithAssemblyRefAliases.IsNil) { // forward assembly ref aliases to the first method: cdiEncoder.AddForwardModuleInfo(methodDefHandleWithAssemblyRefAliases); } } else { // forward all imports to another method: cdiEncoder.AddForwardMethodInfo(forwardImportScopesToMethodDef); } } var hoistedLocalScopes = GetStateMachineHoistedLocalScopes(pdbReader, methodDefHandle); if (!hoistedLocalScopes.IsDefault) { cdiEncoder.AddStateMachineHoistedLocalScopes(hoistedLocalScopes); } if (dynamicLocals.Count > 0) { cdiEncoder.AddDynamicLocals(dynamicLocals); dynamicLocals.Clear(); } if (tupleLocals.Count > 0) { cdiEncoder.AddTupleElementNames(tupleLocals); tupleLocals.Clear(); } } importGroups.Clear(); // the following blobs map 1:1 CopyCustomDebugInfoRecord(ref cdiEncoder, pdbReader, methodDefHandle, PortableCustomDebugInfoKinds.EncLocalSlotMap, CustomDebugInfoKind.EditAndContinueLocalSlotMap); CopyCustomDebugInfoRecord(ref cdiEncoder, pdbReader, methodDefHandle, PortableCustomDebugInfoKinds.EncLambdaAndClosureMap, CustomDebugInfoKind.EditAndContinueLambdaMap); if (cdiEncoder.RecordCount > 0) { LazyOpenMethod(); pdbWriter.DefineCustomMetadata(cdiEncoder.ToArray()); } cdiBuilder.Clear(); if (methodOpened && aliasedAssemblyRefs.Length > 0 && !isKickOffMethod && methodDefHandleWithAssemblyRefAliases.IsNil) { methodDefHandleWithAssemblyRefAliases = methodDefHandle; } if (methodOpened) { Debug.WriteLine($"Close Method {methodToken:X8}"); pdbWriter.CloseMethod(); } } if (!pdbReader.DebugMetadataHeader.EntryPoint.IsNil) { pdbWriter.SetEntryPoint(MetadataTokens.GetToken(pdbReader.DebugMetadataHeader.EntryPoint)); } var sourceLinkHandle = pdbReader.GetCustomDebugInformation(EntityHandle.ModuleDefinition, PortableCustomDebugInfoKinds.SourceLink); if (!sourceLinkHandle.IsNil) { if ((options & PdbConversionOptions.SuppressSourceLinkConversion) == 0) { ConvertSourceServerData(pdbReader.GetStringUTF8(sourceLinkHandle), pdbWriter, documentNames); } else { pdbWriter.SetSourceLinkData(pdbReader.GetBlobBytes(sourceLinkHandle)); } } SymReaderHelpers.GetWindowsPdbSignature(pdbReader.DebugMetadataHeader.Id, out var guid, out var stamp, out var age); pdbWriter.UpdateSignature(guid, stamp, age); }