private void HandleMethods(
            TypeBuilder typeBuilder,
            Type interfaceType,
            FieldBuilder requesterField,
            FieldInfo classHeadersField,
            AllowAnyStatusCodeAttribute classAllowAnyStatusCodeAttribute,
            SerializationMethodsAttribute classSerializationMethodsAttribute,
            PropertyGrouping properties)
        {
            foreach (var methodInfo in InterfaceAndChildren(interfaceType, x => x.GetTypeInfo().GetMethods()))
            {
                // Exclude property getter / setters, etc
                if (methodInfo.IsSpecialName)
                {
                    continue;
                }

                var parameters        = methodInfo.GetParameters();
                var parameterGrouping = new ParameterGrouping(parameters, methodInfo.Name);

                var methodBuilder     = typeBuilder.DefineMethod(methodInfo.Name, MethodAttributes.Public | MethodAttributes.Virtual, methodInfo.ReturnType, parameters.Select(x => x.ParameterType).ToArray());
                var methodIlGenerator = methodBuilder.GetILGenerator();

                if (methodInfo == disposeMethod)
                {
                    this.AddDisposeMethod(methodIlGenerator, requesterField);
                }
                else
                {
                    var requestAttribute = methodInfo.GetCustomAttribute <RequestAttribute>();
                    if (requestAttribute == null)
                    {
                        throw new ImplementationCreationException(String.Format("Method {0} does not have a suitable [Get] / [Post] / etc attribute on it", methodInfo.Name));
                    }

                    var allowAnyStatusCodeAttribute = methodInfo.GetCustomAttribute <AllowAnyStatusCodeAttribute>();

                    var methodSerializationMethodsAttribute = methodInfo.GetCustomAttribute <SerializationMethodsAttribute>();
                    var serializationMethods = new ResolvedSerializationMethods(classSerializationMethodsAttribute, methodSerializationMethodsAttribute);

                    this.ValidatePathParams(requestAttribute.Path, parameterGrouping.PathParameters.Select(x => x.Attribute.Name ?? x.Parameter.Name).ToList(), properties.Path.Select(x => x.Attribute.Name).ToList(), methodInfo.Name);

                    this.AddRequestInfoCreation(methodIlGenerator, requesterField, requestAttribute);
                    this.AddCancellationTokenIfNeeded(methodIlGenerator, parameterGrouping.CancellationToken);
                    this.AddClassHeadersIfNeeded(methodIlGenerator, classHeadersField);
                    this.AddPropertyHeaders(methodIlGenerator, properties.Headers);
                    this.AddPathProperties(methodIlGenerator, properties.Path);
                    this.AddMethodHeaders(methodIlGenerator, methodInfo);
                    this.AddAllowAnyStatusCodeIfNecessary(methodIlGenerator, allowAnyStatusCodeAttribute ?? classAllowAnyStatusCodeAttribute);
                    this.AddParameters(methodIlGenerator, parameterGrouping, methodInfo.Name, serializationMethods);
                    this.AddRequestMethodInvocation(methodIlGenerator, methodInfo);

                    // Finally, return
                    methodIlGenerator.Emit(OpCodes.Ret);

                    typeBuilder.DefineMethodOverride(methodBuilder, methodInfo);
                }
            }
        }
Example #2
0
        private void AddParameters(
            ILGenerator methodIlGenerator,
            ParameterGrouping parameterGrouping,
            string methodName,
            ResolvedSerializationMethods serializationMethods)
        {
            // If there's a body, add it
            if (parameterGrouping.Body != null)
            {
                var body = parameterGrouping.Body.Value;
                this.AddBody(methodIlGenerator, serializationMethods.ResolveBody(body.Attribute.SerializationMethod), body.Parameter.ParameterType, (short)body.Index);
            }

            // If there's a query map, add it
            foreach (var queryMap in parameterGrouping.QueryMaps)
            {
                var method = MakeQueryMapMethodInfo(queryMap.Parameter.ParameterType);
                if (method == null)
                {
                    throw new ImplementationCreationException(String.Format("[QueryMap] parameter is not of type IDictionary or IDictionary<TKey, TValue> (or one of their descendents). Method: {0}", methodName));
                }
                this.AddQueryMap(methodIlGenerator, queryMap.Parameter.ParameterType, (short)queryMap.Index, method, serializationMethods.ResolveQuery(queryMap.Attribute.SerializationMethod));
            }

            foreach (var queryParameter in parameterGrouping.QueryParameters)
            {
                var method = MakeQueryParameterMethodInfo(queryParameter.Parameter.ParameterType);
                this.AddQueryParam(methodIlGenerator, queryParameter.Attribute.Name ?? queryParameter.Parameter.Name, (short)queryParameter.Index, method, serializationMethods.ResolveQuery(queryParameter.Attribute.SerializationMethod));
            }

            foreach (var plainParameter in parameterGrouping.PlainParameters)
            {
                var method = MakeQueryParameterMethodInfo(plainParameter.Parameter.ParameterType);
                this.AddQueryParam(methodIlGenerator, plainParameter.Parameter.Name, (short)plainParameter.Index, method, QuerySerializationMethod.ToString);
            }

            foreach (var pathParameter in parameterGrouping.PathParameters)
            {
                var method = addPathParameterMethod.MakeGenericMethod(pathParameter.Parameter.ParameterType);
                this.AddPathParam(methodIlGenerator, pathParameter.Attribute.Name ?? pathParameter.Parameter.Name, (short)pathParameter.Index, method);
            }

            foreach (var headerParameter in parameterGrouping.HeaderParameters)
            {
                if (headerParameter.Attribute.Value != null)
                {
                    throw new ImplementationCreationException(String.Format("[Header(\"{0}\", \"{1}\")] for method {2} must have the form [Header(\"Name\")], not [Header(\"Name\", \"Value\")]", headerParameter.Attribute.Name, headerParameter.Attribute.Value, methodName));
                }
                if (headerParameter.Attribute.Name.Contains(':'))
                {
                    throw new ImplementationCreationException(String.Format("[Header(\"{0}\")] on method {1} must not have a colon in its name", headerParameter.Attribute.Name, methodName));
                }
                var typedMethod = addHeaderParameterMethod.MakeGenericMethod(headerParameter.Parameter.ParameterType);
                this.AddHeaderParameter(methodIlGenerator, headerParameter.Attribute.Name, (short)headerParameter.Index, typedMethod);
            }
        }
Example #3
0
        private void GenerateMethod(TypeEmitter typeEmitter, List <EmittedProperty> emittedProperties, MethodModel method)
        {
            var methodEmitter        = typeEmitter.EmitMethod(method);
            var serializationMethods = new ResolvedSerializationMethods(this.typeModel.SerializationMethodsAttribute?.Attribute, method.SerializationMethodsAttribute?.Attribute);

            if (method.RequestAttribute == null)
            {
                this.diagnostics.ReportMethodMustHaveRequestAttribute(method);
            }
            else
            {
                string?path = method.RequestAttribute.Attribute.Path;
                this.ValidatePathParams(method, path);
                this.ValidateCancellationTokenParams(method);
                this.ValidateMultipleBodyParams(method);
                this.ValidateHttpRequestMessageParams(method);

                methodEmitter.EmitRequestInfoCreation(method.RequestAttribute.Attribute);

                var resolvedAllowAnyStatusCode = method.AllowAnyStatusCodeAttribute ?? this.typeModel.TypeAllowAnyStatusCodeAttribute;
                if (resolvedAllowAnyStatusCode?.Attribute.AllowAnyStatusCode == true)
                {
                    methodEmitter.EmitSetAllowAnyStatusCode();
                }

                if (this.typeModel.BasePathAttribute?.Attribute.BasePath != null)
                {
                    methodEmitter.EmitSetBasePath(this.typeModel.BasePathAttribute.Attribute.BasePath);
                }

                this.GenerateMethodProperties(methodEmitter, emittedProperties, serializationMethods);

                foreach (var methodHeader in method.HeaderAttributes)
                {
                    if (methodHeader.Attribute.Name.Contains(":"))
                    {
                        this.diagnostics.ReportHeaderOnMethodMustNotHaveColonInName(method, methodHeader);
                    }

                    methodEmitter.EmitAddMethodHeader(methodHeader);
                }

                this.GenerateMethodParameters(methodEmitter, method, serializationMethods);

                if (!methodEmitter.TryEmitRequestMethodInvocation())
                {
                    this.diagnostics.ReportMethodMustHaveValidReturnType(method);
                }
            }
        }
Example #4
0
        private void HandleMethods(
            TypeBuilder typeBuilder,
            Type interfaceType,
            FieldBuilder requesterField,
            FieldInfo classHeadersField,
            AllowAnyStatusCodeAttribute classAllowAnyStatusCodeAttribute,
            SerializationMethodsAttribute classSerializationMethodsAttribute,
            List <KeyValuePair <HeaderAttribute, FieldBuilder> > propertyHeaders)
        {
            foreach (var methodInfo in InterfaceAndChildren(interfaceType, x => x.GetMethods()))
            {
                // Exclude property getter / setters, etc
                if (methodInfo.IsSpecialName)
                {
                    continue;
                }

                var requestAttribute = methodInfo.GetCustomAttribute <RequestAttribute>();
                if (requestAttribute == null)
                {
                    throw new ImplementationCreationException(String.Format("Method {0} does not have a suitable [Get] / [Post] / etc attribute on it", methodInfo.Name));
                }

                var allowAnyStatusCodeAttribute = methodInfo.GetCustomAttribute <AllowAnyStatusCodeAttribute>();

                var methodSerializationMethodsAttribute = methodInfo.GetCustomAttribute <SerializationMethodsAttribute>();
                var serializationMethods = new ResolvedSerializationMethods(classSerializationMethodsAttribute, methodSerializationMethodsAttribute);

                var parameters        = methodInfo.GetParameters();
                var parameterGrouping = new ParameterGrouping(parameters, methodInfo.Name);

                this.ValidatePathParams(requestAttribute.Path, parameterGrouping.PathParameters.Select(x => x.Attribute.Name ?? x.Parameter.Name), methodInfo.Name);

                var methodBuilder     = typeBuilder.DefineMethod(methodInfo.Name, MethodAttributes.Public | MethodAttributes.Virtual, methodInfo.ReturnType, parameters.Select(x => x.ParameterType).ToArray());
                var methodIlGenerator = methodBuilder.GetILGenerator();

                this.AddRequestInfoCreation(methodIlGenerator, requesterField, requestAttribute);

                // If there's a cancellationtoken, add that
                if (parameterGrouping.CancellationToken.HasValue)
                {
                    methodIlGenerator.Emit(OpCodes.Dup);
                    methodIlGenerator.Emit(OpCodes.Ldarg, (short)parameterGrouping.CancellationToken.Value.Index);
                    methodIlGenerator.Emit(OpCodes.Callvirt, cancellationTokenSetter);
                }

                // If there are any class headers, add them
                if (classHeadersField != null)
                {
                    // requestInfo.ClassHeaders = classHeaders
                    methodIlGenerator.Emit(OpCodes.Dup);
                    methodIlGenerator.Emit(OpCodes.Ldsfld, classHeadersField);
                    methodIlGenerator.Emit(OpCodes.Callvirt, setClassHeadersMethod);
                }

                // If there are any property headers, add them
                foreach (var propertyHeader in propertyHeaders)
                {
                    var typedMethod = addPropertyHeaderMethod.MakeGenericMethod(propertyHeader.Value.FieldType);
                    methodIlGenerator.Emit(OpCodes.Dup);
                    methodIlGenerator.Emit(OpCodes.Ldstr, propertyHeader.Key.Name);
                    methodIlGenerator.Emit(OpCodes.Ldarg_0);
                    methodIlGenerator.Emit(OpCodes.Ldfld, propertyHeader.Value);
                    if (propertyHeader.Key.Value == null)
                    {
                        methodIlGenerator.Emit(OpCodes.Ldnull);
                    }
                    else
                    {
                        methodIlGenerator.Emit(OpCodes.Ldstr, propertyHeader.Key.Value);
                    }
                    methodIlGenerator.Emit(OpCodes.Callvirt, typedMethod);
                }

                // If there are any method headers, add them
                var methodHeaders = methodInfo.GetCustomAttributes <HeaderAttribute>();

                foreach (var methodHeader in methodHeaders)
                {
                    if (methodHeader.Name.Contains(':'))
                    {
                        throw new ImplementationCreationException(String.Format("[Header(\"{0}\")] on method {1} must not have colon in its name", methodHeader.Name, methodInfo.Name));
                    }
                    this.AddMethodHeader(methodIlGenerator, methodHeader);
                }

                // If we want to allow any status code, set that
                var resolvedAllowAnyStatusAttribute = allowAnyStatusCodeAttribute ?? classAllowAnyStatusCodeAttribute;
                if (resolvedAllowAnyStatusAttribute != null && resolvedAllowAnyStatusAttribute.AllowAnyStatusCode)
                {
                    methodIlGenerator.Emit(OpCodes.Dup);
                    methodIlGenerator.Emit(OpCodes.Ldc_I4_1);
                    methodIlGenerator.Emit(OpCodes.Callvirt, allowAnyStatusCodeSetter);
                }

                this.AddParameters(methodIlGenerator, parameterGrouping, methodInfo.Name, serializationMethods);

                this.AddRequestMethodInvocation(methodIlGenerator, methodInfo);

                // Finally, return
                methodIlGenerator.Emit(OpCodes.Ret);

                typeBuilder.DefineMethodOverride(methodBuilder, methodInfo);
            }
        }
Example #5
0
        private void GenerateMethodParameters(MethodEmitter methodEmitter, MethodModel method, ResolvedSerializationMethods serializationMethods)
        {
            foreach (var parameter in method.Parameters)
            {
                var attributes = parameter.GetAllSetAttributes().ToList();

                if (parameter.IsByRef)
                {
                    this.diagnostics.ReportParameterMustNotBeByRef(method, parameter);
                }

                if (parameter.IsCancellationToken)
                {
                    if (attributes.Count > 0)
                    {
                        this.diagnostics.ReportCancellationTokenMustHaveZeroAttributes(method, parameter);
                    }

                    methodEmitter.EmitSetCancellationToken(parameter);
                }
                else
                {
                    if (attributes.Count > 1)
                    {
                        this.diagnostics.ReportParameterMustHaveZeroOrOneAttributes(method, parameter, attributes);
                    }

                    if (parameter.HeaderAttribute != null)
                    {
                        if (parameter.HeaderAttribute.Attribute.Value != null)
                        {
                            this.diagnostics.ReportHeaderParameterMustNotHaveValue(method, parameter);
                        }
                        if (parameter.HeaderAttribute.Attribute.Name.Contains(":"))
                        {
                            this.diagnostics.ReportHeaderParameterMustNotHaveColonInName(method, parameter);
                        }

                        methodEmitter.EmitAddHeaderParameter(parameter);
                    }
                    else if (parameter.PathAttribute != null)
                    {
                        methodEmitter.EmitAddPathParameter(
                            parameter,
                            serializationMethods.ResolvePath(parameter.PathAttribute.Attribute.SerializationMethod));
                    }
                    else if (parameter.QueryAttribute != null)
                    {
                        methodEmitter.EmitAddQueryParameter(
                            parameter,
                            serializationMethods.ResolveQuery(parameter.QueryAttribute.Attribute.SerializationMethod));
                    }
                    else if (parameter.HttpRequestMessagePropertyAttribute != null)
                    {
                        methodEmitter.EmitAddHttpRequestMessagePropertyParameter(parameter);
                    }
                    else if (parameter.RawQueryStringAttribute != null)
                    {
                        methodEmitter.EmitAddRawQueryStringParameter(parameter);
                    }
                    else if (parameter.QueryMapAttribute != null)
                    {
                        if (!methodEmitter.TryEmitAddQueryMapParameter(parameter, serializationMethods.ResolveQuery(parameter.QueryMapAttribute.Attribute.SerializationMethod)))
                        {
                            this.diagnostics.ReportQueryMapParameterIsNotADictionary(method, parameter);
                        }
                    }
                    else if (parameter.BodyAttribute != null)
                    {
                        methodEmitter.EmitSetBodyParameter(
                            parameter,
                            serializationMethods.ResolveBody(parameter.BodyAttribute.Attribute.SerializationMethod));
                    }
                    else
                    {
                        methodEmitter.EmitAddQueryParameter(parameter, serializationMethods.ResolveQuery(QuerySerializationMethod.Default));
                    }
                }
            }
        }
Example #6
0
 private void GenerateMethodProperties(MethodEmitter methodEmitter, List <EmittedProperty> emittedProperties, ResolvedSerializationMethods serializationMethods)
 {
     foreach (var property in emittedProperties)
     {
         // We've already validated these
         if (property.PropertyModel.HeaderAttribute != null)
         {
             methodEmitter.EmitAddHeaderProperty(property);
         }
         else if (property.PropertyModel.PathAttribute != null)
         {
             methodEmitter.EmitAddPathProperty(
                 property,
                 serializationMethods.ResolvePath(property.PropertyModel.PathAttribute.Attribute.SerializationMethod));
         }
         else if (property.PropertyModel.QueryAttribute != null)
         {
             methodEmitter.EmitAddQueryProperty(
                 property,
                 serializationMethods.ResolveQuery(property.PropertyModel.QueryAttribute.Attribute.SerializationMethod));
         }
         else if (property.PropertyModel.HttpRequestMessagePropertyAttribute != null)
         {
             methodEmitter.EmitAddHttpRequestMessagePropertyProperty(property);
         }
     }
 }