private MethodBuilder CreateMethodOverride(MethodBuilder delegateMethod) { MethodAttributes attrs = methodToOverride.Attributes & ~MethodAttributes.NewSlot & ~MethodAttributes.Abstract; MethodBuilder methodBuilder = typeBuilder.DefineMethod(methodToOverride.Name, attrs); var paramMapper = new MethodOverrideParameterMapper(methodToOverride); paramMapper.SetupParameters(methodBuilder); methodBuilder.SetReturnType(paramMapper.GetParameterType(methodToOverride.ReturnType)); methodBuilder.SetParameters(methodParameters.Select(pi => paramMapper.GetParameterType(pi.ParameterType)).ToArray()); int paramNum = 1; foreach (ParameterInfo pi in methodParameters) { methodBuilder.DefineParameter(paramNum++, pi.Attributes, pi.Name); } ILGenerator il = methodBuilder.GetILGenerator(); LocalBuilder methodReturn = il.DeclareLocal(typeof(IMethodReturn)); LocalBuilder ex = il.DeclareLocal(typeof(Exception)); LocalBuilder parameterArray = il.DeclareLocal(typeof(object[])); LocalBuilder inputs = il.DeclareLocal(typeof(VirtualMethodInvocation)); // Create instance of VirtualMethodInvocation il.Emit(OpCodes.Ldarg_0); // target object il.Emit(OpCodes.Ldtoken, methodToOverride); if (methodToOverride.DeclaringType.IsGenericType) { il.Emit(OpCodes.Ldtoken, methodToOverride.DeclaringType); il.Emit(OpCodes.Call, MethodBaseMethods.GetMethodForGenericFromHandle); } else { il.Emit(OpCodes.Call, MethodBaseMethods.GetMethodFromHandle); // target method } EmitLoadConstant(il, methodParameters.Length); il.Emit(OpCodes.Newarr, typeof(object)); // object[] parameters if (methodParameters.Length > 0) { il.Emit(OpCodes.Stloc, parameterArray); for (int i = 0; i < methodParameters.Length; ++i) { il.Emit(OpCodes.Ldloc, parameterArray); EmitLoadConstant(il, i); EmitLoadArgument(il, i); if (methodParameters[i].ParameterType.IsValueType || methodParameters[i].ParameterType.IsGenericParameter) { il.Emit(OpCodes.Box, paramMapper.GetParameterType(methodParameters[i].ParameterType)); } else if (methodParameters[i].ParameterType.IsByRef) { Type elementType = paramMapper.GetElementType(methodParameters[i].ParameterType); il.Emit(OpCodes.Ldobj, elementType); if (elementType.IsValueType || elementType.IsGenericParameter) { il.Emit(OpCodes.Box, elementType); } } il.Emit(OpCodes.Stelem_Ref); } il.Emit(OpCodes.Ldloc, parameterArray); } il.Emit(OpCodes.Newobj, VirtualMethodInvocationMethods.VirtualMethodInvocation); il.Emit(OpCodes.Stloc, inputs); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, proxyInterceptionPipelineField); il.Emit(OpCodes.Ldloc, inputs); // Put delegate reference onto the stack il.Emit(OpCodes.Ldarg_0); MethodInfo invokeTarget = delegateMethod; if(delegateMethod.IsGenericMethod) { invokeTarget = delegateMethod.MakeGenericMethod(paramMapper.MappedGenericParameters); } il.Emit(OpCodes.Ldftn, invokeTarget); il.Emit(OpCodes.Newobj, InvokeInterceptionBehaviorDelegateMethods.InvokeInterceptionBehaviorDelegate); // And call the pipeline il.Emit(OpCodes.Call, InterceptionBehaviorPipelineMethods.Invoke); il.Emit(OpCodes.Stloc, methodReturn); // Was there an exception? Label noException = il.DefineLabel(); il.Emit(OpCodes.Ldloc, methodReturn); il.EmitCall(OpCodes.Callvirt, IMethodReturnMethods.GetException, null); il.Emit(OpCodes.Stloc, ex); il.Emit(OpCodes.Ldloc, ex); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ceq); il.Emit(OpCodes.Brtrue_S, noException); il.Emit(OpCodes.Ldloc, ex); il.Emit(OpCodes.Throw); il.MarkLabel(noException); // handle return value if (MethodHasReturnValue) { il.Emit(OpCodes.Ldloc, methodReturn); il.EmitCall(OpCodes.Callvirt, IMethodReturnMethods.GetReturnValue, null); if (ReturnType.IsValueType || ReturnType.IsGenericParameter) { il.Emit(OpCodes.Unbox_Any, paramMapper.GetParameterType(ReturnType)); } else { il.Emit(OpCodes.Castclass, paramMapper.GetParameterType(ReturnType)); } } // handle byref parameters if (methodParameters.Length > 0) { int outArgIndex = 0; foreach (int parameterIndex in OutputParameterIndices) { // Get parameter value (the address) onto the stack) Type elementType = paramMapper.GetElementType(methodParameters[parameterIndex].ParameterType); EmitLoadArgument(il, parameterIndex); // Get result of output parameter out of the results array il.Emit(OpCodes.Ldloc, methodReturn); il.Emit(OpCodes.Callvirt, IMethodReturnMethods.GetOutputs); EmitLoadConstant(il, outArgIndex); il.Emit(OpCodes.Callvirt, IListMethods.GetItem); EmitUnboxOrCast(il, elementType); // And store the results il.Emit(OpCodes.Stobj, elementType); ++outArgIndex; } } il.Emit(OpCodes.Ret); return methodBuilder; }
private MethodBuilder CreateMethodOverride(MethodBuilder delegateMethod) { string methodName = this.explicitImplementation ? methodToOverride.DeclaringType.Name + "." + methodToOverride.Name : methodToOverride.Name; MethodBuilder methodBuilder = typeBuilder.DefineMethod( methodName, this.explicitImplementation ? ExplicitImplementationAttributes : ImplicitImplementationAttributes); var paramMapper = new MethodOverrideParameterMapper(methodToOverride); paramMapper.SetupParameters(methodBuilder, this.targetInterfaceParameterMapper); methodBuilder.SetReturnType(paramMapper.GetReturnType()); methodBuilder.SetParameters(methodParameters.Select(pi => paramMapper.GetParameterType(pi.ParameterType)).ToArray()); if (this.explicitImplementation) { this.typeBuilder.DefineMethodOverride(methodBuilder, this.methodToOverride); } int paramNum = 1; foreach (ParameterInfo pi in methodParameters) { methodBuilder.DefineParameter(paramNum++, pi.Attributes, pi.Name); } ILGenerator il = methodBuilder.GetILGenerator(); LocalBuilder methodReturn = il.DeclareLocal(typeof(IMethodReturn)); LocalBuilder ex = il.DeclareLocal(typeof(Exception)); LocalBuilder parameterArray = il.DeclareLocal(typeof(object[])); LocalBuilder inputs = il.DeclareLocal(typeof(VirtualMethodInvocation)); // Create instance of VirtualMethodInvocation il.Emit(OpCodes.Ldarg_0); // target object // If we have a targetField, that means we're building a proxy and // should use it as the target object. If we don't, we're building // a type interceptor and should leave the this pointer as the // target. if (targetField != null) { il.Emit(OpCodes.Ldfld, targetField); } // If we have a generic method, we want to make sure we're using the open constructed generic method // so when a closed generic version of the method is invoked the actual type parameters are used il.Emit( OpCodes.Ldtoken, methodToOverride.IsGenericMethodDefinition ? methodToOverride.MakeGenericMethod(paramMapper.GenericMethodParameters) : methodToOverride); if (methodToOverride.DeclaringType.IsGenericType) { // if the declaring type is generic, we need to get the method from the target type il.Emit(OpCodes.Ldtoken, targetInterface); il.Emit(OpCodes.Call, MethodBaseMethods.GetMethodForGenericFromHandle); } else { il.Emit(OpCodes.Call, MethodBaseMethods.GetMethodFromHandle); // target method } EmitLoadConstant(il, methodParameters.Length); il.Emit(OpCodes.Newarr, typeof(object)); // object[] parameters if (methodParameters.Length > 0) { il.Emit(OpCodes.Stloc, parameterArray); for (int i = 0; i < methodParameters.Length; ++i) { il.Emit(OpCodes.Ldloc, parameterArray); EmitLoadConstant(il, i); EmitLoadArgument(il, i); Type elementType = paramMapper.GetParameterType(methodParameters[i].ParameterType); if (elementType.IsByRef) { elementType = paramMapper.GetElementType(methodParameters[i].ParameterType); il.Emit(OpCodes.Ldobj, elementType); } EmitBox(il, elementType); il.Emit(OpCodes.Stelem_Ref); } il.Emit(OpCodes.Ldloc, parameterArray); } il.Emit(OpCodes.Newobj, VirtualMethodInvocationMethods.VirtualMethodInvocation); il.Emit(OpCodes.Stloc, inputs); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, proxyInterceptionPipelineField); il.Emit(OpCodes.Ldloc, inputs); // Put delegate reference onto the stack il.Emit(OpCodes.Ldarg_0); MethodInfo callTarget = delegateMethod; if (callTarget.IsGenericMethod) { callTarget = delegateMethod.MakeGenericMethod(paramMapper.GenericMethodParameters); } il.Emit(OpCodes.Ldftn, callTarget); il.Emit(OpCodes.Newobj, InvokeInterceptionBehaviorDelegateMethods.InvokeInterceptionBehaviorDelegate); // And call the pipeline il.Emit(OpCodes.Call, InterceptionBehaviorPipelineMethods.Invoke); il.Emit(OpCodes.Stloc, methodReturn); // Was there an exception? Label noException = il.DefineLabel(); il.Emit(OpCodes.Ldloc, methodReturn); il.EmitCall(OpCodes.Callvirt, IMethodReturnMethods.GetException, null); il.Emit(OpCodes.Stloc, ex); il.Emit(OpCodes.Ldloc, ex); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ceq); il.Emit(OpCodes.Brtrue_S, noException); il.Emit(OpCodes.Ldloc, ex); il.Emit(OpCodes.Throw); il.MarkLabel(noException); // Unpack any ref/out parameters if (methodParameters.Length > 0) { int outputArgNum = 0; for (paramNum = 0; paramNum < methodParameters.Length; ++paramNum) { ParameterInfo pi = methodParameters[paramNum]; if (pi.ParameterType.IsByRef) { // Get the original parameter value - address of the ref or out EmitLoadArgument(il, paramNum); // Get the value of this output parameter out of the Outputs collection il.Emit(OpCodes.Ldloc, methodReturn); il.Emit(OpCodes.Callvirt, IMethodReturnMethods.GetOutputs); EmitLoadConstant(il, outputArgNum++); il.Emit(OpCodes.Callvirt, IListMethods.GetItem); EmitUnboxOrCast(il, paramMapper.GetElementType(pi.ParameterType)); // And store in the caller il.Emit(OpCodes.Stobj, paramMapper.GetElementType(pi.ParameterType)); } } } if (MethodHasReturnValue) { il.Emit(OpCodes.Ldloc, methodReturn); il.EmitCall(OpCodes.Callvirt, IMethodReturnMethods.GetReturnValue, null); EmitUnboxOrCast(il, paramMapper.GetReturnType()); } il.Emit(OpCodes.Ret); return methodBuilder; }
/// <summary> /// Emit the code required to defer the logging of a task until completion. /// </summary> /// <param name="methodBuilder">The method to append to.</param> /// <param name="parameterType">The type of parameter being passed.</param> /// <param name="faultedMethod">A faulted method to call if the task is faulted. null if there is no handler.</param> private static void EmitTaskCompletion(MethodBuilder methodBuilder, Type parameterType, MethodBuilder faultedMethod) { // this only applies to tasks if (parameterType != typeof(Task) && !parameterType.IsSubclassOf(typeof(Task))) return; /* we have a task, so we want to implement: * if (!task.IsCompleted) * { * return task.ContinueWith(t => Foo_Completed(t), TaskContinuationOptions.ExecuteSynchronously); * } * else if (task.IsFaulted) * { * this.Foo_Faulted(t.Exception); * return task; * } * else * ...whatever gets emitted next */ var mIL = methodBuilder.GetILGenerator(); // if (task.IsCompleted) skip this whole thing var isCompleted = mIL.DefineLabel(); mIL.Emit(OpCodes.Ldarg_1); mIL.Emit(OpCodes.Call, typeof(Task).GetProperty("IsCompleted").GetGetMethod()); mIL.Emit(OpCodes.Brtrue, isCompleted); // it's not completed, so // task.ContinueWith(t => Foo_Completed(t), TaskContinuationOptions.ExecuteSynchronously) // first clear the return value off the stack mIL.Emit(OpCodes.Pop); var actionType = typeof(Action<>).MakeGenericType(parameterType); mIL.Emit(OpCodes.Ldarg_1); mIL.Emit(OpCodes.Ldarg_0); var callbackMethod = methodBuilder.IsGenericMethod ? methodBuilder.MakeGenericMethod(parameterType.GetGenericArguments()) : methodBuilder; mIL.Emit(OpCodes.Ldftn, callbackMethod); mIL.Emit(OpCodes.Newobj, actionType.GetConstructor(new Type[] { typeof(object), typeof(IntPtr) })); mIL.Emit(OpCodes.Ldc_I4, (int)TaskContinuationOptions.ExecuteSynchronously); var continuation = parameterType.GetMethod("ContinueWith", new Type[] { actionType, typeof(TaskContinuationOptions) }); mIL.Emit(OpCodes.Call, continuation); // the new return value is this continuation // return it mIL.Emit(OpCodes.Ret); mIL.MarkLabel(isCompleted); // time to check if it is faulted if (faultedMethod != null) { /* * if (task.IsFaulted) * { * Foo_Faulted(task.Exception); * return task; * } */ var isNotFaulted = mIL.DefineLabel(); mIL.Emit(OpCodes.Ldarg_1); mIL.Emit(OpCodes.Call, typeof(Task).GetProperty("IsFaulted").GetGetMethod()); mIL.Emit(OpCodes.Brfalse, isNotFaulted); // call _Faulted to record the exception mIL.Emit(OpCodes.Ldarg_0); // this mIL.Emit(OpCodes.Ldarg_1); // task.Exception mIL.Emit(OpCodes.Call, typeof(Task).GetProperty("Exception").GetGetMethod()); mIL.Emit(OpCodes.Call, faultedMethod); if (faultedMethod.ReturnType != typeof(void)) mIL.Emit(OpCodes.Pop); // return the task mIL.Emit(OpCodes.Ret); mIL.MarkLabel(isNotFaulted); } }