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