private SimpleField <TMessage> BuildSimpleField <TMessage, TProperty>(FieldHandlerDefinition definition) { var toReturn = new SimpleField <TMessage>(); var messageParameter = Expression.Parameter(definition.MessageType, "message"); var datomParameter = Expression.Parameter(typeof(Datom), "datom"); var datomValue = Expression.Property(datomParameter, $"{nameof(Datom.Value)}"); var deserializedValue = definition.PrimitiveSerializer.GetDeserializeExpression(datomValue); if (definition.IsRepeated) { var datomArrayIndex = Expression.Property(datomParameter, $"{nameof(Datom.ParameterArrayIndex)}"); var insertIntoRepeatedFieldMethod = definition.PropertyOuterType.GetMethod($"{nameof(RepeatedField<bool>.Insert)}"); var insertExpression = Expression.Call( messageParameter, insertIntoRepeatedFieldMethod, datomArrayIndex, deserializedValue ); var insertFunction = Expression.Lambda <Action <TMessage, Datom> >( insertExpression, messageParameter, datomParameter ).Compile(); toReturn.Deserialize = insertFunction; toReturn.Serialize = BuildSerializeFunction <TMessage, TProperty>(definition); } else { var fieldExpression = Expression.PropertyOrField(messageParameter, definition.Property.Name); var setFieldExpression = Expression.Assign(fieldExpression, deserializedValue); var setFieldFunction = Expression.Lambda <Action <TMessage, Datom> >( setFieldExpression, messageParameter, datomParameter ).Compile(); toReturn.Deserialize = setFieldFunction; toReturn.Serialize = BuildSerializeFunction <TMessage, TProperty>(definition); } return(toReturn); }
private Func <TMessage, Datom, IEnumerable <Datom> > BuildSerializeFunction <TMessage, TProperty>(FieldHandlerDefinition definition) { var messageParameter = Expression.Parameter(definition.MessageType, "message"); var prototypeParameter = Expression.Parameter(typeof(Datom), "prototype"); var fieldOnMessage = Expression.Property(messageParameter, definition.Property); var messageTypeRegistrationType = typeof(TypeRegistration <>).MakeGenericType(definition.MessageType); var messageIdGetterExpressionBuilder = (Func <ParameterExpression, Expression>)messageTypeRegistrationType .GetField($"{nameof(TypeRegistration<int>.KeyGetterExpressionBuilder)}") .GetValue(definition.MessageTypeRegistration); var arrayElementParameter = Expression.Parameter(definition.PropertyType, "element"); var arrayIndexParameter = Expression.Parameter(typeof(int), "i"); var arrayIndexAsUInt = Expression.Convert(arrayIndexParameter, typeof(uint)); var parameterIndexExpression = definition.IsRepeated ? (Expression)arrayIndexAsUInt : (Expression)Expression.Constant((uint)0, typeof(uint)); var referencedObjectExpression = definition.IsRepeated ? (Expression)arrayElementParameter : (Expression)fieldOnMessage; Expression valueExpression; Expression shouldSerializeExpression; if (definition.IsReference) { var propertyTypeRegistration = (TypeRegistration <TProperty>)_typeRegistry.TypeRegistrationByType(definition.PropertyType); var arrayElementIdentityProperty = propertyTypeRegistration.KeyGetterExpressionBuilder(referencedObjectExpression); valueExpression = _primitiveSerializers[typeof(ulong)].GetSerializeExpression(arrayElementIdentityProperty); shouldSerializeExpression = Expression.NotEqual(referencedObjectExpression, Expression.Constant(null)); } else { valueExpression = definition.PrimitiveSerializer.GetSerializeExpression(referencedObjectExpression); shouldSerializeExpression = definition.PrimitiveSerializer.GetShouldSerializeExpression(referencedObjectExpression); } var messageIdentityExpression = messageIdGetterExpressionBuilder(messageParameter); var constructInstance = Expression.New( typeof(Datom).GetConstructors().OrderByDescending(x => x.GetParameters().Count()).First(), Expression.Property(prototypeParameter, $"{nameof(Datom.AggregateType)}"), //ushort aggregateTypeId, Expression.Property(prototypeParameter, $"{nameof(Datom.AggregateIdentity)}"), //ulong aggregateId, Expression.Constant(definition.MessageTypeRegistration.TypeId, typeof(ushort)), //ushort type, messageIdentityExpression, //ulong identity, Expression.Constant((ushort)definition.FieldDescriptor.FieldNumber, typeof(ushort)), //ushort parameter, parameterIndexExpression, //uint parameterIndex, valueExpression, //byte[] value, Expression.Property(prototypeParameter, $"{nameof(Datom.TransactionId)}"), //ulong transactionId, Expression.Property(prototypeParameter, $"{nameof(Datom.Action)}") //DatomAction action ); Expression serializeExpression; if (definition.IsRepeated) { var mapperDelegateType = typeof(Func <, ,>).MakeGenericType(definition.PropertyType, typeof(int), typeof(Datom)); var mapper = Expression.Lambda(mapperDelegateType, constructInstance, arrayElementParameter, arrayIndexParameter); var iEnumerableType = typeof(IEnumerable <>).MakeGenericType(definition.PropertyType); var fieldOnMessageAsIEnumerable = Expression.Convert(fieldOnMessage, iEnumerableType); //public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate); var whereMethod = typeof(Enumerable).GetMethods() .First(x => x.Name == $"{nameof(Enumerable.Where)}" && x.GetParameters()[1].ParameterType.GetGenericArguments().Length == 2) .MakeGenericMethod(definition.PropertyType); //public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, int, TResult> selector); var selectMethod = typeof(Enumerable).GetMethods() .First(x => x.Name == $"{nameof(Enumerable.Select)}" && x.GetParameters()[1].ParameterType.GetGenericArguments().Length == 3) .MakeGenericMethod(definition.PropertyType, typeof(Datom)); var shouldSerializeLambda = Expression.Lambda <Func <TProperty, bool> >(shouldSerializeExpression, arrayElementParameter); var whereExpression = Expression.Call(whereMethod, fieldOnMessageAsIEnumerable, shouldSerializeLambda); serializeExpression = Expression.Call(selectMethod, whereExpression, mapper); } else { var yieldMethod = typeof(ProtobufDatomSerializer) .GetMethod($"{nameof(ProtobufDatomSerializer.Yield)}", BindingFlags.Static | BindingFlags.NonPublic) .MakeGenericMethod(typeof(Datom)); serializeExpression = Expression.Condition( shouldSerializeExpression, Expression.Call(yieldMethod, constructInstance), Expression.Constant(Enumerable.Empty <Datom>(), typeof(IEnumerable <Datom>)) ); } return(Expression.Lambda <Func <TMessage, Datom, IEnumerable <Datom> > >( serializeExpression, messageParameter, prototypeParameter ).Compile()); }
private ReferenceField <TMessage> BuildReferenceField <TMessage, TProperty>(FieldHandlerDefinition definition) { var toReturn = new ReferenceField <TMessage>(); var messageParameter = Expression.Parameter(definition.MessageType, "message"); var fieldOnMessage = Expression.Property(messageParameter, definition.Property); var repeatedFieldIndexParameter = Expression.Parameter(typeof(int), "repeatedFieldIndex"); var referencedObjectParameter = Expression.Parameter(typeof(object), "referencedObject"); var referencedObjectCasted = Expression.Convert(referencedObjectParameter, definition.PropertyType); if (definition.IsRepeated) { var insertIntoRepeatedFieldMethod = definition.PropertyOuterType.GetMethod($"{nameof(RepeatedField<bool>.Insert)}"); var insertExpression = Expression.Call( fieldOnMessage, insertIntoRepeatedFieldMethod, repeatedFieldIndexParameter, referencedObjectCasted ); var insertFunction = Expression.Lambda <Action <TMessage, int, object> >( insertExpression, messageParameter, repeatedFieldIndexParameter, referencedObjectParameter ).Compile(); toReturn.SetReference = insertFunction; var fieldOnMessageAsIEnumerableObject = Expression.Convert(fieldOnMessage, typeof(IEnumerable <object>)); toReturn.Follow = Expression.Lambda <Func <TMessage, IEnumerable <object> > >(fieldOnMessageAsIEnumerableObject, messageParameter).Compile(); toReturn.Serialize = BuildSerializeFunction <TMessage, TProperty>(definition); } else { var setFieldExpression = Expression.Assign(fieldOnMessage, referencedObjectCasted); var setFieldFunction = Expression.Lambda <Action <TMessage, int, object> >( setFieldExpression, messageParameter, repeatedFieldIndexParameter, referencedObjectParameter ).Compile(); toReturn.SetReference = setFieldFunction; var yieldMethod = typeof(ProtobufDatomSerializer) .GetMethod($"{nameof(ProtobufDatomSerializer.Yield)}", BindingFlags.Static | BindingFlags.NonPublic) .MakeGenericMethod(typeof(object)); var yieldReferencedObject = Expression.Call(yieldMethod, Expression.Convert(fieldOnMessage, typeof(object))); toReturn.Follow = Expression.Lambda <Func <TMessage, IEnumerable <object> > >(yieldReferencedObject, messageParameter).Compile(); toReturn.Serialize = BuildSerializeFunction <TMessage, TProperty>(definition); } //var datomParameter = Expression.Parameter(typeof(Datom), "datom"); //var datomValueAccess = Expression.Property(datomParameter, $"{nameof(Datom.Value)}"); //var convertToULongMethod = typeof(BitConverter).GetMethod($"{nameof(BitConverter.ToUInt64)}"); //var getReferencedId = Expression.Call(convertToULongMethod, datomValueAccess, Expression.Constant(0)); //toReturn.DeserializeIdentity = Expression.Lambda<Func<Datom, ulong>>(getReferencedId, datomParameter).Compile(); toReturn.DeserializeIdentity = (datom) => BitConverter.ToUInt64(datom.Value, 0); if (!_typeRegistry.IsTypeRegistered(definition.PropertyType)) { throw new InvalidOperationException($"Unable to create reference {definition.MessageType.FullName}.{definition.Property.Name}" + $" because the type {definition.PropertyType.FullName} is not registered. "); } toReturn.ReferencedTypeId = _typeRegistry.TypeRegistrationByType(definition.PropertyType).TypeId; return(toReturn); }
private Func <FieldDescriptor, int, KeyValuePair <ushort, FieldHandler> > FieldHandlerBuilder(BaseTypeRegistration typeRegistration) { return((discriptor, fieldIndex) => { var messageType = typeRegistration.Type; var titleCaseName = $"{discriptor.Name.Substring(0, 1).ToUpper()}{discriptor.Name.Substring(1)}"; var property = messageType.GetProperty(titleCaseName); Type propertyOuterType = null; Type propertyType; var isRepeated = property.PropertyType.GetTypeInfo().IsGenericType ? property.PropertyType.GetGenericTypeDefinition() == typeof(RepeatedField <>) : false; if (isRepeated) { propertyOuterType = property.PropertyType; propertyType = propertyOuterType.GetGenericArguments()[0]; } else { propertyType = property.PropertyType; } var isReference = typeof(IMessage).IsAssignableFrom(propertyType); var isPrimitive = _primitiveSerializers.ContainsKey(propertyType); if (!isReference && !isPrimitive) { throw new InvalidOperationException($"Unable to find a primitive serializer for type {property.PropertyType.FullName}."); } var definition = new FieldHandlerDefinition() { FieldDescriptor = discriptor, MessageTypeRegistration = typeRegistration, Property = property, MessageType = messageType, PropertyOuterType = propertyOuterType, PropertyType = propertyType, IsReference = isReference, IsRepeated = isRepeated, PrimitiveSerializer = isPrimitive ? _primitiveSerializers[propertyType] : null }; FieldHandler toReturn; if (isReference) { var buildBuildReferenceField = typeof(ProtobufDatomSerializer) .GetMethod($"{nameof(ProtobufDatomSerializer.BuildReferenceField)}", PrivateInstance) .MakeGenericMethod(messageType, propertyType); toReturn = buildBuildReferenceField.Invoke(this, new object[] { definition }) as FieldHandler; } else { var buildBuildSimpleField = typeof(ProtobufDatomSerializer) .GetMethod($"{nameof(ProtobufDatomSerializer.BuildSimpleField)}", PrivateInstance) .MakeGenericMethod(messageType, propertyType); toReturn = buildBuildSimpleField.Invoke(this, new object[] { definition }) as FieldHandler; } Func <byte[], string> debugDeserializer; if (isPrimitive) { var valueParameter = Expression.Parameter(typeof(byte[]), "value"); var deserialize = _primitiveSerializers[propertyType].GetDeserializeExpression(valueParameter); var toStringMethod = propertyType.GetMethods().First(x => x.Name == "ToString"); var deserializeToString = Expression.Call(deserialize, toStringMethod); debugDeserializer = Expression.Lambda <Func <byte[], string> >(deserializeToString, valueParameter).Compile(); } else { debugDeserializer = (value) => BitConverter.ToUInt64(value, 0).ToString(); } _debugFormatters[new Tuple <ushort, ushort>(typeRegistration.TypeId, (ushort)discriptor.FieldNumber)] = (datom) => { var valueFormat = isReference ? $"{propertyType.Name}[{{0}}]" : "{0}"; var fieldArrayIndex = isRepeated ? $"[{datom.ParameterArrayIndex}]": string.Empty; return $"TX[{datom.TransactionId}] {datom.Action} {typeRegistration.Type.Name}[{datom.Identity}]" + $".{discriptor.Name}{fieldArrayIndex} = {string.Format(valueFormat, debugDeserializer(datom.Value))}"; }; return new KeyValuePair <ushort, FieldHandler>((ushort)discriptor.FieldNumber, toReturn); }); }