Esempio n. 1
0
        private static IJsonFormatter <TKey, TSymbol> GetKeyFormatter()
        {
            if (typeof(TKey).IsEnum)
            {
                return((IJsonFormatter <TKey, TSymbol>)ResolverBase.GetDefaultOrCreate(
                           typeof(EnumStringFormatter <, ,>).MakeGenericType(typeof(TKey), typeof(TSymbol), typeof(TResolver))));
            }

            return(StandardResolvers.GetResolver <TSymbol, TResolver>().GetFormatter <TKey>());
        }
Esempio n. 2
0
        private static SerializeDelegate BuildSerializeDelegate(Type type)
        {
            var writerParameter = Expression.Parameter(typeof(JsonWriter <TSymbol>).MakeByRefType(), "writer");
            var valueParameter  = Expression.Parameter(typeof(object), "value");

            if (type == typeof(object)) // if it's an object we can't do anything about so we write an empty object
            {
                return((ref JsonWriter <TSymbol> writer, object value) =>
                {
                    writer.WriteBeginObject();
                    writer.WriteEndObject();
                });
            }

            var formatterType       = StandardResolvers.GetResolver <TSymbol, TResolver>().GetFormatter(type).GetType();
            var fieldInfo           = formatterType.GetField("Default", BindingFlags.Static | BindingFlags.Public);
            var serializeMethodInfo = formatterType.GetMethod("Serialize");
            var lambda = Expression.Lambda <SerializeDelegate>(
                Expression.Call(Expression.Field(null, fieldInfo), serializeMethodInfo, writerParameter,
                                Expression.Convert(valueParameter, type)), writerParameter, valueParameter);

            return(lambda.Compile());
        }
Esempio n. 3
0
        /// <summary>
        /// Creates the deserializer for both utf8 and utf16
        /// There should not be a large difference between utf8 and utf16 besides member names
        /// </summary>
        protected static DeserializeDelegate <T, TSymbol> BuildDeserializeDelegate <T, TSymbol, TResolver>()
            where TResolver : IJsonFormatterResolver <TSymbol, TResolver>, new() where TSymbol : struct
        {
            var resolver          = StandardResolvers.GetResolver <TSymbol, TResolver>();
            var objectDescription = resolver.GetObjectDescription <T>();
            var memberInfos       = objectDescription.Where(a => a.CanWrite).ToList();
            var readerParameter   = Expression.Parameter(typeof(JsonReader <TSymbol>).MakeByRefType(), "reader");

            // can't deserialize abstract and only support interfaces based on IEnumerable<T> (this includes, IList, IReadOnlyList, IDictionary et al.)
            foreach (var memberInfo in memberInfos)
            {
                var memberType = memberInfo.MemberType;
                if (memberType.IsAbstract)
                {
                    if (memberType.TryGetTypeOfGenericInterface(typeof(IEnumerable <>), out _))
                    {
                        continue;
                    }

                    return(Expression
                           .Lambda <DeserializeDelegate <T, TSymbol> >(Expression.Block(
                                                                           Expression.Throw(Expression.Constant(new NotSupportedException($"{typeof(T).Name} contains abstract members."))),
                                                                           Expression.Default(typeof(T))),
                                                                       readerParameter).Compile());
                }
            }

            if (typeof(T).IsAbstract)
            {
                return(Expression.Lambda <DeserializeDelegate <T, TSymbol> >(Expression.Default(typeof(T)), readerParameter).Compile());
            }

            if (memberInfos.Count == 0)
            {
                Expression createExpression = null;
                if (typeof(T).IsClass)
                {
                    var ci = typeof(T).GetConstructor(Type.EmptyTypes);
                    if (ci != null)
                    {
                        createExpression = Expression.New(ci);
                    }
                }

                if (createExpression == null)
                {
                    createExpression = Expression.Default(typeof(T));
                }

                return(Expression.Lambda <DeserializeDelegate <T, TSymbol> >(createExpression, readerParameter).Compile());
            }

            var        returnValue = Expression.Variable(typeof(T), "result");
            MethodInfo nameSpanMethodInfo;
            MethodInfo tryReadEndObjectMethodInfo;
            MethodInfo beginObjectOrThrowMethodInfo;

            if (typeof(TSymbol) == typeof(char))
            {
                nameSpanMethodInfo         = FindPublicInstanceMethod(readerParameter.Type, nameof(JsonReader <TSymbol> .ReadUtf16NameSpan));
                tryReadEndObjectMethodInfo =
                    FindPublicInstanceMethod(readerParameter.Type, nameof(JsonReader <TSymbol> .TryReadUtf16IsEndObjectOrValueSeparator));
                beginObjectOrThrowMethodInfo = FindPublicInstanceMethod(readerParameter.Type, nameof(JsonReader <TSymbol> .ReadUtf16BeginObjectOrThrow));
            }
            else if (typeof(TSymbol) == typeof(byte))
            {
                nameSpanMethodInfo           = FindPublicInstanceMethod(readerParameter.Type, nameof(JsonReader <TSymbol> .ReadUtf8NameSpan));
                tryReadEndObjectMethodInfo   = FindPublicInstanceMethod(readerParameter.Type, nameof(JsonReader <TSymbol> .TryReadUtf8IsEndObjectOrValueSeparator));
                beginObjectOrThrowMethodInfo = FindPublicInstanceMethod(readerParameter.Type, nameof(JsonReader <TSymbol> .ReadUtf8BeginObjectOrThrow));
            }
            else
            {
                throw new NotSupportedException();
            }

            // We need to decide during generation if we handle constructors or normal member assignment, the difference is done in the functor below
            Func <JsonMemberInfo, Expression> matchExpressionFunctor;

            Expression[] constructorParameterExpresssions = null;
            if (objectDescription.Constructor != null)
            {
                // If we want to use the constructor we serialize into an array of variables internally and then create the object from that
                var dict = objectDescription.ConstructorMapping;
                constructorParameterExpresssions = new Expression[dict.Count];
                foreach (var valueTuple in dict)
                {
                    constructorParameterExpresssions[valueTuple.Value.Index] = Expression.Variable(valueTuple.Value.Type);
                }

                matchExpressionFunctor = memberInfo =>
                {
                    var element       = dict[memberInfo.MemberName];
                    var formatter     = resolver.GetFormatter(memberInfo);
                    var formatterType = formatter.GetType();
                    var fieldInfo     = formatterType.GetField("Default", BindingFlags.Static | BindingFlags.Public);
                    return(Expression.Assign(constructorParameterExpresssions[element.Index],
                                             Expression.Call(Expression.Field(null, fieldInfo),
                                                             FindPublicInstanceMethod(formatterType, "Deserialize", readerParameter.Type.MakeByRefType()),
                                                             readerParameter)));
                };
            }
            else
            {
                // The normal assign to member type
                matchExpressionFunctor = memberInfo =>
                {
                    var formatter     = resolver.GetFormatter(memberInfo);
                    var formatterType = formatter.GetType();
                    var fieldInfo     = formatterType.GetField("Default", BindingFlags.Static | BindingFlags.Public);
                    return(Expression.Assign(Expression.PropertyOrField(returnValue, memberInfo.MemberName),
                                             Expression.Call(Expression.Field(null, fieldInfo),
                                                             FindPublicInstanceMethod(formatterType, "Deserialize", readerParameter.Type.MakeByRefType()),
                                                             readerParameter)));
                };
            }

            var nameSpan           = Expression.Variable(typeof(ReadOnlySpan <TSymbol>), "nameSpan");
            var lengthParameter    = Expression.Variable(typeof(int), "length");
            var endOfBlockLabel    = Expression.Label();
            var nameSpanExpression = Expression.Call(readerParameter, nameSpanMethodInfo);
            var assignNameSpan     = Expression.Assign(nameSpan, nameSpanExpression);
            var lengthExpression   = Expression.Assign(lengthParameter, Expression.PropertyOrField(nameSpan, "Length"));
            var byteNameSpan       = Expression.Variable(typeof(ReadOnlySpan <byte>), "byteNameSpan");
            var parameters         = new List <ParameterExpression> {
                nameSpan, lengthParameter
            };

            if (typeof(TSymbol) == typeof(char))
            {
                // For utf16 we need to convert the attribute name to bytes to feed it to the matching logic
                Expression <Action> functor = () => MemoryMarshal.AsBytes(new ReadOnlySpan <char>());
                var asBytesMethodInfo       = (functor.Body as MethodCallExpression).Method;
                nameSpanExpression = Expression.Call(null, asBytesMethodInfo, assignNameSpan);
                assignNameSpan     = Expression.Assign(byteNameSpan, nameSpanExpression);
                parameters.Add(byteNameSpan);
            }
            else
            {
                byteNameSpan = nameSpan;
            }

            MethodInfo skipNextMethodInfo;

            if (typeof(TSymbol) == typeof(char))
            {
                skipNextMethodInfo = FindPublicInstanceMethod(readerParameter.Type, nameof(JsonReader <TSymbol> .SkipNextUtf16Segment));
            }
            else if (typeof(TSymbol) == typeof(byte))
            {
                skipNextMethodInfo = FindPublicInstanceMethod(readerParameter.Type, nameof(JsonReader <TSymbol> .SkipNextUtf8Segment));
            }
            else
            {
                throw new NotSupportedException();
            }

            var expressions = new List <Expression>
            {
                assignNameSpan,
                lengthExpression,
                MemberComparisonBuilder.Build <TSymbol>(memberInfos, 0, lengthParameter, byteNameSpan, endOfBlockLabel, matchExpressionFunctor),
                Expression.Call(readerParameter, skipNextMethodInfo),
                Expression.Label(endOfBlockLabel),
            };

            var        ifBlock         = Expression.Block(parameters, expressions);
            var        countExpression = Expression.Parameter(typeof(int), "count");
            var        abortExpression = Expression.IsTrue(Expression.Call(readerParameter, tryReadEndObjectMethodInfo, countExpression));
            var        readBeginObject = Expression.Call(readerParameter, beginObjectOrThrowMethodInfo);
            var        loopAbort       = Expression.Label(typeof(void));
            var        returnTarget    = Expression.Label(returnValue.Type);
            Expression block;

            if (objectDescription.Constructor != null)
            {
                var blockParameters = new List <ParameterExpression> {
                    returnValue, countExpression
                };
                // ReSharper disable AssignNullToNotNullAttribute
                blockParameters.AddRange(constructorParameterExpresssions.OfType <ParameterExpression>());
                // ReSharper restore AssignNullToNotNullAttribute
                block = Expression.Block(blockParameters, readBeginObject,
                                         Expression.Loop(
                                             Expression.IfThenElse(abortExpression, Expression.Break(loopAbort),
                                                                   ifBlock), loopAbort
                                             ),
                                         Expression.Assign(returnValue, Expression.New(objectDescription.Constructor, constructorParameterExpresssions)),
                                         Expression.Label(returnTarget, returnValue)
                                         );
            }
            else
            {
                block = Expression.Block(new[] { returnValue, countExpression }, readBeginObject,
                                         Expression.Assign(returnValue, Expression.New(returnValue.Type)),
                                         Expression.Loop(
                                             Expression.IfThenElse(abortExpression, Expression.Break(loopAbort),
                                                                   ifBlock), loopAbort
                                             ),
                                         Expression.Label(returnTarget, returnValue)
                                         );
            }

            var lambda = Expression.Lambda <DeserializeDelegate <T, TSymbol> >(block, readerParameter);

            return(lambda.Compile());
        }
Esempio n. 4
0
        /// <summary>
        /// Creates the serializer for both utf8 and utf16
        /// There should not be a large difference between utf8 and utf16 besides member names
        /// </summary>
        protected static SerializeDelegate <T, TSymbol> BuildSerializeDelegate <T, TSymbol, TResolver>()
            where TResolver : IJsonFormatterResolver <TSymbol, TResolver>, new() where TSymbol : struct
        {
            var resolver              = StandardResolvers.GetResolver <TSymbol, TResolver>();
            var memberInfos           = resolver.GetObjectDescription <T>().Where(a => a.CanRead).ToList();
            var writerParameter       = Expression.Parameter(typeof(JsonWriter <TSymbol>).MakeByRefType(), "writer");
            var valueParameter        = Expression.Parameter(typeof(T), "value");
            var nestingLimitParameter = Expression.Parameter(typeof(int), "nestingLimit");
            var expressions           = new List <Expression>();

            if (RecursionCandidate <T> .IsRecursionCandidate)
            {
                expressions.Add(Expression.IfThen(
                                    Expression.GreaterThan(
                                        nestingLimitParameter, Expression.Constant(NestingLimit)),
                                    Expression.Throw(
                                        Expression.Constant(new InvalidOperationException($"Nesting Limit of {NestingLimit} exceeded in Type {typeof(T).Name}.")))));
            }

            MethodInfo separatorWriteMethodInfo;
            MethodInfo writeBeginObjectMethodInfo;
            MethodInfo writeEndObjectMethodInfo;

            if (typeof(TSymbol) == typeof(char))
            {
                separatorWriteMethodInfo   = FindPublicInstanceMethod(writerParameter.Type, nameof(JsonWriter <TSymbol> .WriteUtf16ValueSeparator));
                writeBeginObjectMethodInfo = FindPublicInstanceMethod(writerParameter.Type, nameof(JsonWriter <TSymbol> .WriteUtf16BeginObject));
                writeEndObjectMethodInfo   = FindPublicInstanceMethod(writerParameter.Type, nameof(JsonWriter <TSymbol> .WriteUtf16EndObject));
            }
            else if (typeof(TSymbol) == typeof(byte))
            {
                separatorWriteMethodInfo   = FindPublicInstanceMethod(writerParameter.Type, nameof(JsonWriter <TSymbol> .WriteUtf8ValueSeparator));
                writeBeginObjectMethodInfo = FindPublicInstanceMethod(writerParameter.Type, nameof(JsonWriter <TSymbol> .WriteUtf8BeginObject));
                writeEndObjectMethodInfo   = FindPublicInstanceMethod(writerParameter.Type, nameof(JsonWriter <TSymbol> .WriteUtf8EndObject));
            }
            else
            {
                throw new NotSupportedException();
            }

            expressions.Add(Expression.Call(writerParameter, writeBeginObjectMethodInfo));
            var writeSeparator = Expression.Variable(typeof(bool), "writeSeparator");

            for (var i = 0; i < memberInfos.Count; i++)
            {
                var        memberInfo         = memberInfos[i];
                var        formatterType      = resolver.GetFormatter(memberInfo).GetType();
                Expression serializerInstance = null;
                MethodInfo serializeMethodInfo;
                Expression memberExpression     = Expression.PropertyOrField(valueParameter, memberInfo.MemberName);
                var        parameterExpressions = new List <Expression> {
                    writerParameter, memberExpression
                };
                var fieldInfo = formatterType.GetField("Default", BindingFlags.Static | BindingFlags.Public);
                if (IsNoRuntimeDecisionRequired(memberInfo.MemberType))
                {
                    var underlyingType = Nullable.GetUnderlyingType(memberInfo.MemberType);
                    // if it's nullable and we don't need the null, we call the underlying provider directly
                    if (memberInfo.ExcludeNull && underlyingType != null)
                    {
                        formatterType = resolver.GetFormatter(memberInfo, underlyingType).GetType();
                        fieldInfo     = formatterType.GetField("Default", BindingFlags.Static | BindingFlags.Public);
                        var methodInfo = memberInfo.MemberType.GetMethod("GetValueOrDefault", Type.EmptyTypes);
                        memberExpression     = Expression.Call(memberExpression, methodInfo);
                        parameterExpressions = new List <Expression> {
                            writerParameter, memberExpression
                        };
                    }

                    serializeMethodInfo = FindPublicInstanceMethod(formatterType, "Serialize", writerParameter.Type.MakeByRefType(),
                                                                   underlyingType ?? memberInfo.MemberType, typeof(int));
                    serializerInstance = Expression.Field(null, fieldInfo);
                }
                else
                {
                    serializeMethodInfo = typeof(BaseFormatter)
                                          .GetMethod(nameof(SerializeRuntimeDecisionInternal), BindingFlags.NonPublic | BindingFlags.Static)
                                          .MakeGenericMethod(memberInfo.MemberType, typeof(TSymbol), typeof(TResolver));
                    parameterExpressions.Add(Expression.Field(null, fieldInfo));
                }

                if (RecursionCandidate.LookupRecursionCandidate(memberInfo.MemberType)) // only for possible candidates
                {
                    parameterExpressions.Add(Expression.Add(nestingLimitParameter, Expression.Constant(1)));
                }
                else
                {
                    parameterExpressions.Add(nestingLimitParameter);
                }

                ConstantExpression[] writeNameExpressions;
                var formattedMemberInfoName = $"\"{memberInfo.Name}\":";

                MethodInfo propertyNameWriterMethodInfo;
                if (typeof(TSymbol) == typeof(char))
                {
                    writeNameExpressions         = new[] { Expression.Constant(formattedMemberInfoName) };
                    propertyNameWriterMethodInfo =
                        FindPublicInstanceMethod(writerParameter.Type, nameof(JsonWriter <TSymbol> .WriteUtf16Verbatim), typeof(string));
                }
                // utf8 has special logic for writing the attribute names as Expression.Constant(byte-Array) is slower than Expression.Constant(string)
                else if (typeof(TSymbol) == typeof(byte))
                {
                    // Everything above a length of 32 is not optimized
                    if (formattedMemberInfoName.Length > 32)
                    {
                        writeNameExpressions         = new[] { Expression.Constant(Encoding.UTF8.GetBytes(formattedMemberInfoName)) };
                        propertyNameWriterMethodInfo =
                            FindPublicInstanceMethod(writerParameter.Type, nameof(JsonWriter <TSymbol> .WriteUtf8Verbatim), typeof(byte[]));
                    }
                    else
                    {
                        writeNameExpressions = GetIntegersForMemberName(formattedMemberInfoName);
                        var typesToMatch = writeNameExpressions.Select(a => a.Value.GetType());
                        propertyNameWriterMethodInfo =
                            FindPublicInstanceMethod(writerParameter.Type, nameof(JsonWriter <TSymbol> .WriteUtf8Verbatim), typesToMatch.ToArray());
                    }
                }
                else
                {
                    throw new NotSupportedException();
                }

                var valueExpressions = new List <Expression>();
                // we need to add the separator, but only if a value was written before
                // we write the separator and set the marker after writing each field
                if (i > 0)
                {
                    valueExpressions.Add(
                        Expression.IfThen(
                            writeSeparator,
                            Expression.Block(
                                Expression.Call(writerParameter, separatorWriteMethodInfo))
                            ));
                }

                valueExpressions.Add(Expression.Call(writerParameter, propertyNameWriterMethodInfo, writeNameExpressions));
                valueExpressions.Add(Expression.Call(serializerInstance, serializeMethodInfo, parameterExpressions));
                valueExpressions.Add(Expression.Assign(writeSeparator, Expression.Constant(true)));
                Expression testNullExpression = null;
                if (memberInfo.ExcludeNull)
                {
                    if (memberInfo.MemberType.IsClass)
                    {
                        testNullExpression = Expression.ReferenceNotEqual(
                            Expression.PropertyOrField(valueParameter, memberInfo.MemberName),
                            Expression.Constant(null));
                    }
                    else if (memberInfo.MemberType.IsValueType && Nullable.GetUnderlyingType(memberInfo.MemberType) != null) // nullable value type
                    {
                        testNullExpression = Expression.IsTrue(
                            Expression.Property(Expression.PropertyOrField(valueParameter, memberInfo.MemberName), "HasValue"));
                    }
                }

                var shouldSerializeExpression = memberInfo.ShouldSerialize != null
                    ? Expression.IsTrue(Expression.Call(valueParameter, memberInfo.ShouldSerialize))
                    : null;

                Expression testExpression = null;
                if (testNullExpression != null && shouldSerializeExpression != null)
                {
                    testExpression = Expression.AndAlso(testNullExpression, shouldSerializeExpression);
                }
                else if (testNullExpression != null)
                {
                    testExpression = testNullExpression;
                }
                else if (shouldSerializeExpression != null)
                {
                    testExpression = shouldSerializeExpression;
                }

                if (testExpression != null)
                {
                    expressions.Add(Expression.IfThen(testExpression, Expression.Block(valueExpressions)));
                }
                else
                {
                    expressions.AddRange(valueExpressions);
                }
            }

            expressions.Add(Expression.Call(writerParameter, writeEndObjectMethodInfo));
            var blockExpression = Expression.Block(new[] { writeSeparator }, expressions);
            var lambda          =
                Expression.Lambda <SerializeDelegate <T, TSymbol> >(blockExpression, writerParameter, valueParameter, nestingLimitParameter);

            return(lambda.Compile());
        }
 public SpanJsonFormatterResolver() : this(StandardResolvers.GetResolver <byte, TResolver>())
 {
 }