private static void UnwindArguments(MethodInfo targetMethod, MethodDebugging generator, LocalBuilder argumentValues, bool isBeforeCall) { if (targetMethod.GetParameters().Length > 0) { for (var argLoad = 0; argLoad < targetMethod.GetParameters().Length; argLoad++) { var parameter = targetMethod.GetParameters()[argLoad]; var parameterType = parameter.ParameterType; if (isBeforeCall || parameterType.IsByRef) { if (parameterType.IsByRef) { generator.Emit(OpCodes.Ldarg, argLoad + 1); } generator.Emit(OpCodes.Ldloc, argumentValues); generator.Emit(OpCodes.Ldc_I4, argLoad); generator.Emit(OpCodes.Ldelem_Ref); // This code is odd. By-ref generic parameters are not reported as generic, // so that's why the FullName null check is done. // Also, the Unbox_Any won't work with by-ref generic arguments, // so the generic type has to be found within the generic arguments array // off of the target method. if (parameterType.IsGenericParameter || parameterType.FullName == null) { if (parameterType.IsByRef) { parameterType = parameterType.GetElementType(); generator.Emit(OpCodes.Unbox_Any, parameterType); generator.Emit(OpCodes.Stobj, parameterType); } else { generator.Emit(OpCodes.Unbox_Any, parameterType); generator.Emit(OpCodes.Starg, argLoad + 1); } } else { if (parameterType.IsValueType || (parameterType.HasElementType && parameterType.GetElementType().IsValueType)) { if (parameterType.IsByRef) { parameterType = parameterType.GetElementType(); generator.Emit(OpCodes.Unbox_Any, parameterType); var indirectCode = ProxyMethodBuilder.GetIndCode(parameterType, false); if (indirectCode == OpCodes.Stobj) { generator.Emit(indirectCode, parameterType); } else { generator.Emit(indirectCode); } } else { generator.Emit(OpCodes.Unbox_Any, parameterType); generator.Emit(OpCodes.Starg, argLoad + 1); } } else { if (parameterType.IsByRef) { parameterType = parameterType.GetElementType(); generator.Emit(OpCodes.Castclass, parameterType); generator.Emit(OpCodes.Stind_Ref); } else { generator.Emit(OpCodes.Castclass, parameterType); generator.Emit(OpCodes.Starg, argLoad + 1); } } } } } } }
private static void WindArguments(MethodInfo targetMethod, MethodDebugging generator, LocalBuilder argumentValues, bool isBeforeCall) { // Set up the arg array if (targetMethod.GetParameters().Length > 0) { for (int argLoad = 0; argLoad < targetMethod.GetParameters().Length; argLoad++) { var parameter = targetMethod.GetParameters()[argLoad]; var parameterType = parameter.ParameterType; if (isBeforeCall || parameterType.IsByRef) { generator.Emit(OpCodes.Ldloc, argumentValues); generator.Emit(OpCodes.Ldc_I4, argLoad); generator.Emit(OpCodes.Ldarg, argLoad + 1); if (parameterType.IsGenericParameter || parameterType.FullName == null) { if (parameterType.IsByRef) { parameterType = parameterType.GetElementType(); generator.Emit(OpCodes.Ldobj, parameterType); generator.Emit(OpCodes.Box, parameterType); } else { generator.Emit(OpCodes.Box, parameterType); } } else if (parameterType.IsValueType || (parameterType.HasElementType && parameterType.GetElementType().IsValueType)) { if (parameterType.IsByRef) { parameterType = parameterType.GetElementType(); var indirectCode = ProxyMethodBuilder.GetIndCode(parameterType, true); if (indirectCode == OpCodes.Ldobj) { generator.Emit(indirectCode, parameterType); } else { generator.Emit(indirectCode); } } generator.Emit(OpCodes.Box, parameterType); } else { if (parameterType.IsByRef) { generator.Emit(OpCodes.Ldind_Ref); } } generator.Emit(OpCodes.Stelem_Ref); } } } }
private static void BuildTargetMethods(TypeBuilder proxyBuilder, MethodBuilder onBeforeMethodInvocation, MethodBuilder onAfterMethodInvocation, Type targetType, FieldBuilder wrappedObject, Dictionary <MethodInfo, MethodMappings> targets, TypeDebugging debug) { var methodAttribs = MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.Private; foreach (var target in targets) { var targetMethod = target.Key; var arguments = new Type[targetMethod.GetParameters().Length]; for (int i = 0; i < targetMethod.GetParameters().Length; i++) { arguments[i] = targetMethod.GetParameters()[i].ParameterType; } var method = proxyBuilder.DefineMethod( targetMethod.Name + Proxy.ProxyExtension, methodAttribs, targetMethod.ReturnType, arguments); ProxyMethodBuilder.HandleGenericMethodArguments(targetMethod, method); // Determine if this method should override // the mapped method (OverridesInterfaceMethods == false) // or a number of itf. methods (OverridesInterfaceMethods == true) var mappings = target.Value; if (!mappings.OverridesInterfaceMethods) { proxyBuilder.DefineMethodOverride(method, targetMethod); } else { for (int itfs = 0; itfs < mappings.MappedMethods.Count; itfs++) { proxyBuilder.DefineMethodOverride(method, mappings.MappedMethods[itfs]); } } using (var generator = debug.GetMethodDebugging(method)) { var argumentValues = generator.DeclareLocal(typeof(object[])); var callMethod = generator.DeclareLocal(typeof(bool)); generator.DeclareLocal(typeof(Type)); var generatedException = generator.DeclareLocal(typeof(Exception)); LocalBuilder returnValue = null; var wrappedReturnValue = generator.DeclareLocal(typeof(object)); var baseMethod = generator.DeclareLocal(typeof(MethodBase)); var endCall = generator.DefineLabel(); // Check for a return value. if (targetMethod.ReturnType != typeof(void)) { returnValue = generator.DeclareLocal(targetMethod.ReturnType); } generator.Emit(OpCodes.Ldnull); generator.Emit(OpCodes.Stloc, generatedException); generator.Emit(OpCodes.Ldc_I4, targetMethod.GetParameters().Length); generator.Emit(OpCodes.Newarr, typeof(object)); generator.Emit(OpCodes.Stloc, argumentValues); // Get the target method. generator.Emit(OpCodes.Ldtoken, targetMethod); if (targetType.IsGenericType) { generator.Emit(OpCodes.Ldtoken, targetType); generator.Emit(OpCodes.Call, typeof(MethodBase).GetMethod(GetMethodFromHandleMethod, new Type[] { typeof(RuntimeMethodHandle), typeof(RuntimeTypeHandle) })); } else { generator.Emit(OpCodes.Call, typeof(MethodBase).GetMethod(GetMethodFromHandleMethod, new Type[] { typeof(RuntimeMethodHandle) })); } generator.Emit(OpCodes.Stloc, baseMethod); // call OnBeforeMethodInvocation. ProxyMethodBuilder.WindArguments(targetMethod, generator, argumentValues, true); generator.Emit(OpCodes.Ldarg_0); generator.Emit(OpCodes.Ldloc, baseMethod); generator.Emit(OpCodes.Ldloc, argumentValues); generator.Emit(OpCodes.Call, onBeforeMethodInvocation); generator.Emit(OpCodes.Stloc, callMethod); ProxyMethodBuilder.UnwindArguments(targetMethod, generator, argumentValues, true); // If the call should be cancelled, break to the end. generator.Emit(OpCodes.Ldloc, callMethod); generator.Emit(OpCodes.Brfalse, endCall); // Call the real method. generator.BeginExceptionBlock(); generator.Emit(OpCodes.Ldarg_0); generator.Emit(OpCodes.Ldfld, wrappedObject); for (var argLoad = 0; argLoad < targetMethod.GetParameters().Length; argLoad++) { generator.Emit(OpCodes.Ldarg, argLoad + 1); } generator.Emit(OpCodes.Callvirt, targetMethod); if (targetMethod.ReturnType != typeof(void)) { generator.Emit(OpCodes.Stloc, returnValue); } // call OnAfterInvocation ProxyMethodBuilder.WindArguments(targetMethod, generator, argumentValues, false); generator.Emit(OpCodes.Ldarg_0); generator.Emit(OpCodes.Ldloc, baseMethod); generator.Emit(OpCodes.Ldloc, argumentValues); ProxyMethodBuilder.WrapReturnValue(targetMethod, generator, returnValue, wrappedReturnValue); generator.Emit(OpCodes.Ldloc, generatedException); generator.Emit(OpCodes.Call, onAfterMethodInvocation); ProxyMethodBuilder.UnwrapReturnValue(targetMethod, generator, returnValue, wrappedReturnValue); ProxyMethodBuilder.UnwindArguments(targetMethod, generator, argumentValues, false); generator.BeginCatchBlock(typeof(Exception)); // call OnAfterInvocationWithException generator.Emit(OpCodes.Stloc, generatedException); ProxyMethodBuilder.WindArguments(targetMethod, generator, argumentValues, false); generator.Emit(OpCodes.Ldarg_0); generator.Emit(OpCodes.Ldloc, baseMethod); generator.Emit(OpCodes.Ldloc, argumentValues); ProxyMethodBuilder.WrapReturnValue(targetMethod, generator, returnValue, wrappedReturnValue); generator.Emit(OpCodes.Ldloc, generatedException); generator.Emit(OpCodes.Call, onAfterMethodInvocation); ProxyMethodBuilder.UnwrapReturnValue(targetMethod, generator, returnValue, wrappedReturnValue); ProxyMethodBuilder.UnwindArguments(targetMethod, generator, argumentValues, false); generator.EndExceptionBlock(); generator.MarkLabel(endCall); // Finally...return. if (targetMethod.ReturnType != typeof(void)) { generator.Emit(OpCodes.Ldloc, returnValue); } generator.Emit(OpCodes.Ret); } } }