private void ProcessIlEmitMethodCall(Instruction emitCallInstruction, out Instruction?nextInstruction)
    {
        var emittedInstruction = CreateInstructionToEmit();

        _il.Replace(emitCallInstruction, emittedInstruction);

        if (emittedInstruction.OpCode.OpCodeType == OpCodeType.Prefix)
        {
            _il.RemoveNopsAfter(emittedInstruction);
        }

        var sequencePoint = _sequencePoints.MapSequencePoint(emitCallInstruction, emittedInstruction);

        if (emittedInstruction.Previous?.OpCode.OpCodeType == OpCodeType.Prefix)
        {
            _sequencePoints.MergeWithPreviousSequencePoint(sequencePoint);
        }

        nextInstruction = emittedInstruction.Next;
        RemoveNopInDebugBuild(ref nextInstruction);
        nextInstruction = emittedInstruction.NextSkipNops();

        switch (emittedInstruction.OpCode.Code)
        {
        case Code.Ret:
        case Code.Endfinally:
        case Code.Endfilter:
        {
            if (nextInstruction?.OpCode == emittedInstruction.OpCode)
            {
                _il.Remove(emittedInstruction);
                _log.Debug($"Removed duplicate {emittedInstruction.OpCode}");
            }

            break;
        }

        case Code.Leave:
        case Code.Leave_S:
        case Code.Throw:
        case Code.Rethrow:
        {
            if (nextInstruction?.OpCode == OpCodes.Leave || nextInstruction?.OpCode == OpCodes.Leave_S)
            {
                _il.RemoveNopsAfter(emittedInstruction);
                _il.Remove(emittedInstruction);
                _il.Replace(nextInstruction, emittedInstruction);
                _log.Debug($"Replaced {nextInstruction.OpCode} with emitted {emittedInstruction.OpCode}");
                nextInstruction = emittedInstruction.NextSkipNops();
            }

            break;
        }
        }

        Instruction CreateInstructionToEmit()
        {
            var method = (MethodReference)emitCallInstruction.Operand;
            var opCode = OpCodeMap.FromCecilFieldName(method.Name);
            var args   = _il.GetArgumentPushInstructionsInSameBasicBlock(emitCallInstruction);

            switch (opCode.OperandType)
            {
            case OperandType.InlineNone:
            {
                if (args.Length != 0)
                {
                    throw new InstructionWeavingException(emitCallInstruction, "Unexpected operand argument");
                }

                return(_il.Create(opCode));
            }

            case OperandType.InlineI:
            case OperandType.ShortInlineI:
            case OperandType.InlineI8:
            case OperandType.InlineR:
            case OperandType.ShortInlineR:
                return(_il.CreateConst(opCode, _consumer.ConsumeArgConst(args.Single())));

            case OperandType.InlineString:
                return(_il.CreateConst(opCode, _consumer.ConsumeArgString(args.Single())));

            case OperandType.InlineType:
            {
                if (method.IsGenericInstance)
                {
                    return(_il.Create(opCode, ((GenericInstanceMethod)method).GenericArguments[0]));
                }

                return(_il.Create(opCode, _consumer.ConsumeArgTypeRef(args.Single())));
            }

            case OperandType.InlineMethod:
                return(_il.Create(opCode, _consumer.ConsumeArgMethodRef(args.Single())));

            case OperandType.InlineField:
                return(_il.Create(opCode, _consumer.ConsumeArgFieldRef(args.Single())));

            case OperandType.InlineTok:
            {
                if (method.IsGenericInstance)
                {
                    return(_il.Create(opCode, ((GenericInstanceMethod)method).GenericArguments[0]));
                }

                return(method.Parameters[0].ParameterType.FullName switch
                    {
                        KnownNames.Full.TypeRefType => _il.Create(opCode, _consumer.ConsumeArgTypeRef(args.Single())),
                        KnownNames.Full.MethodRefType => _il.Create(opCode, _consumer.ConsumeArgMethodRef(args.Single())),
                        KnownNames.Full.FieldRefType => _il.Create(opCode, _consumer.ConsumeArgFieldRef(args.Single())),
                        _ => throw new InstructionWeavingException(emitCallInstruction, $"Unexpected argument type: {method.Parameters[0].ParameterType.FullName}")
                    });
            }
Exemple #2
0
    private TypeRefBuilder ConsumeArgTypeRefBuilder(Instruction instruction)
    {
        if (instruction.OpCode.FlowControl != FlowControl.Call || instruction.Operand is not MethodReference method)
        {
            throw UnexpectedInstruction(instruction, "a method call");
        }

        switch (method.GetElementMethod().FullName)
        {
        case "System.Type System.Type::GetTypeFromHandle(System.RuntimeTypeHandle)":
        {
            var ldToken = _il.GetArgumentPushInstructionsInSameBasicBlock(instruction).Single();
            if (ldToken.OpCode != OpCodes.Ldtoken)
            {
                throw UnexpectedInstruction(ldToken, OpCodes.Ldtoken);
            }

            var builder = new TypeRefBuilder(Module, (TypeReference)ldToken.Operand);

            _il.Remove(ldToken);
            _il.Remove(instruction);
            return(builder);
        }

        case "InlineIL.TypeRef InlineIL.TypeRef::op_Implicit(System.Type)":
        case "InlineIL.TypeRef InlineIL.TypeRef::Type(System.Type)":
        case "System.Void InlineIL.TypeRef::.ctor(System.Type)":
        {
            var builder = ConsumeArgTypeRefBuilder(_il.GetArgumentPushInstructionsInSameBasicBlock(instruction).Single());

            _il.Remove(instruction);
            return(builder);
        }

        case "InlineIL.TypeRef InlineIL.TypeRef::Type()":
        {
            var builder = new TypeRefBuilder(Module, ((GenericInstanceMethod)method).GenericArguments[0]);

            _il.Remove(instruction);
            return(builder);
        }

        case "System.Void InlineIL.TypeRef::.ctor(System.String,System.String)":
        {
            var args         = _il.GetArgumentPushInstructionsInSameBasicBlock(instruction);
            var assemblyName = ConsumeArgString(args[0]);
            var typeName     = ConsumeArgString(args[1]);
            var builder      = new TypeRefBuilder(Module, assemblyName, typeName);

            _il.Remove(instruction);
            return(builder);
        }

        case "InlineIL.TypeRef InlineIL.GenericParameters::get_Item(System.Int32)":
        {
            var args = _il.GetArgumentPushInstructionsInSameBasicBlock(instruction);
            var genericParameterType  = ConsumeArgGenericParameterType(args[0]);
            var genericParameterIndex = ConsumeArgInt32(args[1]);
            var builder = new TypeRefBuilder(Module, genericParameterType, genericParameterIndex);

            _il.Remove(instruction);
            return(builder);
        }

        case "System.Type System.Type::MakeGenericMethodParameter(System.Int32)":
        {
            var args = _il.GetArgumentPushInstructionsInSameBasicBlock(instruction);
            var genericParameterIndex = ConsumeArgInt32(args[0]);
            var builder = new TypeRefBuilder(Module, GenericParameterType.Method, genericParameterIndex);

            _il.Remove(instruction);
            return(builder);
        }

        case "InlineIL.TypeRef InlineIL.TypeRef::MakePointerType()":
        case "System.Type System.Type::MakePointerType()":
        {
            var builder = ConsumeArgTypeRefBuilder(_il.GetArgumentPushInstructionsInSameBasicBlock(instruction).Single());
            builder.MakePointerType();

            _il.Remove(instruction);
            return(builder);
        }

        case "InlineIL.TypeRef InlineIL.TypeRef::MakeByRefType()":
        case "System.Type System.Type::MakeByRefType()":
        {
            var builder = ConsumeArgTypeRefBuilder(_il.GetArgumentPushInstructionsInSameBasicBlock(instruction).Single());
            builder.MakeByRefType();

            _il.Remove(instruction);
            return(builder);
        }

        case "InlineIL.TypeRef InlineIL.TypeRef::MakeArrayType()":
        case "System.Type System.Type::MakeArrayType()":
        {
            var builder = ConsumeArgTypeRefBuilder(_il.GetArgumentPushInstructionsInSameBasicBlock(instruction).Single());
            builder.MakeArrayType(1);

            _il.Remove(instruction);
            return(builder);
        }

        case "InlineIL.TypeRef InlineIL.TypeRef::MakeArrayType(System.Int32)":
        case "System.Type System.Type::MakeArrayType(System.Int32)":
        {
            var args    = _il.GetArgumentPushInstructionsInSameBasicBlock(instruction);
            var builder = ConsumeArgTypeRefBuilder(args[0]);
            var rank    = ConsumeArgInt32(args[1]);
            builder.MakeArrayType(rank);

            _il.Remove(instruction);
            return(builder);
        }

        case "InlineIL.TypeRef InlineIL.TypeRef::MakeGenericType(InlineIL.TypeRef[])":
        case "System.Type System.Type::MakeGenericType(System.Type[])":
        {
            var args        = _il.GetArgumentPushInstructionsInSameBasicBlock(instruction);
            var builder     = ConsumeArgTypeRefBuilder(args[0]);
            var genericArgs = ConsumeArgArray(args[1], ConsumeArgTypeRefBuilder);
            builder.MakeGenericType(genericArgs);

            _il.Remove(instruction);
            return(builder);
        }

        case "InlineIL.TypeRef InlineIL.TypeRef::WithOptionalModifier(InlineIL.TypeRef)":
        {
            var args         = _il.GetArgumentPushInstructionsInSameBasicBlock(instruction);
            var builder      = ConsumeArgTypeRefBuilder(args[0]);
            var modifierType = ConsumeArgTypeRef(args[1]);
            builder.AddOptionalModifier(modifierType);

            _il.Remove(instruction);
            return(builder);
        }

        case "InlineIL.TypeRef InlineIL.TypeRef::WithRequiredModifier(InlineIL.TypeRef)":
        {
            var args         = _il.GetArgumentPushInstructionsInSameBasicBlock(instruction);
            var builder      = ConsumeArgTypeRefBuilder(args[0]);
            var modifierType = ConsumeArgTypeRef(args[1]);
            builder.AddRequiredModifier(modifierType);

            _il.Remove(instruction);
            return(builder);
        }

        default:
            throw UnexpectedInstruction(instruction, "a type reference");
        }
    }