예제 #1
0
        public byte[] SerializeMethodDebugInfo(EmitContext context, IMethodBody methodBody, MethodDefinitionHandle methodHandle, bool emitEncInfo, bool suppressNewCustomDebugInfo, out bool emitExternNamespaces)
        {
            emitExternNamespaces = false;

            // CONSIDER: this may not be the same "first" method as in Dev10, but
            // it shouldn't matter since all methods will still forward to a method
            // containing the appropriate information.
            if (_methodBodyWithModuleInfo == null)
            {
                // This module level information could go on every method (and does in
                // the edit-and-continue case), but - as an optimization - we'll just
                // put it on the first method we happen to encounter and then put a
                // reference to the first method's token in every other method (so they
                // can find the information).
                if (context.Module.GetAssemblyReferenceAliases(context).Any())
                {
                    _methodWithModuleInfo     = methodHandle;
                    _methodBodyWithModuleInfo = methodBody;
                    emitExternNamespaces      = true;
                }
            }

            var pooledBuilder = PooledBlobBuilder.GetInstance();
            var encoder       = new CustomDebugInfoEncoder(pooledBuilder);

            // NOTE: This is an attempt to match Dev10's apparent behavior.  For iterator methods (i.e. the method
            // that appears in source, not the synthesized ones), Dev10 only emits the StateMachineTypeName
            // custom debug info (e.g. there will be no information about the usings that were in scope).
            // NOTE: There seems to be an unusual behavior in ISymUnmanagedWriter where, if all the methods in a type are
            // iterator methods, no custom debug info is emitted for any method.  Adding a single non-iterator
            // method causes the custom debug info to be produced for all methods (including the iterator methods).
            // Since we are making the same ISymUnmanagedWriter calls as Dev10, we see the same behavior (i.e. this
            // is not a regression).
            if (methodBody.StateMachineTypeName != null)
            {
                encoder.AddStateMachineTypeName(methodBody.StateMachineTypeName);
            }
            else
            {
                SerializeNamespaceScopeMetadata(ref encoder, context, methodBody);

                encoder.AddStateMachineHoistedLocalScopes(methodBody.StateMachineHoistedLocalScopes);
            }

            if (!suppressNewCustomDebugInfo)
            {
                SerializeDynamicLocalInfo(ref encoder, methodBody);
                SerializeTupleElementNames(ref encoder, methodBody);

                if (emitEncInfo)
                {
                    var encMethodInfo = MetadataWriter.GetEncMethodDebugInfo(methodBody);
                    SerializeCustomDebugInformation(ref encoder, encMethodInfo);
                }
            }

            byte[] result = encoder.ToArray();
            pooledBuilder.Free();
            return(result);
        }
예제 #2
0
        public void ForwardInfo()
        {
            var builder    = new BlobBuilder();
            var cdiEncoder = new CustomDebugInfoEncoder(builder);

            cdiEncoder.AddForwardMethodInfo(MetadataTokens.MethodDefinitionHandle(0x123456));
            var cdi = cdiEncoder.ToArray();

            Assert.Equal(1, cdiEncoder.RecordCount);

            AssertEx.Equal(new byte[]
            {
                0x04,       // version
                0x01,       // record count
                0x00, 0x00, // alignment

                0x04,       // version
                0x01,       // record kind
                0x00,
                0x00,

                // aligned record size
                0x0C, 0x00, 0x00, 0x00,

                // payload (4B aligned)
                0x56, 0x34, 0x12, 0x06,
            }, cdi);
        }
예제 #3
0
        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);
            }
        }
        internal static void SerializeCustomDebugInformation(ref CustomDebugInfoEncoder encoder, EditAndContinueMethodDebugInformation debugInfo)
        {
            // PERF: note that we pass debugInfo as explicit parameter
            //       that is intentional to avoid capturing debugInfo as that
            //       would result in a lot of delegate allocations here that are otherwise can be avoided.
            if (!debugInfo.LocalSlots.IsDefaultOrEmpty)
            {
                encoder.AddRecord(
                    CustomDebugInfoKind.EditAndContinueLocalSlotMap,
                    debugInfo,
                    (info, builder) => info.SerializeLocalSlots(builder));
            }

            if (!debugInfo.Lambdas.IsDefaultOrEmpty)
            {
                encoder.AddRecord(
                    CustomDebugInfoKind.EditAndContinueLambdaMap,
                    debugInfo,
                    (info, builder) => info.SerializeLambdaMap(builder));
            }

            if (!debugInfo.StateMachineStates.IsDefaultOrEmpty)
            {
                encoder.AddRecord(
                    CustomDebugInfoKind.EditAndContinueStateMachineStateMap,
                    debugInfo,
                    (info, builder) => info.SerializeStateMachineStates(builder));
            }
        }
예제 #5
0
        private static void SerializeTupleElementNames(
            ref CustomDebugInfoEncoder encoder,
            IMethodBody methodBody
            )
        {
            var locals = GetLocalInfoToSerialize(
                methodBody,
                local => !local.TupleElementNames.IsEmpty,
                (scope, local) =>
                (
                    local.Name,
                    local.SlotIndex,
                    scope.StartOffset,
                    scope.EndOffset,
                    local.TupleElementNames
                )
                );

            if (locals == null)
            {
                return;
            }

            encoder.AddTupleElementNames(locals);

            locals.Free();
        }
예제 #6
0
        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);
        }
예제 #7
0
        public void DynamicLocals()
        {
            var builder    = new BlobBuilder();
            var cdiEncoder = new CustomDebugInfoEncoder(builder);

            cdiEncoder.AddDynamicLocals(
                new[]
예제 #8
0
        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);
        }
예제 #9
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();
        }
예제 #10
0
        public byte[] SerializeMethodDebugInfo(EmitContext context, IMethodBody methodBody, MethodDefinitionHandle methodHandle, bool emitEncInfo, bool suppressNewCustomDebugInfo, out bool emitExternNamespaces)
        {
            emitExternNamespaces = false;

            // CONSIDER: this may not be the same "first" method as in Dev10, but
            // it shouldn't matter since all methods will still forward to a method
            // containing the appropriate information.
            if (_methodBodyWithModuleInfo == null)
            {
                // This module level information could go on every method (and does in
                // the edit-and-continue case), but - as an optimization - we'll just
                // put it on the first method we happen to encounter and then put a
                // reference to the first method's token in every other method (so they
                // can find the information).
                if (context.Module.GetAssemblyReferenceAliases(context).Any())
                {
                    _methodWithModuleInfo     = methodHandle;
                    _methodBodyWithModuleInfo = methodBody;
                    emitExternNamespaces      = true;
                }
            }

            var pooledBuilder = PooledBlobBuilder.GetInstance();
            var encoder       = new CustomDebugInfoEncoder(pooledBuilder);

            if (methodBody.StateMachineTypeName != null)
            {
                encoder.AddStateMachineTypeName(methodBody.StateMachineTypeName);
            }
            else
            {
                SerializeNamespaceScopeMetadata(ref encoder, context, methodBody);

                encoder.AddStateMachineHoistedLocalScopes(methodBody.StateMachineHoistedLocalScopes);
            }

            if (!suppressNewCustomDebugInfo)
            {
                SerializeDynamicLocalInfo(ref encoder, methodBody);
                SerializeTupleElementNames(ref encoder, methodBody);

                if (emitEncInfo)
                {
                    var encMethodInfo = MetadataWriter.GetEncMethodDebugInfo(methodBody);
                    SerializeCustomDebugInformation(ref encoder, encMethodInfo);
                }
            }

            byte[] result = encoder.ToArray();
            pooledBuilder.Free();
            return(result);
        }
예제 #11
0
        private static void SerializeTupleElementNames(ref CustomDebugInfoEncoder encoder, IMethodBody methodBody)
        {
            var locals = GetLocalInfoToSerialize(
                methodBody,
                local => !local.TupleElementNames.IsEmpty,
                (scope, local) => (local, scope));

            if (locals == null)
            {
                return;
            }

            encoder.AddRecord(CustomDebugInfoKind.TupleElementNames, locals, SerializeTupleElementNames);
            locals.Free();
        }
예제 #12
0
        public void EncCdiAlignment()
        {
            var slots = ImmutableArray.Create(
                new LocalSlotDebugInfo(SynthesizedLocalKind.UserDefined, new LocalDebugId(-1, 10)),
                new LocalSlotDebugInfo(SynthesizedLocalKind.TryAwaitPendingCaughtException, new LocalDebugId(-20000, 10)));

            var closures = ImmutableArray.Create(
                new ClosureDebugInfo(-100, new DebugId(0, 0)),
                new ClosureDebugInfo(10, new DebugId(1, 0)),
                new ClosureDebugInfo(-200, new DebugId(2, 0)));

            var lambdas = ImmutableArray.Create(
                new LambdaDebugInfo(20, new DebugId(0, 0), 1),
                new LambdaDebugInfo(-50, new DebugId(1, 0), 0),
                new LambdaDebugInfo(-180, new DebugId(2, 0), LambdaDebugInfo.StaticClosureOrdinal));

            var debugInfo = new EditAndContinueMethodDebugInformation(1, slots, closures, lambdas);

            var builder    = new BlobBuilder();
            var cdiEncoder = new CustomDebugInfoEncoder(builder);

            Cci.CustomDebugInfoWriter.SerializeCustomDebugInformation(ref cdiEncoder, debugInfo);
            var cdi = cdiEncoder.ToArray();

            Assert.Equal(2, cdiEncoder.RecordCount);

            AssertEx.Equal(new byte[]
            {
                0x04,       // version
                0x02,       // record count
                0x00, 0x00, // alignment

                0x04,       // version
                0x06,       // record kind
                0x00,
                0x02,       // alignment size

                // aligned record size
                0x18, 0x00, 0x00, 0x00,

                // payload (4B aligned)
                0xFF, 0xC0, 0x00, 0x4E,
                0x20, 0x81, 0xC0, 0x00,
                0x4E, 0x1F, 0x0A, 0x9A,
                0x00, 0x0A, 0x00, 0x00,

                0x04, // version
                0x07, // record kind
                0x00,
                0x00, // alignment size

                // aligned record size
                0x18, 0x00, 0x00, 0x00,

                // payload (4B aligned)
                0x02, 0x80, 0xC8, 0x03,
                0x64, 0x80, 0xD2, 0x00,
                0x80, 0xDC, 0x03, 0x80,
                0x96, 0x02, 0x14, 0x01
            }, cdi);

            var deserialized = CustomDebugInfoReader.GetCustomDebugInfoRecords(cdi).ToArray();

            Assert.Equal(CustomDebugInfoKind.EditAndContinueLocalSlotMap, deserialized[0].Kind);
            Assert.Equal(4, deserialized[0].Version);

            Assert.Equal(new byte[]
            {
                0xFF, 0xC0, 0x00, 0x4E,
                0x20, 0x81, 0xC0, 0x00,
                0x4E, 0x1F, 0x0A, 0x9A,
                0x00, 0x0A
            }, deserialized[0].Data);

            Assert.Equal(CustomDebugInfoKind.EditAndContinueLambdaMap, deserialized[1].Kind);
            Assert.Equal(4, deserialized[1].Version);

            Assert.Equal(new byte[]
            {
                0x02, 0x80, 0xC8, 0x03,
                0x64, 0x80, 0xD2, 0x00,
                0x80, 0xDC, 0x03, 0x80,
                0x96, 0x02, 0x14, 0x01
            }, deserialized[1].Data);
        }
        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);
                    }
        }