Exemplo n.º 1
0
        public void EditAndContinueLambdaAndClosureMap_NegativeSyntaxOffsets()
        {
            var slots = ImmutableArray <LocalSlotDebugInfo> .Empty;

            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 customMetadata = new Cci.MemoryStream();
            var cmw            = new Cci.BinaryWriter(customMetadata);

            new EditAndContinueMethodDebugInformation(0x7b, slots, closures, lambdas).SerializeLambdaMap(cmw);

            var bytes = customMetadata.ToImmutableArray();

            AssertEx.Equal(new byte[] { 0x7C, 0x80, 0xC8, 0x03, 0x64, 0x80, 0xD2, 0x00, 0x80, 0xDC, 0x03, 0x80, 0x96, 0x02, 0x14, 0x01 }, bytes);

            var deserialized = EditAndContinueMethodDebugInformation.Create(default(ImmutableArray <byte>), bytes);

            AssertEx.Equal(closures, deserialized.Closures);
            AssertEx.Equal(lambdas, deserialized.Lambdas);
        }
Exemplo n.º 2
0
        public void WriteData(Cci.BinaryWriter resourceWriter)
        {
            if (fileReference == null)
            {
                try
                {
                    using (Stream stream = streamProvider())
                    {
                        if (stream == null)
                        {
                            throw new InvalidOperationException(CodeAnalysisResources.ResourceStreamProviderShouldReturnNonNullStream);
                        }

                        var count = (int)(stream.Length - stream.Position);
                        resourceWriter.WriteInt(count);

                        var to       = resourceWriter.BaseStream;
                        var position = (int)to.Position;
                        to.Position = (uint)(position + count);
                        resourceWriter.Align(8);

                        var buffer = to.Buffer;
                        stream.Read(buffer, position, count);
                    }
                }
                catch (Exception e)
                {
                    throw new ResourceException(this.name, e);
                }
            }
        }
Exemplo n.º 3
0
        internal void SerializeCustomDebugInformation(ArrayBuilder <Cci.MemoryStream> customDebugInfo)
        {
            if (this.LocalSlots.IsDefaultOrEmpty)
            {
                return;
            }

            Cci.MemoryStream customMetadata = new Cci.MemoryStream();
            Cci.BinaryWriter cmw            = new Cci.BinaryWriter(customMetadata);
            cmw.WriteByte(4); // version
            cmw.WriteByte(6); // kind: EditAndContinueLocalSlotMap
            cmw.Align(4);

            // length (will be patched)
            uint lengthPosition = cmw.BaseStream.Position;

            cmw.WriteUint(0);

            SerializeLocalSlots(cmw);

            uint length = customMetadata.Position;

            // align with values that the reader skips
            while (length % 4 != 0)
            {
                cmw.WriteByte(AlignmentValue);
                length++;
            }

            cmw.BaseStream.Position = lengthPosition;
            cmw.WriteUint(length);
            cmw.BaseStream.Position = length;

            customDebugInfo.Add(customMetadata);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Produces a serialized blob of all constant initializers.
        /// Nonconstat initializers are matched with a zero of corresponding size.
        /// </summary>
        private ImmutableArray <byte> GetRawData(ImmutableArray <BoundExpression> initializers)
        {
            // the initial size is a guess.
            // there is no point to be precise here as MemoryStream always has N + 1 storage
            // and will need to be trimmed regardless
            var stream = new Cci.MemoryStream((uint)(initializers.Length * 4));
            var writer = new Cci.BinaryWriter(stream);

            SerializeArrayRecursive(writer, initializers);

            return(ImmutableArray.Create(stream.Buffer, 0, (int)stream.Position));
        }
Exemplo n.º 5
0
        internal void SerializeLocalSlots(Cci.BinaryWriter writer)
        {
            int syntaxOffsetBaseline = -1;

            foreach (LocalSlotDebugInfo localSlot in this.LocalSlots)
            {
                if (localSlot.Id.SyntaxOffset < syntaxOffsetBaseline)
                {
                    syntaxOffsetBaseline = localSlot.Id.SyntaxOffset;
                }
            }

            if (syntaxOffsetBaseline != -1)
            {
                writer.WriteByte(SyntaxOffsetBaseline);
                writer.WriteCompressedUInt((uint)(-syntaxOffsetBaseline));
            }

            foreach (LocalSlotDebugInfo localSlot in this.LocalSlots)
            {
                SynthesizedLocalKind kind = localSlot.SynthesizedKind;
                Debug.Assert(kind <= SynthesizedLocalKind.MaxValidValueForLocalVariableSerializedToDebugInformation);

                if (!kind.IsLongLived())
                {
                    writer.WriteByte(0);
                    continue;
                }

                byte b = (byte)(kind + 1);
                Debug.Assert((b & (1 << 7)) == 0);

                bool hasOrdinal = localSlot.Id.Ordinal > 0;

                if (hasOrdinal)
                {
                    b |= 1 << 7;
                }

                writer.WriteByte(b);
                writer.WriteCompressedUInt((uint)(localSlot.Id.SyntaxOffset - syntaxOffsetBaseline));

                if (hasOrdinal)
                {
                    writer.WriteCompressedUInt((uint)localSlot.Id.Ordinal);
                }
            }
        }
Exemplo n.º 6
0
        public void EditAndContinueLocalSlotMap_NegativeSyntaxOffsets()
        {
            var slots = ImmutableArray.Create(
                new LocalSlotDebugInfo(SynthesizedLocalKind.UserDefined, new LocalDebugId(-1, 10)),
                new LocalSlotDebugInfo(SynthesizedLocalKind.TryAwaitPendingCaughtException, new LocalDebugId(-20000, 10)));

            var customMetadata = new Cci.MemoryStream();
            var cmw            = new Cci.BinaryWriter(customMetadata);

            new EditAndContinueMethodDebugInformation(slots).SerializeLocalSlots(cmw);

            var bytes = customMetadata.ToImmutableArray();

            AssertEx.Equal(new byte[] { 0xFE, 0xC0, 0x00, 0x4E, 0x20, 0x81, 0xC0, 0x00, 0x4E, 0x1F, 0x0A, 0x9A, 0x00, 0x0A }, bytes);

            var deserialized = EditAndContinueMethodDebugInformation.Create(bytes).LocalSlots;

            AssertEx.Equal(slots, deserialized);
        }
        private Cci.MemoryStream SerializeRecord(byte kind, Action<Cci.BinaryWriter> data)
        {
            Cci.MemoryStream customMetadata = new Cci.MemoryStream();
            Cci.BinaryWriter cmw = new Cci.BinaryWriter(customMetadata);
            cmw.WriteByte(Cci.CustomDebugInfoConstants.CdiVersion);
            cmw.WriteByte(kind); 
            cmw.Align(4);

            // length (will be patched)
            uint lengthPosition = cmw.BaseStream.Position;
            cmw.WriteUint(0);

            data(cmw);

            uint length = customMetadata.Position;
            cmw.BaseStream.Position = lengthPosition;
            cmw.WriteUint(length);
            cmw.BaseStream.Position = length;
            return customMetadata;
        }
Exemplo n.º 8
0
        public void EditAndContinueLambdaAndClosureMap_NoClosures()
        {
            var slots = ImmutableArray <LocalSlotDebugInfo> .Empty;

            var closures = ImmutableArray <ClosureDebugInfo> .Empty;
            var lambdas  = ImmutableArray.Create(new LambdaDebugInfo(20, new DebugId(0, 0), LambdaDebugInfo.StaticClosureOrdinal));

            var customMetadata = new Cci.MemoryStream();
            var cmw            = new Cci.BinaryWriter(customMetadata);

            new EditAndContinueMethodDebugInformation(-1, slots, closures, lambdas).SerializeLambdaMap(cmw);

            var bytes = customMetadata.ToImmutableArray();

            AssertEx.Equal(new byte[] { 0x00, 0x01, 0x00, 0x15, 0x01 }, bytes);

            var deserialized = EditAndContinueMethodDebugInformation.Create(default(ImmutableArray <byte>), bytes);

            AssertEx.Equal(closures, deserialized.Closures);
            AssertEx.Equal(lambdas, deserialized.Lambdas);
        }
        internal void SerializeLambdaMap(Cci.BinaryWriter writer)
        {
            Debug.Assert(this.MethodOrdinal >= -1);
            writer.WriteCompressedUInt((uint)(this.MethodOrdinal + 1));

            int syntaxOffsetBaseline = -1;

            foreach (ClosureDebugInfo info in this.Closures)
            {
                if (info.SyntaxOffset < syntaxOffsetBaseline)
                {
                    syntaxOffsetBaseline = info.SyntaxOffset;
                }
            }

            foreach (LambdaDebugInfo info in this.Lambdas)
            {
                if (info.SyntaxOffset < syntaxOffsetBaseline)
                {
                    syntaxOffsetBaseline = info.SyntaxOffset;
                }
            }

            writer.WriteCompressedUInt((uint)(-syntaxOffsetBaseline));
            writer.WriteCompressedUInt((uint)this.Closures.Length);

            foreach (ClosureDebugInfo info in this.Closures)
            {
                writer.WriteCompressedUInt((uint)(info.SyntaxOffset - syntaxOffsetBaseline));
            }

            foreach (LambdaDebugInfo info in this.Lambdas)
            {
                Debug.Assert(info.ClosureOrdinal >= LambdaDebugInfo.MinClosureOrdinal);
                Debug.Assert(info.Generation == 0);

                writer.WriteCompressedUInt((uint)(info.SyntaxOffset - syntaxOffsetBaseline));
                writer.WriteCompressedUInt((uint)(info.ClosureOrdinal - LambdaDebugInfo.MinClosureOrdinal));
            }
        }
Exemplo n.º 10
0
        public void EditAndContinueLambdaAndClosureMap_NoLambdas()
        {
            // should not happen in practice, but EditAndContinueMethodDebugInformation should handle it just fine

            var slots    = ImmutableArray <LocalSlotDebugInfo> .Empty;
            var closures = ImmutableArray <ClosureDebugInfo> .Empty;
            var lambdas  = ImmutableArray <LambdaDebugInfo> .Empty;

            var customMetadata = new Cci.MemoryStream();
            var cmw            = new Cci.BinaryWriter(customMetadata);

            new EditAndContinueMethodDebugInformation(10, slots, closures, lambdas).SerializeLambdaMap(cmw);

            var bytes = customMetadata.ToImmutableArray();

            AssertEx.Equal(new byte[] { 0x0B, 0x01, 0x00 }, bytes);

            var deserialized = EditAndContinueMethodDebugInformation.Create(default(ImmutableArray <byte>), bytes);

            AssertEx.Equal(closures, deserialized.Closures);
            AssertEx.Equal(lambdas, deserialized.Lambdas);
        }
Exemplo n.º 11
0
        private Cci.MemoryStream SerializeRecord(byte kind, Action <Cci.BinaryWriter> data)
        {
            Cci.MemoryStream customMetadata = new Cci.MemoryStream();
            Cci.BinaryWriter cmw            = new Cci.BinaryWriter(customMetadata);
            cmw.WriteByte(Cci.CustomDebugInfoConstants.CdiVersion);
            cmw.WriteByte(kind);
            cmw.Align(4);

            // length (will be patched)
            uint lengthPosition = cmw.BaseStream.Position;

            cmw.WriteUint(0);

            data(cmw);

            uint length = customMetadata.Position;

            cmw.BaseStream.Position = lengthPosition;
            cmw.WriteUint(length);
            cmw.BaseStream.Position = length;
            return(customMetadata);
        }
Exemplo n.º 12
0
        public void EditAndContinueLambdaAndClosureMap_NegativeSyntaxOffsets()
        {
            var slots = ImmutableArray<LocalSlotDebugInfo>.Empty;

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

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

            var customMetadata = new Cci.MemoryStream();
            var cmw = new Cci.BinaryWriter(customMetadata);

            new EditAndContinueMethodDebugInformation(0x7b, slots, closures, lambdas).SerializeLambdaMap(cmw);

            var bytes = customMetadata.ToImmutableArray();

            AssertEx.Equal(new byte[] { 0x7C, 0x80, 0xC8, 0x03, 0x64, 0x80, 0xD2, 0x00, 0x80, 0xDC, 0x03, 0x80, 0x96, 0x02, 0x14, 0x01 }, bytes);

            var deserialized = EditAndContinueMethodDebugInformation.Create(default(ImmutableArray<byte>), bytes);

            AssertEx.Equal(closures, deserialized.Closures);
            AssertEx.Equal(lambdas, deserialized.Lambdas);
        }
Exemplo n.º 13
0
        private void RealizeBlocks()
        {
            // drop dead code.
            // We do not want to deal with unreachable code even when not optimizing.
            // sometimes dead code may have subtle verification violations
            // for example illegal fall-through in unreachable code is still illegal, 
            // but compiler will not enforce returning from dead code.
            // it is easier to just remove dead code than make sure it is all valid
            MarkReachableBlocks();
            RewriteSpecialBlocks();
            DropUnreachableBlocks();

            if (_optimizations == OptimizationLevel.Release && OptimizeLabels())
            {
                // redo unreachable code elimination if some labels were optimized
                // as that could result in more dead code. 
                MarkAllBlocksUnreachable();
                MarkReachableBlocks();
                DropUnreachableBlocks();
            }

            // some gotos must become leaves
            RewriteBranchesAcrossExceptionHandlers();

            // now we can compute block offsets and adjust branches
            while (ComputeOffsetsAndAdjustBranches())
            {
                // if branches were optimized, we may have more unreachable code
                // redo unreachable code elimination and if anything was dropped redo adjusting.
                MarkAllBlocksUnreachable();
                MarkReachableBlocks();
                if (!DropUnreachableBlocks())
                {
                    // nothing was dropped, we are done adjusting
                    break;
                }
            }

            // Now linearize everything with computed offsets.
            var realizedIlBitStream = Cci.MemoryStream.GetInstance();
            var writer = new Cci.BinaryWriter(realizedIlBitStream);

            for (var block = leaderBlock; block != null; block = block.NextBlock)
            {
                // If the block has any IL markers, we can calculate their real IL offsets now
                int blockFirstMarker = block.FirstILMarker;
                if (blockFirstMarker >= 0)
                {
                    int blockLastMarker = block.LastILMarker;
                    Debug.Assert(blockLastMarker >= blockFirstMarker);
                    for (int i = blockFirstMarker; i <= blockLastMarker; i++)
                    {
                        int blockOffset = _allocatedILMarkers[i].BlockOffset;
                        int absoluteOffset = (int)realizedIlBitStream.Position + blockOffset;
                        _allocatedILMarkers[i] = new ILMarker() { BlockOffset = blockOffset, AbsoluteOffset = absoluteOffset };
                    }
                }

                block.RegularInstructions?.WriteTo(realizedIlBitStream);

                switch (block.BranchCode)
                {
                    case ILOpCode.Nop:
                        break;

                    case ILOpCode.Switch:
                        // switch (N, t1, t2... tN)
                        //  IL ==> ILOpCode.Switch < unsigned int32 > < int32 >... < int32 >

                        WriteOpCode(writer, ILOpCode.Switch);

                        var switchBlock = (SwitchBlock)block;
                        writer.WriteUint(switchBlock.BranchesCount);

                        int switchBlockEnd = switchBlock.Start + switchBlock.TotalSize;

                        var blockBuilder = ArrayBuilder<BasicBlock>.GetInstance();
                        switchBlock.GetBranchBlocks(blockBuilder);

                        foreach (var branchBlock in blockBuilder)
                        {
                            writer.WriteInt(branchBlock.Start - switchBlockEnd);
                        }

                        blockBuilder.Free();

                        break;

                    default:
                        WriteOpCode(writer, block.BranchCode);

                        if (block.BranchLabel != null)
                        {
                            int target = block.BranchBlock.Start;
                            int curBlockEnd = block.Start + block.TotalSize;
                            int offset = target - curBlockEnd;

                            if (block.BranchCode.BranchOperandSize() == 1)
                            {
                                sbyte btOffset = (sbyte)offset;
                                Debug.Assert(btOffset == offset);
                                writer.WriteSbyte(btOffset);
                            }
                            else
                            {
                                writer.WriteInt(offset);
                            }
                        }

                        break;
                }
            }

            this.RealizedIL = realizedIlBitStream.ToArray();
            realizedIlBitStream.Free();

            RealizeSequencePoints();

            this.RealizedExceptionHandlers = _scopeManager.GetExceptionHandlerRegions();
        }
Exemplo n.º 14
0
        private void RealizeBlocks()
        {
            // drop dead code.
            // We do not want to deal with unreachable code even when not optimizing.
            // sometimes dead code may have subtle verification violations
            // for example illegal fall-through in unreachable code is still illegal,
            // but compiler will not enforce returning from dead code.
            // it is easier to just remove dead code than make sure it is all valid
            MarkReachableBlocks();
            RewriteSpecialBlocks();
            DropUnreachableBlocks();

            if (_optimizations == OptimizationLevel.Release && OptimizeLabels())
            {
                // redo unreachable code elimination if some labels were optimized
                // as that could result in more more dead code.
                MarkAllBlocksUnreachable();
                MarkReachableBlocks();
                DropUnreachableBlocks();
            }

            // some gotos must become leaves
            RewriteBranchesAcrossExceptionHandlers();

            // now we can compute block offsets and adjust branches
            while (ComputeOffsetsAndAdjustBranches())
            {
                // if branches were optimized, we may have more unreachable code
                // redo unreachable code elimination and if anything was dropped redo adjusting.
                MarkAllBlocksUnreachable();
                MarkReachableBlocks();
                if (!DropUnreachableBlocks())
                {
                    // nothing was dropped, we are done adjusting
                    break;
                }
            }

            // Now linearize everything with computed offsets.
            var realizedIlBitStream = Cci.MemoryStream.GetInstance();
            var writer = new Cci.BinaryWriter(realizedIlBitStream);

            for (var block = leaderBlock; block != null; block = block.NextBlock)
            {
                // If the block has any IL markers, we can calculate their real IL offsets now
                int blockFirstMarker = block.FirstILMarker;
                if (blockFirstMarker >= 0)
                {
                    int blockLastMarker = block.LastILMarker;
                    Debug.Assert(blockLastMarker >= blockFirstMarker);
                    for (int i = blockFirstMarker; i <= blockLastMarker; i++)
                    {
                        int blockOffset    = _allocatedILMarkers[i].BlockOffset;
                        int absoluteOffset = (int)realizedIlBitStream.Position + blockOffset;
                        _allocatedILMarkers[i] = new ILMarker()
                        {
                            BlockOffset = blockOffset, AbsoluteOffset = absoluteOffset
                        };
                    }
                }

                block.RegularInstructions?.WriteTo(realizedIlBitStream);

                switch (block.BranchCode)
                {
                case ILOpCode.Nop:
                    break;

                case ILOpCode.Switch:
                    // switch (N, t1, t2... tN)
                    //  IL ==> ILOpCode.Switch < unsigned int32 > < int32 >... < int32 >

                    WriteOpCode(writer, ILOpCode.Switch);

                    var switchBlock = (SwitchBlock)block;
                    writer.WriteUint(switchBlock.BranchesCount);

                    int switchBlockEnd = switchBlock.Start + switchBlock.TotalSize;

                    var blockBuilder = ArrayBuilder <BasicBlock> .GetInstance();

                    switchBlock.GetBranchBlocks(blockBuilder);

                    foreach (var branchBlock in blockBuilder)
                    {
                        writer.WriteInt(branchBlock.Start - switchBlockEnd);
                    }

                    blockBuilder.Free();

                    break;

                default:
                    WriteOpCode(writer, block.BranchCode);

                    if (block.BranchLabel != null)
                    {
                        int target      = block.BranchBlock.Start;
                        int curBlockEnd = block.Start + block.TotalSize;
                        int offset      = target - curBlockEnd;

                        if (block.BranchCode.BranchOperandSize() == 1)
                        {
                            sbyte btOffset = (sbyte)offset;
                            Debug.Assert(btOffset == offset);
                            writer.WriteSbyte(btOffset);
                        }
                        else
                        {
                            writer.WriteInt(offset);
                        }
                    }

                    break;
                }
            }

            this.RealizedIL = realizedIlBitStream.ToArray();
            realizedIlBitStream.Free();

            RealizeSequencePoints();

            this.RealizedExceptionHandlers = _scopeManager.GetExceptionHandlerRegions();
        }
Exemplo n.º 15
0
        public void EditAndContinueLambdaAndClosureMap_NoLambdas()
        {
            // should not happen in practice, but EditAndContinueMethodDebugInformation should handle it just fine

            var slots = ImmutableArray<LocalSlotDebugInfo>.Empty;
            var closures = ImmutableArray<ClosureDebugInfo>.Empty;
            var lambdas = ImmutableArray<LambdaDebugInfo>.Empty;

            var customMetadata = new Cci.MemoryStream();
            var cmw = new Cci.BinaryWriter(customMetadata);

            new EditAndContinueMethodDebugInformation(10, slots, closures, lambdas).SerializeLambdaMap(cmw);

            var bytes = customMetadata.ToImmutableArray();

            AssertEx.Equal(new byte[] { 0x0B, 0x01, 0x00 }, bytes);

            var deserialized = EditAndContinueMethodDebugInformation.Create(default(ImmutableArray<byte>), bytes);

            AssertEx.Equal(closures, deserialized.Closures);
            AssertEx.Equal(lambdas, deserialized.Lambdas);
        }
Exemplo n.º 16
0
        public void EditAndContinueLambdaAndClosureMap_NoClosures()
        {
            var slots = ImmutableArray<LocalSlotDebugInfo>.Empty;

            var closures = ImmutableArray<ClosureDebugInfo>.Empty;
            var lambdas = ImmutableArray.Create(new LambdaDebugInfo(20, LambdaDebugInfo.StaticClosureOrdinal, 0));

            var customMetadata = new Cci.MemoryStream();
            var cmw = new Cci.BinaryWriter(customMetadata);

            new EditAndContinueMethodDebugInformation(-1, slots, closures, lambdas).SerializeLambdaMap(cmw);

            var bytes = customMetadata.ToImmutableArray();

            AssertEx.Equal(new byte[] { 0x00, 0x01, 0x00, 0x15, 0x01 }, bytes);

            var deserialized = EditAndContinueMethodDebugInformation.Create(default(ImmutableArray<byte>), bytes);

            AssertEx.Equal(closures, deserialized.Closures);
            AssertEx.Equal(lambdas, deserialized.Lambdas);
        }
Exemplo n.º 17
0
        public void EditAndContinueLocalSlotMap_NegativeSyntaxOffsets()
        {
            var slots = ImmutableArray.Create(
                new LocalSlotDebugInfo(SynthesizedLocalKind.UserDefined, new LocalDebugId(-1, 10)),
                new LocalSlotDebugInfo(SynthesizedLocalKind.TryAwaitPendingCaughtException, new LocalDebugId(-20000, 10)));

            var closures = ImmutableArray<ClosureDebugInfo>.Empty;
            var lambdas = ImmutableArray<LambdaDebugInfo>.Empty;

            var customMetadata = new Cci.MemoryStream();
            var cmw = new Cci.BinaryWriter(customMetadata);

            new EditAndContinueMethodDebugInformation(123, slots, closures, lambdas).SerializeLocalSlots(cmw);

            var bytes = customMetadata.ToImmutableArray();
            AssertEx.Equal(new byte[] { 0xFF, 0xC0, 0x00, 0x4E, 0x20, 0x81, 0xC0, 0x00, 0x4E, 0x1F, 0x0A, 0x9A, 0x00, 0x0A }, bytes);

            var deserialized = EditAndContinueMethodDebugInformation.Create(bytes, default(ImmutableArray<byte>)).LocalSlots;

            AssertEx.Equal(slots, deserialized);
        }
        /// <summary>
        /// Produces a serialized blob of all constant initializers.
        /// Nonconstat initializers are matched with a zero of corresponding size.
        /// </summary>
        private ImmutableArray<byte> GetRawData(ImmutableArray<BoundExpression> initializers)
        {
            // the initial size is a guess.
            // there is no point to be precise here as MemoryStream always has N + 1 storage 
            // and will need to be trimmed regardless
            var stream = new Cci.MemoryStream((uint)(initializers.Length * 4));
            var writer = new Cci.BinaryWriter(stream);

            SerializeArrayRecursive(writer, initializers);

            return ImmutableArray.Create(stream.Buffer, 0, (int)stream.Position);
        }