123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876 |
- // JsonKit v0.5 - A simple but flexible Json library in a single .cs file.
- //
- // Copyright (C) 2014 Topten Software (contact@toptensoftware.com) All rights reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this product
- // except in compliance with the License. You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software distributed under the
- // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
- // either express or implied. See the License for the specific language governing permissions
- // and limitations under the License.
- #if !JSONKIT_NO_EMIT
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.IO;
- using System.Reflection;
- using System.Globalization;
- using System.Reflection.Emit;
- namespace Topten.JsonKit
- {
- static class Emit
- {
- // 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);
- });
- };
- }
- }
- static void LoadContantFromObject(ILGenerator il, object value)
- {
- switch (Type.GetTypeCode(value.GetType()))
- {
- case TypeCode.Boolean:
- il.Emit(OpCodes.Ldc_I4, ((bool)value) ? 1 : 0);
- break;
- case TypeCode.Char:
- case TypeCode.SByte:
- case TypeCode.Byte:
- case TypeCode.Int16:
- case TypeCode.UInt16:
- case TypeCode.Int32:
- case TypeCode.UInt32:
- il.Emit(OpCodes.Ldc_I4, (int)value);
- break;
- case TypeCode.Int64:
- il.Emit(OpCodes.Ldc_I8, (long)value);
- break;
- case TypeCode.UInt64:
- il.Emit(OpCodes.Ldc_I8, (long)(ulong)value);
- break;
- case TypeCode.Single:
- il.Emit(OpCodes.Ldc_R4, (float)value);
- break;
- case TypeCode.Double:
- il.Emit(OpCodes.Ldc_R8, (double)value);
- break;
- default:
- throw new InvalidOperationException($"JsonKit doesn't support the type `{value.GetType().ToString()}` for ExcludeIfEquals");
- }
- }
- public static void WriteKeyAndValueCheckIfEmpty(IJsonWriter w, object o, string key)
- {
- if (o == null)
- return;
- // Check if empty
- if (o is System.Collections.IEnumerable e)
- {
- if (!e.GetEnumerator().MoveNext())
- return;
- }
- w.WriteKeyNoEscaping(key);
- w.WriteValue(o);
- }
- // Pseudo box lets us pass a value type by reference. Used during
- // deserialization of value types.
- interface IPseudoBox
- {
- object GetValue();
- }
- [Obfuscation(Exclude = true, ApplyToMembers = true)]
- class PseudoBox<T> : IPseudoBox where T : struct
- {
- public T value = default(T);
- object IPseudoBox.GetValue() { return value; }
- }
- // 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;
- }
- }
- // Helper to make the call to a PsuedoBox value's IJsonLoading or IJsonLoaded
- static Action<object, IJsonReader> MakeInterfaceCall(Type type, Type tItf)
- {
- // Interface supported?
- if (!tItf.IsAssignableFrom(type))
- return null;
- // Resolve the box type
- var boxType = typeof(PseudoBox<>).MakeGenericType(type);
- // Create method
- var method = new DynamicMethod("dynamic_invoke_" + tItf.Name, null, new Type[] { typeof(object), typeof(IJsonReader) }, true);
- var il = method.GetILGenerator();
- // Call interface method
- il.Emit(OpCodes.Ldarg_0);
- il.Emit(OpCodes.Castclass, boxType);
- il.Emit(OpCodes.Ldflda, boxType.GetField("value"));
- il.Emit(OpCodes.Ldarg_1);
- il.Emit(OpCodes.Call, type.GetInterfaceMap(tItf).TargetMethods[0]);
- il.Emit(OpCodes.Ret);
- // Done
- return (Action<object, IJsonReader>)method.CreateDelegate(typeof(Action<object, IJsonReader>));
- }
- // Similar to above but for IJsonLoadField
- static Func<object, IJsonReader, string, bool> MakeLoadFieldCall(Type type)
- {
- // Interface supported?
- var tItf = typeof(IJsonLoadField);
- if (!tItf.IsAssignableFrom(type))
- return null;
- // Resolve the box type
- var boxType = typeof(PseudoBox<>).MakeGenericType(type);
- // Create method
- var method = new DynamicMethod("dynamic_invoke_" + tItf.Name, typeof(bool), new Type[] { typeof(object), typeof(IJsonReader), typeof(string) }, true);
- var il = method.GetILGenerator();
- // Call interface method
- il.Emit(OpCodes.Ldarg_0);
- il.Emit(OpCodes.Castclass, boxType);
- il.Emit(OpCodes.Ldflda, boxType.GetField("value"));
- il.Emit(OpCodes.Ldarg_1);
- il.Emit(OpCodes.Ldarg_2);
- il.Emit(OpCodes.Call, type.GetInterfaceMap(tItf).TargetMethods[0]);
- il.Emit(OpCodes.Ret);
- // Done
- return (Func<object, IJsonReader, string, bool>)method.CreateDelegate(typeof(Func<object, IJsonReader, string, bool>));
- }
- // 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) =>
- {
- try
- {
- // 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);
- }
- catch (Exception x)
- {
- var loadex = obj as IJsonLoadException;
- if (loadex != null)
- loadex.OnJsonLoadException(reader, x);
- }
- };
- // Since we've created the ParseInto handler, we might as well register
- // as a Parse handler too.
- RegisterIntoParser(type, parseInto);
- // Done
- return parseInto;
- }
- // Registers a ParseInto handler as Parse handler that instantiates the object
- // and then parses into it.
- static void RegisterIntoParser(Type type, Action<IJsonReader, object> parseInto)
- {
- // Check type has a parameterless constructor
- var con = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[0], null);
- if (con == null)
- return;
- // Create a dynamic method that can do the work
- var method = new DynamicMethod("dynamic_factory", typeof(object), new Type[] { typeof(IJsonReader), typeof(Action<IJsonReader, object>) }, true);
- var il = method.GetILGenerator();
- // Create the new object
- var locObj = il.DeclareLocal(typeof(object));
- il.Emit(OpCodes.Newobj, con);
- il.Emit(OpCodes.Dup); // For return value
- il.Emit(OpCodes.Stloc, locObj);
- il.Emit(OpCodes.Ldarg_1); // parseinto delegate
- il.Emit(OpCodes.Ldarg_0); // IJsonReader
- il.Emit(OpCodes.Ldloc, locObj); // new object instance
- il.Emit(OpCodes.Callvirt, typeof(Action<IJsonReader, object>).GetMethod("Invoke"));
- il.Emit(OpCodes.Ret);
- var factory = (Func<IJsonReader, Action<IJsonReader, object>, object>)method.CreateDelegate(typeof(Func<IJsonReader, Action<IJsonReader, object>, object>));
- Json.RegisterParser(type, (reader, type2) =>
- {
- return factory(reader, parseInto);
- });
- }
- // Generate the MSIL to retrieve a value for a particular field or property from a IJsonReader
- private static void GenerateGetJsonValue(JsonMemberInfo m, ILGenerator il)
- {
- Action<string> generateCallToHelper = helperName =>
- {
- // Call the helper
- il.Emit(OpCodes.Ldarg_0);
- il.Emit(OpCodes.Call, typeof(Emit).GetMethod(helperName, new Type[] { typeof(IJsonReader) }));
- // Move to next token
- il.Emit(OpCodes.Ldarg_0);
- il.Emit(OpCodes.Callvirt, typeof(IJsonReader).GetMethod("NextToken", new Type[] { }));
- };
- Type[] numericTypes = new Type[] {
- typeof(int), typeof(uint), typeof(long), typeof(ulong),
- typeof(short), typeof(ushort), typeof(decimal),
- typeof(byte), typeof(sbyte),
- typeof(double), typeof(float)
- };
- if (m.MemberType == typeof(string))
- {
- generateCallToHelper("GetLiteralString");
- }
- else if (m.MemberType == typeof(bool))
- {
- generateCallToHelper("GetLiteralBool");
- }
- else if (m.MemberType == typeof(char))
- {
- generateCallToHelper("GetLiteralChar");
- }
- else if (numericTypes.Contains(m.MemberType))
- {
- // Get raw number string
- il.Emit(OpCodes.Ldarg_0);
- il.Emit(OpCodes.Call, typeof(Emit).GetMethod("GetLiteralNumber", new Type[] { typeof(IJsonReader) }));
- // Convert to a string
- il.Emit(OpCodes.Call, typeof(CultureInfo).GetProperty("InvariantCulture").GetGetMethod());
- il.Emit(OpCodes.Call, m.MemberType.GetMethod("Parse", new Type[] { typeof(string), typeof(IFormatProvider) }));
- //
- il.Emit(OpCodes.Ldarg_0);
- il.Emit(OpCodes.Callvirt, typeof(IJsonReader).GetMethod("NextToken", new Type[] { }));
- }
- else
- {
- il.Emit(OpCodes.Ldarg_0);
- il.Emit(OpCodes.Ldtoken, m.MemberType);
- il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", new Type[] { typeof(RuntimeTypeHandle) }));
- il.Emit(OpCodes.Callvirt, typeof(IJsonReader).GetMethod("Parse", new Type[] { typeof(Type) }));
- il.Emit(m.MemberType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, m.MemberType);
- }
- }
- // Helper to fetch a literal bool from an IJsonReader
- [Obfuscation(Exclude = true)]
- public static bool GetLiteralBool(IJsonReader r)
- {
- switch (r.GetLiteralKind())
- {
- case LiteralKind.True:
- return true;
- case LiteralKind.False:
- return false;
- default:
- throw new InvalidDataException("expected a boolean value");
- }
- }
- // Helper to fetch a literal character from an IJsonReader
- [Obfuscation(Exclude = true)]
- public static char GetLiteralChar(IJsonReader r)
- {
- if (r.GetLiteralKind() != LiteralKind.String)
- throw new InvalidDataException("expected a single character string literal");
- var str = r.GetLiteralString();
- if (str == null || str.Length != 1)
- throw new InvalidDataException("expected a single character string literal");
- return str[0];
- }
- // Helper to fetch a literal string from an IJsonReader
- [Obfuscation(Exclude = true)]
- public static string GetLiteralString(IJsonReader r)
- {
- switch (r.GetLiteralKind())
- {
- case LiteralKind.Null: return null;
- case LiteralKind.String: return r.GetLiteralString();
- }
- throw new InvalidDataException("expected a string literal");
- }
- // Helper to fetch a literal number from an IJsonReader (returns the raw string)
- [Obfuscation(Exclude = true)]
- public static string GetLiteralNumber(IJsonReader r)
- {
- switch (r.GetLiteralKind())
- {
- case LiteralKind.SignedInteger:
- case LiteralKind.UnsignedInteger:
- case LiteralKind.FloatingPoint:
- return r.GetLiteralString();
- }
- throw new InvalidDataException("expected a numeric literal");
- }
- }
- }
- #endif
|