/// <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)); }
/// <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); }