Пример #1
0
        protected static TDelegate BuildDeserializeDelegateExpressions <TDelegate, TReturn>(ParameterExpression inputExpression, Expression nameSpanExpression)
        {
            var nameSpan         = Expression.Variable(typeof(ReadOnlySpan <TSymbol>), "nameSpan");
            var returnValue      = Expression.Variable(typeof(TReturn), "returnValue");
            var lengthParameter  = Expression.Variable(typeof(int), "length");
            var endOfBlockLabel  = Expression.Label();
            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, returnValue
            };

            if (typeof(TSymbol) == typeof(char))
            {
                var asBytesMethodInfo = FindGenericMethod(typeof(MemoryMarshal), nameof(MemoryMarshal.AsBytes), BindingFlags.Public | BindingFlags.Static,
                                                          typeof(char), typeof(ReadOnlySpan <>));
                nameSpanExpression = Expression.Call(null, asBytesMethodInfo, assignNameSpan);
                assignNameSpan     = Expression.Assign(byteNameSpan, nameSpanExpression);
                parameters.Add(byteNameSpan);
            }
            else
            {
                byteNameSpan = nameSpan;
            }

            var memberInfos = new List <JsonMemberInfo>();
            var dict        = new Dictionary <string, TReturn>();

            foreach (var name in Enum.GetNames(typeof(T)))
            {
                var formattedValue = GetFormattedValue(name);
                memberInfos.Add(new JsonMemberInfo(name, typeof(T), null, formattedValue, false, true, false, null, null));
                var value = Enum.Parse(typeof(T), name);
                dict.Add(name, (TReturn)Convert.ChangeType(value, typeof(TReturn)));
            }

            Expression MatchExpressionFunctor(JsonMemberInfo memberInfo)
            {
                var enumValue = dict[memberInfo.MemberName];

                return(Expression.Assign(returnValue, Expression.Constant(enumValue)));
            }

            var returnTarget = Expression.Label(returnValue.Type);
            var returnLabel  = Expression.Label(returnTarget, returnValue);
            var expressions  = new List <Expression>
            {
                assignNameSpan,
                lengthExpression,
                MemberComparisonBuilder.Build <TSymbol>(memberInfos, 0, lengthParameter, byteNameSpan, endOfBlockLabel, MatchExpressionFunctor),
                Expression.Throw(Expression.Constant(new InvalidOperationException())),
                Expression.Label(endOfBlockLabel),
                returnLabel
            };
            var blockExpression  = Expression.Block(parameters, expressions);
            var lambdaExpression =
                Expression.Lambda <TDelegate>(blockExpression, inputExpression);

            return(lambdaExpression.Compile());
        }
Пример #2
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());
        }
Пример #3
0
        /// <summary>
        ///     Not sure if it's useful to build something like the automaton from the compelxformatter here
        /// </summary>
        private static DeserializeDelegate BuildDeserializeDelegate()
        {
            var        readerParameter = Expression.Parameter(typeof(JsonReader <TSymbol>).MakeByRefType(), "reader");
            MethodInfo nameSpanMethodInfo;

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

            var returnValue        = Expression.Variable(typeof(T), "returnValue");
            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, returnValue
            };

            if (typeof(TSymbol) == typeof(char))
            {
                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;
            }

            var memberInfos = new List <JsonMemberInfo>();
            var dict        = new Dictionary <string, T>();

            foreach (var value in Enum.GetValues(typeof(T)))
            {
                var formattedValue = GetFormattedValue(value);
                memberInfos.Add(new JsonMemberInfo(value.ToString(), typeof(T), null, formattedValue, false, true, false, null));
                dict.Add(value.ToString(), (T)value);
            }

            Expression MatchExpressionFunctor(JsonMemberInfo memberInfo)
            {
                var enumValue = dict[memberInfo.MemberName];

                return(Expression.Assign(returnValue, Expression.Constant(enumValue)));
            }

            var returnTarget = Expression.Label(returnValue.Type);
            var returnLabel  = Expression.Label(returnTarget, returnValue);
            var expressions  = new List <Expression>
            {
                assignNameSpan,
                lengthExpression,
                MemberComparisonBuilder.Build <TSymbol>(memberInfos, 0, lengthParameter, byteNameSpan, endOfBlockLabel, MatchExpressionFunctor),
                Expression.Throw(Expression.Constant(new InvalidOperationException())),
                Expression.Label(endOfBlockLabel),
                returnLabel
            };
            var blockExpression  = Expression.Block(parameters, expressions);
            var lambdaExpression =
                Expression.Lambda <DeserializeDelegate>(blockExpression, readerParameter);

            return(lambdaExpression.Compile());
        }