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