Exemple #1
0
        /// <summary>
        /// Encodes a method body and adds it to the method body stream.
        /// </summary>
        /// <param name="codeSize">Number of bytes to be reserved for instructions.</param>
        /// <param name="maxStack">Max stack.</param>
        /// <param name="exceptionRegionCount">Number of exception regions.</param>
        /// <param name="hasSmallExceptionRegions">True if the exception regions should be encoded in 'small' format.</param>
        /// <param name="localVariablesSignature">Local variables signature handle.</param>
        /// <param name="attributes">Attributes.</param>
        /// <param name="hasDynamicStackAllocation">True if the method allocates from dynamic local memory pool (<c>localloc</c> instruction).</param>
        /// <returns>The offset of the encoded body within the method body stream.</returns>
        /// <exception cref="ArgumentOutOfRangeException">
        /// <paramref name="codeSize"/>, <paramref name="exceptionRegionCount"/>, or <paramref name="maxStack"/> is out of allowed range.
        /// </exception>
        public MethodBody AddMethodBody(
            int codeSize,
            int maxStack                  = 8,
            int exceptionRegionCount      = 0,
            bool hasSmallExceptionRegions = true,
            StandaloneSignatureHandle localVariablesSignature = default,
            MethodBodyAttributes attributes = MethodBodyAttributes.InitLocals,
            bool hasDynamicStackAllocation  = false)
        {
            if (codeSize < 0)
            {
                Throw.ArgumentOutOfRange(nameof(codeSize));
            }

            if (unchecked ((uint)maxStack) > ushort.MaxValue)
            {
                Throw.ArgumentOutOfRange(nameof(maxStack));
            }

            if (!ExceptionRegionEncoder.IsExceptionRegionCountInBounds(exceptionRegionCount))
            {
                Throw.ArgumentOutOfRange(nameof(exceptionRegionCount));
            }

            int bodyOffset   = SerializeHeader(codeSize, (ushort)maxStack, exceptionRegionCount, attributes, localVariablesSignature, hasDynamicStackAllocation);
            var instructions = Builder.ReserveBytes(codeSize);

            var regionEncoder = (exceptionRegionCount > 0) ?
                                ExceptionRegionEncoder.SerializeTableHeader(Builder, exceptionRegionCount, hasSmallExceptionRegions) : default;

            return(new MethodBody(bodyOffset, instructions, regionEncoder));
        }
        public void Add_Errors()
        {
            Assert.Throws <InvalidOperationException>(() => default(ExceptionRegionEncoder).Add(ExceptionRegionKind.Fault, 0, 0, 0, 0));

            var builder      = new BlobBuilder();
            var smallEncoder = new ExceptionRegionEncoder(builder, hasSmallFormat: true);
            var fatEncoder   = new ExceptionRegionEncoder(builder, hasSmallFormat: false);

            Assert.Throws <ArgumentOutOfRangeException>(() => smallEncoder.Add(ExceptionRegionKind.Finally, -1, 2, 4, 5));
            Assert.Throws <ArgumentOutOfRangeException>(() => smallEncoder.Add(ExceptionRegionKind.Finally, 1, -1, 4, 5));
            Assert.Throws <ArgumentOutOfRangeException>(() => smallEncoder.Add(ExceptionRegionKind.Finally, 1, 2, -1, 5));
            Assert.Throws <ArgumentOutOfRangeException>(() => smallEncoder.Add(ExceptionRegionKind.Finally, 1, 2, 4, -1));

            Assert.Throws <ArgumentOutOfRangeException>(() => smallEncoder.Add(ExceptionRegionKind.Finally, 0x10000, 2, 4, 5));
            Assert.Throws <ArgumentOutOfRangeException>(() => smallEncoder.Add(ExceptionRegionKind.Finally, 1, 0x100, 4, 5));
            Assert.Throws <ArgumentOutOfRangeException>(() => smallEncoder.Add(ExceptionRegionKind.Finally, 1, 2, 0x10000, 5));
            Assert.Throws <ArgumentOutOfRangeException>(() => smallEncoder.Add(ExceptionRegionKind.Finally, 1, 2, 4, 0x100));

            Assert.Throws <ArgumentOutOfRangeException>(() => fatEncoder.Add(ExceptionRegionKind.Finally, -1, 2, 4, 5));
            Assert.Throws <ArgumentOutOfRangeException>(() => fatEncoder.Add(ExceptionRegionKind.Finally, 1, -1, 4, 5));
            Assert.Throws <ArgumentOutOfRangeException>(() => fatEncoder.Add(ExceptionRegionKind.Finally, 1, 2, -1, 5));
            Assert.Throws <ArgumentOutOfRangeException>(() => fatEncoder.Add(ExceptionRegionKind.Finally, 1, 2, 4, -1));

            Assert.Throws <ArgumentOutOfRangeException>(() => fatEncoder.Add((ExceptionRegionKind)5, 1, 2, 4, 5));
            Assert.Throws <ArgumentOutOfRangeException>(() => fatEncoder.Add(ExceptionRegionKind.Filter, 1, 2, 4, 5, filterOffset: -1));
            Assert.Throws <ArgumentException>(() => fatEncoder.Add(ExceptionRegionKind.Catch, 1, 2, 4, 5, catchType: default(EntityHandle)));
            Assert.Throws <ArgumentException>(() => fatEncoder.Add(ExceptionRegionKind.Catch, 1, 2, 4, 5, catchType: MetadataTokens.ImportScopeHandle(1)));
        }
Exemple #3
0
        /// <summary>
        /// Encodes a method body and adds it to the method body stream.
        /// </summary>
        /// <param name="instructionEncoder">Instruction encoder.</param>
        /// <param name="maxStack">Max stack.</param>
        /// <param name="localVariablesSignature">Local variables signature handle.</param>
        /// <param name="attributes">Attributes.</param>
        /// <param name="hasDynamicStackAllocation">True if the method allocates from dynamic local memory pool (the IL contains <c>localloc</c> instruction).
        /// </param>
        /// <returns>The offset of the encoded body within the method body stream.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="instructionEncoder"/> has default value.</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="maxStack"/> is out of range [0, <see cref="ushort.MaxValue"/>].</exception>
        /// <exception cref="InvalidOperationException">
        /// A label targeted by a branch in the instruction stream has not been marked,
        /// or the distance between a branch instruction and the target label doesn't fit the size of the instruction operand.
        /// </exception>
        public int AddMethodBody(
            InstructionEncoder instructionEncoder,
            int maxStack = 8,
            StandaloneSignatureHandle localVariablesSignature = default,
            MethodBodyAttributes attributes = MethodBodyAttributes.InitLocals,
            bool hasDynamicStackAllocation  = false)
        {
            if (unchecked ((uint)maxStack) > ushort.MaxValue)
            {
                Throw.ArgumentOutOfRange(nameof(maxStack));
            }

            // The branch fixup code expects the operands of branch instructions in the code builder to be contiguous.
            // That's true when we emit thru InstructionEncoder. Taking it as a parameter instead of separate
            // code and flow builder parameters ensures they match each other.
            var codeBuilder = instructionEncoder.CodeBuilder;
            var flowBuilder = instructionEncoder.ControlFlowBuilder;

            if (codeBuilder == null)
            {
                Throw.ArgumentNull(nameof(instructionEncoder));
            }

            int exceptionRegionCount = flowBuilder?.ExceptionHandlerCount ?? 0;

            if (!ExceptionRegionEncoder.IsExceptionRegionCountInBounds(exceptionRegionCount))
            {
                Throw.ArgumentOutOfRange(nameof(instructionEncoder), SR.TooManyExceptionRegions);
            }

            // Note (see also https://github.com/dotnet/corefx/issues/26910)
            //
            // We could potentially automatically determine whether a tiny method with no variables and InitLocals flag set
            // has localloc instruction and thus needs a fat header. We could parse the IL stored in codeBuilder.
            // However, it would unnecessarily slow down emit of virtually all tiny methods, which do not use localloc
            // and would only address uninitialized memory issues in very rare scenarios when the pointer returned by
            // localloc is not stored in a local variable but passed to a method call or stored in a field.
            //
            // Since emitting code with localloc is already a pretty advanced scenario that emits unsafe code
            // that can be potentially incorrect in many other ways we decide that it's not worth the complexity
            // and a perf regression to do so. Instead we rely on the caller to let us know if there is a localloc
            // in the code they emitted.

            int bodyOffset = SerializeHeader(codeBuilder.Count, (ushort)maxStack, exceptionRegionCount, attributes, localVariablesSignature, hasDynamicStackAllocation);

            if (flowBuilder?.BranchCount > 0)
            {
                flowBuilder.CopyCodeAndFixupBranches(codeBuilder, Builder);
            }
            else
            {
                codeBuilder.WriteContentTo(Builder);
            }

            flowBuilder?.SerializeExceptionTable(Builder);

            return(bodyOffset);
        }
        public void Add_Small()
        {
            var builder = new BlobBuilder();
            var encoder = new ExceptionRegionEncoder(builder, hasSmallFormat: true);

            encoder.Add(ExceptionRegionKind.Catch, 1, 2, 4, 5, catchType: MetadataTokens.TypeDefinitionHandle(1));

            AssertEx.Equal(new byte[]
            {
                0x00, 0x00,            // kind
                0x01, 0x00,            // try offset
                0x02,                  // try length
                0x04, 0x00,            // handler offset
                0x05,                  // handler length
                0x01, 0x00, 0x00, 0x02 // catch type
            }, builder.ToArray());
            builder.Clear();

            encoder.Add(ExceptionRegionKind.Filter, 0xffff, 0xff, 0xffff, 0xff, filterOffset: int.MaxValue);

            AssertEx.Equal(new byte[]
            {
                0x01, 0x00,            // kind
                0xff, 0xff,            // try offset
                0xff,                  // try length
                0xff, 0xff,            // handler offset
                0xff,                  // handler length
                0xff, 0xff, 0xff, 0x7f // filter offset
            }, builder.ToArray());
            builder.Clear();

            encoder.Add(ExceptionRegionKind.Fault, 0xffff, 0xff, 0xffff, 0xff);

            AssertEx.Equal(new byte[]
            {
                0x04, 0x00,            // kind
                0xff, 0xff,            // try offset
                0xff,                  // try length
                0xff, 0xff,            // handler offset
                0xff,                  // handler length
                0x00, 0x00, 0x00, 0x00
            }, builder.ToArray());
            builder.Clear();

            encoder.Add(ExceptionRegionKind.Finally, 0, 0, 0, 0);

            AssertEx.Equal(new byte[]
            {
                0x02, 0x00,            // kind
                0x00, 0x00,            // try offset
                0x00,                  // try length
                0x00, 0x00,            // handler offset
                0x00,                  // handler length
                0x00, 0x00, 0x00, 0x00
            }, builder.ToArray());
            builder.Clear();
        }
        public void Add_Small()
        {
            var builder = new BlobBuilder();
            var encoder = new ExceptionRegionEncoder(builder, hasSmallFormat: true);

            encoder.Add(ExceptionRegionKind.Catch, 1, 2, 4, 5, catchType: MetadataTokens.TypeDefinitionHandle(1));

            AssertEx.Equal(new byte[]
            {
                0x00, 0x00,            // kind
                0x01, 0x00,            // try offset
                0x02,                  // try length
                0x04, 0x00,            // handler offset
                0x05,                  // handler length
                0x01, 0x00, 0x00, 0x02 // catch type
            }, builder.ToArray());
            builder.Clear();

            encoder.Add(ExceptionRegionKind.Filter, 0xffff, 0xff, 0xffff, 0xff, filterOffset: int.MaxValue);

            AssertEx.Equal(new byte[]
            {
                0x01, 0x00,            // kind
                0xff, 0xff,            // try offset
                0xff,                  // try length
                0xff, 0xff,            // handler offset
                0xff,                  // handler length
                0xff, 0xff, 0xff, 0x7f // filter offset
            }, builder.ToArray());
            builder.Clear();

            encoder.Add(ExceptionRegionKind.Fault, 0xffff, 0xff, 0xffff, 0xff);

            AssertEx.Equal(new byte[]
            {
                0x04, 0x00,            // kind
                0xff, 0xff,            // try offset
                0xff,                  // try length
                0xff, 0xff,            // handler offset
                0xff,                  // handler length
                0x00, 0x00, 0x00, 0x00
            }, builder.ToArray());
            builder.Clear();

            encoder.Add(ExceptionRegionKind.Finally, 0, 0, 0, 0);

            AssertEx.Equal(new byte[]
            {
                0x02, 0x00,            // kind
                0x00, 0x00,            // try offset
                0x00,                  // try length
                0x00, 0x00,            // handler offset
                0x00,                  // handler length
                0x00, 0x00, 0x00, 0x00
            }, builder.ToArray());
            builder.Clear();
        }
 public void IsSmallRegionCount()
 {
     Assert.True(ExceptionRegionEncoder.IsSmallRegionCount(0));
     Assert.True(ExceptionRegionEncoder.IsSmallRegionCount(20));
     Assert.False(ExceptionRegionEncoder.IsSmallRegionCount(-1));
     Assert.False(ExceptionRegionEncoder.IsSmallRegionCount(21));
     Assert.False(ExceptionRegionEncoder.IsSmallRegionCount(int.MinValue));
     Assert.False(ExceptionRegionEncoder.IsSmallRegionCount(int.MaxValue));
 }
        public void IsSmallExceptionRegion()
        {
            Assert.True(ExceptionRegionEncoder.IsSmallExceptionRegion(0, 0));
            Assert.True(ExceptionRegionEncoder.IsSmallExceptionRegion(ushort.MaxValue, byte.MaxValue));

            Assert.False(ExceptionRegionEncoder.IsSmallExceptionRegion(ushort.MaxValue + 1, byte.MaxValue));
            Assert.False(ExceptionRegionEncoder.IsSmallExceptionRegion(ushort.MaxValue, byte.MaxValue + 1));

            Assert.False(ExceptionRegionEncoder.IsSmallExceptionRegion(-1, 0));
            Assert.False(ExceptionRegionEncoder.IsSmallExceptionRegion(0, -1));
            Assert.False(ExceptionRegionEncoder.IsSmallExceptionRegion(int.MinValue, int.MinValue));
            Assert.False(ExceptionRegionEncoder.IsSmallExceptionRegion(int.MaxValue, int.MaxValue));
            Assert.False(ExceptionRegionEncoder.IsSmallExceptionRegion(int.MaxValue, int.MinValue));
            Assert.False(ExceptionRegionEncoder.IsSmallExceptionRegion(int.MinValue, int.MaxValue));
        }
Exemple #8
0
        /// <summary>
        /// Encodes a method body and adds it to the method body stream.
        /// </summary>
        /// <param name="instructionEncoder">Instruction encoder.</param>
        /// <param name="maxStack">Max stack.</param>
        /// <param name="localVariablesSignature">Local variables signature handle.</param>
        /// <param name="attributes">Attributes.</param>
        /// <returns>The offset of the encoded body within the method body stream.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="instructionEncoder"/> has default value.</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="maxStack"/> is out of range [0, <see cref="ushort.MaxValue"/>].</exception>
        /// <exception cref="InvalidOperationException">
        /// A label targeted by a branch in the instruction stream has not been marked,
        /// or the distance between a branch instruction and the target label is doesn't fit the size of the instruction operand.
        /// </exception>
        public int AddMethodBody(
            InstructionEncoder instructionEncoder,
            int maxStack = 8,
            StandaloneSignatureHandle localVariablesSignature = default(StandaloneSignatureHandle),
            MethodBodyAttributes attributes = MethodBodyAttributes.InitLocals)
        {
            if (unchecked ((uint)maxStack) > ushort.MaxValue)
            {
                Throw.ArgumentOutOfRange(nameof(maxStack));
            }

            // The branch fixup code expects the operands of branch instructions in the code builder to be contiguous.
            // That's true when we emit thru InstructionEncoder. Taking it as a parameter instead of separate
            // code and flow builder parameters ensures they match each other.
            var codeBuilder = instructionEncoder.CodeBuilder;
            var flowBuilder = instructionEncoder.ControlFlowBuilder;

            if (codeBuilder == null)
            {
                Throw.ArgumentNull(nameof(instructionEncoder));
            }

            int exceptionRegionCount = flowBuilder?.ExceptionHandlerCount ?? 0;

            if (!ExceptionRegionEncoder.IsExceptionRegionCountInBounds(exceptionRegionCount))
            {
                Throw.ArgumentOutOfRange(nameof(instructionEncoder), SR.TooManyExceptionRegions);
            }

            int bodyOffset = SerializeHeader(codeBuilder.Count, (ushort)maxStack, exceptionRegionCount, attributes, localVariablesSignature);

            if (flowBuilder?.BranchCount > 0)
            {
                flowBuilder.CopyCodeAndFixupBranches(codeBuilder, Builder);
            }
            else
            {
                codeBuilder.WriteContentTo(Builder);
            }

            flowBuilder?.SerializeExceptionTable(Builder);

            return(bodyOffset);
        }
        public void SerializeTableHeader()
        {
            var builder = new BlobBuilder();

            builder.WriteByte(0xff);
            ExceptionRegionEncoder.SerializeTableHeader(builder, ExceptionRegionEncoder.MaxSmallExceptionRegions, hasSmallRegions: true);
            AssertEx.Equal(new byte[]
            {
                0xff, 0x00, 0x00, 0x00, // padding
                0x01,                   // flags
                0xf4,                   // size
                0x00, 0x00
            }, builder.ToArray());
            builder.Clear();

            builder.WriteByte(0xff);
            ExceptionRegionEncoder.SerializeTableHeader(builder, ExceptionRegionEncoder.MaxExceptionRegions, hasSmallRegions: false);
            AssertEx.Equal(new byte[]
            {
                0xff, 0x00, 0x00, 0x00, // padding
                0x41,                   // flags
                0xf4, 0xff, 0xff,       // size
            }, builder.ToArray());
        }
        public void Add_Errors()
        {
            Assert.Throws<InvalidOperationException>(() => default(ExceptionRegionEncoder).Add(ExceptionRegionKind.Fault, 0, 0, 0, 0));
        
            var builder = new BlobBuilder();
            var smallEncoder = new ExceptionRegionEncoder(builder, hasSmallFormat: true);
            var fatEncoder = new ExceptionRegionEncoder(builder, hasSmallFormat: false);

            Assert.Throws<ArgumentOutOfRangeException>(() => smallEncoder.Add(ExceptionRegionKind.Finally, -1, 2, 4, 5));
            Assert.Throws<ArgumentOutOfRangeException>(() => smallEncoder.Add(ExceptionRegionKind.Finally, 1, -1, 4, 5));
            Assert.Throws<ArgumentOutOfRangeException>(() => smallEncoder.Add(ExceptionRegionKind.Finally, 1, 2, -1, 5));
            Assert.Throws<ArgumentOutOfRangeException>(() => smallEncoder.Add(ExceptionRegionKind.Finally, 1, 2, 4, -1));

            Assert.Throws<ArgumentOutOfRangeException>(() => smallEncoder.Add(ExceptionRegionKind.Finally, 0x10000, 2, 4, 5));
            Assert.Throws<ArgumentOutOfRangeException>(() => smallEncoder.Add(ExceptionRegionKind.Finally, 1, 0x100, 4, 5));
            Assert.Throws<ArgumentOutOfRangeException>(() => smallEncoder.Add(ExceptionRegionKind.Finally, 1, 2, 0x10000, 5));
            Assert.Throws<ArgumentOutOfRangeException>(() => smallEncoder.Add(ExceptionRegionKind.Finally, 1, 2, 4, 0x100));

            Assert.Throws<ArgumentOutOfRangeException>(() => fatEncoder.Add(ExceptionRegionKind.Finally, -1, 2, 4, 5));
            Assert.Throws<ArgumentOutOfRangeException>(() => fatEncoder.Add(ExceptionRegionKind.Finally, 1, -1, 4, 5));
            Assert.Throws<ArgumentOutOfRangeException>(() => fatEncoder.Add(ExceptionRegionKind.Finally, 1, 2, -1, 5));
            Assert.Throws<ArgumentOutOfRangeException>(() => fatEncoder.Add(ExceptionRegionKind.Finally, 1, 2, 4, -1));

            Assert.Throws<ArgumentOutOfRangeException>(() => fatEncoder.Add((ExceptionRegionKind)5, 1, 2, 4, 5));
            Assert.Throws<ArgumentOutOfRangeException>(() => fatEncoder.Add(ExceptionRegionKind.Filter, 1, 2, 4, 5, filterOffset: -1));
            Assert.Throws<ArgumentException>(() => fatEncoder.Add(ExceptionRegionKind.Catch, 1, 2, 4, 5, catchType: default(EntityHandle)));
            Assert.Throws<ArgumentException>(() => fatEncoder.Add(ExceptionRegionKind.Catch, 1, 2, 4, 5, catchType: MetadataTokens.ImportScopeHandle(1)));
        }
Exemple #11
0
 internal MethodBody(int bodyOffset, Blob instructions, ExceptionRegionEncoder exceptionRegions)
 {
     Offset           = bodyOffset;
     Instructions     = instructions;
     ExceptionRegions = exceptionRegions;
 }
 internal MethodBody(int bodyOffset, Blob instructions, ExceptionRegionEncoder exceptionRegions)
 {
     Offset = bodyOffset;
     Instructions = instructions;
     ExceptionRegions = exceptionRegions;
 }