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);
        }
Пример #3
0
        private void Marshal(MethodGenerationContext ctx)
        {
            // Snippet from Dylan's notepad (omg look at this dude still using a paper notepad in 2019)
            //
            // Main
            // |------> 0 --|      i.e. Generate the next wrapper when the previous wrapper
            //      |-- 1 <-|           needs to emit its native call.
            //      |-> 2 --|
            //      |-- 3 <-|
            //      |------------> Native

            // Get the first marshalling stage
            var iteration   = 0;
            var typeBuilder = (TypeBuilder)ctx.DestinationMethod.DeclaringType;
            var firstStage  = GetNextStage(new ParameterMarshalContext(ctx.OriginalMethod.ReturnParameter),
                                           ctx.OriginalMethod.GetParameters().Select(x => new ParameterMarshalContext(x)).ToArray(), -1, Stages,
                                           out var index);

            // If we don't have a first stage, we have no marshalling to do.
            // Just pass it to the regular generator :D
            if (firstStage is null)
            {
                BaseGenerator.GenerateMethod(ctx);
                return;
            }

            // Define the first marshalling wrapper
            var entry = typeBuilder.DefineMethod(
                GetName(firstStage, ctx.OriginalMethod.Name, iteration++),
                MethodAttributes.Private | MethodAttributes.Final | MethodAttributes.HideBySig,
                ctx.OriginalMethod.CallingConvention, ctx.OriginalMethod.ReturnType,
                ctx.OriginalMethod.ReturnParameter.GetRequiredCustomModifiers(),
                ctx.OriginalMethod.ReturnParameter.GetOptionalCustomModifiers(),
                ctx.OriginalMethod.GetParameters().Select(x => x.ParameterType).ToArray(),
                ctx.OriginalMethod.GetParameters().Select(x => x.GetRequiredCustomModifiers()).ToArray(),
                ctx.OriginalMethod.GetParameters().Select(x => x.GetOptionalCustomModifiers()).ToArray());

            MarshalUtils.CopyGenericTypes(ctx.OriginalMethod, entry);
            entry.SetImplementationFlags(MethodImplAttributes.AggressiveInlining | (MethodImplAttributes)512);
            ctx.DestinationMethod.SetImplementationFlags(MethodImplAttributes.AggressiveInlining |
                                                         (MethodImplAttributes)512);

            // Generate the marshalling wrappers (the first one will call EmitCall, which will then gen the next, etc.)
            firstStage.Marshal(new MethodMarshalContext(ctx.DestinationMethod, ctx.Slot, entry,
                                                        new ParameterMarshalContext(ctx.OriginalMethod.ReturnParameter),
                                                        ctx.OriginalMethod.GetParameters().Select(x => new ParameterMarshalContext(x)).ToArray(), EmitCall
                                                        ));

            // Emit the actual method using the default generator
            ctx.IL.Emit(OpCodes.Ldarg_0);
            BaseGenerator.EmitPrologue(ctx);  // pass all parameters as-is
            ctx.IL.Emit(OpCodes.Call, entry); // call the first marshalling wrapper
            BaseGenerator.EmitEpilogue(ctx);  // call the default epilogue generator
            BaseGenerator.EmitReturn(ctx);    // call the default return generator

            // A recursive local function for handling generating & calling the next wrapper from the previous wrapper.
            void EmitCall(MethodBuilder wip, ParameterMarshalContext ret, ParameterMarshalContext[] parameters,
                          ILGenerator il)
            {
                var nextStage = GetNextStage(ret, parameters, index, Stages, out index);

                // If no more stages are available, emit the native call and go home.
                if (nextStage is null)
                {
                    NativeReturnType     = ret.Type;
                    NativeParameterTypes = parameters.Select(x => x.Type).ToArray();
                    var terminator = typeBuilder.DefineMethod(
                        GetName(null, ctx.OriginalMethod.Name, iteration++),
                        MethodAttributes.Private | MethodAttributes.Final | MethodAttributes.HideBySig,
                        wip.CallingConvention, ret.Type, ret.RequiredModifiers, ret.OptionalModifiers,
                        parameters.Select(x => x.Type).ToArray(),
                        parameters.Select(x => x.RequiredModifiers).ToArray(),
                        parameters.Select(x => x.OptionalModifiers).ToArray());
                    var til = terminator.GetILGenerator();
                    for (var i = 0; i < parameters.Length; i++)
                    {
                        til.Emit(OpCodes.Ldarg, i + 1);
                    }

                    BaseGenerator.EmitEntryPoint(til, ctx.Slot, ctx.EntryPoint);
                    BaseGenerator.EmitNativeCall(til, ctx.Convention, ctx.OriginalMethod, ret.Type,
                                                 parameters.Select(x => x.Type).ToArray());
                    til.Emit(OpCodes.Ret);
                    terminator.SetImplementationFlags(MethodImplAttributes.AggressiveInlining | (MethodImplAttributes)512);
                    il.Emit(OpCodes.Call, terminator);
                }
                else
                {
                    var call = typeBuilder.DefineMethod(
                        GetName(nextStage, ctx.OriginalMethod.Name, iteration++),
                        MethodAttributes.Private | MethodAttributes.Final | MethodAttributes.HideBySig,
                        wip.CallingConvention, ret.Type, ret.RequiredModifiers, ret.OptionalModifiers,
                        parameters.Select(x => x.Type).ToArray(),
                        parameters.Select(x => x.RequiredModifiers).ToArray(),
                        parameters.Select(x => x.OptionalModifiers).ToArray());
                    MarshalUtils.CopyGenericTypes(ctx.OriginalMethod, call);
                    call = nextStage.Marshal(new MethodMarshalContext(wip, ctx.Slot, call, ret, parameters,
                                                                      EmitCall));
                    call.SetImplementationFlags(MethodImplAttributes.AggressiveInlining | (MethodImplAttributes)512);
                    il.Emit(OpCodes.Call, call);
                }
            }
        }
 private void GenerateMethodFromPathAttribute(TypeBuilder typeBuilder, ImplementationRequest request,
                                              MethodGenerationContext generationContext, MethodInfo methodInfo, Attribute[] attributes, Attribute pathAttribute)
 {
 }
        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 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 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);
            }
        }