Example #1
0
            public void MethodCall(MethodCall methodCall)
            {
                bool isStatic = methodCall.Operand is TypeAccess;

                if (!isStatic)
                {
                    bool emitBox       = false;
                    bool emitStoreLoad = false;

                    if (
                        (methodCall.Operand is FieldAccess || methodCall.Operand is Constant) &&
                        methodCall.Operand.Type.IsValueType &&
                        !methodCall.MethodInfo.DeclaringType.IsValueType
                        )
                    {
                        emitBox = true;
                    }
                    else if (
                        methodCall.Operand.Type.IsValueType && (
                            !(methodCall.Operand is FieldAccess) ||
                            methodCall.MethodInfo.DeclaringType.IsValueType
                            )
                        )
                    {
                        emitStoreLoad = true;
                    }

                    // Signal field access that we're using the field as a
                    // parameter.

                    _fieldAsParameter =
                        methodCall.Operand is FieldAccess &&
                        methodCall.MethodInfo.DeclaringType.IsValueType &&
                        !emitStoreLoad;

                    try
                    {
                        methodCall.Operand.Accept(this);
                    }
                    finally
                    {
                        _fieldAsParameter = false;
                    }

                    if (emitBox)
                    {
                        _il.Emit(OpCodes.Box, methodCall.Operand.Type);
                    }
                    else if (emitStoreLoad)
                    {
                        Debug.Assert(methodCall.Operand.Type == methodCall.MethodInfo.DeclaringType);

                        var builder = _il.DeclareLocal(methodCall.Operand.Type);

                        _il.Emit(OpCodes.Stloc, builder);
                        _il.Emit(OpCodes.Ldloca, builder);
                    }
                }

                var parameters = methodCall.MethodInfo.GetParameters();
                var arguments  = methodCall.Arguments;

                bool paramsMethod =
                    parameters.Length > 0 &&
                    parameters[parameters.Length - 1].GetCustomAttributes(typeof(ParamArrayAttribute), true).Length == 1;

                int mandatoryParameterCount =
                    paramsMethod
                    ? parameters.Length - 1
                    : parameters.Length;

                for (int i = 0; i < mandatoryParameterCount; i++)
                {
                    Emit(arguments[i], parameters[i].ParameterType);
                }

                if (paramsMethod)
                {
                    var  paramsType  = parameters[parameters.Length - 1].ParameterType;
                    var  elementType = paramsType.GetElementType();
                    bool emitted     = false;

                    // When the params argument is missing, a new array is issued.

                    if (arguments.Count == mandatoryParameterCount)
                    {
                        ILUtil.EmitEmptyArray(_il, elementType);
                        emitted = true;
                    }
                    else if (arguments.Count == mandatoryParameterCount + 1)
                    {
                        var lastArgument = arguments[arguments.Count - 1];

                        // Null arguments are passed blindly.
                        if (lastArgument is Constant constant && constant.Value == null)
                        {
                            ILUtil.EmitNull(_il);
                            emitted = true;
                        }
                        else
                        {
                            // So are array arguments that can be casted.

                            if (
                                lastArgument.Type.IsArray &&
                                TypeUtil.CanCastImplicitely(lastArgument.Type, paramsType, false)
                                )
                            {
                                Emit(lastArgument, paramsType);
                                emitted = true;
                            }
                        }
                    }

                    // If we didn't find a shortcut, emit the array with all
                    // arguments.

                    if (!emitted)
                    {
                        ILUtil.EmitArray(
                            _il,
                            elementType,
                            arguments.Count - mandatoryParameterCount,
                            p => Emit(arguments[mandatoryParameterCount + p], elementType)
                            );
                    }
                }