private void ImplementMethod()

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