/// <summary> /// Parses a Thrift struct from the specified TypeInfo. /// </summary> public static ThriftStruct ParseStruct(TypeInfo typeInfo) { if (!_knownStructs.ContainsKey(typeInfo)) { if (typeInfo.IsInterface || typeInfo.IsAbstract) { throw ThriftParsingException.NotAConcreteType(typeInfo); } var attr = typeInfo.GetCustomAttribute <ThriftStructAttribute>(); if (attr == null) { throw ThriftParsingException.StructWithoutAttribute(typeInfo); } var fields = typeInfo.DeclaredProperties .Select(f => ParseField(f)) .Where(f => f != null) .ToArray(); // The type may have been added during fields parsing if (!_knownStructs.ContainsKey(typeInfo)) { _knownStructs.Add(typeInfo, new ThriftStruct(new ThriftStructHeader(attr.Name), fields, typeInfo)); } } return(_knownStructs[typeInfo]); }
/// <summary> /// Parses a Thrift service from the specified TypeInfo. /// </summary> public static ThriftService ParseService(TypeInfo typeInfo) { if (!_knownServices.ContainsKey(typeInfo)) { if (!typeInfo.IsInterface) { throw ThriftParsingException.NotAService(typeInfo); } var attr = typeInfo.GetCustomAttribute <ThriftServiceAttribute>(); if (attr == null) { throw ThriftParsingException.ServiceWithoutAttribute(typeInfo); } var methods = typeInfo.DeclaredMethods.ToDictionary(m => m.Name, m => ParseMethod(m)); if (methods.Count == 0) { throw ThriftParsingException.NoMethods(typeInfo); } _knownServices.Add(typeInfo, new ThriftService(attr.Name, methods)); } return(_knownServices[typeInfo]); }
/// <summary> /// Parses a Thrift method parameter from the specified ParameterInfo. /// </summary> private static ThriftParameter ParseMethodParameter(ParameterInfo parameterInfo) { var attr = parameterInfo.GetCustomAttribute <ThriftParameterAttribute>(); if (attr == null) { throw ThriftParsingException.ParameterWithoutAttribute(parameterInfo); } return(new ThriftParameter(attr.Id, attr.Name, parameterInfo.ParameterType.GetTypeInfo(), attr.ThriftConverter)); }
/// <summary> /// Parses a Thrift field from the specified PropertyInfo. /// If the PropertyInfo is not declared as a Thrift field, returns null. /// </summary> private static ThriftField ParseField(PropertyInfo propertyInfo) { var attr = propertyInfo.GetCustomAttribute <ThriftFieldAttribute>(); if (attr == null) { return(null); } var propertyTypeInfo = propertyInfo.PropertyType.GetTypeInfo(); var nullableType = Nullable.GetUnderlyingType(propertyInfo.PropertyType); if (attr.IsRequired) { if (attr.DefaultValue != null) { throw ThriftParsingException.DefaultValueOnRequiredField(propertyInfo); } if (nullableType != null) { throw ThriftParsingException.RequiredNullableField(propertyInfo); } } else { if (attr.DefaultValue == null && propertyTypeInfo.IsValueType && nullableType == null) { throw ThriftParsingException.OptionalValueField(propertyInfo); } } if (attr.DefaultValue != null) { if (attr.Converter == null) { if (attr.DefaultValue.GetType() != (nullableType ?? propertyInfo.PropertyType)) { throw ThriftParsingException.DefaultValueOfWrongType(propertyInfo); } } else { if (attr.DefaultValue.GetType() != attr.ThriftConverter.FromType) { throw ThriftParsingException.DefaultValueOfWrongType(propertyInfo); } } } return(new ThriftField(attr.Id, attr.Name, attr.IsRequired, attr.DefaultValue, attr.ThriftConverter, propertyInfo)); }
/// <summary> /// Parses all Thrift "throws" clauses on the specified MethodInfo. /// </summary> private static ThriftThrowsClause[] ParseThrowsClauses(MethodInfo methodInfo) { var clauses = methodInfo.GetCustomAttributes <ThriftThrowsAttribute>() .Select(a => new ThriftThrowsClause(a.Id, a.Name, a.ExceptionTypeInfo, a.ThriftConverter)) .ToArray(); var wrongClause = clauses.FirstOrDefault(c => !c.UnderlyingTypeInfo.Extends(typeof(Exception))); if (wrongClause != null) { throw ThriftParsingException.NotAnException(wrongClause.UnderlyingTypeInfo, methodInfo); } return(clauses); }
/// <summary> /// Gets an instantiable version of the specified type, for the specified interface type, /// with the specified concrete type if needed, throwing the specified exception if the type /// does implement the interface but cannot be instantiated. /// Returns null if the type does not implement the interface. /// </summary> private static Tuple <TypeInfo, Type[]> GetInstantiableVersion(TypeInfo typeInfo, Type interfaceType, Type concreteType, Func <TypeInfo, Exception> errorProvider) { if (typeInfo.IsInterface) { if (typeInfo.GenericTypeArguments.Length > 0) { var unbound = typeInfo.GetGenericTypeDefinition(); if (unbound == interfaceType) { return(Tuple.Create( concreteType.MakeGenericType(typeInfo.GenericTypeArguments).GetTypeInfo(), typeInfo.GenericTypeArguments )); } } } Tuple <TypeInfo, Type[]> instantiableVersion = null; foreach (var iface in typeInfo.ImplementedInterfaces.Where(i => i.GenericTypeArguments.Length > 0)) { var unboundIface = iface.GetGenericTypeDefinition(); if (unboundIface == interfaceType) { if (typeInfo.IsAbstract || !typeInfo.DeclaredConstructors.Any(c => c.GetParameters().Length == 0)) { throw errorProvider(typeInfo); } if (instantiableVersion != null) { throw ThriftParsingException.CollectionWithMultipleGenericImplementations(typeInfo); } instantiableVersion = Tuple.Create(typeInfo, iface.GenericTypeArguments); } } return(instantiableVersion); }
/// <summary> /// Parses a Thrift method from the specified MethodInfo. /// If the MethodInfo is not declared as a Thrift method, returns null. /// </summary> private static ThriftMethod ParseMethod(MethodInfo methodInfo) { var attr = methodInfo.GetCustomAttribute <ThriftMethodAttribute>(); if (attr == null) { throw ThriftParsingException.MethodWithoutAttribute(methodInfo); } var throwsClauses = ParseThrowsClauses(methodInfo); if (attr.IsOneWay && throwsClauses.Length != 0) { throw ThriftParsingException.OneWayMethodWithExceptions(methodInfo); } var unwrapped = methodInfo.ReturnType.UnwrapTask(); if (unwrapped == null) { throw ThriftParsingException.SynchronousMethod(methodInfo); } if (attr.IsOneWay && unwrapped != typeof(void)) { throw ThriftParsingException.OneWayMethodWithResult(methodInfo); } var methodParameters = methodInfo.GetParameters(); var parameters = methodParameters.Where(p => p.ParameterType != typeof(CancellationToken)) .Select(p => ParseMethodParameter(p)) .ToArray(); if (methodParameters.Length - parameters.Length > 1) { throw ThriftParsingException.MoreThanOneCancellationToken(methodInfo); } return(new ThriftMethod(attr.Name, attr.IsOneWay, new ThriftReturnValue(unwrapped.GetTypeInfo(), attr.ThriftConverter), throwsClauses, parameters)); }
/// <summary> /// Initializes a new instance of the ThriftType class from the specified .NET type. /// </summary> private ThriftType(Type type) { Id = ThriftTypeId.Empty; TypeInfo = type.GetTypeInfo(); if (type == typeof(void)) { return; } var underlyingNullableType = Nullable.GetUnderlyingType(type); if (underlyingNullableType != null) { NullableType = ThriftType.Get(underlyingNullableType); Id = NullableType.Id; IsEnum = NullableType.IsEnum; return; } if (PrimitiveIds.ContainsKey(type)) { Id = PrimitiveIds[type]; return; } if (TypeInfo.IsEnum) { if (TypeInfo.GetCustomAttribute <ThriftEnumAttribute>() == null) { throw ThriftParsingException.EnumWithoutAttribute(TypeInfo); } if (Enum.GetUnderlyingType(type) != typeof(int)) { throw ThriftParsingException.NonInt32Enum(TypeInfo); } Id = ThriftTypeId.Int32; IsEnum = true; return; } if (TypeInfo.IsValueType) { throw ThriftParsingException.UnknownValueType(TypeInfo); } if (TypeInfo.IsArray) { Id = ThriftTypeId.List; return; } var mapInterfaceAndArgs = GetInstantiableVersion(TypeInfo, typeof(IDictionary <,>), typeof(Dictionary <,>), p => ThriftParsingException.UnsupportedMap(p)); if (mapInterfaceAndArgs != null) { Id = ThriftTypeId.Map; TypeInfo = mapInterfaceAndArgs.Item1; _collectionGenericArgs = mapInterfaceAndArgs.Item2; } var setInterfaceAndArgs = GetInstantiableVersion(TypeInfo, typeof(ISet <>), typeof(HashSet <>), p => ThriftParsingException.UnsupportedSet(p)); if (setInterfaceAndArgs != null) { if (mapInterfaceAndArgs != null) { throw ThriftParsingException.CollectionWithOrthogonalInterfaces(TypeInfo); } Id = ThriftTypeId.Set; TypeInfo = setInterfaceAndArgs.Item1; _collectionGenericArgs = setInterfaceAndArgs.Item2; } var listInterfaceAndArgs = GetInstantiableVersion(TypeInfo, typeof(IList <>), typeof(List <>), p => ThriftParsingException.UnsupportedList(p)); if (listInterfaceAndArgs != null) { if (mapInterfaceAndArgs != null || setInterfaceAndArgs != null) { throw ThriftParsingException.CollectionWithOrthogonalInterfaces(TypeInfo); } Id = ThriftTypeId.List; TypeInfo = listInterfaceAndArgs.Item1; _collectionGenericArgs = listInterfaceAndArgs.Item2; } if (Id == ThriftTypeId.Empty) { Id = ThriftTypeId.Struct; } }