System.Dynamic.Utils.DelegateHelpers.CreateObjectArrayDelegateRefEmit C# (CSharp) Method

CreateObjectArrayDelegateRefEmit() private static method

private static CreateObjectArrayDelegateRefEmit ( Type delegateType, object>.System handler ) : Delegate
delegateType Type
handler object>.System
return Delegate
        private static Delegate CreateObjectArrayDelegateRefEmit(Type delegateType, System.Func<object[], object> handler)
        {
            MethodInfo delegateInvokeMethod = delegateType.GetMethod("Invoke");

            Type returnType = delegateInvokeMethod.ReturnType;
            bool hasReturnValue = returnType != typeof(void);

            ParameterInfo[] parameters = delegateInvokeMethod.GetParameters();
            Type[] paramTypes = new Type[parameters.Length + 1];
            paramTypes[0] = typeof(Func<object[], object>);
            for (int i = 0; i < parameters.Length; i++)
            {
                paramTypes[i + 1] = parameters[i].ParameterType;
            }

            DynamicMethod thunkMethod = new DynamicMethod("Thunk", returnType, paramTypes);
            ILGenerator ilgen = thunkMethod.GetILGenerator();

            LocalBuilder argArray = ilgen.DeclareLocal(typeof(object[]));
            LocalBuilder retValue = ilgen.DeclareLocal(typeof(object));

            // create the argument array
            ilgen.Emit(OpCodes.Ldc_I4, parameters.Length);
            ilgen.Emit(OpCodes.Newarr, typeof(object));
            ilgen.Emit(OpCodes.Stloc, argArray);

            // populate object array
            bool hasRefArgs = false;
            for (int i = 0; i < parameters.Length; i++)
            {
                bool paramIsByReference = parameters[i].ParameterType.IsByRef;
                Type paramType = parameters[i].ParameterType;
                if (paramIsByReference)
                    paramType = paramType.GetElementType();

                hasRefArgs = hasRefArgs || paramIsByReference;

                ilgen.Emit(OpCodes.Ldloc, argArray);
                ilgen.Emit(OpCodes.Ldc_I4, i);
                ilgen.Emit(OpCodes.Ldarg, i + 1);

                if (paramIsByReference)
                {
                    ilgen.Emit(OpCodes.Ldobj, paramType);
                }
                Type boxType = ConvertToBoxableType(paramType);
                ilgen.Emit(OpCodes.Box, boxType);
                ilgen.Emit(OpCodes.Stelem_Ref);
            }

            if (hasRefArgs)
            {
                ilgen.BeginExceptionBlock();
            }

            // load delegate
            ilgen.Emit(OpCodes.Ldarg_0);

            // load array
            ilgen.Emit(OpCodes.Ldloc, argArray);

            // invoke Invoke
            MethodInfo invoke = typeof(Func<object[], object>).GetMethod("Invoke");
            ilgen.Emit(OpCodes.Callvirt, invoke);
            ilgen.Emit(OpCodes.Stloc, retValue);

            if (hasRefArgs)
            {
                // copy back ref/out args
                ilgen.BeginFinallyBlock();
                for (int i = 0; i < parameters.Length; i++)
                {
                    if (parameters[i].ParameterType.IsByRef)
                    {
                        Type byrefToType = parameters[i].ParameterType.GetElementType();

                        // update parameter
                        ilgen.Emit(OpCodes.Ldarg, i + 1);
                        ilgen.Emit(OpCodes.Ldloc, argArray);
                        ilgen.Emit(OpCodes.Ldc_I4, i);
                        ilgen.Emit(OpCodes.Ldelem_Ref);
                        ilgen.Emit(OpCodes.Unbox_Any, byrefToType);
                        ilgen.Emit(OpCodes.Stobj, byrefToType);
                    }
                }
                ilgen.EndExceptionBlock();
            }

            if (hasReturnValue)
            {
                ilgen.Emit(OpCodes.Ldloc, retValue);
                ilgen.Emit(OpCodes.Unbox_Any, ConvertToBoxableType(returnType));
            }

            ilgen.Emit(OpCodes.Ret);

            // TODO: we need to cache these.
            return thunkMethod.CreateDelegate(delegateType, handler);
        }