Cilador.Clone.CloningExtensions.ApplyLocalVariableTranslation C# (CSharp) Method

ApplyLocalVariableTranslation() public static method

Examines the instruction and, if it accesses a local variable, provides a new version that translates the access by the given count.
If the instruction does not reference a local variable or if the translation is zero, then the original instruction will be returned. Likewise, if the instruction operand is a VariableDefinition and the translation doesn't cause the opcode to change (e.g. from stloc_S to stloc), then the original instruction is returned.
public static ApplyLocalVariableTranslation ( this instruction, int translate, IEnumerable variables ) : Instruction
instruction this Instruction to look at.
translate int How to translate the referenced parameter, if any is referenced. Negative is a left translation, and positive is right.
variables IEnumerable Collection of variables that may be referenced.
return Mono.Cecil.Cil.Instruction
        public static Instruction ApplyLocalVariableTranslation(
            this Instruction instruction,
            int translate,
            IEnumerable<VariableDefinition> variables)
        {
            Contract.Requires(instruction != null);
            Contract.Requires(variables != null);

            int? initialVariableIndex;
            if (translate == 0 || !instruction.TryGetVariableIndex(out initialVariableIndex)) { return instruction; }
            Contract.Assert(initialVariableIndex.HasValue);

            var newIndex = initialVariableIndex.Value + translate;
            if (newIndex < 0)
            {
                throw new InvalidOperationException("A variable index less than 0 cannot be used.");
            }
            if (newIndex > ushort.MaxValue)
            {
                throw new InvalidOperationException($"A variable index greater than {ushort.MaxValue} cannot be used.");
            }

            var indexedVariable = new Lazy<VariableDefinition>(
                () =>
                {
                    var value = instruction.Operand as VariableDefinition ??
                                variables.FirstOrDefault(variable => variable.Index == initialVariableIndex.Value);

                    if (value == null)
                    {
                        throw new InvalidOperationException("An instruction references a variable that cannot be found.");
                    }

                    return value;
                });

            if (instruction.OpCode.Code.IsStoreVariableOpCode())
            {
                switch(newIndex)
                {
                    case 0:
                        return Instruction.Create(OpCodes.Stloc_0);

                    case 1:
                        return Instruction.Create(OpCodes.Stloc_1);

                    case 2:
                        return Instruction.Create(OpCodes.Stloc_2);

                    case 3:
                        return Instruction.Create(OpCodes.Stloc_3);

                    default:
                        // only make a new instruction if the opcode needs to change
                        if (newIndex <= byte.MaxValue)
                        {
                            return instruction.OpCode.Code == Code.Stloc_S ?
                                instruction :
                                Instruction.Create(OpCodes.Stloc_S, indexedVariable.Value);
                        }

                        return instruction.OpCode.Code == Code.Stloc ?
                            instruction :
                            Instruction.Create(OpCodes.Stloc, indexedVariable.Value);
                }
            }

            if (instruction.OpCode.Code.IsLoadVariableOpCode())
            {
                switch (newIndex)
                {
                    case 0:
                        return Instruction.Create(OpCodes.Ldloc_0);

                    case 1:
                        return Instruction.Create(OpCodes.Ldloc_1);

                    case 2:
                        return Instruction.Create(OpCodes.Ldloc_2);

                    case 3:
                        return Instruction.Create(OpCodes.Ldloc_3);

                    default:
                        // only make a new instruction if the opcode needs to change
                        if (newIndex <= byte.MaxValue)
                        {
                            return instruction.OpCode.Code == Code.Ldloc_S ?
                                instruction :
                                Instruction.Create(OpCodes.Ldloc_S, indexedVariable.Value);
                        }

                        return instruction.OpCode.Code == Code.Ldloc ?
                            instruction :
                            Instruction.Create(OpCodes.Ldloc, indexedVariable.Value);
                }
            }

            if (!instruction.OpCode.Code.IsLoadVariableAddressOpCode())
            {
                throw new InvalidOperationException("Expected a variable store or load opcode.");
            }

            // only make a new instruction if the opcode needs to change
            if (newIndex <= byte.MaxValue)
            {
                return instruction.OpCode.Code == Code.Ldloca_S ?
                    instruction :
                    Instruction.Create(OpCodes.Ldloca_S, indexedVariable.Value);
            }

            return instruction.OpCode.Code == Code.Ldloca ?
                instruction :
                Instruction.Create(OpCodes.Ldloca, indexedVariable.Value);
        }