/// <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))); }
/// <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 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)); }
/// <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))); }
internal MethodBody(int bodyOffset, Blob instructions, ExceptionRegionEncoder exceptionRegions) { Offset = bodyOffset; Instructions = instructions; ExceptionRegions = exceptionRegions; }