private void GenerateRpcExecuteInformation(TypeBuilder typeBuilder, ImplementationRequest request,
                                                   MethodGenerationContext generationContext, MethodInfo methodInfo, Attribute[] attributes, HttpMethod method)
        {
            var fieldName = "EasyRpcInfo_" + methodInfo.Name;

            generationContext.ExecutionInfoField =
                typeBuilder.DefineField(fieldName, typeof(RpcExecuteInformation), FieldAttributes.Static | FieldAttributes.Public);

            generationContext.ExecuteInformation = new RpcExecuteInformation
            {
                Method         = method,
                ClientProvider = request.ClientProvider,
                Serializer     = request.DefaultSerializer
            };

            generationContext.GenerationContext.FinalizeTypeActions.Add(t =>
            {
                var field = t.GetField(fieldName, BindingFlags.Static | BindingFlags.Public);

                if (field == null)
                {
                    throw new Exception($"Could not locate static field {fieldName} on {t.Name} for interface {methodInfo.DeclaringType?.Name}.{methodInfo.Name}");
                }

                field.SetValue(null, generationContext.ExecuteInformation);
            });
        }
        private void PopulateGenerationContext(TypeBuilder typeBuilder, ImplementationRequest request, MethodGenerationContext generationContext, MethodInfo methodInfo, Attribute[] attributes)
        {
            HttpMethod method = HttpMethod.Post;

            if (request.ExposeDefaultMethod == ExposeDefaultMethod.PostOnly)
            {
                var classNameString  = methodInfo.DeclaringType?.Name;
                var methodNameString = methodInfo.Name;

                if (request.NamingConventionService != null)
                {
                    classNameString  = request.NamingConventionService.GetNameForType(methodInfo.DeclaringType);
                    methodNameString = request.NamingConventionService.GetMethodName(methodInfo);
                }

                generationContext.PathTemplate = request.BasePath + classNameString + "/" + methodNameString;

                generationContext.BodyParameters = new List <ParameterInfo>();

                generationContext.BodyParameters.AddRange(methodInfo.GetParameters());
            }
            else
            {
                throw new NotImplementedException();
            }

            GenerateRpcExecuteInformation(typeBuilder, request, generationContext, methodInfo, attributes, method);
        }
        private void GenerateMethodAsDefaultExpose(TypeBuilder typeBuilder, ImplementationRequest request,
                                                   MethodGenerationContext generationContext, MethodInfo methodInfo, Attribute[] attributes)
        {
            PopulateGenerationContext(typeBuilder, request, generationContext, methodInfo, attributes);

            GenerateMethodImplementation(typeBuilder, request, generationContext, methodInfo, attributes);
        }
        protected virtual TypeBuilder CreateTypeBuilder(ImplementationRequest request,
                                                        GenerationContext generationContext)
        {
            var interfaceType = _moduleBuilder.DefineType("ClientInterfaceProxy" + _proxyCount, TypeAttributes.Public);

            interfaceType.AddInterfaceImplementation(request.InterfaceType);

            return(interfaceType);
        }
        private void GenerateConstructorIL(TypeBuilder typeBuilder, ImplementationRequest request, GenerationContext generationContext, ConstructorBuilder constructor)
        {
            var ilGenerator = constructor.GetILGenerator();

            ilGenerator.Emit(OpCodes.Ldarg_0);
            ilGenerator.Emit(OpCodes.Ldarg_1);
            ilGenerator.Emit(OpCodes.Stfld, generationContext.RpcExecutionServiceField);
            ilGenerator.Emit(OpCodes.Ret);
        }
        private void GenerateMethods(TypeBuilder typeBuilder, ImplementationRequest request,
                                     GenerationContext generationContext)
        {
            foreach (var methodInfo in request.InterfaceType.GetMethods(BindingFlags.Instance | BindingFlags.Public))
            {
                var methodGenerationContext = new MethodGenerationContext(generationContext);

                GenerateMethod(typeBuilder, request, methodGenerationContext, methodInfo);
            }
        }
        private void GenerateConstructor(TypeBuilder typeBuilder, ImplementationRequest request,
                                         GenerationContext generationContext)
        {
            generationContext.RpcExecutionServiceField = typeBuilder.DefineField("_rpcExecutionService", typeof(IRpcExecutionService), FieldAttributes.Private);

            var constructor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard,
                                                            new[] { typeof(IRpcExecutionService) });

            constructor.DefineParameter(1, ParameterAttributes.None, "rpcExecutionService");

            GenerateConstructorIL(typeBuilder, request, generationContext, constructor);
        }
        private void GenerateMethodImplementation(TypeBuilder typeBuilder, ImplementationRequest request, MethodGenerationContext generationContext, MethodInfo methodInfo, Attribute[] attributes)
        {
            var parameters = methodInfo.GetParameters();

            var methodAttributes = MethodAttributes.Public |
                                   MethodAttributes.HideBySig |
                                   MethodAttributes.Virtual |
                                   MethodAttributes.NewSlot;

            var methodBuilder = typeBuilder.DefineMethod(methodInfo.Name, methodAttributes, CallingConventions.Standard,
                                                         methodInfo.ReturnType, parameters.Select(p => p.ParameterType).ToArray());


            GenerateMethodImplementationIL(typeBuilder, request, generationContext, methodInfo, attributes, methodBuilder);
        }
        private void GenerateMethod(TypeBuilder typeBuilder, ImplementationRequest request, MethodGenerationContext generationContext, MethodInfo methodInfo)
        {
            var attributes = methodInfo.GetCustomAttributes <Attribute>(true).ToArray();

            if (attributes.Any(a => a is IgnoreMethodAttribute))
            {
                return;
            }

            var pathAttribute = attributes.FirstOrDefault(a => a is IPathAttribute);

            if (pathAttribute != null)
            {
                GenerateMethodFromPathAttribute(typeBuilder, request, generationContext, methodInfo, attributes, pathAttribute);
            }
            else
            {
                GenerateMethodAsDefaultExpose(typeBuilder, request, generationContext, methodInfo, attributes);
            }
        }
        private void GenerateBodyVariable(TypeBuilder typeBuilder, ImplementationRequest request, ILGenerator il,
                                          MethodGenerationContext generationContext,
                                          MethodInfo methodInfo)
        {
            // single parameter is loaded directly from the parameter no need for a local variable
            if (request.SingleParameterToBody &&
                generationContext.BodyParameters.Count == 1)
            {
                return;
            }

            var serializedType =
                _serializationTypeCreator.CreateSerializationTypeForMethod(generationContext.BodyParameters);

            var bodyVariable = il.DeclareLocal(serializedType);

            generationContext.BodyVariable = bodyVariable;
            var constructor = serializedType.GetConstructor(new Type[0]);

            il.Emit(OpCodes.Newobj, constructor);
            il.Emit(OpCodes.Stloc, generationContext.BodyVariable);

            foreach (var bodyParameter in generationContext.BodyParameters)
            {
                var propertyInfo = serializedType.GetProperty(bodyParameter.Name);

                var setMethod = propertyInfo?.GetSetMethod();

                if (setMethod == null)
                {
                    // should never get here as the type is auto generated
                    throw new Exception($"Could not find set method on serialized type {serializedType.Name} property {bodyParameter.Name}");
                }

                il.Emit(OpCodes.Ldloc, bodyVariable);
                il.EmitLoadArg(bodyParameter.Position + 1);
                il.EmitMethodCall(setMethod);
            }
        }
        private void GenerateMethodImplementationIL(TypeBuilder typeBuilder, ImplementationRequest request,
                                                    MethodGenerationContext generationContext, MethodInfo methodInfo, Attribute[] attributes,
                                                    MethodBuilder methodBuilder)
        {
            var il = methodBuilder.GetILGenerator();

            if (generationContext.UrlParameters != null &&
                generationContext.UrlParameters.Count > 0)
            {
                throw new NotImplementedException();
            }

            if (generationContext.BodyParameters != null &&
                generationContext.BodyParameters.Count > 0)
            {
                GenerateBodyVariable(typeBuilder, request, il, generationContext, methodInfo);
            }

            GenerateRpcExecutionMethodCall(generationContext, methodInfo, il);

            il.Emit(OpCodes.Ret);
        }
        public Type GenerateImplementationForInterface(ImplementationRequest request)
        {
            lock (_lock)
            {
                _proxyCount++;

                var generationContext = new GenerationContext();

                var typeBuilder = CreateTypeBuilder(request, generationContext);

                GenerateConstructor(typeBuilder, request, generationContext);

                GenerateMethods(typeBuilder, request, generationContext);

                var type = typeBuilder.CreateTypeInfo().AsType();

                foreach (var finalizeTypeAction in generationContext.FinalizeTypeActions)
                {
                    finalizeTypeAction(type);
                }

                return(type);
            }
        }
 private void GenerateMethodFromPathAttribute(TypeBuilder typeBuilder, ImplementationRequest request,
                                              MethodGenerationContext generationContext, MethodInfo methodInfo, Attribute[] attributes, Attribute pathAttribute)
 {
 }