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 records = new ArrayBuilder<Cci.PooledBlobBuilder>();

            Cci.CustomDebugInfoWriter.SerializeCustomDebugInformation(debugInfo, records);
            var cdi = Cci.CustomDebugInfoWriter.SerializeCustomDebugMetadata(records);

            Assert.Equal(2, records.Count);

            AssertEx.Equal(new byte[]
            {
                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
            }, records[0].ToArray());

            AssertEx.Equal(new byte[]
            {
                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
            }, records[1].ToArray());

            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);
        }
Beispiel #2
0
 protected abstract ImmutableArray <EncLocalInfo> GetLocalSlotMapFromMetadata(StandaloneSignatureHandle handle, EditAndContinueMethodDebugInformation debugInfo);
        private static void SerializeCustomDebugInformation(EditAndContinueMethodDebugInformation debugInfo, ArrayBuilder<MemoryStream> customDebugInfo)
        {
            if (!debugInfo.LocalSlots.IsDefaultOrEmpty)
            {
                customDebugInfo.Add(SerializeRecord(CDI.CdiKindEditAndContinueLocalSlotMap, debugInfo.SerializeLocalSlots));
            }

            if (!debugInfo.Lambdas.IsDefaultOrEmpty)
            {
                customDebugInfo.Add(SerializeRecord(CDI.CdiKindEditAndContinueLambdaMap, debugInfo.SerializeLambdaMap));
            }
        }
Beispiel #4
0
        private static PooledBlobBuilder SerializeRecord(
            byte kind,
            EditAndContinueMethodDebugInformation debugInfo,
            Action<EditAndContinueMethodDebugInformation, BlobBuilder> recordSerializer)
        {
            var cmw = PooledBlobBuilder.GetInstance();
            cmw.WriteByte(CDI.CdiVersion);
            cmw.WriteByte(kind);
            cmw.WriteByte(0);

            // alignment size and length (will be patched)
            var alignmentSizeAndLengthWriter = cmw.ReserveBytes(sizeof(byte) + sizeof(uint));

            recordSerializer(debugInfo, cmw);

            int length = cmw.Position;
            int alignedLength = 4 * ((length + 3) / 4);
            byte alignmentSize = (byte)(alignedLength - length);
            cmw.WriteBytes(0, alignmentSize);

            // fill in alignment size and length:
            alignmentSizeAndLengthWriter.WriteByte(alignmentSize);
            alignmentSizeAndLengthWriter.WriteUInt32((uint)alignedLength);

            return cmw;
        }
Beispiel #5
0
        // internal for testing
        internal static void SerializeCustomDebugInformation(EditAndContinueMethodDebugInformation debugInfo, ArrayBuilder<PooledBlobBuilder> customDebugInfo)
        {
            // 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)
            {
                customDebugInfo.Add(
                    SerializeRecord(
                        CDI.CdiKindEditAndContinueLocalSlotMap, 
                        debugInfo,
                        (info, builder) => info.SerializeLocalSlots(builder)));
            }

            if (!debugInfo.Lambdas.IsDefaultOrEmpty)
            {
                customDebugInfo.Add(
                    SerializeRecord(
                        CDI.CdiKindEditAndContinueLambdaMap,
                        debugInfo,
                        (info, builder) => info.SerializeLambdaMap(builder)));
            }
        }
        public byte[] SerializeMethodDebugInfo(EmitContext context, IMethodBody methodBody, uint methodToken, bool isEncDelta, 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) //UNDONE: || edit-and-continue
            {
                // 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())
                {
                    _methodTokenWithModuleInfo = methodToken;
                    _methodBodyWithModuleInfo = methodBody;
                    emitExternNamespaces = true;
                }
            }

            var customDebugInfo = ArrayBuilder<MemoryStream>.GetInstance();

            SerializeIteratorClassMetadata(methodBody, customDebugInfo);

            // 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 ForwardIterator and IteratorLocal
            // 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)
            {
                SerializeNamespaceScopeMetadata(context, methodBody, customDebugInfo);
                SerializeStateMachineLocalScopes(methodBody, customDebugInfo);
            }

            if (!suppressNewCustomDebugInfo)
            {
                SerializeDynamicLocalInfo(methodBody, customDebugInfo);

                // delta doesn't need this information - we use information recorded by previous generation emit
                if (!isEncDelta)
                {
                    ImmutableArray<LocalSlotDebugInfo> encLocalSlots;

                    // Kickoff method of a state machine (async/iterator method) doens't have any interesting locals,
                    // so we use its EnC method debug info to store information about locals hoisted to the state machine.
                    var encSlotInfo = methodBody.StateMachineHoistedLocalSlots;
                    if (encSlotInfo.IsDefault)
                    {
                        encLocalSlots = GetLocalSlotDebugInfos(methodBody.LocalVariables);
                    }
                    else
                    {
                        encLocalSlots = GetLocalSlotDebugInfos(encSlotInfo);
                    }

                    var encMethodInfo = new EditAndContinueMethodDebugInformation(methodBody.MethodOrdinal, encLocalSlots, methodBody.ClosureDebugInfo, methodBody.LambdaDebugInfo);
                    encMethodInfo.SerializeCustomDebugInformation(customDebugInfo);
                }
            }

            byte[] result = SerializeCustomDebugMetadata(customDebugInfo);
            customDebugInfo.Free();
            return result;
        }