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