< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 232
Coverable lines: 232
Total lines: 491
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 70
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(...)100%110%
OpCode(...)0%440%
Token(...)100%110%
Token(...)0%220%
LoadString(...)100%110%
Call(...)0%660%
Call(...)100%110%
Call(...)100%110%
Call(...)100%110%
CallIndirect(...)100%110%
LoadConstantI4(...)0%13130%
LoadConstantI8(...)100%110%
LoadConstantR4(...)100%110%
LoadConstantR8(...)100%110%
LoadLocal(...)0%990%
StoreLocal(...)0%990%
LoadLocalAddress(...)0%440%
LoadArgument(...)0%990%
LoadArgumentAddress(...)0%440%
StoreArgument(...)0%440%
DefineLabel()100%110%
LabelOperand(...)0%220%
Branch(...)100%110%
Switch(...)0%220%
MarkLabel(...)100%110%
GetBranchBuilder()0%220%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/Encoding/InstructionEncoder.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
 4using System.Diagnostics;
 5
 6namespace System.Reflection.Metadata.Ecma335
 7{
 8    /// <summary>
 9    /// Encodes instructions.
 10    /// </summary>
 11    public readonly struct InstructionEncoder
 12    {
 13        /// <summary>
 14        /// Underlying builder where encoded instructions are written to.
 15        /// </summary>
 016        public BlobBuilder CodeBuilder { get; }
 17
 18        /// <summary>
 19        /// Builder tracking labels, branches and exception handlers.
 20        /// </summary>
 21        /// <remarks>
 22        /// If null the encoder doesn't support construction of control flow.
 23        /// </remarks>
 024        public ControlFlowBuilder? ControlFlowBuilder { get; }
 25
 26        /// <summary>
 27        /// Creates an encoder backed by code and control-flow builders.
 28        /// </summary>
 29        /// <param name="codeBuilder">Builder to write encoded instructions to.</param>
 30        /// <param name="controlFlowBuilder">
 31        /// Builder tracking labels, branches and exception handlers.
 32        /// Must be specified to be able to use some of the control-flow factory methods of <see cref="InstructionEncode
 33        /// such as <see cref="Branch(ILOpCode, LabelHandle)"/>, <see cref="DefineLabel"/>, <see cref="MarkLabel(LabelHa
 34        /// </param>
 35        public InstructionEncoder(BlobBuilder codeBuilder, ControlFlowBuilder? controlFlowBuilder = null)
 036        {
 037            ArgumentNullException.ThrowIfNull(codeBuilder);
 38
 039            CodeBuilder = codeBuilder;
 040            ControlFlowBuilder = controlFlowBuilder;
 041        }
 42
 43        /// <summary>
 44        /// Offset of the next encoded instruction.
 45        /// </summary>
 046        public int Offset => CodeBuilder.Count;
 47
 48        /// <summary>
 49        /// Encodes specified op-code.
 50        /// </summary>
 51        public void OpCode(ILOpCode code)
 052        {
 053            ControlFlowBuilder?.ValidateNotInSwitch();
 054            if (unchecked((byte)code) == (ushort)code)
 055            {
 056                CodeBuilder.WriteByte((byte)code);
 057            }
 58            else
 059            {
 60                // IL opcodes that occupy two bytes are written to
 61                // the byte stream with the high-order byte first,
 62                // in contrast to the little-endian format of the
 63                // numeric arguments and tokens.
 064                CodeBuilder.WriteUInt16BE((ushort)code);
 065            }
 066        }
 67
 68        /// <summary>
 69        /// Encodes a token.
 70        /// </summary>
 71        public void Token(EntityHandle handle)
 072        {
 073            Token(MetadataTokens.GetToken(handle));
 074        }
 75
 76        /// <summary>
 77        /// Encodes a token.
 78        /// </summary>
 79        public void Token(int token)
 080        {
 081            ControlFlowBuilder?.ValidateNotInSwitch();
 082            CodeBuilder.WriteInt32(token);
 083        }
 84
 85        /// <summary>
 86        /// Encodes <code>ldstr</code> instruction and its operand.
 87        /// </summary>
 88        public void LoadString(UserStringHandle handle)
 089        {
 090            OpCode(ILOpCode.Ldstr);
 091            Token(MetadataTokens.GetToken(handle));
 092        }
 93
 94        /// <summary>
 95        /// Encodes <code>call</code> instruction and its operand.
 96        /// </summary>
 97        public void Call(EntityHandle methodHandle)
 098        {
 099            if (methodHandle.Kind != HandleKind.MethodDefinition &&
 0100                methodHandle.Kind != HandleKind.MethodSpecification &&
 0101                methodHandle.Kind != HandleKind.MemberReference)
 0102            {
 0103                Throw.InvalidArgument_Handle(nameof(methodHandle));
 104            }
 105
 0106            OpCode(ILOpCode.Call);
 0107            Token(methodHandle);
 0108        }
 109
 110        /// <summary>
 111        /// Encodes <code>call</code> instruction and its operand.
 112        /// </summary>
 113        public void Call(MethodDefinitionHandle methodHandle)
 0114        {
 0115            OpCode(ILOpCode.Call);
 0116            Token(methodHandle);
 0117        }
 118
 119        /// <summary>
 120        /// Encodes <code>call</code> instruction and its operand.
 121        /// </summary>
 122        public void Call(MethodSpecificationHandle methodHandle)
 0123        {
 0124            OpCode(ILOpCode.Call);
 0125            Token(methodHandle);
 0126        }
 127
 128        /// <summary>
 129        /// Encodes <code>call</code> instruction and its operand.
 130        /// </summary>
 131        public void Call(MemberReferenceHandle methodHandle)
 0132        {
 0133            OpCode(ILOpCode.Call);
 0134            Token(methodHandle);
 0135        }
 136
 137        /// <summary>
 138        /// Encodes <code>calli</code> instruction and its operand.
 139        /// </summary>
 140        public void CallIndirect(StandaloneSignatureHandle signature)
 0141        {
 0142            OpCode(ILOpCode.Calli);
 0143            Token(signature);
 0144        }
 145
 146        /// <summary>
 147        /// Encodes <see cref="int"/> constant load instruction.
 148        /// </summary>
 149        public void LoadConstantI4(int value)
 0150        {
 151            ILOpCode code;
 0152            switch (value)
 153            {
 0154                case -1: code = ILOpCode.Ldc_i4_m1; break;
 0155                case 0: code = ILOpCode.Ldc_i4_0; break;
 0156                case 1: code = ILOpCode.Ldc_i4_1; break;
 0157                case 2: code = ILOpCode.Ldc_i4_2; break;
 0158                case 3: code = ILOpCode.Ldc_i4_3; break;
 0159                case 4: code = ILOpCode.Ldc_i4_4; break;
 0160                case 5: code = ILOpCode.Ldc_i4_5; break;
 0161                case 6: code = ILOpCode.Ldc_i4_6; break;
 0162                case 7: code = ILOpCode.Ldc_i4_7; break;
 0163                case 8: code = ILOpCode.Ldc_i4_8; break;
 164
 165                default:
 0166                    if (unchecked((sbyte)value == value))
 0167                    {
 0168                        OpCode(ILOpCode.Ldc_i4_s);
 0169                        CodeBuilder.WriteSByte((sbyte)value);
 0170                    }
 171                    else
 0172                    {
 0173                        OpCode(ILOpCode.Ldc_i4);
 0174                        CodeBuilder.WriteInt32(value);
 0175                    }
 176
 0177                    return;
 178            }
 179
 0180            OpCode(code);
 0181        }
 182
 183        /// <summary>
 184        /// Encodes <see cref="long"/> constant load instruction.
 185        /// </summary>
 186        public void LoadConstantI8(long value)
 0187        {
 0188            OpCode(ILOpCode.Ldc_i8);
 0189            CodeBuilder.WriteInt64(value);
 0190        }
 191
 192        /// <summary>
 193        /// Encodes <see cref="float"/> constant load instruction.
 194        /// </summary>
 195        public void LoadConstantR4(float value)
 0196        {
 0197            OpCode(ILOpCode.Ldc_r4);
 0198            CodeBuilder.WriteSingle(value);
 0199        }
 200
 201        /// <summary>
 202        /// Encodes <see cref="double"/> constant load instruction.
 203        /// </summary>
 204        public void LoadConstantR8(double value)
 0205        {
 0206            OpCode(ILOpCode.Ldc_r8);
 0207            CodeBuilder.WriteDouble(value);
 0208        }
 209
 210        /// <summary>
 211        /// Encodes local variable load instruction.
 212        /// </summary>
 213        /// <param name="slotIndex">Index of the local variable slot.</param>
 214        /// <exception cref="ArgumentOutOfRangeException"><paramref name="slotIndex"/> is negative or greater than <see 
 215        public void LoadLocal(int slotIndex)
 0216        {
 0217            switch (slotIndex)
 218            {
 0219                case 0: OpCode(ILOpCode.Ldloc_0); break;
 0220                case 1: OpCode(ILOpCode.Ldloc_1); break;
 0221                case 2: OpCode(ILOpCode.Ldloc_2); break;
 0222                case 3: OpCode(ILOpCode.Ldloc_3); break;
 223
 224                default:
 0225                    if (unchecked((uint)slotIndex) <= byte.MaxValue)
 0226                    {
 0227                        OpCode(ILOpCode.Ldloc_s);
 0228                        CodeBuilder.WriteByte((byte)slotIndex);
 0229                    }
 0230                    else if (unchecked((uint)slotIndex) <= ushort.MaxValue)
 0231                    {
 0232                        OpCode(ILOpCode.Ldloc);
 0233                        CodeBuilder.WriteUInt16((ushort)slotIndex);
 0234                    }
 235                    else
 0236                    {
 0237                        Throw.ArgumentOutOfRange(nameof(slotIndex));
 238                    }
 239
 0240                    break;
 241            }
 0242        }
 243
 244        /// <summary>
 245        /// Encodes local variable store instruction.
 246        /// </summary>
 247        /// <param name="slotIndex">Index of the local variable slot.</param>
 248        /// <exception cref="ArgumentOutOfRangeException"><paramref name="slotIndex"/> is negative or greater than <see 
 249        public void StoreLocal(int slotIndex)
 0250        {
 0251            switch (slotIndex)
 252            {
 0253                case 0: OpCode(ILOpCode.Stloc_0); break;
 0254                case 1: OpCode(ILOpCode.Stloc_1); break;
 0255                case 2: OpCode(ILOpCode.Stloc_2); break;
 0256                case 3: OpCode(ILOpCode.Stloc_3); break;
 257
 258                default:
 0259                    if (unchecked((uint)slotIndex) <= byte.MaxValue)
 0260                    {
 0261                        OpCode(ILOpCode.Stloc_s);
 0262                        CodeBuilder.WriteByte((byte)slotIndex);
 0263                    }
 0264                    else if (unchecked((uint)slotIndex) <= ushort.MaxValue)
 0265                    {
 0266                        OpCode(ILOpCode.Stloc);
 0267                        CodeBuilder.WriteUInt16((ushort)slotIndex);
 0268                    }
 269                    else
 0270                    {
 0271                        Throw.ArgumentOutOfRange(nameof(slotIndex));
 272                    }
 273
 0274                    break;
 275            }
 0276        }
 277
 278        /// <summary>
 279        /// Encodes local variable address load instruction.
 280        /// </summary>
 281        /// <param name="slotIndex">Index of the local variable slot.</param>
 282        /// <exception cref="ArgumentOutOfRangeException"><paramref name="slotIndex"/> is negative or greater than <see 
 283        public void LoadLocalAddress(int slotIndex)
 0284        {
 0285            if (unchecked((uint)slotIndex) <= byte.MaxValue)
 0286            {
 0287                OpCode(ILOpCode.Ldloca_s);
 0288                CodeBuilder.WriteByte((byte)slotIndex);
 0289            }
 0290            else if (unchecked((uint)slotIndex) <= ushort.MaxValue)
 0291            {
 0292                OpCode(ILOpCode.Ldloca);
 0293                CodeBuilder.WriteUInt16((ushort)slotIndex);
 0294            }
 295            else
 0296            {
 0297                Throw.ArgumentOutOfRange(nameof(slotIndex));
 298            }
 0299        }
 300
 301        /// <summary>
 302        /// Encodes argument load instruction.
 303        /// </summary>
 304        /// <param name="argumentIndex">Index of the argument.</param>
 305        /// <exception cref="ArgumentOutOfRangeException"><paramref name="argumentIndex"/> is negative or greater than <
 306        public void LoadArgument(int argumentIndex)
 0307        {
 0308            switch (argumentIndex)
 309            {
 0310                case 0: OpCode(ILOpCode.Ldarg_0); break;
 0311                case 1: OpCode(ILOpCode.Ldarg_1); break;
 0312                case 2: OpCode(ILOpCode.Ldarg_2); break;
 0313                case 3: OpCode(ILOpCode.Ldarg_3); break;
 314
 315                default:
 0316                    if (unchecked((uint)argumentIndex) <= byte.MaxValue)
 0317                    {
 0318                        OpCode(ILOpCode.Ldarg_s);
 0319                        CodeBuilder.WriteByte((byte)argumentIndex);
 0320                    }
 0321                    else if (unchecked((uint)argumentIndex) <= ushort.MaxValue)
 0322                    {
 0323                        OpCode(ILOpCode.Ldarg);
 0324                        CodeBuilder.WriteUInt16((ushort)argumentIndex);
 0325                    }
 326                    else
 0327                    {
 0328                        Throw.ArgumentOutOfRange(nameof(argumentIndex));
 329                    }
 330
 0331                    break;
 332            }
 0333        }
 334
 335        /// <summary>
 336        /// Encodes argument address load instruction.
 337        /// </summary>
 338        /// <param name="argumentIndex">Index of the argument.</param>
 339        /// <exception cref="ArgumentOutOfRangeException"><paramref name="argumentIndex"/> is negative or greater than <
 340        public void LoadArgumentAddress(int argumentIndex)
 0341        {
 0342            if (unchecked((uint)argumentIndex) <= byte.MaxValue)
 0343            {
 0344                OpCode(ILOpCode.Ldarga_s);
 0345                CodeBuilder.WriteByte((byte)argumentIndex);
 0346            }
 0347            else if (unchecked((uint)argumentIndex) <= ushort.MaxValue)
 0348            {
 0349                OpCode(ILOpCode.Ldarga);
 0350                CodeBuilder.WriteUInt16((ushort)argumentIndex);
 0351            }
 352            else
 0353            {
 0354                Throw.ArgumentOutOfRange(nameof(argumentIndex));
 355            }
 0356        }
 357
 358        /// <summary>
 359        /// Encodes argument store instruction.
 360        /// </summary>
 361        /// <param name="argumentIndex">Index of the argument.</param>
 362        /// <exception cref="ArgumentOutOfRangeException"><paramref name="argumentIndex"/> is negative or greater than <
 363        public void StoreArgument(int argumentIndex)
 0364        {
 0365            if (unchecked((uint)argumentIndex) <= byte.MaxValue)
 0366            {
 0367                OpCode(ILOpCode.Starg_s);
 0368                CodeBuilder.WriteByte((byte)argumentIndex);
 0369            }
 0370            else if (unchecked((uint)argumentIndex) <= ushort.MaxValue)
 0371            {
 0372                OpCode(ILOpCode.Starg);
 0373                CodeBuilder.WriteUInt16((ushort)argumentIndex);
 0374            }
 375            else
 0376            {
 0377                Throw.ArgumentOutOfRange(nameof(argumentIndex));
 378            }
 0379        }
 380
 381        /// <summary>
 382        /// Defines a label that can later be used to mark and refer to a location in the instruction stream.
 383        /// </summary>
 384        /// <returns>Label handle.</returns>
 385        /// <exception cref="InvalidOperationException"><see cref="ControlFlowBuilder"/> is null.</exception>
 386        public LabelHandle DefineLabel()
 0387        {
 0388            return GetBranchBuilder().AddLabel();
 0389        }
 390
 391        internal void LabelOperand(ILOpCode code, LabelHandle label, int instructionEndDisplacement, int ilOffset)
 0392        {
 0393            GetBranchBuilder().AddBranch(Offset, label, instructionEndDisplacement, ilOffset, code);
 394
 395            // -1 points in the middle of the branch instruction and is thus invalid.
 396            // We want to produce invalid IL so that if the caller doesn't patch the branches
 397            // the branch instructions will be invalid in an obvious way.
 0398            if (instructionEndDisplacement == 1)
 0399            {
 0400                CodeBuilder.WriteSByte(-1);
 0401            }
 402            else
 0403            {
 0404                CodeBuilder.WriteInt32(-1);
 0405            }
 0406        }
 407
 408        /// <summary>
 409        /// Encodes a branch instruction.
 410        /// </summary>
 411        /// <param name="code">Branch instruction to encode.</param>
 412        /// <param name="label">Label of the target location in instruction stream.</param>
 413        /// <exception cref="ArgumentException"><paramref name="code"/> is not a branch instruction.</exception>
 414        /// <exception cref="ArgumentException"><paramref name="label"/> was not defined by this encoder.</exception>
 415        /// <exception cref="InvalidOperationException"><see cref="ControlFlowBuilder"/> is null.</exception>
 416        /// <exception cref="ArgumentNullException"><paramref name="label"/> has default value.</exception>
 417        public void Branch(ILOpCode code, LabelHandle label)
 0418        {
 419            // throws if code is not a branch:
 0420            int operandSize = code.GetBranchOperandSize();
 421            // We want the offset before we add the opcode.
 0422            int ilOffset = Offset;
 423
 0424            OpCode(code);
 0425            LabelOperand(code, label, operandSize, ilOffset);
 0426        }
 427
 428        /// <summary>
 429        /// Starts encoding a switch instruction.
 430        /// </summary>
 431        /// <param name="branchCount">The number of branches the instruction will have.</param>
 432        /// <returns>A <see cref="SwitchInstructionEncoder"/> that will
 433        /// be used to emit the labels for the branches.</returns>
 434        /// <remarks>
 435        /// Before using this <see cref="InstructionEncoder"/> in any other way,
 436        /// the method <see cref="SwitchInstructionEncoder.Branch(LabelHandle)"/>
 437        /// must be called on the returned value exactly <paramref name="branchCount"/>
 438        /// times. Failure to do so will throw <see cref="InvalidOperationException"/>.
 439        /// </remarks>
 440        /// <exception cref="ArgumentOutOfRangeException"><paramref name="branchCount"/>
 441        /// less than or equal to zero.</exception>
 442        public SwitchInstructionEncoder Switch(int branchCount)
 0443        {
 0444            if (branchCount <= 0)
 0445            {
 0446                Throw.ArgumentOutOfRange(nameof(branchCount));
 447            }
 0448            ControlFlowBuilder branchBuilder = GetBranchBuilder();
 449
 450            // We want the offset before we add the opcode.
 0451            int ilOffset = Offset;
 452
 0453            OpCode(ILOpCode.Switch);
 0454            branchBuilder.RemainingSwitchBranches = branchCount;
 0455            CodeBuilder.WriteUInt32((uint)branchCount);
 456
 457            // We calculate the offset where the instruction will end.
 458            // The Offset property now accounts for the opcode byte and
 459            // the four bits of the count, and we also add four bytes for
 460            // each branch we are expecting from the user to emit.
 0461            int instructionEnd = Offset + 4 * branchCount;
 0462            return new SwitchInstructionEncoder(this, ilOffset, instructionEnd);
 0463        }
 464
 465        /// <summary>
 466        /// Associates specified label with the current IL offset.
 467        /// </summary>
 468        /// <param name="label">Label to mark.</param>
 469        /// <remarks>
 470        /// A single label may be marked multiple times, the last offset wins.
 471        /// </remarks>
 472        /// <exception cref="InvalidOperationException"><see cref="ControlFlowBuilder"/> is null.</exception>
 473        /// <exception cref="ArgumentException"><paramref name="label"/> was not defined by this encoder.</exception>
 474        /// <exception cref="ArgumentNullException"><paramref name="label"/> has default value.</exception>
 475        public void MarkLabel(LabelHandle label)
 0476        {
 0477            GetBranchBuilder().MarkLabel(Offset, label);
 0478        }
 479
 480        private ControlFlowBuilder GetBranchBuilder()
 0481        {
 0482            if (ControlFlowBuilder == null)
 0483            {
 0484                Throw.ControlFlowBuilderNotAvailable();
 485            }
 486
 0487            return ControlFlowBuilder;
 0488        }
 489    }
 490}
 491