/// <summary> /// Creates an expression reading the specified struct type. /// </summary> private static LambdaExpression CreateReaderForStruct(ThriftStruct thriftStruct) { var protocolParam = Expression.Parameter(typeof(IThriftProtocol)); var structType = thriftStruct.TypeInfo.AsType(); var structVar = Expression.Variable(structType); var wireFields = thriftStruct.Fields.Select(f => ThriftWireField.Field(f, structVar)).ToList(); return(Expression.Lambda( Expression.Block( structType, new[] { structVar }, Expression.Assign( structVar, Expression.New(structType) ), CreateReaderForFields(protocolParam, wireFields), // return value structVar ), protocolParam )); }
/// <summary> /// Creates an expression writing the specified struct type. /// </summary> private static LambdaExpression CreateWriterForStruct(ThriftStruct thriftStruct) { var valueParam = Expression.Parameter(thriftStruct.TypeInfo.AsType()); var protocolParam = Expression.Parameter(typeof(IThriftProtocol)); var methodContents = new List <Expression> { Expression.Call( protocolParam, Methods.IThriftProtocol_WriteStructHeader, Expression.New( Constructors.ThriftStructHeader, Expression.Constant(thriftStruct.Header.Name) ) ) }; foreach (var field in thriftStruct.Fields) { methodContents.Add(CreateWriterForField(protocolParam, ThriftWireField.Field(field, valueParam))); } methodContents.Add(Expression.Call(protocolParam, Methods.IThriftProtocol_WriteFieldStop)); methodContents.Add(Expression.Call(protocolParam, Methods.IThriftProtocol_WriteStructEnd)); return(Expression.Lambda( Expression.Block(methodContents), valueParam, protocolParam )); }
/// <summary> /// Creates a writer for the specified method. /// </summary> private static Expression <Action <object[], IThriftProtocol> > CreateWriterForMethod(ThriftMethod method) { var argsParam = Expression.Parameter(typeof(object[])); var protocolParam = Expression.Parameter(typeof(IThriftProtocol)); var methodContents = new List <Expression> { Expression.Call( protocolParam, Methods.IThriftProtocol_WriteMessageHeader, Expression.New( Constructors.ThriftMessageHeader, Expression.Constant(method.Name), Expression.Constant(method.IsOneWay ? ThriftMessageType.OneWay : ThriftMessageType.Call) ) ), Expression.Call( protocolParam, Methods.IThriftProtocol_WriteStructHeader, Expression.New( Constructors.ThriftStructHeader, Expression.Constant("") ) ) }; for (int n = 0; n < method.Parameters.Count; n++) { var wireField = ThriftWireField.Parameter(method.Parameters[n], argsParam, n); methodContents.Add(ThriftStructWriter.CreateWriterForField(protocolParam, wireField)); } methodContents.Add(Expression.Call(protocolParam, Methods.IThriftProtocol_WriteFieldStop)); methodContents.Add(Expression.Call(protocolParam, Methods.IThriftProtocol_WriteStructEnd)); methodContents.Add(Expression.Call(protocolParam, Methods.IThriftProtocol_WriteMessageEnd)); return(Expression.Lambda <Action <object[], IThriftProtocol> >( Expression.Block(methodContents), argsParam, protocolParam )); }
/// <summary> /// Creates a reader for the specified method. /// </summary> private static LambdaExpression CreateReaderForMethod(ThriftMethod method) { var protocolParam = Expression.Parameter(typeof(IThriftProtocol)); var headerVariable = Expression.Variable(typeof(ThriftMessageHeader)); ParameterExpression hasReturnVariable = null; ParameterExpression returnVariable = null; if (method.ReturnValue.UnderlyingTypeInfo != TypeInfos.Void) { hasReturnVariable = Expression.Variable(typeof(bool)); returnVariable = Expression.Variable(method.ReturnValue.UnderlyingTypeInfo.AsType()); } var wireFields = new List <ThriftWireField>(); if (returnVariable != null) { wireFields.Add(ThriftWireField.ReturnValue(method.ReturnValue, returnVariable, hasReturnVariable)); } foreach (var exception in method.Exceptions) { wireFields.Add(ThriftWireField.ThrowsClause(exception)); } var statements = new List <Expression> { Expression.Assign( headerVariable, Expression.Call( protocolParam, Methods.IThriftProtocol_ReadMessageHeader ) ), Expression.IfThen( // Do not use IsFalse, not supported by UWP's expression interpreter Expression.Equal( Expression.Call( Methods.Enum_IsDefined, // The second argument is absolutely crucial here. // System.Type is an abstract class, implemented by an internal framework class System.RuntimeType // Not specifying the argument leads to the expression's type being typeof(System.RuntimeType), // which crashes on frameworks that restrict access to non-public framework types, such as Windows Phone 8.1 Expression.Constant(typeof(ThriftMessageType), typeof(Type)), Expression.Convert( Expression.Field(headerVariable, Fields.ThriftMessageHeader_MessageType), typeof(object) ) ), Expression.Constant(false) ), Expression.Throw( Expression.New( Constructors.ThriftProtocolException, Expression.Constant(ThriftProtocolExceptionType.InvalidMessageType) ) ) ), Expression.IfThen( Expression.Equal( Expression.Field(headerVariable, Fields.ThriftMessageHeader_MessageType), Expression.Constant(ThriftMessageType.Exception) ), Expression.Throw( Expression.Call( typeof(ThriftStructReader), "Read", new[] { typeof(ThriftProtocolException) }, protocolParam ) ) ), ThriftStructReader.CreateReaderForFields(protocolParam, wireFields), Expression.Call(protocolParam, Methods.IThriftProtocol_ReadMessageEnd) }; if (returnVariable != null) { statements.Add( Expression.IfThen( Expression.Equal( hasReturnVariable, Expression.Constant(false) ), Expression.Throw( Expression.New( Constructors.ThriftProtocolException, Expression.Constant(ThriftProtocolExceptionType.MissingResult) ) ) ) ); } if (returnVariable == null) { statements.Add(Expression.Constant(null)); } else { statements.Add(returnVariable); } return(Expression.Lambda( Expression.Block( returnVariable?.Type ?? typeof(object), returnVariable == null ? new[] { headerVariable } : new[] { headerVariable, hasReturnVariable, returnVariable }, statements ), new[] { protocolParam } )); }
/// <summary> /// Creates an expression writing the specified field. /// </summary> public static Expression CreateWriterForField(ParameterExpression protocolParam, ThriftWireField field) { Expression getter = field.Getter; bool isUnderlyingNullable = Nullable.GetUnderlyingType(field.UnderlyingTypeInfo.AsType()) != null; if (isUnderlyingNullable) { getter = Expression.Property(getter, "Value"); } if (field.Converter != null) { getter = field.Converter.CreateCall("ConvertBack", getter); } var writingExpr = Expression.Block( Expression.Call( protocolParam, Methods.IThriftProtocol_WriteFieldHeader, Expression.New( Constructors.ThriftFieldHeader, Expression.Constant(field.Id), Expression.Constant(field.Name), Expression.Constant(field.WireType.Id) ) ), CreateWriterForType( protocolParam, field.WireType, getter ), Expression.Call(protocolParam, Methods.IThriftProtocol_WriteFieldEnd) ); switch (field.Kind) { case ThriftWireFieldState.AlwaysPresent: return(writingExpr); case ThriftWireFieldState.Required: // It's possible to have a nullable required field if it's converted to a class if (field.UnderlyingTypeInfo.IsClass || isUnderlyingNullable) { return(Expression.IfThenElse( field.NullChecker, Expression.Throw( Expression.Call( Methods.ThriftSerializationException_RequiredFieldIsNull, Expression.Constant(field.Name) ) ), writingExpr )); } return(writingExpr); default: if (field.DefaultValue == null && (field.UnderlyingTypeInfo.IsClass || isUnderlyingNullable)) { return(Expression.IfThen( // Do not use IsFalse, not supported by UWP's expression interpreter Expression.Equal( field.NullChecker, Expression.Constant(false) ), writingExpr )); } var condition = Expression.NotEqual( getter, Expression.Constant(field.DefaultValue) ); if (field.UnderlyingTypeInfo.IsClass || isUnderlyingNullable) { condition = Expression.AndAlso( // Do not use IsTrue, not supported by UWP's expression interpreter Expression.Equal( field.NullChecker, Expression.Constant(false) ), condition ); } return(Expression.IfThen(condition, writingExpr)); } }