// Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. #if !NET_4_6 #if !FEATURE_DYNAMIC_DELEGATE using System.Reflection; using System.Reflection.Emit; #endif namespace System.Dynamic.Utils { internal static class DelegateHelpers { private static readonly MethodInfo _funcInvoke = typeof(Func).GetMethod("Invoke"); internal static Delegate CreateObjectArrayDelegate(Type delegateType, Func handler) { #if !FEATURE_DYNAMIC_DELEGATE return CreateObjectArrayDelegateRefEmit(delegateType, handler); #else return Internal.Runtime.Augments.DynamicDelegateAugments.CreateObjectArrayDelegate(delegateType, handler); #endif } #if !FEATURE_DYNAMIC_DELEGATE // We will generate the following code: // // object ret; // object[] args = new object[parameterCount]; // args[0] = param0; // args[1] = param1; // ... // try { // ret = handler.Invoke(args); // } finally { // param0 = (T0)args[0]; // only generated for each byref argument // } // return (TRet)ret; private static Delegate CreateObjectArrayDelegateRefEmit(Type delegateType, Func handler) { var delegateInvokeMethod = delegateType.GetMethod("Invoke"); var returnType = delegateInvokeMethod.ReturnType; var hasReturnValue = returnType != typeof(void); var parameters = delegateInvokeMethod.GetParameters(); var paramTypes = new Type[parameters.Length + 1]; paramTypes[0] = typeof(Func); for (var i = 0; i < parameters.Length; i++) { paramTypes[i + 1] = parameters[i].ParameterType; } var thunkMethod = new DynamicMethod("Thunk", returnType, paramTypes); var ilgen = thunkMethod.GetILGenerator(); var argArray = ilgen.DeclareLocal(typeof(object[])); var 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 var hasRefArgs = false; for (var i = 0; i < parameters.Length; i++) { var paramIsByReference = parameters[i].ParameterType.IsByRef; var 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); } var 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 var invoke = _funcInvoke; ilgen.Emit(OpCodes.Callvirt, invoke); ilgen.Emit(OpCodes.Stloc, retValue); if (hasRefArgs) { // copy back ref/out args ilgen.BeginFinallyBlock(); for (var i = 0; i < parameters.Length; i++) { if (parameters[i].ParameterType.IsByRef) { var 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); } private static Type ConvertToBoxableType(Type t) { return (t.IsPointer) ? typeof(IntPtr) : t; } #endif } } #endif