/// <summary> /// Gets a dynamically generated type that implements a given interface in terms of a <see cref="JsonRpc"/> instance. /// </summary> /// <param name="serviceInterface">The interface that describes the RPC contract, and that the client proxy should implement.</param> /// <returns>The generated type.</returns> internal static TypeInfo Get(TypeInfo serviceInterface) { Requires.NotNull(serviceInterface, nameof(serviceInterface)); VerifySupported(serviceInterface.IsInterface, Resources.ClientProxyTypeArgumentMustBeAnInterface, serviceInterface); TypeInfo generatedType; lock (GeneratedProxiesByInterface) { if (GeneratedProxiesByInterface.TryGetValue(serviceInterface, out generatedType)) { return(generatedType); } } var methodNameMap = new JsonRpc.MethodNameMap(serviceInterface); lock (ProxyModuleBuilder) { var interfaces = new List <Type> { serviceInterface.AsType(), }; interfaces.Add(typeof(IJsonRpcClientProxy)); var proxyTypeBuilder = ProxyModuleBuilder.DefineType( string.Format(CultureInfo.InvariantCulture, "_proxy_{0}_{1}", serviceInterface.FullName, Guid.NewGuid()), TypeAttributes.Public, typeof(object), interfaces.ToArray()); Type proxyType = proxyTypeBuilder; const FieldAttributes fieldAttributes = FieldAttributes.Private | FieldAttributes.InitOnly; var jsonRpcField = proxyTypeBuilder.DefineField("rpc", typeof(JsonRpc), fieldAttributes); var optionsField = proxyTypeBuilder.DefineField("options", typeof(JsonRpcProxyOptions), fieldAttributes); VerifySupported(!FindAllOnThisAndOtherInterfaces(serviceInterface, i => i.DeclaredProperties).Any(), Resources.UnsupportedPropertiesOnClientProxyInterface, serviceInterface); // Implement events var ctorActions = new List <Action <ILGenerator> >(); foreach (var evt in FindAllOnThisAndOtherInterfaces(serviceInterface, i => i.DeclaredEvents)) { VerifySupported(evt.EventHandlerType.Equals(typeof(EventHandler)) || (evt.EventHandlerType.GetTypeInfo().IsGenericType&& evt.EventHandlerType.GetGenericTypeDefinition().Equals(typeof(EventHandler <>))), Resources.UnsupportedEventHandlerTypeOnClientProxyInterface, evt); // public event EventHandler EventName; var evtBuilder = proxyTypeBuilder.DefineEvent(evt.Name, evt.Attributes, evt.EventHandlerType); // private EventHandler eventName; var evtField = proxyTypeBuilder.DefineField(evt.Name, evt.EventHandlerType, FieldAttributes.Private); // add_EventName var addRemoveHandlerParams = new Type[] { evt.EventHandlerType }; const MethodAttributes methodAttributes = MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.NewSlot | MethodAttributes.Virtual; var addMethod = proxyTypeBuilder.DefineMethod($"add_{evt.Name}", methodAttributes, null, addRemoveHandlerParams); ImplementEventAccessor(addMethod.GetILGenerator(), evtField, DelegateCombineMethod); evtBuilder.SetAddOnMethod(addMethod); // remove_EventName var removeMethod = proxyTypeBuilder.DefineMethod($"remove_{evt.Name}", methodAttributes, null, addRemoveHandlerParams); ImplementEventAccessor(removeMethod.GetILGenerator(), evtField, DelegateRemoveMethod); evtBuilder.SetRemoveOnMethod(removeMethod); // void OnEventName(EventArgs args) var eventArgsType = evt.EventHandlerType.GetTypeInfo().GetDeclaredMethod(nameof(EventHandler.Invoke)).GetParameters()[1].ParameterType; var raiseEventMethod = proxyTypeBuilder.DefineMethod( $"On{evt.Name}", MethodAttributes.HideBySig | MethodAttributes.Private, null, new Type[] { eventArgsType }); ImplementRaiseEventMethod(raiseEventMethod.GetILGenerator(), evtField, jsonRpcField); ctorActions.Add(new Action <ILGenerator>(il => { var addLocalRpcMethod = typeof(JsonRpc).GetRuntimeMethod(nameof(JsonRpc.AddLocalRpcMethod), new Type[] { typeof(string), typeof(Delegate) }); var delegateCtor = typeof(Action <>).MakeGenericType(eventArgsType).GetTypeInfo().DeclaredConstructors.Single(); // rpc.AddLocalRpcMethod("EventName", new Action<EventArgs>(this.OnEventName)); il.Emit(OpCodes.Ldarg_1); // .ctor's rpc parameter // First argument to AddLocalRpcMethod is the method name. // Run it through the method name transform. // this.options.EventNameTransform.Invoke("clrOrAttributedMethodName") il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, optionsField); il.EmitCall(OpCodes.Callvirt, EventNameTransformPropertyGetter, null); il.Emit(OpCodes.Ldstr, evt.Name); il.EmitCall(OpCodes.Callvirt, EventNameTransformInvoke, null); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldftn, raiseEventMethod); il.Emit(OpCodes.Newobj, delegateCtor); il.Emit(OpCodes.Callvirt, addLocalRpcMethod); })); } // .ctor(JsonRpc, JsonRpcProxyOptions) { var ctor = proxyTypeBuilder.DefineConstructor( MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.Standard, new Type[] { typeof(JsonRpc), typeof(JsonRpcProxyOptions) }); var il = ctor.GetILGenerator(); // : base() il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Call, ObjectCtor); // this.rpc = rpc; il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Stfld, jsonRpcField); // this.options = options; il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Stfld, optionsField); // Emit IL that supports events. foreach (var action in ctorActions) { action(il); } il.Emit(OpCodes.Ret); } // IDisposable.Dispose() { var disposeMethod = proxyTypeBuilder.DefineMethod(nameof(IDisposable.Dispose), MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual); var il = disposeMethod.GetILGenerator(); // this.rpc.Dispose(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, jsonRpcField); MethodInfo jsonRpcDisposeMethod = typeof(JsonRpc).GetTypeInfo().GetDeclaredMethods(nameof(JsonRpc.Dispose)).Single(m => m.GetParameters().Length == 0); il.EmitCall(OpCodes.Callvirt, jsonRpcDisposeMethod, EmptyTypes); il.Emit(OpCodes.Ret); proxyTypeBuilder.DefineMethodOverride(disposeMethod, typeof(IDisposable).GetTypeInfo().GetDeclaredMethod(nameof(IDisposable.Dispose))); } // IJsonRpcClientProxy.JsonRpc property { var jsonRpcProperty = proxyTypeBuilder.DefineProperty( nameof(IJsonRpcClientProxy.JsonRpc), PropertyAttributes.None, typeof(JsonRpc), parameterTypes: null); // get_JsonRpc() method var jsonRpcPropertyGetter = proxyTypeBuilder.DefineMethod( "get_" + nameof(IJsonRpcClientProxy.JsonRpc), MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.SpecialName, typeof(JsonRpc), Type.EmptyTypes); var il = jsonRpcPropertyGetter.GetILGenerator(); // return this.jsonRpc; il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, jsonRpcField); il.Emit(OpCodes.Ret); proxyTypeBuilder.DefineMethodOverride(jsonRpcPropertyGetter, typeof(IJsonRpcClientProxy).GetTypeInfo().GetDeclaredProperty(nameof(IJsonRpcClientProxy.JsonRpc)).GetMethod); jsonRpcProperty.SetGetMethod(jsonRpcPropertyGetter); } var invokeAsyncMethodInfos = typeof(JsonRpc).GetTypeInfo().DeclaredMethods.Where(m => m.Name == nameof(JsonRpc.InvokeAsync) && m.GetParameters()[1].ParameterType == typeof(object[])).ToArray(); var invokeAsyncOfTaskMethodInfo = invokeAsyncMethodInfos.Single(m => !m.IsGenericMethod); var invokeAsyncOfTaskOfTMethodInfo = invokeAsyncMethodInfos.Single(m => m.IsGenericMethod); var invokeWithCancellationAsyncMethodInfos = typeof(JsonRpc).GetTypeInfo().DeclaredMethods.Where(m => m.Name == nameof(JsonRpc.InvokeWithCancellationAsync)); var invokeWithCancellationAsyncOfTaskMethodInfo = invokeWithCancellationAsyncMethodInfos.Single(m => !m.IsGenericMethod); var invokeWithCancellationAsyncOfTaskOfTMethodInfo = invokeWithCancellationAsyncMethodInfos.Single(m => m.IsGenericMethod); var invokeWithParameterObjectAsyncMethodInfos = typeof(JsonRpc).GetTypeInfo().DeclaredMethods.Where(m => m.Name == nameof(JsonRpc.InvokeWithParameterObjectAsync)); var invokeWithParameterObjectAsyncOfTaskMethodInfo = invokeWithParameterObjectAsyncMethodInfos.Single(m => !m.IsGenericMethod); var invokeWithParameterObjectAsyncOfTaskOfTMethodInfo = invokeWithParameterObjectAsyncMethodInfos.Single(m => m.IsGenericMethod); foreach (var method in FindAllOnThisAndOtherInterfaces(serviceInterface, i => i.DeclaredMethods).Where(m => !m.IsSpecialName)) { bool returnTypeIsTask = method.ReturnType == typeof(Task) || (method.ReturnType.GetTypeInfo().IsGenericType&& method.ReturnType.GetGenericTypeDefinition() == typeof(Task <>)); bool returnTypeIsValueTask = method.ReturnType == typeof(ValueTask) || (method.ReturnType.GetTypeInfo().IsGenericType&& method.ReturnType.GetGenericTypeDefinition() == typeof(ValueTask <>)); VerifySupported(returnTypeIsTask || returnTypeIsValueTask, Resources.UnsupportedMethodReturnTypeOnClientProxyInterface, method, method.ReturnType.FullName); VerifySupported(!method.IsGenericMethod, Resources.UnsupportedGenericMethodsOnClientProxyInterface, method); ParameterInfo[] methodParameters = method.GetParameters(); var methodBuilder = proxyTypeBuilder.DefineMethod( method.Name, MethodAttributes.Private | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, method.ReturnType, methodParameters.Select(p => p.ParameterType).ToArray()); var il = methodBuilder.GetILGenerator(); // this.rpc il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, jsonRpcField); // First argument to InvokeAsync is the method name. // Run it through the method name transform. // this.options.MethodNameTransform.Invoke("clrOrAttributedMethodName") il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, optionsField); il.EmitCall(OpCodes.Callvirt, MethodNameTransformPropertyGetter, null); il.Emit(OpCodes.Ldstr, methodNameMap.GetRpcMethodName(method)); il.EmitCall(OpCodes.Callvirt, MethodNameTransformInvoke, null); var positionalArgsLabel = il.DefineLabel(); ParameterInfo cancellationTokenParameter = methodParameters.FirstOrDefault(p => p.ParameterType == typeof(CancellationToken)); int argumentCountExcludingCancellationToken = methodParameters.Length - (cancellationTokenParameter != null ? 1 : 0); VerifySupported(cancellationTokenParameter == null || cancellationTokenParameter.Position == methodParameters.Length - 1, Resources.CancellationTokenMustBeLastParameter, method); bool hasReturnValue = method.ReturnType.GetTypeInfo().IsGenericType; // if (this.options.ServerRequiresNamedArguments) { il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, optionsField); il.EmitCall(OpCodes.Callvirt, ServerRequiresNamedArgumentsPropertyGetter, null); il.Emit(OpCodes.Brfalse_S, positionalArgsLabel); // The second argument is a single parameter object. { if (argumentCountExcludingCancellationToken > 0) { ConstructorInfo paramObjectCtor = CreateParameterObjectType(methodParameters.Take(argumentCountExcludingCancellationToken).ToArray(), proxyType); for (int i = 0; i < argumentCountExcludingCancellationToken; i++) { il.Emit(OpCodes.Ldarg, i + 1); } il.Emit(OpCodes.Newobj, paramObjectCtor); } else { il.Emit(OpCodes.Ldnull); } MethodInfo invokingMethod = hasReturnValue ? invokeWithParameterObjectAsyncOfTaskOfTMethodInfo.MakeGenericMethod(method.ReturnType.GetTypeInfo().GenericTypeArguments[0]) : invokeWithParameterObjectAsyncOfTaskMethodInfo; if (cancellationTokenParameter != null) { il.Emit(OpCodes.Ldarg, cancellationTokenParameter.Position + 1); } else { il.Emit(OpCodes.Call, CancellationTokenNonePropertyGetter); } il.EmitCall(OpCodes.Callvirt, invokingMethod, null); if (returnTypeIsValueTask) { // We must convert the Task or Task<T> returned from JsonRpc into a ValueTask or ValueTask<T> il.Emit(OpCodes.Newobj, method.ReturnType.GetTypeInfo().GetConstructor(new Type[] { invokingMethod.ReturnType })); } il.Emit(OpCodes.Ret); } // The second argument is an array of arguments for the RPC method. il.MarkLabel(positionalArgsLabel); { il.Emit(OpCodes.Ldc_I4, argumentCountExcludingCancellationToken); il.Emit(OpCodes.Newarr, typeof(object)); for (int i = 0; i < argumentCountExcludingCancellationToken; i++) { il.Emit(OpCodes.Dup); // duplicate the array on the stack il.Emit(OpCodes.Ldc_I4, i); // push the index of the array to be initialized. il.Emit(OpCodes.Ldarg, i + 1); // push the associated argument if (methodParameters[i].ParameterType.GetTypeInfo().IsValueType) { il.Emit(OpCodes.Box, methodParameters[i].ParameterType); // box if the argument is a value type } il.Emit(OpCodes.Stelem_Ref); // set the array element. } MethodInfo invokingMethod; if (cancellationTokenParameter != null) { il.Emit(OpCodes.Ldarg, cancellationTokenParameter.Position + 1); invokingMethod = hasReturnValue ? invokeWithCancellationAsyncOfTaskOfTMethodInfo : invokeWithCancellationAsyncOfTaskMethodInfo; } else { invokingMethod = hasReturnValue ? invokeAsyncOfTaskOfTMethodInfo : invokeAsyncOfTaskMethodInfo; } // Construct the InvokeAsync<T> method with the T argument supplied if we have a return type. if (hasReturnValue) { invokingMethod = invokingMethod.MakeGenericMethod(method.ReturnType.GetTypeInfo().GenericTypeArguments[0]); } il.EmitCall(OpCodes.Callvirt, invokingMethod, null); if (returnTypeIsValueTask) { // We must convert the Task or Task<T> returned from JsonRpc into a ValueTask or ValueTask<T> il.Emit(OpCodes.Newobj, method.ReturnType.GetTypeInfo().GetConstructor(new Type[] { invokingMethod.ReturnType })); } il.Emit(OpCodes.Ret); } proxyTypeBuilder.DefineMethodOverride(methodBuilder, method); } generatedType = proxyTypeBuilder.CreateTypeInfo(); } lock (GeneratedProxiesByInterface) { if (!GeneratedProxiesByInterface.TryGetValue(serviceInterface, out var raceGeneratedType)) { GeneratedProxiesByInterface.Add(serviceInterface, generatedType); } else { // Ensure we only expose the same generated type externally. generatedType = raceGeneratedType; } } #if SaveAssembly AssemblyBuilder.Save(ProxyModuleBuilder.ScopeName); System.IO.File.Move(ProxyModuleBuilder.ScopeName, ProxyModuleBuilder.ScopeName + ".dll"); #endif return(generatedType); }
internal static TypeInfo Get(TypeInfo serviceInterface, bool disposable) { Requires.NotNull(serviceInterface, nameof(serviceInterface)); VerifySupported(serviceInterface.IsInterface, Resources.ClientProxyTypeArgumentMustBeAnInterface, serviceInterface); TypeInfo generatedType; var proxyCache = disposable ? DisposableGeneratedProxiesByInterface : GeneratedProxiesByInterface; lock (proxyCache) { if (proxyCache.TryGetValue(serviceInterface, out generatedType)) { return(generatedType); } } var methodNameMap = new JsonRpc.MethodNameMap(serviceInterface); lock (ProxyModuleBuilder) { var interfaces = new List <Type> { serviceInterface.AsType(), }; if (disposable) { interfaces.Add(typeof(IDisposable)); } var proxyTypeBuilder = ProxyModuleBuilder.DefineType( string.Format(CultureInfo.InvariantCulture, "_proxy_{0}_{1}", serviceInterface.FullName, Guid.NewGuid()), TypeAttributes.Public, typeof(object), interfaces.ToArray()); var jsonRpcField = proxyTypeBuilder.DefineField("rpc", typeof(JsonRpc), FieldAttributes.Private | FieldAttributes.InitOnly); VerifySupported(!serviceInterface.DeclaredProperties.Any(), Resources.UnsupportedPropertiesOnClientProxyInterface, serviceInterface); // Implement events var ctorActions = new List <Action <ILGenerator> >(); foreach (var evt in serviceInterface.DeclaredEvents) { VerifySupported(evt.EventHandlerType.Equals(typeof(EventHandler)) || (evt.EventHandlerType.GetTypeInfo().IsGenericType&& evt.EventHandlerType.GetGenericTypeDefinition().Equals(typeof(EventHandler <>))), Resources.UnsupportedEventHandlerTypeOnClientProxyInterface, evt); // public event EventHandler EventName; var evtBuilder = proxyTypeBuilder.DefineEvent(evt.Name, evt.Attributes, evt.EventHandlerType); // private EventHandler eventName; var evtField = proxyTypeBuilder.DefineField(evt.Name, evt.EventHandlerType, FieldAttributes.Private); // add_EventName var addRemoveHandlerParams = new Type[] { evt.EventHandlerType }; const MethodAttributes methodAttributes = MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.NewSlot | MethodAttributes.Virtual; var addMethod = proxyTypeBuilder.DefineMethod($"add_{evt.Name}", methodAttributes, null, addRemoveHandlerParams); ImplementEventAccessor(addMethod.GetILGenerator(), evtField, DelegateCombineMethod); evtBuilder.SetAddOnMethod(addMethod); // remove_EventName var removeMethod = proxyTypeBuilder.DefineMethod($"remove_{evt.Name}", methodAttributes, null, addRemoveHandlerParams); ImplementEventAccessor(removeMethod.GetILGenerator(), evtField, DelegateRemoveMethod); evtBuilder.SetRemoveOnMethod(removeMethod); // void OnEventName(EventArgs args) var eventArgsType = evt.EventHandlerType.GetTypeInfo().GetDeclaredMethod(nameof(EventHandler.Invoke)).GetParameters()[1].ParameterType; var raiseEventMethod = proxyTypeBuilder.DefineMethod( $"On{evt.Name}", MethodAttributes.HideBySig | MethodAttributes.Private, null, new Type[] { eventArgsType }); ImplementRaiseEventMethod(raiseEventMethod.GetILGenerator(), evtField, jsonRpcField); ctorActions.Add(new Action <ILGenerator>(il => { var addLocalRpcMethod = typeof(JsonRpc).GetRuntimeMethod(nameof(JsonRpc.AddLocalRpcMethod), new Type[] { typeof(string), typeof(Delegate) }); var delegateCtor = typeof(Action <>).MakeGenericType(eventArgsType).GetTypeInfo().DeclaredConstructors.Single(); // rpc.AddLocalRpcMethod("EventName", new Action<EventArgs>(this.OnEventName)); il.Emit(OpCodes.Ldarg_1); // .ctor's rpc parameter il.Emit(OpCodes.Ldstr, evt.Name); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldftn, raiseEventMethod); il.Emit(OpCodes.Newobj, delegateCtor); il.Emit(OpCodes.Callvirt, addLocalRpcMethod); })); } // .ctor(JsonRpc) { var ctor = proxyTypeBuilder.DefineConstructor(MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.Standard, new Type[] { typeof(JsonRpc) }); var il = ctor.GetILGenerator(); // : base() il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Call, ObjectCtor); // this.rpc = rpc; il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Stfld, jsonRpcField); // Emit IL that supports events. foreach (var action in ctorActions) { action(il); } il.Emit(OpCodes.Ret); } // IDisposable.Dispose() if (disposable) { var disposeMethod = proxyTypeBuilder.DefineMethod(nameof(IDisposable.Dispose), MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual); var il = disposeMethod.GetILGenerator(); // this.rpc.Dispose(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, jsonRpcField); MethodInfo jsonRpcDisposeMethod = typeof(JsonRpc).GetTypeInfo().GetDeclaredMethods(nameof(JsonRpc.Dispose)).Single(m => m.GetParameters().Length == 0); il.EmitCall(OpCodes.Callvirt, jsonRpcDisposeMethod, EmptyTypes); il.Emit(OpCodes.Ret); proxyTypeBuilder.DefineMethodOverride(disposeMethod, typeof(IDisposable).GetTypeInfo().GetDeclaredMethod(nameof(IDisposable.Dispose))); } var invokeAsyncMethodInfos = typeof(JsonRpc).GetTypeInfo().DeclaredMethods.Where(m => m.Name == nameof(JsonRpc.InvokeAsync) && m.GetParameters()[1].ParameterType == typeof(object[])).ToArray(); var invokeAsyncOfTaskMethodInfo = invokeAsyncMethodInfos.Single(m => !m.IsGenericMethod); var invokeAsyncOfTaskOfTMethodInfo = invokeAsyncMethodInfos.Single(m => m.IsGenericMethod); var invokeWithCancellationAsyncMethodInfos = typeof(JsonRpc).GetTypeInfo().DeclaredMethods.Where(m => m.Name == nameof(JsonRpc.InvokeWithCancellationAsync)); var invokeWithCancellationAsyncOfTaskMethodInfo = invokeWithCancellationAsyncMethodInfos.Single(m => !m.IsGenericMethod); var invokeWithCancellationAsyncOfTaskOfTMethodInfo = invokeWithCancellationAsyncMethodInfos.Single(m => m.IsGenericMethod); foreach (var method in serviceInterface.DeclaredMethods.Where(m => !m.IsSpecialName)) { VerifySupported(method.ReturnType == typeof(Task) || (method.ReturnType.GetTypeInfo().IsGenericType&& method.ReturnType.GetGenericTypeDefinition() == typeof(Task <>)), Resources.UnsupportedMethodReturnTypeOnClientProxyInterface, method, method.ReturnType.FullName); VerifySupported(!method.IsGenericMethod, Resources.UnsupportedGenericMethodsOnClientProxyInterface, method); ParameterInfo[] methodParameters = method.GetParameters(); var methodBuilder = proxyTypeBuilder.DefineMethod( method.Name, MethodAttributes.Private | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, method.ReturnType, methodParameters.Select(p => p.ParameterType).ToArray()); var il = methodBuilder.GetILGenerator(); // this.rpc il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, jsonRpcField); // First argument to InvokeAsync is the method name. il.Emit(OpCodes.Ldstr, methodNameMap.GetRpcMethodName(method)); // The second argument is an array of arguments for the RPC method. il.Emit(OpCodes.Ldc_I4, methodParameters.Count(p => p.ParameterType != typeof(CancellationToken))); il.Emit(OpCodes.Newarr, typeof(object)); ParameterInfo cancellationTokenParameter = null; for (int i = 0; i < methodParameters.Length; i++) { if (methodParameters[i].ParameterType == typeof(CancellationToken)) { cancellationTokenParameter = methodParameters[i]; continue; } il.Emit(OpCodes.Dup); // duplicate the array on the stack il.Emit(OpCodes.Ldc_I4, i); // push the index of the array to be initialized. il.Emit(OpCodes.Ldarg, i + 1); // push the associated argument if (methodParameters[i].ParameterType.GetTypeInfo().IsValueType) { il.Emit(OpCodes.Box, methodParameters[i].ParameterType); // box if the argument is a value type } il.Emit(OpCodes.Stelem_Ref); // set the array element. } bool hasReturnValue = method.ReturnType.GetTypeInfo().IsGenericType; MethodInfo invokingMethod; if (cancellationTokenParameter != null) { il.Emit(OpCodes.Ldarg, cancellationTokenParameter.Position + 1); invokingMethod = hasReturnValue ? invokeWithCancellationAsyncOfTaskOfTMethodInfo : invokeWithCancellationAsyncOfTaskMethodInfo; } else { invokingMethod = hasReturnValue ? invokeAsyncOfTaskOfTMethodInfo : invokeAsyncOfTaskMethodInfo; } // Construct the InvokeAsync<T> method with the T argument supplied if we have a return type. if (hasReturnValue) { invokingMethod = invokingMethod.MakeGenericMethod(method.ReturnType.GetTypeInfo().GenericTypeArguments[0]); } il.EmitCall(OpCodes.Callvirt, invokingMethod, null); il.Emit(OpCodes.Ret); proxyTypeBuilder.DefineMethodOverride(methodBuilder, method); } generatedType = proxyTypeBuilder.CreateTypeInfo(); } lock (proxyCache) { if (!proxyCache.TryGetValue(serviceInterface, out var raceGeneratedType)) { proxyCache.Add(serviceInterface, generatedType); } else { // Ensure we only expose the same generated type externally. generatedType = raceGeneratedType; } } return(generatedType); }