public Instruction GetInstruction(Instruction instruction)
            {
                _usages++;
                if (OpCodeHelper.IsLoadArg(instruction))
                {
                    if (_usages == 1)
                    {
                        return(Defer());
                    }

                    ProcessDeferred();
                    return(GetLdArgInstruction());
                }

                ProcessDeferred();
                if (OpCodeHelper.IsLoadArgA(instruction))
                {
                    return(OpCodeHelper.CreateLoadLocA(GetVariableDefinition()));
                }

                if (OpCodeHelper.IsStoreArg(instruction))
                {
                    return(OpCodeHelper.CreateStoreLoc(GetVariableDefinition()));
                }

                throw new NotSupportedException($"Unknown arg instruction {instruction.OpCode}");
            }
 private Instruction GetLdArgInstruction()
 {
     if (CanInline)
     {
         var ldArgInstruction = OpCodeHelper.Clone(_pushInstruction);
         _inlinedInstructions.Add(ldArgInstruction);
         return(ldArgInstruction);
     }
     return(OpCodeHelper.CreateLoadLoc(GetVariableDefinition()));
 }
            public void Finish()
            {
                if (_deferredInstruction != null)
                {
                    // keep on stack
                    if (KeepOnStack)
                    {
                        _inlineMethodWeaver.Remove(_deferredInstruction);
                        _deferredInstruction = null;
                        return;
                    }
                }

                ProcessDeferred();
                if (_variableDefinition == null)
                {
                    if (CanRemovePush)
                    {
                        _inlineMethodWeaver.Remove(_pushInstruction);
                    }
                    else
                    {
                        // neutralize push instruction
                        if (_pushInstruction == null)
                        {
                            InsertConsumeTopArg(Instruction.Create(OpCodes.Pop));
                        }
                        else
                        {
                            for (var i = 0; i < _pushInstruction.GetPushCount(); i++)
                            {
                                _inlineMethodWeaver.InsertAfter(_pushInstruction, Instruction.Create(OpCodes.Pop));
                            }
                        }
                    }
                }
                else
                {
                    // place store loc
                    var storeLoc = OpCodeHelper.CreateStoreLoc(_variableDefinition);
                    if (_pushInstruction == null)
                    {
                        InsertConsumeTopArg(storeLoc);
                    }
                    else
                    {
                        _inlineMethodWeaver.InsertAfter(_pushInstruction, storeLoc);
                    }
                }

                _inlineMethodWeaver._argStack.Consume(this);
            }
 private void ProcessDeferred()
 {
     if (_deferredInstruction != null)
     {
         var ldArgInstruction = GetLdArgInstruction();
         OpCodeHelper.ReplaceInstruction(_deferredInstruction, ldArgInstruction);
         if (_inlinedInstructions.Count > 0 && _inlinedInstructions[_inlinedInstructions.Count - 1] == ldArgInstruction)
         {
             _inlinedInstructions[_inlinedInstructions.Count - 1] = _deferredInstruction;
         }
         _deferredInstruction = null;
     }
 }
            private VariableDefinition GetVariableDefinition()
            {
                if (_variableDefinition == null)
                {
                    _variableDefinition = new VariableDefinition(_inlineMethodWeaver._parameters[_paramIndex].ParameterType);
                    _inlineMethodWeaver._parentMethod.Body.Variables.Add(_variableDefinition);

                    // revert inline
                    if (_inlinedInstructions.Count > 0)
                    {
                        foreach (var inlinedInstruction in _inlinedInstructions)
                        {
                            OpCodeHelper.ReplaceInstruction(inlinedInstruction, OpCodeHelper.CreateLoadLoc(_variableDefinition));
                        }
                        _inlinedInstructions.Clear();
                    }
                }

                return(_variableDefinition);
            }
        public void Process()
        {
            CreateVars();
            CreateArgs();

            var innerVariables  = _method.Body.Variables;
            var parentVariables = _parentMethod.Body.Variables;

            var isLoadArgs = true;

            // inline body
            var instructions = _method.Body.Instructions;

            foreach (var instruction in instructions)
            {
                var         nextInstruction = instruction.Next;
                Instruction newInstruction  = null;

                // arg
                var parameterDefinition = OpCodeHelper.GetArgParameterDefinition(instruction, _parameters);
                if (parameterDefinition != null)
                {
                    var arg = _args[parameterDefinition.Sequence];
                    newInstruction = arg.GetInstruction(instruction);
                    if (isLoadArgs)
                    {
                        if (OpCodeHelper.IsLoadArg(instruction) && arg.IsDeferred)
                        {
                            _firstLoadArgs.Add(new LoadArgInfo(parameterDefinition.Sequence));
                        }
                        else
                        {
                            isLoadArgs = false;
                        }
                    }
                }
                else
                {
                    if (isLoadArgs && instruction.OpCode != OpCodes.Nop)
                    {
                        isLoadArgs = false;
                    }
                }

                // loc
                var innerVariableDefinition = OpCodeHelper.GetLocVariableDefinition(instruction, innerVariables);
                if (innerVariableDefinition != null)
                {
                    newInstruction = OpCodeHelper.CreateVarInstruction(instruction,
                                                                       parentVariables[innerVariableDefinition.Index + _firstInnerVariableIndex]);
                }

                // branch
                if (instruction.OpCode.OperandType == OperandType.InlineBrTarget ||
                    instruction.OpCode.OperandType == OperandType.ShortInlineBrTarget)
                {
                    var target = (Instruction)instruction.Operand;
                    // fix target ret
                    if (target?.OpCode.Code == Code.Ret)
                    {
                        newInstruction = Instruction.Create(instruction.OpCode, _callInstruction.Next);
                    }
                }

                // ret
                if (instruction.OpCode.Code == Code.Ret)
                {
                    // skip last return
                    if (nextInstruction == null)
                    {
                        break;
                    }

                    newInstruction = Instruction.Create(OpCodes.Br, _callInstruction.Next);
                }

                if (newInstruction == null)
                {
                    newInstruction = OpCodeHelper.Clone(instruction);
                }

                _instructionMap[instruction] = newInstruction;
                AppendToBody(newInstruction);
            }

            FinishArgs();
            Remove(_callInstruction);

            // replace call target
            if (_firstBodyInstruction != null || _beforeBodyInstruction != null)
            {
                _instructionMap[_callInstruction] = _beforeBodyInstruction ?? _firstBodyInstruction;
            }

            FixInstructions();
        }
        private void FixInstructions()
        {
            // TODO: optimize (do once per parent method)
            // fix targets, extend instructions
            var instruction             = _parentMethod.Body.Instructions.FirstOrDefault();
            var offset                  = 0;
            var shortBranchInstructions = new List <Instruction>();

            while (instruction != null)
            {
                var nextInstruction = instruction.Next;

                if (instruction.Operand is Instruction opInstruction)
                {
                    if (GetInstructionFromMap(opInstruction, out var newInstruction))
                    {
                        instruction.Operand = newInstruction;
                    }
                }
                else if (instruction.Operand is Instruction[] opInstructions)
                {
                    for (var index = 0; index < opInstructions.Length; index++)
                    {
                        if (GetInstructionFromMap(opInstructions[index], out var newInstruction))
                        {
                            opInstructions[index] = newInstruction;
                        }
                    }
                }

                if (instruction.OpCode.OperandType == OperandType.ShortInlineBrTarget)
                {
                    shortBranchInstructions.Add(instruction);
                }

                // extend short variable instructions to long if needed
                OpCodeHelper.ExtendVariableOpCode(instruction);

                instruction.Offset = offset;
                offset            += instruction.GetSize();

                instruction = nextInstruction;
            }

            // extend short branch instructions if needed
            bool wasExtended;

            do
            {
                wasExtended = false;
                for (var i = shortBranchInstructions.Count - 1; i >= 0; i--)
                {
                    var shortBranchInstruction = shortBranchInstructions[i];
                    var target = (Instruction)shortBranchInstruction.Operand;
                    var diff   = target.Offset - shortBranchInstruction.Offset - shortBranchInstruction.GetSize();
                    if (diff < sbyte.MinValue || diff > sbyte.MaxValue)
                    {
                        OpCodeHelper.ExtendBranchOpCode(shortBranchInstruction);
                        shortBranchInstructions.RemoveAt(i);
                        wasExtended = true;
                    }
                }

                if (wasExtended && shortBranchInstructions.Count > 0)
                {
                    CalcParentMethodOffsets();
                }
            } while (wasExtended);
        }