Ejemplo n.º 1
0
        // Make a parser for value types
        public static Func <IJsonReader, Type, object> MakeParser(Type type)
        {
            System.Diagnostics.Debug.Assert(type.IsValueType);

            // ParseJson method?
            var parseJson = ReflectionInfo.FindParseJson(type);

            if (parseJson != null)
            {
                if (parseJson.GetParameters()[0].ParameterType == typeof(IJsonReader))
                {
                    var method = new DynamicMethod("invoke_ParseJson", typeof(Object), new Type[] { typeof(IJsonReader), typeof(Type) }, true);
                    var il     = method.GetILGenerator();

                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Call, parseJson);
                    il.Emit(OpCodes.Box, type);
                    il.Emit(OpCodes.Ret);
                    return((Func <IJsonReader, Type, object>)method.CreateDelegate(typeof(Func <IJsonReader, Type, object>)));
                }
                else
                {
                    var method = new DynamicMethod("invoke_ParseJson", typeof(Object), new Type[] { typeof(string) }, true);
                    var il     = method.GetILGenerator();

                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Call, parseJson);
                    il.Emit(OpCodes.Box, type);
                    il.Emit(OpCodes.Ret);
                    var invoke = (Func <string, object>)method.CreateDelegate(typeof(Func <string, object>));

                    return((r, t) =>
                    {
                        if (r.GetLiteralKind() == LiteralKind.String)
                        {
                            var o = invoke(r.GetLiteralString());
                            r.NextToken();
                            return o;
                        }
                        throw new InvalidDataException(string.Format("Expected string literal for type {0}", type.FullName));
                    });
                }
            }
            else
            {
                // Get the reflection info for this type
                var ri = ReflectionInfo.GetReflectionInfo(type);
                if (ri == null)
                {
                    return(null);
                }

                // We'll create setters for each property/field
                var setters = new Dictionary <string, Action <IJsonReader, object> >();

                // Store the value in a pseudo box until it's fully initialized
                var boxType = typeof(PseudoBox <>).MakeGenericType(type);

                // Process all members
                foreach (var m in ri.Members)
                {
                    // Ignore write only properties
                    var pi = m.Member as PropertyInfo;
                    var fi = m.Member as FieldInfo;
                    if (pi != null && pi.GetSetMethod(true) == null)
                    {
                        continue;
                    }

                    // Create a dynamic method that can do the work
                    var method = new DynamicMethod("dynamic_parser", null, new Type[] { typeof(IJsonReader), typeof(object) }, true);
                    var il     = method.GetILGenerator();

                    // Load the target
                    il.Emit(OpCodes.Ldarg_1);
                    il.Emit(OpCodes.Castclass, boxType);
                    il.Emit(OpCodes.Ldflda, boxType.GetField("value"));

                    // Get the value
                    GenerateGetJsonValue(m, il);

                    // Assign it
                    if (pi != null)
                    {
                        il.Emit(OpCodes.Call, pi.GetSetMethod(true));
                    }
                    if (fi != null)
                    {
                        il.Emit(OpCodes.Stfld, fi);
                    }

                    // Done
                    il.Emit(OpCodes.Ret);

                    // Store in the map of setters
                    setters.Add(m.JsonKey, (Action <IJsonReader, object>)method.CreateDelegate(typeof(Action <IJsonReader, object>)));
                }

                // Create helpers to invoke the interfaces (this is painful but avoids having to really box
                // the value in order to call the interface).
                Action <object, IJsonReader>             invokeLoading = MakeInterfaceCall(type, typeof(IJsonLoading));
                Action <object, IJsonReader>             invokeLoaded  = MakeInterfaceCall(type, typeof(IJsonLoaded));
                Func <object, IJsonReader, string, bool> invokeField   = MakeLoadFieldCall(type);

                // Create the parser
                Func <IJsonReader, Type, object> parser = (reader, Type) =>
                {
                    // Create pseudobox (ie: new PseudoBox<Type>)
                    var box = DecoratingActivator.CreateInstance(boxType);

                    // Call IJsonLoading
                    if (invokeLoading != null)
                    {
                        invokeLoading(box, reader);
                    }

                    // Read the dictionary
                    reader.ParseDictionary(key =>
                    {
                        // Call IJsonLoadField
                        if (invokeField != null && invokeField(box, reader, key))
                        {
                            return;
                        }

                        // Get a setter and invoke it if found
                        Action <IJsonReader, object> setter;
                        if (setters.TryGetValue(key, out setter))
                        {
                            setter(reader, box);
                        }
                    });

                    // IJsonLoaded
                    if (invokeLoaded != null)
                    {
                        invokeLoaded(box, reader);
                    }

                    // Return the value
                    return(((IPseudoBox)box).GetValue());
                };

                // Done
                return(parser);
            }
        }
Ejemplo n.º 2
0
        // Create an "into parser" that can parse from IJsonReader into a reference type (ie: a class)
        public static Action <IJsonReader, object> MakeIntoParser(Type type)
        {
            System.Diagnostics.Debug.Assert(!type.IsValueType);

            // Get the reflection info for this type
            var ri = ReflectionInfo.GetReflectionInfo(type);

            if (ri == null)
            {
                return(null);
            }

            // We'll create setters for each property/field
            var setters = new Dictionary <string, Action <IJsonReader, object> >();

            // Process all members
            foreach (var m in ri.Members)
            {
                // Ignore write only properties
                var pi = m.Member as PropertyInfo;
                var fi = m.Member as FieldInfo;
                if (pi != null && pi.GetSetMethod(true) == null)
                {
                    continue;
                }

                // Ignore read only properties that has KeepInstance attribute
                if (pi != null && pi.GetGetMethod(true) == null && m.KeepInstance)
                {
                    continue;
                }

                // Create a dynamic method that can do the work
                var method = new DynamicMethod("dynamic_parser", null, new Type[] { typeof(IJsonReader), typeof(object) }, true);
                var il     = method.GetILGenerator();

                // Load the target
                il.Emit(OpCodes.Ldarg_1);
                il.Emit(OpCodes.Castclass, type);

                // Try to keep existing instance?
                if (m.KeepInstance)
                {
                    // Get existing existing instance
                    il.Emit(OpCodes.Dup);
                    if (pi != null)
                    {
                        il.Emit(OpCodes.Callvirt, pi.GetGetMethod(true));
                    }
                    else
                    {
                        il.Emit(OpCodes.Ldfld, fi);
                    }

                    var existingInstance        = il.DeclareLocal(m.MemberType);
                    var lblExistingInstanceNull = il.DefineLabel();

                    // Keep a copy of the existing instance in a locale
                    il.Emit(OpCodes.Dup);
                    il.Emit(OpCodes.Stloc, existingInstance);

                    // Compare to null
                    il.Emit(OpCodes.Ldnull);
                    il.Emit(OpCodes.Ceq);
                    il.Emit(OpCodes.Brtrue_S, lblExistingInstanceNull);

                    il.Emit(OpCodes.Ldarg_0);                       // reader
                    il.Emit(OpCodes.Ldloc, existingInstance);       // into
                    il.Emit(OpCodes.Callvirt, typeof(IJsonReader).GetMethod("ParseInto", new Type[] { typeof(Object) }));

                    il.Emit(OpCodes.Pop);       // Clean up target left on stack (1)
                    il.Emit(OpCodes.Ret);

                    il.MarkLabel(lblExistingInstanceNull);
                }

                // Get the value from IJsonReader
                GenerateGetJsonValue(m, il);

                // Assign it
                if (pi != null)
                {
                    il.Emit(OpCodes.Callvirt, pi.GetSetMethod(true));
                }
                if (fi != null)
                {
                    il.Emit(OpCodes.Stfld, fi);
                }

                // Done
                il.Emit(OpCodes.Ret);

                // Store the handler in map
                setters.Add(m.JsonKey, (Action <IJsonReader, object>)method.CreateDelegate(typeof(Action <IJsonReader, object>)));
            }


            // Now create the parseInto delegate
            Action <IJsonReader, object> parseInto = (reader, obj) =>
            {
                // Call IJsonLoading
                var loading = obj as IJsonLoading;
                if (loading != null)
                {
                    loading.OnJsonLoading(reader);
                }

                // Cache IJsonLoadField
                var lf = obj as IJsonLoadField;

                // Read dictionary keys
                reader.ParseDictionary(key =>
                {
                    // Call IJsonLoadField
                    if (lf != null && lf.OnJsonField(reader, key))
                    {
                        return;
                    }

                    // Call setters
                    Action <IJsonReader, object> setter;
                    if (setters.TryGetValue(key, out setter))
                    {
                        setter(reader, obj);
                    }
                });

                // Call IJsonLoaded
                var loaded = obj as IJsonLoaded;
                if (loaded != null)
                {
                    loaded.OnJsonLoaded(reader);
                }
            };

            // Since we've created the ParseInto handler, we might as well register
            // as a Parse handler too.
            RegisterIntoParser(type, parseInto);

            // Done
            return(parseInto);
        }
Ejemplo n.º 3
0
        // Generates a function that when passed an object of specified type, renders it to an IJsonWriter
        public static Action <IJsonWriter, object> MakeFormatter(Type type)
        {
            var formatJson = ReflectionInfo.FindFormatJson(type);

            if (formatJson != null)
            {
                var method = new DynamicMethod("invoke_formatJson", null, new Type[] { typeof(IJsonWriter), typeof(Object) }, true);
                var il     = method.GetILGenerator();
                if (formatJson.ReturnType == typeof(string))
                {
                    // w.WriteStringLiteral(o.FormatJson())
                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Ldarg_1);
                    il.Emit(OpCodes.Unbox, type);
                    il.Emit(OpCodes.Call, formatJson);
                    il.Emit(OpCodes.Callvirt, typeof(IJsonWriter).GetMethod("WriteStringLiteral"));
                }
                else
                {
                    // o.FormatJson(w);
                    il.Emit(OpCodes.Ldarg_1);
                    il.Emit(type.IsValueType ? OpCodes.Unbox : OpCodes.Castclass, type);
                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(type.IsValueType ? OpCodes.Call : OpCodes.Callvirt, formatJson);
                }
                il.Emit(OpCodes.Ret);
                return((Action <IJsonWriter, object>)method.CreateDelegate(typeof(Action <IJsonWriter, object>)));
            }
            else
            {
                // Get the reflection info for this type
                var ri = ReflectionInfo.GetReflectionInfo(type);
                if (ri == null)
                {
                    return(null);
                }

                // Create a dynamic method that can do the work
                var method = new DynamicMethod("dynamic_formatter", null, new Type[] { typeof(IJsonWriter), typeof(object) }, true);
                var il     = method.GetILGenerator();

                // Cast/unbox the target object and store in local variable
                var locTypedObj = il.DeclareLocal(type);
                il.Emit(OpCodes.Ldarg_1);
                il.Emit(type.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, type);
                il.Emit(OpCodes.Stloc, locTypedObj);

                // Get Invariant CultureInfo (since we'll probably be needing this)
                var locInvariant = il.DeclareLocal(typeof(IFormatProvider));
                il.Emit(OpCodes.Call, typeof(CultureInfo).GetProperty("InvariantCulture").GetGetMethod());
                il.Emit(OpCodes.Stloc, locInvariant);

                // These are the types we'll call .ToString(Culture.InvariantCulture) on
                var toStringTypes = new Type[] {
                    typeof(int), typeof(uint), typeof(long), typeof(ulong),
                    typeof(short), typeof(ushort), typeof(decimal),
                    typeof(byte), typeof(sbyte)
                };

                // Theses types we also generate for
                var otherSupportedTypes = new Type[] {
                    typeof(double), typeof(float), typeof(string), typeof(char)
                };

                // Call IJsonWriting if implemented
                if (typeof(IJsonWriting).IsAssignableFrom(type))
                {
                    if (type.IsValueType)
                    {
                        il.Emit(OpCodes.Ldloca, locTypedObj);
                        il.Emit(OpCodes.Ldarg_0);
                        il.Emit(OpCodes.Call, type.GetInterfaceMap(typeof(IJsonWriting)).TargetMethods[0]);
                    }
                    else
                    {
                        il.Emit(OpCodes.Ldloc, locTypedObj);
                        il.Emit(OpCodes.Castclass, typeof(IJsonWriting));
                        il.Emit(OpCodes.Ldarg_0);
                        il.Emit(OpCodes.Callvirt, typeof(IJsonWriting).GetMethod("OnJsonWriting", new Type[] { typeof(IJsonWriter) }));
                    }
                }

                // Process all members
                foreach (var m in ri.Members)
                {
                    // Dont save deprecated properties
                    if (m.Deprecated)
                    {
                        continue;
                    }

                    // Ignore write only properties
                    var pi = m.Member as PropertyInfo;
                    if (pi != null && pi.GetGetMethod(true) == null)
                    {
                        continue;
                    }

                    // Get the member type
                    var memberType = m.MemberType;

                    // Get the field/property value and store it in a local
                    LocalBuilder locValue = il.DeclareLocal(memberType);
                    il.Emit(type.IsValueType ? OpCodes.Ldloca : OpCodes.Ldloc, locTypedObj);
                    if (pi != null)
                    {
                        il.Emit(type.IsValueType ? OpCodes.Call : OpCodes.Callvirt, pi.GetGetMethod(true));
                    }
                    if (m.Member is FieldInfo fi)
                    {
                        il.Emit(OpCodes.Ldfld, fi);
                    }
                    il.Emit(OpCodes.Stloc, locValue);

                    // A label for early exit if not writing this member
                    Label lblFinishedMember = il.DefineLabel();

                    // Helper to generate IL to write the key
                    void EmitWriteKey()
                    {
                        // Write the Json key
                        il.Emit(OpCodes.Ldarg_0);
                        il.Emit(OpCodes.Ldstr, m.JsonKey);
                        il.Emit(OpCodes.Callvirt, typeof(IJsonWriter).GetMethod("WriteKeyNoEscaping", new Type[] { typeof(string) }));
                    }

                    // Is it a nullable type?
                    var typeUnderlying = Nullable.GetUnderlyingType(memberType);
                    if (typeUnderlying != null)
                    {
                        // Define some labels
                        var lblHasValue = il.DefineLabel();

                        // Call HasValue
                        il.Emit(OpCodes.Ldloca, locValue);
                        il.Emit(OpCodes.Call, memberType.GetProperty("HasValue").GetGetMethod());
                        il.Emit(OpCodes.Brtrue, lblHasValue);

                        // HasValue returned false, so either omit the key entirely, or write it as "null"
                        if (!m.ExcludeIfNull)
                        {
                            // Write the key
                            EmitWriteKey();

                            // No value, write "null"
                            il.Emit(OpCodes.Ldarg_0);
                            il.Emit(OpCodes.Ldstr, "null");
                            il.Emit(OpCodes.Callvirt, typeof(IJsonWriter).GetMethod("WriteRaw", new Type[] { typeof(string) }));
                        }
                        il.Emit(OpCodes.Br_S, lblFinishedMember);

                        // Get it's value
                        il.MarkLabel(lblHasValue);
                        il.Emit(OpCodes.Ldloca, locValue);
                        il.Emit(OpCodes.Call, memberType.GetProperty("Value").GetGetMethod());

                        // Switch to the underlying type from here on
                        locValue = il.DeclareLocal(typeUnderlying);
                        il.Emit(OpCodes.Stloc, locValue);
                        memberType = typeUnderlying;
                    }
                    else
                    {
                        if (m.ExcludeIfNull && !type.IsValueType)
                        {
                            il.Emit(OpCodes.Ldloc, locValue);
                            il.Emit(OpCodes.Brfalse_S, lblFinishedMember);
                        }
                    }

                    if (m.ExcludeIfEquals != null)
                    {
                        il.Emit(OpCodes.Ldloc, locValue);
                        var targetValue = Convert.ChangeType(m.ExcludeIfEquals, m.MemberType);
                        LoadContantFromObject(il, targetValue);
                        il.Emit(OpCodes.Ceq);
                        il.Emit(OpCodes.Brtrue_S, lblFinishedMember);
                    }

                    // ToString()
                    if (toStringTypes.Contains(memberType))
                    {
                        EmitWriteKey();
                        il.Emit(OpCodes.Ldarg_0);
                        il.Emit(memberType.IsValueType ? OpCodes.Ldloca : OpCodes.Ldloc, locValue);
                        il.Emit(OpCodes.Ldloc, locInvariant);
                        il.Emit(OpCodes.Call, memberType.GetMethod("ToString", new Type[] { typeof(IFormatProvider) }));
                        il.Emit(OpCodes.Callvirt, typeof(IJsonWriter).GetMethod("WriteRaw", new Type[] { typeof(string) }));
                    }

                    // ToString("R")
                    else if (memberType == typeof(float) || memberType == typeof(double))
                    {
                        EmitWriteKey();
                        il.Emit(OpCodes.Ldarg_0);
                        il.Emit(memberType.IsValueType ? OpCodes.Ldloca : OpCodes.Ldloc, locValue);
                        il.Emit(OpCodes.Ldstr, "R");
                        il.Emit(OpCodes.Ldloc, locInvariant);
                        il.Emit(OpCodes.Call, memberType.GetMethod("ToString", new Type[] { typeof(string), typeof(IFormatProvider) }));
                        il.Emit(OpCodes.Callvirt, typeof(IJsonWriter).GetMethod("WriteRaw", new Type[] { typeof(string) }));
                    }

                    // String?
                    else if (memberType == typeof(string))
                    {
                        EmitWriteKey();
                        il.Emit(OpCodes.Ldarg_0);
                        il.Emit(OpCodes.Ldloc, locValue);
                        il.Emit(OpCodes.Callvirt, typeof(IJsonWriter).GetMethod("WriteStringLiteral", new Type[] { typeof(string) }));
                    }

                    // Char?
                    else if (memberType == typeof(char))
                    {
                        EmitWriteKey();
                        il.Emit(OpCodes.Ldarg_0);
                        il.Emit(OpCodes.Ldloca, locValue);
                        il.Emit(OpCodes.Call, memberType.GetMethod("ToString", new Type[] { }));
                        il.Emit(OpCodes.Callvirt, typeof(IJsonWriter).GetMethod("WriteStringLiteral", new Type[] { typeof(string) }));
                    }

                    // Bool?
                    else if (memberType == typeof(bool))
                    {
                        EmitWriteKey();
                        il.Emit(OpCodes.Ldarg_0);
                        var lblTrue = il.DefineLabel();
                        var lblCont = il.DefineLabel();
                        il.Emit(OpCodes.Ldloc, locValue);
                        il.Emit(OpCodes.Brtrue_S, lblTrue);
                        il.Emit(OpCodes.Ldstr, "false");
                        il.Emit(OpCodes.Br_S, lblCont);
                        il.MarkLabel(lblTrue);
                        il.Emit(OpCodes.Ldstr, "true");
                        il.MarkLabel(lblCont);
                        il.Emit(OpCodes.Callvirt, typeof(IJsonWriter).GetMethod("WriteRaw", new Type[] { typeof(string) }));
                    }

                    // NB: We don't support DateTime as it's format can be changed

                    else
                    {
                        // Load writer
                        il.Emit(OpCodes.Ldarg_0);

                        // Load value
                        il.Emit(OpCodes.Ldloc, locValue);
                        if (memberType.IsValueType)
                        {
                            il.Emit(OpCodes.Box, memberType);
                        }

                        // Write the key and value
                        if (m.ExcludeIfEmpty)
                        {
                            il.Emit(OpCodes.Ldstr, m.JsonKey);
                            il.Emit(OpCodes.Call, typeof(Emit).GetMethod("WriteKeyAndValueCheckIfEmpty"));
                        }
                        else
                        {
                            EmitWriteKey();
                            il.Emit(OpCodes.Callvirt, typeof(IJsonWriter).GetMethod("WriteValue", new Type[] { typeof(object) }));
                        }
                    }

                    il.MarkLabel(lblFinishedMember);
                }

                // Call IJsonWritten
                if (typeof(IJsonWritten).IsAssignableFrom(type))
                {
                    if (type.IsValueType)
                    {
                        il.Emit(OpCodes.Ldloca, locTypedObj);
                        il.Emit(OpCodes.Ldarg_0);
                        il.Emit(OpCodes.Call, type.GetInterfaceMap(typeof(IJsonWritten)).TargetMethods[0]);
                    }
                    else
                    {
                        il.Emit(OpCodes.Ldloc, locTypedObj);
                        il.Emit(OpCodes.Castclass, typeof(IJsonWriting));
                        il.Emit(OpCodes.Ldarg_0);
                        il.Emit(OpCodes.Callvirt, typeof(IJsonWriting).GetMethod("OnJsonWritten", new Type[] { typeof(IJsonWriter) }));
                    }
                }

                // Done!
                il.Emit(OpCodes.Ret);
                var impl = (Action <IJsonWriter, object>)method.CreateDelegate(typeof(Action <IJsonWriter, object>));

                // Wrap it in a call to WriteDictionary
                return((w, obj) =>
                {
                    w.WriteDictionary(() =>
                    {
                        impl(w, obj);
                    });
                });
            }
        }