/// <summary> /// Gets the Thrift wire type associated with the specified type. /// </summary> public static ThriftType Get(Type type) { if (!_knownTypes.ContainsKey(type)) { var thriftType = new ThriftType(type); _knownTypes.TryAdd(type, thriftType); // This has to be done this way because otherwise self-referencing types will loop // since they'd call ThriftType.Get before they were themselves added to _knownTypes switch (thriftType.Id) { case ThriftTypeId.Map: thriftType.KeyType = ThriftType.Get(thriftType._collectionGenericArgs[0]); thriftType.ValueType = ThriftType.Get(thriftType._collectionGenericArgs[1]); break; case ThriftTypeId.List: case ThriftTypeId.Set: if (thriftType.TypeInfo.IsArray) { thriftType.ElementType = ThriftType.Get(thriftType.TypeInfo.GetElementType()); } else { thriftType.ElementType = ThriftType.Get(thriftType._collectionGenericArgs[0]); } break; } } return(_knownTypes[type]); }
/// <summary> /// Initializes a new instance of the ThriftWireField class with the specified values. /// </summary> private ThriftWireField(short id, string name, ThriftType wireType, TypeInfo underlyingTypeInfo, ThriftWireFieldState state, object defaultValue, ThriftConverter converter, Expression getter, Func <Expression, Expression> setter) { Id = id; Name = name; WireType = wireType; UnderlyingTypeInfo = underlyingTypeInfo; Kind = state; DefaultValue = defaultValue; Converter = converter; Getter = getter; Setter = setter; // NullChecker is required because UWP doesn't implement Equal/NotEqual on nullables. if (getter != null) { if (Nullable.GetUnderlyingType(underlyingTypeInfo.AsType()) == null) { if (!underlyingTypeInfo.IsValueType) { NullChecker = Expression.Equal( getter, Expression.Constant(null) ); } } else { // Can't use HasValue, not supported by UWP's expression interpreter NullChecker = Expression.Call( typeof(object), "ReferenceEquals", Type.EmptyTypes, Expression.Convert( getter, typeof(object) ), Expression.Constant(null) ); } } }
/// <summary> /// Creates an expression reading the specified map type. /// </summary> private static Expression CreateReaderForMap(ParameterExpression protocolParam, ThriftType thriftType) { var mapType = thriftType.TypeInfo.AsType(); var mapVar = Expression.Variable(mapType); var headerVar = Expression.Variable(typeof(ThriftMapHeader)); var countVar = Expression.Variable(typeof(int)); var endOfLoop = Expression.Label(); return(Expression.Block( mapType, new[] { mapVar, headerVar, countVar }, Expression.Assign( mapVar, Expression.New(mapType) ), Expression.Assign( headerVar, Expression.Call(protocolParam, Methods.IThriftProtocol_ReadMapHeader) ), CreateTypeIdAssert( thriftType.KeyType.Id, Expression.Field(headerVar, Fields.ThriftMapHeader_KeyTypeId) ), CreateTypeIdAssert( thriftType.ValueType.Id, Expression.Field(headerVar, Fields.ThriftMapHeader_ValueTypeId) ), Expression.Assign( countVar, Expression.Constant(0) ), Expression.Loop( Expression.IfThenElse( Expression.Equal( countVar, Expression.Field(headerVar, Fields.ThriftMapHeader_Count) ), Expression.Break(endOfLoop), Expression.Block( Expression.Call( mapVar, "Add", Types.None, CreateReaderForType(protocolParam, thriftType.KeyType), CreateReaderForType(protocolParam, thriftType.ValueType) ), Expression.PostIncrementAssign(countVar) ) ), endOfLoop ), Expression.Call(protocolParam, Methods.IThriftProtocol_ReadMapEnd), // return value mapVar )); }
/// <summary> /// Creates an expression reading for the specified type. /// </summary> private static Expression CreateReaderForType(ParameterExpression protocolParam, ThriftType thriftType) { if (thriftType.NullableType != null) { return(Expression.Convert( CreateReaderForType(protocolParam, thriftType.NullableType), thriftType.TypeInfo.AsType() )); } switch (thriftType.Id) { case ThriftTypeId.Boolean: return(Expression.Call(protocolParam, Methods.IThriftProtocol_ReadBoolean)); case ThriftTypeId.SByte: return(Expression.Call(protocolParam, Methods.IThriftProtocol_ReadSByte)); case ThriftTypeId.Double: return(Expression.Call(protocolParam, Methods.IThriftProtocol_ReadDouble)); case ThriftTypeId.Int16: return(Expression.Call(protocolParam, Methods.IThriftProtocol_ReadInt16)); case ThriftTypeId.Int32: if (thriftType.TypeInfo.IsEnum) { return(Expression.Convert( Expression.Call(protocolParam, Methods.IThriftProtocol_ReadInt32), thriftType.TypeInfo.AsType() )); } return(Expression.Call(protocolParam, Methods.IThriftProtocol_ReadInt32)); case ThriftTypeId.Int64: return(Expression.Call(protocolParam, Methods.IThriftProtocol_ReadInt64)); case ThriftTypeId.Binary: if (thriftType.TypeInfo == TypeInfos.String) { return(Expression.Call(protocolParam, Methods.IThriftProtocol_ReadString)); } return(Expression.Call(protocolParam, Methods.IThriftProtocol_ReadBinary)); case ThriftTypeId.Map: return(CreateReaderForMap(protocolParam, thriftType)); case ThriftTypeId.Set: case ThriftTypeId.List: if (thriftType.TypeInfo.IsArray) { return(CreateReaderForArray(protocolParam, thriftType)); } return(CreateReaderForListOrSet(protocolParam, thriftType)); default: return(Expression.Call( typeof(ThriftStructReader), "Read", new[] { thriftType.TypeInfo.AsType() }, protocolParam )); } }
/// <summary> /// Creates an expression reading the specified list or set type. /// </summary> private static Expression CreateReaderForListOrSet(ParameterExpression protocolParam, ThriftType thriftType) { var collectionType = thriftType.TypeInfo.AsType(); var collectionVar = Expression.Variable(collectionType); var headerVar = Expression.Variable(typeof(ThriftCollectionHeader)); var countVar = Expression.Variable(typeof(int)); var endOfLoop = Expression.Label(); return(Expression.Block( collectionType, new[] { collectionVar, headerVar, countVar }, Expression.Assign( headerVar, Expression.Call( protocolParam, thriftType.Id == ThriftTypeId.List ? Methods.IThriftProtocol_ReadListHeader : Methods.IThriftProtocol_ReadSetHeader ) ), CreateTypeIdAssert( thriftType.ElementType.Id, Expression.Field(headerVar, Fields.ThriftCollectionHeader_ElementTypeId) ), Expression.Assign( collectionVar, Expression.New(collectionType) ), Expression.Assign( countVar, Expression.Constant(0) ), Expression.Loop( Expression.IfThenElse( Expression.Equal( countVar, Expression.Field(headerVar, Fields.ThriftCollectionHeader_Count) ), Expression.Break(endOfLoop), Expression.Block( Expression.Call( collectionVar, "Add", Types.None, CreateReaderForType(protocolParam, thriftType.ElementType) ), Expression.PostIncrementAssign(countVar) ) ), endOfLoop ), Expression.Call( protocolParam, thriftType.Id == ThriftTypeId.List ? Methods.IThriftProtocol_ReadListEnd : Methods.IThriftProtocol_ReadSetEnd ), // return value collectionVar )); }
/// <summary> /// Creates an expression reading the specified array type. /// </summary> private static Expression CreateReaderForArray(ParameterExpression protocolParam, ThriftType thriftType) { var arrayType = thriftType.TypeInfo.AsType(); var itemType = thriftType.ElementType.TypeInfo.AsType(); var arrayVar = Expression.Variable(arrayType); var headerVar = Expression.Variable(typeof(ThriftCollectionHeader)); var lengthVar = Expression.Variable(typeof(int)); var endOfLoop = Expression.Label(); return(Expression.Block( arrayType, new[] { arrayVar, headerVar, lengthVar }, Expression.Assign( headerVar, Expression.Call(protocolParam, Methods.IThriftProtocol_ReadListHeader) ), CreateTypeIdAssert( thriftType.ElementType.Id, Expression.Field(headerVar, Fields.ThriftCollectionHeader_ElementTypeId) ), Expression.Assign( arrayVar, Expression.NewArrayBounds( itemType, Expression.Field(headerVar, Fields.ThriftCollectionHeader_Count) ) ), Expression.Assign( lengthVar, Expression.Constant(0) ), Expression.Loop( Expression.IfThenElse( Expression.Equal( lengthVar, Expression.Field(headerVar, Fields.ThriftCollectionHeader_Count) ), Expression.Break(endOfLoop), Expression.Block( Expression.Assign( Expression.ArrayAccess( arrayVar, lengthVar ), CreateReaderForType(protocolParam, thriftType.ElementType) ), Expression.PostIncrementAssign(lengthVar) ) ), endOfLoop ), Expression.Call(protocolParam, Methods.IThriftProtocol_ReadListEnd), // return value arrayVar )); }
/// <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; } }
/// <summary> /// Initializes a new instance of the ThriftConvertibleValue class with the specified underlying type and converter. /// </summary> protected ThriftConvertibleValue(TypeInfo typeInfo, ThriftConverter converter) { WireType = ThriftType.Get(converter?.FromType ?? typeInfo.AsType()); Converter = converter; }
/// <summary> /// Creates an expression writing the specified array type. /// </summary> private static Expression CreateWriterForArray(ParameterExpression protocolParam, ThriftType thriftType, Expression value) { var counterVar = Expression.Variable(typeof(int)); var endOfLoop = Expression.Label(); return(Expression.Block( new[] { counterVar }, Expression.Call( protocolParam, Methods.IThriftProtocol_WriteListHeader, Expression.New( Constructors.ThriftCollectionHeader, Expression.Property(value, "Length"), Expression.Constant(thriftType.ElementType.Id) ) ), Expression.Assign( counterVar, Expression.Constant(0) ), Expression.Loop( Expression.IfThenElse( Expression.LessThan( counterVar, Expression.Property(value, "Length") ), Expression.Block( CreateWriterForType( protocolParam, thriftType.ElementType, Expression.ArrayAccess(value, counterVar) ), Expression.PostIncrementAssign(counterVar) ), Expression.Break(endOfLoop) ), endOfLoop ), Expression.Call( protocolParam, Methods.IThriftProtocol_WriteListEnd ) )); }
/// <summary> /// Creates an expression writing the specified map type. /// </summary> private static Expression CreateWriterForMap(ParameterExpression protocolParam, ThriftType thriftType, Expression value) { // This code does not use IEnumerable.GetEnumerator, in order to use the "better" enumerator on collections // that implement their own, e.g. List<T> has a struct-returning GetEnumerator(), and a ref-returning IEnumerable<T>.GetEnumerator() var getEnumeratorMethod = thriftType.TypeInfo.AsType().GetRuntimeMethod("GetEnumerator", Types.None); var endOfLoop = Expression.Label(); var enumeratorVar = Expression.Variable(getEnumeratorMethod.ReturnType); return(Expression.Block( new[] { enumeratorVar }, Expression.Call( protocolParam, Methods.IThriftProtocol_WriteMapHeader, Expression.New( Constructors.ThriftMapHeader, Expression.Property(value, "Count"), Expression.Constant(thriftType.KeyType.Id), Expression.Constant(thriftType.ValueType.Id) ) ), Expression.Assign( enumeratorVar, Expression.Call(value, getEnumeratorMethod) ), Expression.Loop( Expression.IfThenElse( // Do not use IsTrue, not supported by UWP's expression interpreter Expression.Equal( Expression.Call(enumeratorVar, "MoveNext", Types.None), Expression.Constant(true) ), Expression.Block( CreateWriterForType( protocolParam, thriftType.KeyType, Expression.Property( Expression.Property(enumeratorVar, "Current"), "Key" ) ), CreateWriterForType( protocolParam, thriftType.ValueType, Expression.Property( Expression.Property(enumeratorVar, "Current"), "Value" ) ) ), Expression.Break(endOfLoop) ), endOfLoop ), Expression.Call(protocolParam, Methods.IThriftProtocol_WriteMapEnd) )); }
/// <summary> /// Creates an expression writing the specified type. /// </summary> private static Expression CreateWriterForType(ParameterExpression protocolParam, ThriftType thriftType, Expression value) { switch (thriftType.Id) { case ThriftTypeId.Boolean: return(Expression.Call(protocolParam, Methods.IThriftProtocol_WriteBoolean, value)); case ThriftTypeId.SByte: return(Expression.Call(protocolParam, Methods.IThriftProtocol_WriteSByte, value)); case ThriftTypeId.Double: return(Expression.Call(protocolParam, Methods.IThriftProtocol_WriteDouble, value)); case ThriftTypeId.Int16: return(Expression.Call(protocolParam, Methods.IThriftProtocol_WriteInt16, value)); case ThriftTypeId.Int32: if (thriftType.IsEnum) { value = Expression.Convert(value, typeof(int)); } return(Expression.Call(protocolParam, Methods.IThriftProtocol_WriteInt32, value)); case ThriftTypeId.Int64: return(Expression.Call(protocolParam, Methods.IThriftProtocol_WriteInt64, value)); case ThriftTypeId.Binary: if (thriftType.TypeInfo == TypeInfos.String) { return(Expression.Call(protocolParam, Methods.IThriftProtocol_WriteString, value)); } return(Expression.Call(protocolParam, Methods.IThriftProtocol_WriteBinary, value)); case ThriftTypeId.Map: return(CreateWriterForMap(protocolParam, thriftType, value)); case ThriftTypeId.Set: case ThriftTypeId.List: if (thriftType.TypeInfo.IsArray) { return(CreateWriterForArray(protocolParam, thriftType, value)); } return(CreateWriterForListOrSet(protocolParam, thriftType, value)); default: return(Expression.Call( typeof(ThriftStructWriter), "Write", new[] { value.Type }, value, protocolParam )); } }
/// <summary> /// Creates an expression writing the specified list or set type. /// </summary> private static Expression CreateWriterForListOrSet(ParameterExpression protocolParam, ThriftType thriftType, Expression value) { // same remark as in CreateWriterForMap var getEnumeratorMethod = thriftType.TypeInfo.AsType().GetRuntimeMethod("GetEnumerator", Types.None); var enumeratorVar = Expression.Variable(getEnumeratorMethod.ReturnType); var endOfLoop = Expression.Label(); return(Expression.Block( new[] { enumeratorVar }, Expression.Call( protocolParam, thriftType.Id == ThriftTypeId.List ? Methods.IThriftProtocol_WriteListHeader : Methods.IThriftProtocol_WriteSetHeader, Expression.New( Constructors.ThriftCollectionHeader, Expression.Property(value, "Count"), Expression.Constant(thriftType.ElementType.Id) ) ), Expression.Assign( enumeratorVar, Expression.Call(value, getEnumeratorMethod) ), Expression.Loop( Expression.IfThenElse( // Do not use IsTrue, not supported by UWP's expression interpreter Expression.Equal( Expression.Call(enumeratorVar, "MoveNext", Types.None), Expression.Constant(true) ), CreateWriterForType( protocolParam, thriftType.ElementType, Expression.Property(enumeratorVar, "Current") ), Expression.Break(endOfLoop) ), endOfLoop ), Expression.Call( protocolParam, thriftType.Id == ThriftTypeId.List ? Methods.IThriftProtocol_WriteListEnd : Methods.IThriftProtocol_WriteSetEnd ) )); }