/// <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); }
/// <summary> /// Adds catch region. /// </summary> /// <param name="tryStart">Label marking the first instruction of the try block.</param> /// <param name="tryEnd">Label marking the instruction immediately following the try block.</param> /// <param name="handlerStart">Label marking the first instruction of the handler.</param> /// <param name="handlerEnd">Label marking the instruction immediately following the handler.</param> /// <param name="catchType">The type of exception to be caught: <see cref="TypeDefinitionHandle"/>, <see cref="TypeReferenceHandle"/> or <see cref="TypeSpecificationHandle"/>.</param> /// <exception cref="ArgumentException">A label was not defined by an instruction encoder this builder is associated with.</exception> /// <exception cref="ArgumentException"><paramref name="catchType"/> is not a valid type handle.</exception> /// <exception cref="ArgumentNullException">A label has default value.</exception> public void AddCatchRegion(LabelHandle tryStart, LabelHandle tryEnd, LabelHandle handlerStart, LabelHandle handlerEnd, EntityHandle catchType) { if (!ExceptionRegionEncoder.IsValidCatchTypeHandle(catchType)) { Throw.InvalidArgument_Handle(nameof(catchType)); } AddExceptionRegion(ExceptionRegionKind.Catch, tryStart, tryEnd, handlerStart, handlerEnd, catchType: catchType); }
internal void SerializeExceptionTable(BlobBuilder builder) { if (_lazyExceptionHandlers == null || _lazyExceptionHandlers.Count == 0) { return; } var regionEncoder = ExceptionRegionEncoder.SerializeTableHeader(builder, _lazyExceptionHandlers.Count, HasSmallExceptionRegions()); foreach (var handler in _lazyExceptionHandlers) { // Note that labels have been validated when added to the handler list, // they might not have been marked though. int tryStart = GetLabelOffsetChecked(handler.TryStart); int tryEnd = GetLabelOffsetChecked(handler.TryEnd); int handlerStart = GetLabelOffsetChecked(handler.HandlerStart); int handlerEnd = GetLabelOffsetChecked(handler.HandlerEnd); if (tryStart > tryEnd) { Throw.InvalidOperation(StarkPlatform.Reflection.Resources.SR.Format(SR.InvalidExceptionRegionBounds, tryStart, tryEnd)); } if (handlerStart > handlerEnd) { Throw.InvalidOperation(StarkPlatform.Reflection.Resources.SR.Format(SR.InvalidExceptionRegionBounds, handlerStart, handlerEnd)); } int catchTokenOrOffset; switch (handler.Kind) { case ExceptionRegionKind.Catch: catchTokenOrOffset = MetadataTokens.GetToken(handler.CatchType); break; case ExceptionRegionKind.Filter: catchTokenOrOffset = GetLabelOffsetChecked(handler.FilterStart); break; default: catchTokenOrOffset = 0; break; } regionEncoder.AddUnchecked( handler.Kind, tryStart, tryEnd - tryStart, handlerStart, handlerEnd - handlerStart, catchTokenOrOffset); } }
private bool HasSmallExceptionRegions() { Debug.Assert(_lazyExceptionHandlers != null); if (!ExceptionRegionEncoder.IsSmallRegionCount(_lazyExceptionHandlers.Count)) { return(false); } foreach (var handler in _lazyExceptionHandlers) { if (!ExceptionRegionEncoder.IsSmallExceptionRegionFromBounds(GetLabelOffsetChecked(handler.TryStart), GetLabelOffsetChecked(handler.TryEnd)) || !ExceptionRegionEncoder.IsSmallExceptionRegionFromBounds(GetLabelOffsetChecked(handler.HandlerStart), GetLabelOffsetChecked(handler.HandlerEnd))) { return(false); } } return(true); }
internal MethodBody(int bodyOffset, Blob instructions, ExceptionRegionEncoder exceptionRegions) { Offset = bodyOffset; Instructions = instructions; ExceptionRegions = exceptionRegions; }