in rd-net/RdFramework.Reflection/ProxyGenerator.cs [397:526]
private void ImplementMethod(TypeBuilderContext ctx, MethodInfo method)
{
var typebuilder = ctx.Builder;
// add field for IRdCall instance
var requestType = GetRequstType(method)[0];
var responseType = GetResponseType(method, true);
Assertion.Require(!requestType.IsByRef, "ByRef is not supported. ({0}.{1})", typebuilder, requestType);
Assertion.Require(!responseType.IsByRef, "ByRef is not supported. ({0}.{1})", typebuilder, responseType);
var fieldType = typeof(IRdCall<,>).MakeGenericType(requestType, responseType);
var field = typebuilder.DefineField(ProxyFieldName(method), fieldType , FieldAttributes.Public);
var isSyncCall = !typeof(IAsyncResult).IsAssignableFrom(method.ReturnType);
FieldInfo? timeoutsField = null;
if (isSyncCall && method.GetCustomAttribute<RpcTimeoutAttribute>() is { } timeouts)
{
timeoutsField = ctx.DefineCustomRpcTimeout(timeouts, method);
}
var parameters = method.GetParameters();
MethodBuilder methodbuilder = typebuilder.DefineMethod(method.Name,
MethodAttributes.Final | MethodAttributes.Virtual | MethodAttributes.Private,
method.CallingConvention,
method.ReturnType,
method.ReturnParameter.GetRequiredCustomModifiers(),
method.ReturnParameter.GetOptionalCustomModifiers(),
parameters.Select(param => param.ParameterType).ToArray(),
parameters.Select(param => param.GetRequiredCustomModifiers()).ToArray(),
parameters.Select(param => param.GetOptionalCustomModifiers()).ToArray());
ILGenerator ilgen = methodbuilder.GetILGenerator();
// load IRdCall field for further call
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Ldfld, field);
int lifetimeArgument = -1;
// Lifetime
for (int i = 0; i < parameters.Length; i++)
{
if (parameters[i].ParameterType == typeof(Lifetime))
{
Assertion.Require(lifetimeArgument == -1, "Only one lifetime parameter is allowed");
lifetimeArgument = i;
}
}
if (lifetimeArgument != -1)
LoadArgument(ilgen, lifetimeArgument + 1);
else
{
ilgen.Emit(OpCodes.Call, Members.EternalLifetimeGet);
}
// TReq
if (parameters.Length - (lifetimeArgument == -1 ? 0 : 1) > 0)
{
// Others arguments, skip `this` argument (0)
for (int i = 0; i < parameters.Length; i++)
{
if (i != lifetimeArgument)
{
ReflectionSerializerVerifier.AssertValidScalar(parameters[i].ParameterType.GetTypeInfo());
// load args
LoadArgument(ilgen, i + 1 /* #0 is `self/this` argument */);
}
}
var genArgs = requestType.GetGenericArguments();
if (genArgs.Length > MaxTuplePayload)
{
var toInit = new Stack<ConstructorInfo>();
var rest = genArgs[MaxTuplePayload];
while (rest != null)
{
toInit.Push(rest.GetConstructors().Single());
var ar = rest.GetGenericArguments();
rest = ar.Length > MaxTuplePayload ? ar[MaxTuplePayload] : null;
}
while (toInit.Count != 0)
{
ilgen.Emit(OpCodes.Newobj, toInit.Pop());
}
}
// create tuple and load it to stack
ilgen.Emit(OpCodes.Newobj, requestType.GetConstructors().Single());
}
else
{
ilgen.Emit(OpCodes.Ldsfld, Members.UnitInstance);
}
if (isSyncCall)
{
// RpcTimeouts
var rpcTimeoutsField = timeoutsField ?? ctx.DefaultTimeoutField;
if (rpcTimeoutsField != null)
ilgen.Emit(OpCodes.Ldsfld, rpcTimeoutsField);
else
ilgen.Emit(OpCodes.Ldnull);
ilgen.Emit(OpCodes.Call, Members.SyncNested4.MakeGenericMethod(requestType, responseType));
}
else
{
// Start(Lifetime, TReq, Scheduler)
var startMethod = ProxyGeneratorMembers.StartRdCall(fieldType);
// async
ilgen.Emit(OpCodes.Ldnull); // ResponseScheduler
ilgen.Emit(OpCodes.Callvirt, startMethod.NotNull("fieldType.GetMethod(Start) != null"));
ilgen.Emit(OpCodes.Call, Members.ToTask.MakeGenericMethod(responseType));
}
if (method.ReturnType == typeof(void))
{
ilgen.Emit(OpCodes.Pop);
}
else
{
// ilgen.Emit(OpCodes.Ldnull);
}
ilgen.Emit(OpCodes.Ret);
typebuilder.DefineMethodOverride(methodbuilder, method);
}