< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 89
Coverable lines: 89
Total lines: 273
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 44
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)0%220%
AddMethodBody(...)100%110%
AddMethodBody(...)0%880%
.ctor(...)100%110%
AddMethodBody(...)100%110%
AddMethodBody(...)0%16160%
SerializeHeader(...)0%18180%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/MethodBodyStreamEncoder.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4namespace System.Reflection.Metadata.Ecma335
 5{
 6    /// <summary>
 7    /// Encodes method body stream.
 8    /// </summary>
 9    public readonly struct MethodBodyStreamEncoder
 10    {
 011        public BlobBuilder Builder { get; }
 12
 13        public MethodBodyStreamEncoder(BlobBuilder builder)
 014        {
 015            ArgumentNullException.ThrowIfNull(builder);
 16
 17            // Fat methods are 4-byte aligned. We calculate the alignment relative to the start of the ILStream.
 18            //
 19            // See ECMA-335 paragraph 25.4.5, Method data section:
 20            // "At the next 4-byte boundary following the method body can be extra method data sections."
 021            if ((builder.Count % 4) != 0)
 022            {
 023                throw new ArgumentException(SR.BuilderMustAligned, nameof(builder));
 24            }
 25
 026            Builder = builder;
 027        }
 28
 29        /// <summary>
 30        /// Encodes a method body and adds it to the method body stream.
 31        /// </summary>
 32        /// <param name="codeSize">Number of bytes to be reserved for instructions.</param>
 33        /// <param name="maxStack">Max stack.</param>
 34        /// <param name="exceptionRegionCount">Number of exception regions.</param>
 35        /// <param name="hasSmallExceptionRegions">True if the exception regions should be encoded in 'small' format.</p
 36        /// <param name="localVariablesSignature">Local variables signature handle.</param>
 37        /// <param name="attributes">Attributes.</param>
 38        /// <returns>The offset of the encoded body within the method body stream.</returns>
 39        /// <exception cref="ArgumentOutOfRangeException">
 40        /// <paramref name="codeSize"/>, <paramref name="exceptionRegionCount"/>, or <paramref name="maxStack"/> is out 
 41        /// </exception>
 42        public MethodBody AddMethodBody(
 43            int codeSize,
 44            int maxStack,
 45            int exceptionRegionCount,
 46            bool hasSmallExceptionRegions,
 47            StandaloneSignatureHandle localVariablesSignature,
 48            MethodBodyAttributes attributes)
 049            => AddMethodBody(codeSize, maxStack, exceptionRegionCount, hasSmallExceptionRegions, localVariablesSignature
 50
 51        /// <summary>
 52        /// Encodes a method body and adds it to the method body stream.
 53        /// </summary>
 54        /// <param name="codeSize">Number of bytes to be reserved for instructions.</param>
 55        /// <param name="maxStack">Max stack.</param>
 56        /// <param name="exceptionRegionCount">Number of exception regions.</param>
 57        /// <param name="hasSmallExceptionRegions">True if the exception regions should be encoded in 'small' format.</p
 58        /// <param name="localVariablesSignature">Local variables signature handle.</param>
 59        /// <param name="attributes">Attributes.</param>
 60        /// <param name="hasDynamicStackAllocation">True if the method allocates from dynamic local memory pool (<c>loca
 61        /// <returns>The offset of the encoded body within the method body stream.</returns>
 62        /// <exception cref="ArgumentOutOfRangeException">
 63        /// <paramref name="codeSize"/>, <paramref name="exceptionRegionCount"/>, or <paramref name="maxStack"/> is out 
 64        /// </exception>
 65        public MethodBody AddMethodBody(
 66            int codeSize,
 67            int maxStack = 8,
 68            int exceptionRegionCount = 0,
 69            bool hasSmallExceptionRegions = true,
 70            StandaloneSignatureHandle localVariablesSignature = default,
 71            MethodBodyAttributes attributes = MethodBodyAttributes.InitLocals,
 72            bool hasDynamicStackAllocation = false)
 073        {
 074            if (codeSize < 0)
 075            {
 076                Throw.ArgumentOutOfRange(nameof(codeSize));
 77            }
 78
 079            if (unchecked((uint)maxStack) > ushort.MaxValue)
 080            {
 081                Throw.ArgumentOutOfRange(nameof(maxStack));
 82            }
 83
 084            if (!ExceptionRegionEncoder.IsExceptionRegionCountInBounds(exceptionRegionCount))
 085            {
 086                Throw.ArgumentOutOfRange(nameof(exceptionRegionCount));
 87            }
 88
 089            int bodyOffset = SerializeHeader(codeSize, (ushort)maxStack, exceptionRegionCount, attributes, localVariable
 090            var instructions = Builder.ReserveBytes(codeSize);
 91
 092            var regionEncoder = (exceptionRegionCount > 0) ?
 093                ExceptionRegionEncoder.SerializeTableHeader(Builder, exceptionRegionCount, hasSmallExceptionRegions) : d
 94
 095            return new MethodBody(bodyOffset, instructions, regionEncoder);
 096        }
 97
 98        public readonly struct MethodBody
 99        {
 100            /// <summary>
 101            /// Offset of the encoded method body in method body stream.
 102            /// </summary>
 0103            public int Offset { get; }
 104
 105            /// <summary>
 106            /// Blob reserved for instructions.
 107            /// </summary>
 0108            public Blob Instructions { get; }
 109
 110            /// <summary>
 111            /// Use to encode exception regions to the method body.
 112            /// </summary>
 0113            public ExceptionRegionEncoder ExceptionRegions { get; }
 114
 115            internal MethodBody(int bodyOffset, Blob instructions, ExceptionRegionEncoder exceptionRegions)
 0116            {
 0117                Offset = bodyOffset;
 0118                Instructions = instructions;
 0119                ExceptionRegions = exceptionRegions;
 0120            }
 121        }
 122
 123        /// <summary>
 124        /// Encodes a method body and adds it to the method body stream.
 125        /// </summary>
 126        /// <param name="instructionEncoder">Instruction encoder.</param>
 127        /// <param name="maxStack">Max stack.</param>
 128        /// <param name="localVariablesSignature">Local variables signature handle.</param>
 129        /// <param name="attributes">Attributes.</param>
 130        /// <returns>The offset of the encoded body within the method body stream.</returns>
 131        /// <exception cref="ArgumentNullException"><paramref name="instructionEncoder"/> has default value.</exception>
 132        /// <exception cref="ArgumentOutOfRangeException"><paramref name="maxStack"/> is out of range [0, <see cref="ush
 133        /// <exception cref="InvalidOperationException">
 134        /// A label targeted by a branch in the instruction stream has not been marked,
 135        /// or the distance between a branch instruction and the target label doesn't fit the size of the instruction op
 136        /// </exception>
 137        public int AddMethodBody(
 138            InstructionEncoder instructionEncoder,
 139            int maxStack,
 140            StandaloneSignatureHandle localVariablesSignature,
 141            MethodBodyAttributes attributes)
 0142            => AddMethodBody(instructionEncoder, maxStack, localVariablesSignature, attributes, hasDynamicStackAllocatio
 143
 144        /// <summary>
 145        /// Encodes a method body and adds it to the method body stream.
 146        /// </summary>
 147        /// <param name="instructionEncoder">Instruction encoder.</param>
 148        /// <param name="maxStack">Max stack.</param>
 149        /// <param name="localVariablesSignature">Local variables signature handle.</param>
 150        /// <param name="attributes">Attributes.</param>
 151        /// <param name="hasDynamicStackAllocation">True if the method allocates from dynamic local memory pool (the IL 
 152        /// </param>
 153        /// <returns>The offset of the encoded body within the method body stream.</returns>
 154        /// <exception cref="ArgumentNullException"><paramref name="instructionEncoder"/> has default value.</exception>
 155        /// <exception cref="ArgumentOutOfRangeException"><paramref name="maxStack"/> is out of range [0, <see cref="ush
 156        /// <exception cref="InvalidOperationException">
 157        /// A label targeted by a branch in the instruction stream has not been marked,
 158        /// or the distance between a branch instruction and the target label doesn't fit the size of the instruction op
 159        /// </exception>
 160        public int AddMethodBody(
 161            InstructionEncoder instructionEncoder,
 162            int maxStack = 8,
 163            StandaloneSignatureHandle localVariablesSignature = default,
 164            MethodBodyAttributes attributes = MethodBodyAttributes.InitLocals,
 165            bool hasDynamicStackAllocation = false)
 0166        {
 0167            if (unchecked((uint)maxStack) > ushort.MaxValue)
 0168            {
 0169                Throw.ArgumentOutOfRange(nameof(maxStack));
 170            }
 171
 172            // The branch fixup code expects the operands of branch instructions in the code builder to be contiguous.
 173            // That's true when we emit thru InstructionEncoder. Taking it as a parameter instead of separate
 174            // code and flow builder parameters ensures they match each other.
 0175            var codeBuilder = instructionEncoder.CodeBuilder;
 0176            var flowBuilder = instructionEncoder.ControlFlowBuilder;
 177
 0178            if (codeBuilder == null)
 0179            {
 0180                Throw.ArgumentNull(nameof(instructionEncoder));
 181            }
 182
 0183            int exceptionRegionCount = flowBuilder?.ExceptionHandlerCount ?? 0;
 0184            if (!ExceptionRegionEncoder.IsExceptionRegionCountInBounds(exceptionRegionCount))
 0185            {
 0186                Throw.ArgumentOutOfRange(nameof(instructionEncoder), SR.TooManyExceptionRegions);
 187            }
 188
 0189            flowBuilder?.ValidateNotInSwitch();
 190
 191            // Note (see also https://github.com/dotnet/runtime/issues/24948)
 192            //
 193            // We could potentially automatically determine whether a tiny method with no variables and InitLocals flag 
 194            // has localloc instruction and thus needs a fat header. We could parse the IL stored in codeBuilder.
 195            // However, it would unnecessarily slow down emit of virtually all tiny methods, which do not use localloc
 196            // and would only address uninitialized memory issues in very rare scenarios when the pointer returned by
 197            // localloc is not stored in a local variable but passed to a method call or stored in a field.
 198            //
 199            // Since emitting code with localloc is already a pretty advanced scenario that emits unsafe code
 200            // that can be potentially incorrect in many other ways we decide that it's not worth the complexity
 201            // and a perf regression to do so. Instead we rely on the caller to let us know if there is a localloc
 202            // in the code they emitted.
 203
 0204            int bodyOffset = SerializeHeader(codeBuilder.Count, (ushort)maxStack, exceptionRegionCount, attributes, loca
 205
 0206            if (flowBuilder?.BranchCount > 0)
 0207            {
 0208                flowBuilder.CopyCodeAndFixupBranches(codeBuilder, Builder);
 0209            }
 210            else
 0211            {
 0212                codeBuilder.WriteContentTo(Builder);
 0213            }
 214
 0215            flowBuilder?.SerializeExceptionTable(Builder);
 216
 0217            return bodyOffset;
 0218        }
 219
 220        private int SerializeHeader(
 221            int codeSize,
 222            ushort maxStack,
 223            int exceptionRegionCount,
 224            MethodBodyAttributes attributes,
 225            StandaloneSignatureHandle localVariablesSignature,
 226            bool hasDynamicStackAllocation)
 0227        {
 228            const int TinyFormat = 2;
 229            const int FatFormat = 3;
 230            const int MoreSections = 8;
 231            const byte InitLocals = 0x10;
 232
 0233            bool initLocals = (attributes & MethodBodyAttributes.InitLocals) != 0;
 234
 0235            bool isTiny = codeSize < 64 &&
 0236                          maxStack <= 8 &&
 0237                          localVariablesSignature.IsNil && (!hasDynamicStackAllocation || !initLocals) &&
 0238                          exceptionRegionCount == 0;
 239
 240            int offset;
 0241            if (isTiny)
 0242            {
 0243                offset = Builder.Count;
 0244                Builder.WriteByte((byte)((codeSize << 2) | TinyFormat));
 0245            }
 246            else
 0247            {
 0248                Builder.Align(4);
 249
 0250                offset = Builder.Count;
 251
 0252                ushort flags = (3 << 12) | FatFormat;
 0253                if (exceptionRegionCount > 0)
 0254                {
 0255                    flags |= MoreSections;
 0256                }
 257
 0258                if (initLocals)
 0259                {
 0260                    flags |= InitLocals;
 0261                }
 262
 0263                Builder.WriteUInt16((ushort)((int)attributes | flags));
 0264                Builder.WriteUInt16(maxStack);
 0265                Builder.WriteInt32(codeSize);
 0266                Builder.WriteInt32(localVariablesSignature.IsNil ? 0 : MetadataTokens.GetToken(localVariablesSignature))
 0267            }
 268
 0269            return offset;
 0270        }
 271    }
 272}
 273