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;
}