Ejemplo n.º 1
0
        private static void SerializeDynamicLocalInfo(ref CustomDebugInfoEncoder encoder, IMethodBody methodBody)
        {
            if (!methodBody.HasDynamicLocalVariables)
            {
                return;
            }

            byte[] GetDynamicFlags(ILocalDefinition local)
            {
                var dynamicTransformFlags = local.DynamicTransformFlags;
                var flags = new byte[CustomDebugInfoEncoder.DynamicAttributeSize];

                for (int k = 0; k < dynamicTransformFlags.Length; k++)
                {
                    if (dynamicTransformFlags[k])
                    {
                        flags[k] = 1;
                    }
                }

                return(flags);
            }

            var dynamicLocals = GetLocalInfoToSerialize(
                methodBody,
                local =>
            {
                var dynamicTransformFlags = local.DynamicTransformFlags;
                return(!dynamicTransformFlags.IsEmpty &&
                       dynamicTransformFlags.Length <= CustomDebugInfoEncoder.DynamicAttributeSize &&
                       local.Name.Length < CustomDebugInfoEncoder.IdentifierSize);
            },
                (scope, local) => (local.Name, GetDynamicFlags(local), local.DynamicTransformFlags.Length, (local.SlotIndex < 0) ? 0 : local.SlotIndex));

            if (dynamicLocals == null)
            {
                return;
            }

            encoder.AddDynamicLocals(dynamicLocals);
            dynamicLocals.Free();
        }
        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);
        }
        public static void Convert(Stream peStream, Stream sourcePdbStream, Stream targetPdbStream)
        {
            using (var peReader = new PEReader(peStream))
                using (var pdbReaderProvider = MetadataReaderProvider.FromPortablePdbStream(sourcePdbStream))
                    using (var pdbWriter = new PdbWriter(peReader.GetMetadataReader()))
                    {
                        var metadataReader = peReader.GetMetadataReader();
                        var metadataModel  = new MetadataModel(metadataReader);

                        var pdbReader = pdbReaderProvider.GetMetadataReader();

                        var documentWriters         = new ArrayBuilder <ISymUnmanagedDocumentWriter>(pdbReader.Documents.Count);
                        var symSequencePointBuilder = new SequencePointsBuilder(capacity: 64);
                        var declaredExternAliases   = new HashSet <string>();
                        var importStringsBuilder    = new List <string>();
                        var importCountsPerScope    = new List <int>();
                        var cdiBuilder    = new BlobBuilder();
                        var dynamicLocals = new List <(string LocalName, byte[] Flags, int Count, int SlotIndex)>();

                        // state for calculating import string forwarding:
                        var lastImportScopeHandle          = default(ImportScopeHandle);
                        var lastImportScopeMethodDefHandle = default(MethodDefinitionHandle);
                        var importStringsMap = new Dictionary <ImmutableArray <string>, MethodDefinitionHandle>();

                        var aliasedAssemblyRefs = GetAliasedAssemblyRefs(pdbReader);
                        var kickOffMethodToMoveNextMethodMap = GetStateMachineMethodMap(pdbReader);

                        string vbDefaultNamespace             = MetadataUtilities.GetVisualBasicDefaultNamespace(metadataReader);
                        bool   vbSemantics                    = vbDefaultNamespace != null;
                        string vbDefaultNamespaceImportString = vbSemantics ? "*" + vbDefaultNamespace : null;

                        foreach (var documentHandle in pdbReader.Documents)
                        {
                            var document     = pdbReader.GetDocument(documentHandle);
                            var languageGuid = pdbReader.GetGuid(document.Language);

                            documentWriters.Add(pdbWriter.DefineDocument(
                                                    name: pdbReader.GetString(document.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);

                        var firstMethodDefHandle = 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);

                            // methods without debug info:
                            if (methodDebugInfo.Document.IsNil && methodDebugInfo.SequencePointsBlob.IsNil)
                            {
                                continue;
                            }

                            // methods without method body don't currently have any debug information:
                            if (methodDef.RelativeVirtualAddress == 0)
                            {
                                continue;
                            }

                            var methodBody = peReader.GetMethodBody(methodDef.RelativeVirtualAddress);

                            pdbWriter.OpenMethod(methodToken);

                            var forwardImportScopesToMethodDef = default(MethodDefinitionHandle);
                            Debug.Assert(dynamicLocals.Count == 0);

                            bool isFirstMethodScope = true;
                            while (currentLocalScope.Method == methodDefHandle)
                            {
                                if (isFirstMethodScope)
                                {
                                    if (lastImportScopeHandle == currentLocalScope.ImportScope)
                                    {
                                        // forward to a method that has the same imports:
                                        forwardImportScopesToMethodDef = lastImportScopeMethodDefHandle;
                                    }
                                    else
                                    {
                                        Debug.Assert(importStringsBuilder.Count == 0);
                                        Debug.Assert(declaredExternAliases.Count == 0);
                                        Debug.Assert(importCountsPerScope.Count == 0);

                                        AddImportStrings(importStringsBuilder, importCountsPerScope, declaredExternAliases, pdbReader, metadataModel, currentLocalScope.ImportScope, aliasedAssemblyRefs, vbDefaultNamespaceImportString);
                                        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;
                                        }

                                        lastImportScopeHandle = currentLocalScope.ImportScope;
                                    }

                                    if (vbSemantics && !forwardImportScopesToMethodDef.IsNil)
                                    {
                                        pdbWriter.UsingNamespace("@" + MetadataTokens.GetToken(forwardImportScopesToMethodDef));
                                    }
                                }
                                else
                                {
                                    pdbWriter.OpenScope(currentLocalScope.StartOffset);
                                }

                                foreach (var localConstantHandle in currentLocalScope.GetLocalConstants())
                                {
                                    var    constant = pdbReader.GetLocalConstant(localConstantHandle);
                                    string name     = pdbReader.GetString(constant.Name);

                                    if (name.Length > MaxEntityNameLength)
                                    {
                                        // TODO: report warning
                                        continue;
                                    }

                                    var(value, signature) = PortableConstantSignature.GetConstantValueAndSignature(pdbReader, localConstantHandle, pdbWriter.MetadataImport);
                                    if (!metadataModel.TryGetStandaloneSignatureHandle(signature, out var constantSignatureHandle))
                                    {
                                        // TODO: report warning

                                        // TODO:
                                        // Currently the EEs require signature to match exactly the type of the value.
                                        // We could relax that and use the type of the value regardless of the signature for primitive types.
                                        // Then we could use any signature here.
                                        continue;
                                    }

                                    pdbWriter.DefineLocalConstant(name, value, MetadataTokens.GetToken(constantSignatureHandle));

                                    var dynamicFlags = MetadataUtilities.ReadDynamicCustomDebugInformation(pdbReader, localConstantHandle);
                                    if (TryGetDynamicLocal(name, 0, dynamicFlags, out var dynamicLocal))
                                    {
                                        dynamicLocals.Add(dynamicLocal);
                                    }
                                }

                                foreach (var localVariableHandle in currentLocalScope.GetLocalVariables())
                                {
                                    var    variable = pdbReader.GetLocalVariable(localVariableHandle);
                                    string name     = pdbReader.GetString(variable.Name);

                                    if (name.Length > MaxEntityNameLength)
                                    {
                                        // TODO: report warning
                                        continue;
                                    }

                                    int localSignatureToken = methodBody.LocalSignature.IsNil ? 0 : MetadataTokens.GetToken(methodBody.LocalSignature);
                                    pdbWriter.DefineLocalVariable(variable.Index, name, variable.Attributes, localSignatureToken);

                                    var dynamicFlags = MetadataUtilities.ReadDynamicCustomDebugInformation(pdbReader, localVariableHandle);
                                    if (TryGetDynamicLocal(name, variable.Index, dynamicFlags, out var dynamicLocal))
                                    {
                                        dynamicLocals.Add(dynamicLocal);
                                    }
                                }

                                if (!isFirstMethodScope)
                                {
                                    pdbWriter.CloseScope(currentLocalScope.EndOffset - (vbSemantics ? 1 : 0));
                                }

                                currentLocalScope  = NextLocalScope();
                                isFirstMethodScope = false;
                            }

                            WriteSequencePoints(pdbWriter, documentWriters, symSequencePointBuilder, methodDebugInfo.GetSequencePoints());

                            // async method data:
                            var asyncData = MetadataUtilities.ReadAsyncMethodData(pdbReader, methodDebugInfoHandle);
                            if (!asyncData.IsNone)
                            {
                                pdbWriter.SetAsyncInfo(
                                    moveNextMethodToken: methodToken,
                                    kickoffMethodToken: MetadataTokens.GetToken(asyncData.KickoffMethod),
                                    catchHandlerOffset: asyncData.CatchHandlerOffset,
                                    yieldOffsets: asyncData.YieldOffsets,
                                    resumeOffsets: asyncData.ResumeOffsets);
                            }

                            // custom debug information:
                            var cdiEncoder = new CustomDebugInfoEncoder(cdiBuilder);
                            if (kickOffMethodToMoveNextMethodMap.TryGetValue(methodDefHandle, out var moveNextHandle))
                            {
                                cdiEncoder.AddReferenceToIteratorClass(GetIteratorTypeName(metadataReader, moveNextHandle));
                            }
                            else
                            {
                                if (!vbSemantics)
                                {
                                    if (forwardImportScopesToMethodDef.IsNil)
                                    {
                                        // record the number of import strings in each scope:
                                        cdiEncoder.AddUsingInfo(importCountsPerScope);

                                        if (!firstMethodDefHandle.IsNil)
                                        {
                                            // forward assembly ref aliases to the first method:
                                            cdiEncoder.AddReferenceToMethodWithModuleInfo(firstMethodDefHandle);
                                        }
                                    }
                                    else
                                    {
                                        // forward all imports to another method:
                                        cdiEncoder.AddReferenceToPreviousMethodWithUsingInfo(forwardImportScopesToMethodDef);
                                    }
                                }

                                var hoistedLocalScopes = GetStateMachineHoistedLocalScopes(pdbReader, methodDefHandle);
                                if (!hoistedLocalScopes.IsDefault)
                                {
                                    cdiEncoder.AddStateMachineLocalScopes(hoistedLocalScopes);
                                }
                            }

                            if (dynamicLocals.Count > 0)
                            {
                                cdiEncoder.AddDynamicLocals(dynamicLocals);
                                dynamicLocals.Clear();
                            }

                            // the following blobs map 1:1
                            CopyCustomDebugInfoRecord(ref cdiEncoder, pdbReader, methodDefHandle, PortableCustomDebugInfoKinds.TupleElementNames, CustomDebugInfoKind.TupleElementNames);
                            CopyCustomDebugInfoRecord(ref cdiEncoder, pdbReader, methodDefHandle, PortableCustomDebugInfoKinds.EncLocalSlotMap, CustomDebugInfoKind.EditAndContinueLocalSlotMap);
                            CopyCustomDebugInfoRecord(ref cdiEncoder, pdbReader, methodDefHandle, PortableCustomDebugInfoKinds.EncLambdaAndClosureMap, CustomDebugInfoKind.EditAndContinueLambdaMap);

                            cdiBuilder.Clear();

                            if (firstMethodDefHandle.IsNil)
                            {
                                firstMethodDefHandle = methodDefHandle;

                                foreach (var(assemblyRefHandle, alias) in aliasedAssemblyRefs)
                                {
                                    var assemblyRef = metadataReader.GetAssemblyReference(assemblyRefHandle);
                                    pdbWriter.UsingNamespace("Z" + alias + " " + MetadataHelpers.GetAssemblyDisplayName(metadataReader, assemblyRef));
                                }
                            }

                            pdbWriter.CloseMethod(methodBody.GetILReader().Length);
                        }

                        pdbWriter.WriteTo(targetPdbStream);
                    }
        }