/* SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. */ /* * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. * * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch B.V. licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file 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. */ #region Utf8Json License https://github.com/neuecc/Utf8Json/blob/master/LICENSE // MIT License // // Copyright (c) 2017 Yoshifumi Kawai // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #endregion using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Text.RegularExpressions; using System.Threading; using OpenSearch.Net.Utf8Json.Formatters; using OpenSearch.Net.Utf8Json.Internal; using OpenSearch.Net.Utf8Json.Internal.Emit; namespace OpenSearch.Net.Utf8Json.Resolvers { /// /// ObjectResolver by dynamic code generation. /// internal static class DynamicObjectResolver { /// AllowPrivate:False, ExcludeNull:False, NameMutate:Original public static readonly IJsonFormatterResolver Default = DynamicObjectResolverAllowPrivateFalseExcludeNullFalseNameMutateOriginal.Instance; /// AllowPrivate:False, ExcludeNull:True, NameMutate:CamelCase public static readonly IJsonFormatterResolver ExcludeNullCamelCase = DynamicObjectResolverAllowPrivateFalseExcludeNullTrueNameMutateCamelCase.Instance; /// AllowPrivate:True, ExcludeNull:True, NameMutate:CamelCase public static readonly IJsonFormatterResolver AllowPrivateExcludeNullCamelCase = DynamicObjectResolverAllowPrivateTrueExcludeNullTrueNameMutateCamelCase.Instance; public static IJsonFormatterResolver Create(Func propertyMapper, Lazy> mutator, bool excludeNull) => new CustomDynamicObjectResolver(propertyMapper, mutator, excludeNull); } internal sealed class CustomDynamicObjectResolver : IJsonFormatterResolver { private readonly Func _propertyMapper; private readonly Lazy> _mutator; private readonly bool _excludeNull; private readonly ThreadsafeTypeKeyHashTable _formatters = new ThreadsafeTypeKeyHashTable(); public CustomDynamicObjectResolver(Func propertyMapper, Lazy> mutator, bool excludeNull) { _propertyMapper = propertyMapper; _mutator = mutator; _excludeNull = excludeNull; } public IJsonFormatter GetFormatter() => (IJsonFormatter)_formatters.GetOrAdd(typeof(T), type => DynamicObjectTypeBuilder.BuildFormatterToDynamicMethod(this, _mutator.Value, _propertyMapper, _excludeNull, false)); } #region DynamicAssembly internal sealed class DynamicObjectResolverAllowPrivateFalseExcludeNullFalseNameMutateOriginal : IJsonFormatterResolver { // configuration public static readonly IJsonFormatterResolver Instance = new DynamicObjectResolverAllowPrivateFalseExcludeNullFalseNameMutateOriginal(); private static readonly Func NameMutator = StringMutator.Original; private static readonly bool ExcludeNull = false; private static readonly string ModuleName = $"{ResolverConfig.Namespace}.{nameof(DynamicObjectResolverAllowPrivateFalseExcludeNullFalseNameMutateOriginal)}"; private static readonly DynamicAssembly Assembly; static DynamicObjectResolverAllowPrivateFalseExcludeNullFalseNameMutateOriginal() => Assembly = new DynamicAssembly(ModuleName); private DynamicObjectResolverAllowPrivateFalseExcludeNullFalseNameMutateOriginal() { } public IJsonFormatter GetFormatter() => FormatterCache.formatter; private static class FormatterCache { public static readonly IJsonFormatter formatter; static FormatterCache() => formatter = (IJsonFormatter)DynamicObjectTypeBuilder.BuildFormatterToAssembly(Assembly, Instance, NameMutator, null, ExcludeNull); } } internal sealed class DynamicObjectResolverAllowPrivateFalseExcludeNullTrueNameMutateCamelCase : IJsonFormatterResolver { // configuration public static readonly IJsonFormatterResolver Instance = new DynamicObjectResolverAllowPrivateFalseExcludeNullTrueNameMutateCamelCase(); private static readonly Func NameMutator = StringMutator.ToCamelCase; private static readonly bool ExcludeNull = true; private static readonly string ModuleName = $"{ResolverConfig.Namespace}.{nameof(DynamicObjectResolverAllowPrivateFalseExcludeNullTrueNameMutateCamelCase)}"; private static readonly DynamicAssembly Assembly; static DynamicObjectResolverAllowPrivateFalseExcludeNullTrueNameMutateCamelCase() => Assembly = new DynamicAssembly(ModuleName); private DynamicObjectResolverAllowPrivateFalseExcludeNullTrueNameMutateCamelCase() { } public IJsonFormatter GetFormatter() => FormatterCache.formatter; private static class FormatterCache { public static readonly IJsonFormatter formatter; static FormatterCache() => formatter = (IJsonFormatter)DynamicObjectTypeBuilder.BuildFormatterToAssembly(Assembly, Instance, NameMutator, null, ExcludeNull); } } #endregion #region DynamicMethod internal sealed class DynamicObjectResolverAllowPrivateTrueExcludeNullTrueNameMutateCamelCase : IJsonFormatterResolver { // configuration public static readonly IJsonFormatterResolver Instance = new DynamicObjectResolverAllowPrivateTrueExcludeNullTrueNameMutateCamelCase(); private static readonly Func NameMutator = StringMutator.ToCamelCase; private static readonly bool ExcludeNull = true; public IJsonFormatter GetFormatter() => FormatterCache.formatter; private static class FormatterCache { public static readonly IJsonFormatter formatter; static FormatterCache() => formatter = (IJsonFormatter)DynamicObjectTypeBuilder.BuildFormatterToDynamicMethod(Instance, NameMutator, null, ExcludeNull, true); } } #endregion internal static class DynamicObjectTypeBuilder { private static readonly Regex SubtractFullNameRegex = new Regex(@", Version=\d+.\d+.\d+.\d+, Culture=\w+, PublicKeyToken=\w+", RegexOptions.Compiled); private static int _nameSequence; private static readonly HashSet IgnoreTypes = new HashSet { typeof(object), typeof(short), typeof(int), typeof(long), typeof(ushort), typeof(uint), typeof(ulong), typeof(float), typeof(double), typeof(bool), typeof(byte), typeof(sbyte), typeof(decimal), typeof(char), typeof(string), typeof(Guid), typeof(TimeSpan), typeof(DateTime), typeof(DateTimeOffset), }; private static readonly HashSet JsonPrimitiveTypes = new HashSet { typeof(short), typeof(int), typeof(long), typeof(ushort), typeof(uint), typeof(ulong), typeof(float), typeof(double), typeof(bool), typeof(byte), typeof(sbyte), typeof(string), }; public static object BuildFormatterToAssembly(DynamicAssembly assembly, IJsonFormatterResolver selfResolver, Func mutator, Func propertyMapper, bool excludeNull) { var type = typeof(T); if (type.IsNullable()) { type = type.GenericTypeArguments[0]; var innerFormatter = selfResolver.GetFormatterDynamic(type); if (innerFormatter == null) return null; return (IJsonFormatter)Activator.CreateInstance(typeof(StaticNullableFormatter<>).MakeGenericType(type), innerFormatter); } if (typeof(Exception).IsAssignableFrom(type)) return BuildAnonymousFormatter(typeof(T), mutator, propertyMapper, excludeNull, false, true); if (type.IsAnonymous() || TryGetInterfaceEnumerableElementType(typeof(T), out _)) return BuildAnonymousFormatter(typeof(T), mutator, propertyMapper, excludeNull, false, false); var formatterTypeInfo = BuildType(assembly, typeof(T), mutator, propertyMapper, excludeNull); if (formatterTypeInfo == null) return null; return (IJsonFormatter)Activator.CreateInstance(formatterTypeInfo.AsType()); } public static object BuildFormatterToDynamicMethod(IJsonFormatterResolver selfResolver, Func mutator, Func propertyMapper, bool excludeNull, bool allowPrivate) { var type = typeof(T); if (type.IsNullable()) { type = type.GenericTypeArguments[0]; var innerFormatter = selfResolver.GetFormatterDynamic(type); if (innerFormatter == null) return null; return (IJsonFormatter)Activator.CreateInstance(typeof(StaticNullableFormatter<>).MakeGenericType(type), innerFormatter); } if (typeof(Exception).IsAssignableFrom(type)) return BuildAnonymousFormatter(typeof(T), mutator, propertyMapper, excludeNull, false, true); return BuildAnonymousFormatter(typeof(T), mutator, propertyMapper, excludeNull, allowPrivate, false); } private static TypeInfo BuildType(DynamicAssembly assembly, Type type, Func mutator, Func propertyMapper, bool excludeNull) { if (IgnoreTypes.Contains(type)) return null; var serializationInfo = new MetaType(type, mutator, propertyMapper, false); var hasShouldSerialize = serializationInfo.Members.Any(x => x.ShouldSerializeMethodInfo != null); var formatterType = typeof(IJsonFormatter<>).MakeGenericType(type); var typeBuilder = assembly.DefineType(ResolverConfig.Namespace + "." + SubtractFullNameRegex.Replace(type.FullName, "").Replace(".", "_") + "Formatter" + Interlocked.Increment(ref _nameSequence), TypeAttributes.NotPublic | TypeAttributes.Sealed, null, new[] { formatterType }); FieldBuilder stringByteKeysField; Dictionary customFormatterLookup; // for serialize, bake cache. { var method = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, Type.EmptyTypes); stringByteKeysField = typeBuilder.DefineField("stringByteKeys", typeof(byte[][]), FieldAttributes.Private | FieldAttributes.InitOnly); var il = method.GetILGenerator(); customFormatterLookup = BuildConstructor(typeBuilder, serializationInfo, method, stringByteKeysField, il, excludeNull, hasShouldSerialize); } { var method = typeBuilder.DefineMethod("Serialize", MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual, null, new[] { typeof(JsonWriter).MakeByRefType(), type, typeof(IJsonFormatterResolver) }); var il = method.GetILGenerator(); BuildSerialize(type, serializationInfo, il, () => { il.EmitLoadThis(); il.EmitLdfld(stringByteKeysField); }, (index, member) => { FieldInfo fi; if (!customFormatterLookup.TryGetValue(member, out fi)) return false; il.EmitLoadThis(); il.EmitLdfld(fi); return true; }, excludeNull, hasShouldSerialize, 1); // firstArgIndex:0 is this. } { var method = typeBuilder.DefineMethod("Deserialize", MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual, type, new Type[] { typeof(JsonReader).MakeByRefType(), typeof(IJsonFormatterResolver) }); var il = method.GetILGenerator(); BuildDeserialize(type, serializationInfo, il, (index, member) => { FieldInfo fi; if (!customFormatterLookup.TryGetValue(member, out fi)) return false; il.EmitLoadThis(); il.EmitLdfld(fi); return true; }, false, 1); // firstArgIndex:0 is this. } return typeBuilder.CreateTypeInfo(); } public static object BuildAnonymousFormatter(Type type, Func nameMutator, Func propertyMapper, bool excludeNull, bool allowPrivate, bool isException) { if (IgnoreTypes.Contains(type)) return false; MetaType serializationInfo; if (isException) { var ignoreSet = new HashSet(new[] { "TargetSite", "ClassName", "InnerException" }.Select(x => nameMutator(x))); // special case for exception, modify serializationInfo = new MetaType(type, nameMutator, propertyMapper, false); serializationInfo.BestMatchConstructor = null; serializationInfo.ConstructorParameters = new MetaMember[0]; serializationInfo.Members = new[] { new StringConstantValueMetaMember(nameMutator("ClassName"), type.FullName) } .Concat(serializationInfo.Members.Where(x => !ignoreSet.Contains(x.Name))) .Concat(new[] { new InnerExceptionMetaMember(nameMutator("InnerException")) }) .ToArray(); } else { serializationInfo = new MetaType(type, nameMutator, propertyMapper, allowPrivate); // can be allowPrivate:true } var hasShouldSerialize = serializationInfo.Members.Any(x => x.ShouldSerializeMethodInfo != null); // build instance instead of emit constructor. var stringByteKeysField = new List(); var i = 0; foreach (var item in serializationInfo.Members.Where(x => x.IsReadable)) { if (excludeNull || hasShouldSerialize) { stringByteKeysField.Add(JsonWriter.GetEncodedPropertyName(item.Name)); } else { if (i == 0) { stringByteKeysField.Add(JsonWriter.GetEncodedPropertyNameWithBeginObject(item.Name)); } else { stringByteKeysField.Add(JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator(item.Name)); } } i++; } var serializeCustomFormatters = new List(); var deserializeCustomFormatters = new List(); foreach (var item in serializationInfo.Members.Where(x => x.IsReadable)) { var attr = item.GetCustomAttribute(true); if (attr != null) { var formatterType = attr.Attribute.FormatterType; if (attr.Attribute.FormatterType.IsGenericType && !attr.Attribute.FormatterType.IsConstructedGenericType && attr.Attribute.FormatterType.GetGenericArguments().Length == 1) formatterType = attr.Attribute.FormatterType.MakeGenericType(item.PropertyInfo.PropertyType); var formatter = Activator.CreateInstance(formatterType, attr.Attribute.Arguments); serializeCustomFormatters.Add(formatter); } else if (item.JsonFormatter != null) serializeCustomFormatters.Add(item.JsonFormatter); else serializeCustomFormatters.Add(null); } foreach (var item in serializationInfo.Members) // not only for writable because for use ctor. { var attr = item.GetCustomAttribute(true); if (attr != null) { var formatterType = attr.Attribute.FormatterType; if (attr.Attribute.FormatterType.IsGenericType && !attr.Attribute.FormatterType.IsConstructedGenericType && attr.Attribute.FormatterType.GetGenericArguments().Length == 1) formatterType = attr.Attribute.FormatterType.MakeGenericType(item.PropertyInfo.PropertyType); var formatter = Activator.CreateInstance(formatterType, attr.Attribute.Arguments); deserializeCustomFormatters.Add(formatter); } else if (item.JsonFormatter != null) deserializeCustomFormatters.Add(item.JsonFormatter); else deserializeCustomFormatters.Add(null); } var serialize = new DynamicMethod("Serialize", null, new Type[] { typeof(byte[][]), typeof(object[]), typeof(JsonWriter).MakeByRefType(), type, typeof(IJsonFormatterResolver) }, type.Module, true); { var il = serialize.GetILGenerator(); BuildSerialize(type, serializationInfo, il, () => { il.EmitLdarg(0); }, (index, member) => { if (serializeCustomFormatters.Count == 0) return false; if (serializeCustomFormatters[index] == null) return false; il.EmitLdarg(1); // read object[] il.EmitLdc_I4(index); il.Emit(OpCodes.Ldelem_Ref); // object il.Emit(OpCodes.Castclass, serializeCustomFormatters[index].GetType()); return true; }, excludeNull, hasShouldSerialize, 2); } var deserialize = new DynamicMethod("Deserialize", type, new Type[] { typeof(object[]), typeof(JsonReader).MakeByRefType(), typeof(IJsonFormatterResolver) }, type.Module, true); { var il = deserialize.GetILGenerator(); BuildDeserialize(type, serializationInfo, il, (index, member) => { if (deserializeCustomFormatters.Count == 0) return false; if (deserializeCustomFormatters[index] == null) return false; il.EmitLdarg(0); // read object[] il.EmitLdc_I4(index); il.Emit(OpCodes.Ldelem_Ref); // object il.Emit(OpCodes.Castclass, deserializeCustomFormatters[index].GetType()); return true; }, true, 1); } object serializeDelegate = serialize.CreateDelegate(typeof(AnonymousJsonSerializeAction<>).MakeGenericType(type)); object deserializeDelegate = deserialize.CreateDelegate(typeof(AnonymousJsonDeserializeFunc<>).MakeGenericType(type)); return Activator.CreateInstance(typeof(DynamicMethodAnonymousFormatter<>).MakeGenericType(type), stringByteKeysField.ToArray(), serializeCustomFormatters.ToArray(), deserializeCustomFormatters.ToArray(), serializeDelegate, deserializeDelegate); } private static Dictionary BuildConstructor(TypeBuilder builder, MetaType info, ConstructorInfo method, FieldBuilder stringByteKeysField, ILGenerator il, bool excludeNull, bool hasShouldSerialize) { il.EmitLdarg(0); il.Emit(OpCodes.Call, EmitInfo.ObjectCtor); var writeCount = info.Members.Count(x => x.IsReadable); il.EmitLdarg(0); il.EmitLdc_I4(writeCount); il.Emit(OpCodes.Newarr, typeof(byte[])); var i = 0; foreach (var item in info.Members.Where(x => x.IsReadable)) { il.Emit(OpCodes.Dup); il.EmitLdc_I4(i); il.Emit(OpCodes.Ldstr, item.Name); if (excludeNull || hasShouldSerialize) { il.EmitCall(EmitInfo.JsonWriter.GetEncodedPropertyName); } else { if (i == 0) il.EmitCall(EmitInfo.JsonWriter.GetEncodedPropertyNameWithBeginObject); else il.EmitCall(EmitInfo.JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator); } il.Emit(OpCodes.Stelem_Ref); i++; } il.Emit(OpCodes.Stfld, stringByteKeysField); var customFormatterField = BuildCustomFormatterField(builder, info, il); il.Emit(OpCodes.Ret); return customFormatterField; } private static Dictionary BuildCustomFormatterField(TypeBuilder builder, MetaType info, ILGenerator il) { var dict = new Dictionary(); foreach (var item in info.Members.Where(x => x.IsReadable || x.IsWritable)) { var attr = item.GetCustomAttribute(true); if (attr != null) { Type formatterType; if (attr.Attribute.FormatterType.IsGenericType && !attr.Attribute.FormatterType.IsConstructedGenericType) { // generic types need to be deconstructed var types = item.Type.IsGenericType ? item.Type.GenericTypeArguments : new[] { item.Type }; formatterType = attr.Attribute.FormatterType.MakeGenericType(types); } else formatterType = attr.Attribute.FormatterType; var f = builder.DefineField(item.Name + "_formatter", formatterType, FieldAttributes.Private | FieldAttributes.InitOnly); var bindingFlags = (int)(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); var attrVar = il.DeclareLocal(typeof(JsonFormatterAttribute)); // get the JsonFormatter from the declaring type il.Emit(OpCodes.Ldtoken, attr.DeclaringType); il.EmitCall(EmitInfo.GetTypeFromHandle); il.Emit(OpCodes.Ldstr, item.MemberName); il.EmitLdc_I4(bindingFlags); if (item.IsProperty) il.EmitCall(EmitInfo.TypeGetProperty); else il.EmitCall(EmitInfo.TypeGetField); il.EmitTrue(); il.EmitCall(EmitInfo.GetCustomAttributeJsonFormatterAttribute); il.EmitStloc(attrVar); var formatterVar = il.DeclareLocal(typeof(Type)); il.EmitLdloc(attrVar); il.EmitCall(EmitInfo.JsonFormatterAttr.FormatterType); il.EmitStloc(formatterVar); // see if formatter is open generic type if (attr.Attribute.FormatterType.IsGenericType && !attr.Attribute.FormatterType.IsConstructedGenericType) { var typesVar = il.DeclareLocal(typeof(Type[])); if (item.Type.IsGenericType) { // construct a generic type from the open formatter type and the generic type arguments from the member type il.Emit(OpCodes.Ldtoken, item.Type); il.EmitCall(EmitInfo.GetTypeFromHandle); il.EmitCall(EmitInfo.TypeGetGenericArguments); il.EmitStloc(typesVar); } else { // create a type array of length 1 with the member type in index 0 // and construct a generic type from the open formatter type using this il.EmitLdc_I4(1); il.Emit(OpCodes.Newarr, typeof(Type)); il.Emit(OpCodes.Dup); il.EmitLdc_I4(0); il.Emit(OpCodes.Ldtoken, item.Type); il.EmitCall(EmitInfo.GetTypeFromHandle); il.Emit(OpCodes.Stelem_Ref); il.EmitStloc(typesVar); } il.EmitLdloc(formatterVar); il.EmitLdloc(typesVar); il.EmitCall(EmitInfo.MakeGenericType); il.EmitStloc(formatterVar); } il.EmitLoadThis(); il.EmitLdloc(formatterVar); il.EmitLdloc(attrVar); il.EmitCall(EmitInfo.JsonFormatterAttr.Arguments); il.EmitCall(EmitInfo.ActivatorCreateInstance); il.Emit(OpCodes.Castclass, formatterType); il.Emit(OpCodes.Stfld, f); dict.Add(item, f); } } return dict; } private static void BuildSerialize(Type type, MetaType info, ILGenerator il, Action emitStringByteKeys, Func tryEmitLoadCustomFormatter, bool excludeNull, bool hasShouldSerialize, int firstArgIndex) { var argWriter = new ArgumentField(il, firstArgIndex); var argValue = new ArgumentField(il, firstArgIndex + 1, type); var argResolver = new ArgumentField(il, firstArgIndex + 2); // special case for serialize exception... var innerExceptionMetaMember = info.Members.OfType().FirstOrDefault(); if (innerExceptionMetaMember != null) { innerExceptionMetaMember.ArgWriter = argWriter; innerExceptionMetaMember.ArgValue = argValue; innerExceptionMetaMember.ArgResolver = argResolver; } // Special case for serialize IEnumerable<>. if (info.IsClass && info.BestMatchConstructor == null) { if (TryGetInterfaceEnumerableElementType(type, out var elementType)) { var t = typeof(IEnumerable<>).MakeGenericType(elementType); argResolver.EmitLoad(); il.EmitCall(EmitInfo.GetFormatterWithVerify.MakeGenericMethod(t)); argWriter.EmitLoad(); argValue.EmitLoad(); argResolver.EmitLoad(); il.EmitCall(EmitInfo.Serialize(t)); il.Emit(OpCodes.Ret); return; } } // if(value == null) { writer.WriteNull(); return; } if (info.IsClass) { var elseBody = il.DefineLabel(); argValue.EmitLoad(); il.Emit(OpCodes.Brtrue_S, elseBody); argWriter.EmitLoad(); il.EmitCall(EmitInfo.JsonWriter.WriteNull); il.Emit(OpCodes.Ret); // return; il.MarkLabel(elseBody); } // special case for exception if (type == typeof(Exception)) { //var exceptionType = value.GetType(); //if (exceptionType != typeof(Exception)) //{ // JsonSerializer.NonGeneric.Serialize(exceptionType, ref writer, value, formatterResolver); // return; //} var elseBody = il.DefineLabel(); var exceptionType = il.DeclareLocal(typeof(Type)); argValue.EmitLoad(); il.EmitCall(EmitInfo.GetTypeMethod); il.EmitStloc(exceptionType); il.EmitLdloc(exceptionType); il.Emit(OpCodes.Ldtoken, typeof(Exception)); il.EmitCall(EmitInfo.GetTypeFromHandle); il.EmitCall(EmitInfo.TypeEquals); il.Emit(OpCodes.Brtrue, elseBody); il.EmitLdloc(exceptionType); argWriter.EmitLoad(); argValue.EmitLoad(); argResolver.EmitLoad(); il.EmitCall(EmitInfo.NongenericSerialize); il.Emit(OpCodes.Ret); // return; il.MarkLabel(elseBody); } // for-loop WriteRaw -> WriteValue, EndObject LocalBuilder wrote = null; var endObjectLabel = il.DefineLabel(); Label[] labels = null; if (excludeNull || hasShouldSerialize) { // wrote = false; writer.WriteBeginObject(); wrote = il.DeclareLocal(typeof(bool)); argWriter.EmitLoad(); il.EmitCall(EmitInfo.JsonWriter.WriteBeginObject); labels = info.Members.Where(x => x.IsReadable).Select(_ => il.DefineLabel()).ToArray(); } var index = 0; foreach (var item in info.Members.Where(x => x.IsReadable)) { if (excludeNull || hasShouldSerialize) { il.MarkLabel(labels[index]); // if(value.X != null) if (excludeNull) { if (item.Type.IsNullable()) { var local = il.DeclareLocal(item.Type); argValue.EmitLoad(); item.EmitLoadValue(il); il.EmitStloc(local); il.EmitLdloca(local); il.EmitCall(EmitInfo.GetNullableHasValue(item.Type.GetGenericArguments()[0])); il.Emit(OpCodes.Brfalse_S, (index < labels.Length - 1) ? labels[index + 1] : endObjectLabel); // null, next label } else if (!item.Type.IsValueType && !(item is StringConstantValueMetaMember)) { argValue.EmitLoad(); item.EmitLoadValue(il); il.Emit(OpCodes.Brfalse_S, (index < labels.Length - 1) ? labels[index + 1] : endObjectLabel); // null, next label } } if (hasShouldSerialize && item.ShouldSerializeMethodInfo != null) { argValue.EmitLoad(); il.EmitCall(item.ShouldSerializeMethodInfo); il.Emit(OpCodes.Brfalse_S, (index < labels.Length - 1) ? labels[index + 1] : endObjectLabel); // false, next label } if (item.ShouldSerializeTypeMethodInfo != null) { argValue.EmitLoad(); item.EmitLoadValue(il); argResolver.EmitLoad(); il.EmitCall(item.ShouldSerializeTypeMethodInfo); il.Emit(OpCodes.Brfalse_S, (index < labels.Length - 1) ? labels[index + 1] : endObjectLabel); // false, next label } // if(wrote) var toWrite = il.DefineLabel(); var flagTrue = il.DefineLabel(); il.EmitLdloc(wrote); il.Emit(OpCodes.Brtrue_S, flagTrue); il.EmitTrue(); il.EmitStloc(wrote); il.Emit(OpCodes.Br, toWrite); il.MarkLabel(flagTrue); argWriter.EmitLoad(); il.EmitCall(EmitInfo.JsonWriter.WriteValueSeparator); il.MarkLabel(toWrite); } // WriteRaw argWriter.EmitLoad(); emitStringByteKeys(); il.EmitLdc_I4(index); il.Emit(OpCodes.Ldelem_Ref); // same as in constructor byte[] rawField; if (excludeNull || hasShouldSerialize) { rawField = JsonWriter.GetEncodedPropertyName(item.Name); } else { rawField = (index == 0) ? JsonWriter.GetEncodedPropertyNameWithBeginObject(item.Name) : JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator(item.Name); } if (rawField.Length < 32) { if (UnsafeMemory.Is32Bit) il.EmitCall(typeof(UnsafeMemory32).GetRuntimeMethod("WriteRaw" + rawField.Length, new[] { typeof(JsonWriter).MakeByRefType(), typeof(byte[]) })); else il.EmitCall(typeof(UnsafeMemory64).GetRuntimeMethod("WriteRaw" + rawField.Length, new[] { typeof(JsonWriter).MakeByRefType(), typeof(byte[]) })); } else il.EmitCall(EmitInfo.UnsafeMemory_MemoryCopy); // EmitValue EmitSerializeValue(item, il, index, tryEmitLoadCustomFormatter, argWriter, argValue, argResolver); index++; } il.MarkLabel(endObjectLabel); // for case of empty if (!excludeNull && index == 0) { argWriter.EmitLoad(); il.EmitCall(EmitInfo.JsonWriter.WriteBeginObject); } argWriter.EmitLoad(); il.EmitCall(EmitInfo.JsonWriter.WriteEndObject); il.Emit(OpCodes.Ret); } private static void EmitSerializeValue(MetaMember member, ILGenerator il, int index, Func tryEmitLoadCustomFormatter, ArgumentField writer, ArgumentField argValue, ArgumentField argResolver) { var t = member.Type; if (member is InnerExceptionMetaMember innerExceptionMetaMember) innerExceptionMetaMember.EmitSerializeDirectly(il); else if (tryEmitLoadCustomFormatter(index, member)) { writer.EmitLoad(); argValue.EmitLoad(); member.EmitLoadValue(il); argResolver.EmitLoad(); il.EmitCall(EmitInfo.Serialize(t)); } else if (JsonPrimitiveTypes.Contains(t)) { writer.EmitLoad(); argValue.EmitLoad(); member.EmitLoadValue(il); il.EmitCall(typeof(JsonWriter).GetDeclaredMethods("Write" + t.Name).OrderByDescending(x => x.GetParameters().Length).First()); } else { argResolver.EmitLoad(); il.Emit(OpCodes.Call, EmitInfo.GetFormatterWithVerify.MakeGenericMethod(t)); writer.EmitLoad(); argValue.EmitLoad(); member.EmitLoadValue(il); argResolver.EmitLoad(); il.EmitCall(EmitInfo.Serialize(t)); } } private static void BuildDeserialize(Type type, MetaType info, ILGenerator il, Func tryEmitLoadCustomFormatter, bool useGetUninitializedObject, int firstArgIndex) { if (info.IsClass && info.BestMatchConstructor == null && !(useGetUninitializedObject && info.IsConcreteClass)) { il.Emit(OpCodes.Ldstr, "generated serializer for " + type.Name + " does not support deserialize."); il.Emit(OpCodes.Newobj, EmitInfo.InvalidOperationExceptionConstructor); il.Emit(OpCodes.Throw); return; } var argReader = new ArgumentField(il, firstArgIndex); var argResolver = new ArgumentField(il, firstArgIndex + 1); // if (reader.ReadIsNull()) return null; { var elseBody = il.DefineLabel(); argReader.EmitLoad(); il.EmitCall(EmitInfo.JsonReader.ReadIsNull); il.Emit(OpCodes.Brfalse_S, elseBody); if (info.IsClass) { il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ret); // return; } else { il.Emit(OpCodes.Ldstr, "json value is null, struct is not supported"); il.Emit(OpCodes.Newobj, EmitInfo.InvalidOperationExceptionConstructor); il.Emit(OpCodes.Throw); } il.MarkLabel(elseBody); } // read '{' argReader.EmitLoad(); il.EmitCall(EmitInfo.JsonReader.ReadIsBeginObjectWithVerify); // check side-effect-free for optimize set member value(reduce is-exists-member on json check) var isSideEffectFreeType = true; if (info.BestMatchConstructor != null) { isSideEffectFreeType = IsSideEffectFreeConstructorType(info.BestMatchConstructor); // if set only property, it is not side-effect but same as has side-effect var hasSetOnlyMember = info.Members.Any(x => !x.IsReadable && x.IsWritable); if (hasSetOnlyMember) isSideEffectFreeType = false; } // make local fields var infoList = info.Members .Select(item => new DeserializeInfo { MemberInfo = item, LocalField = il.DeclareLocal(item.Type), IsDeserializedField = isSideEffectFreeType ? null : il.DeclareLocal(typeof(bool)) }) .ToArray(); var countField = il.DeclareLocal(typeof(int)); // read member loop { var automata = new AutomataDictionary(); for (var i = 0; i < info.Members.Length; i++) { automata.Add(JsonWriter.GetEncodedPropertyNameWithoutQuotation(info.Members[i].Name), i); } var baseBytes = il.DeclareLocal(typeof(byte[])); var buffer = il.DeclareLocal(typeof(byte).MakeByRefType(), true); var keyArraySegment = il.DeclareLocal(typeof(ArraySegment)); var longKey = il.DeclareLocal(typeof(ulong)); var p = il.DeclareLocal(typeof(byte*)); var rest = il.DeclareLocal(typeof(int)); // baseBytes = reader.GetBufferUnsafe(); // fixed (byte* buffer = &baseBytes[0]) { argReader.EmitLoad(); il.EmitCall(EmitInfo.JsonReader.GetBufferUnsafe); il.EmitStloc(baseBytes); il.EmitLdloc(baseBytes); il.EmitLdc_I4(0); il.Emit(OpCodes.Ldelema, typeof(byte)); il.EmitStloc(buffer); // while (!reader.ReadIsEndObjectWithSkipValueSeparator(ref count)) // "}", skip "," when count != 0 var continueWhile = il.DefineLabel(); var breakWhile = il.DefineLabel(); var readNext = il.DefineLabel(); il.MarkLabel(continueWhile); argReader.EmitLoad(); il.EmitLdloca(countField); // ref count field(use ldloca) il.EmitCall(EmitInfo.JsonReader.ReadIsEndObjectWithSkipValueSeparator); il.Emit(OpCodes.Brtrue, breakWhile); // found '}', break argReader.EmitLoad(); il.EmitCall(EmitInfo.JsonReader.ReadPropertyNameSegmentUnsafe); il.EmitStloc(keyArraySegment); // p = buffer + arraySegment.Offset il.EmitLdloc(buffer); il.Emit(OpCodes.Conv_I); il.EmitLdloca(keyArraySegment); il.EmitCall(typeof(ArraySegment).GetRuntimeProperty("Offset").GetMethod); il.Emit(OpCodes.Add); il.EmitStloc(p); // rest = arraySegment.Count il.EmitLdloca(keyArraySegment); il.EmitCall(typeof(ArraySegment).GetRuntimeProperty("Count").GetMethod); il.EmitStloc(rest); // if(rest == 0) goto End il.EmitLdloc(rest); il.Emit(OpCodes.Brfalse, readNext); //// gen automata name lookup automata.EmitMatch(il, p, rest, longKey, x => { var i = x.Value; if (infoList[i].MemberInfo != null) { EmitDeserializeValue(il, infoList[i], i, tryEmitLoadCustomFormatter, argReader, argResolver); if (!isSideEffectFreeType) { il.EmitTrue(); il.EmitStloc(infoList[i].IsDeserializedField); } il.Emit(OpCodes.Br, continueWhile); } else il.Emit(OpCodes.Br, readNext); }, () => { il.Emit(OpCodes.Br, readNext); }); il.MarkLabel(readNext); argReader.EmitLoad(); il.EmitCall(EmitInfo.JsonReader.ReadNextBlock); il.Emit(OpCodes.Br, continueWhile); // loop again il.MarkLabel(breakWhile); // end fixed il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Conv_U); il.EmitStloc(buffer); } // create result object var localResult = EmitNewObject(il, type, info, infoList, isSideEffectFreeType); if (localResult != null) il.Emit(OpCodes.Ldloc, localResult); il.Emit(OpCodes.Ret); } private static void EmitDeserializeValue(ILGenerator il, DeserializeInfo info, int index, Func tryEmitLoadCustomFormatter, ArgumentField reader, ArgumentField argResolver) { var member = info.MemberInfo; var t = member.Type; if (tryEmitLoadCustomFormatter(index, member)) { reader.EmitLoad(); argResolver.EmitLoad(); il.EmitCall(EmitInfo.Deserialize(t)); } else if (JsonPrimitiveTypes.Contains(t)) { reader.EmitLoad(); il.EmitCall(typeof(JsonReader).GetDeclaredMethods("Read" + t.Name).OrderByDescending(x => x.GetParameters().Length).First()); } else { argResolver.EmitLoad(); il.Emit(OpCodes.Call, EmitInfo.GetFormatterWithVerify.MakeGenericMethod(t)); reader.EmitLoad(); argResolver.EmitLoad(); il.EmitCall(EmitInfo.Deserialize(t)); } il.EmitStloc(info.LocalField); } private static LocalBuilder EmitNewObject(ILGenerator il, Type type, MetaType info, DeserializeInfo[] members, bool isSideEffectFreeType) { if (info.IsClass) { LocalBuilder result = null; if (!isSideEffectFreeType) { result = il.DeclareLocal(type); } if (info.BestMatchConstructor != null) { foreach (var item in info.ConstructorParameters) { var local = members.First(x => x.MemberInfo == item); il.EmitLdloc(local.LocalField); } il.Emit(OpCodes.Newobj, info.BestMatchConstructor); } else { il.Emit(OpCodes.Ldtoken, type); il.EmitCall(EmitInfo.GetTypeFromHandle); il.EmitCall(EmitInfo.GetUninitializedObject); } if (!isSideEffectFreeType) il.EmitStloc(result); foreach (var item in members.Where(x => x.MemberInfo != null && x.MemberInfo.IsWritable)) { if (isSideEffectFreeType) { var next = il.DefineLabel(); // don't assign the value if it's null var infoType = item.MemberInfo.Type; if (infoType.IsClass || infoType.IsInterface || infoType.IsAbstract) { il.EmitLdloc(item.LocalField); il.Emit(OpCodes.Brfalse, next); } il.Emit(OpCodes.Dup); il.EmitLdloc(item.LocalField); item.MemberInfo.EmitStoreValue(il); il.MarkLabel(next); } else { var next = il.DefineLabel(); il.EmitLdloc(item.IsDeserializedField); il.Emit(OpCodes.Brfalse, next); // don't assign the value if it's null var infoType = item.MemberInfo.Type; if (infoType.IsClass || infoType.IsInterface || infoType.IsAbstract) { il.EmitLdloc(item.LocalField); il.Emit(OpCodes.Brfalse, next); } il.EmitLdloc(result); il.EmitLdloc(item.LocalField); item.MemberInfo.EmitStoreValue(il); il.MarkLabel(next); } } return result; } else { var result = il.DeclareLocal(type); if (info.BestMatchConstructor == null) { il.Emit(OpCodes.Ldloca, result); il.Emit(OpCodes.Initobj, type); } else { foreach (var item in info.ConstructorParameters) { var local = members.First(x => x.MemberInfo == item); il.EmitLdloc(local.LocalField); } il.Emit(OpCodes.Newobj, info.BestMatchConstructor); il.Emit(OpCodes.Stloc, result); } foreach (var item in members.Where(x => x.MemberInfo != null && x.MemberInfo.IsWritable)) { if (isSideEffectFreeType) { var next = il.DefineLabel(); // don't assign the value if it's null var infoType = item.MemberInfo.Type; if (infoType.IsClass || infoType.IsInterface || infoType.IsAbstract) { il.EmitLdloc(item.LocalField); il.Emit(OpCodes.Brfalse, next); } il.EmitLdloca(result); il.EmitLdloc(item.LocalField); item.MemberInfo.EmitStoreValue(il); il.MarkLabel(next); } else { var next = il.DefineLabel(); il.EmitLdloc(item.IsDeserializedField); il.Emit(OpCodes.Brfalse, next); // don't assign the value if it's null var infoType = item.MemberInfo.Type; if (infoType.IsClass || infoType.IsInterface || infoType.IsAbstract) { il.EmitLdloc(item.LocalField); il.Emit(OpCodes.Brfalse, next); } il.EmitLdloca(result); il.EmitLdloc(item.LocalField); item.MemberInfo.EmitStoreValue(il); il.MarkLabel(next); } } return result; // struct returns local result field } } private static bool IsSideEffectFreeConstructorType(ConstructorInfo ctorInfo) { var methodBody = ctorInfo.GetMethodBody(); var array = methodBody?.GetILAsByteArray(); if (array == null) return false; // (ldarg.0, call(empty ctor), ret) == side-effect free. // Release build is 7, Debug build has nop(or nop like code) so should use ILStreamReader var opCodes = new List(); using (var reader = new ILStreamReader(array)) { while (!reader.EndOfStream) { var code = reader.ReadOpCode(); if (code != OpCodes.Nop && code != OpCodes.Ldloc_0 && code != OpCodes.Ldloc_S && code != OpCodes.Stloc_0 && code != OpCodes.Stloc_S && code != OpCodes.Blt && code != OpCodes.Blt_S && code != OpCodes.Bgt && code != OpCodes.Bgt_S) { opCodes.Add(code); if (opCodes.Count == 4) break; } } } if (opCodes.Count == 3 && opCodes[0] == OpCodes.Ldarg_0 && opCodes[1] == OpCodes.Call && opCodes[2] == OpCodes.Ret) { if (ctorInfo.DeclaringType.BaseType == typeof(object)) return true; // use empty constuctor. var baseCtorInfo = ctorInfo.DeclaringType.BaseType.GetConstructor(Type.EmptyTypes); if (baseCtorInfo == null) return false; // check parent constructor return IsSideEffectFreeConstructorType(baseCtorInfo); } return false; } private static bool TryGetInterfaceEnumerableElementType(Type type, out Type elementType) { foreach (var implInterface in type.GetInterfaces()) { if (implInterface.IsGenericType) { var genericTypeDef = implInterface.GetGenericTypeDefinition(); if (genericTypeDef == typeof(IEnumerable<>)) { var args = implInterface.GetGenericArguments(); elementType = args[0]; return true; } } } elementType = null; return false; } private struct DeserializeInfo { public MetaMember MemberInfo; public LocalBuilder LocalField; public LocalBuilder IsDeserializedField; } internal static class EmitInfo { public static readonly ConstructorInfo ObjectCtor = typeof(object).GetDeclaredConstructors().First(x => x.GetParameters().Length == 0); public static readonly MethodInfo GetFormatterWithVerify = typeof(JsonFormatterResolverExtensions).GetRuntimeMethod("GetFormatterWithVerify", new[] { typeof(IJsonFormatterResolver) }); public static readonly MethodInfo UnsafeMemory_MemoryCopy = ExpressionUtility.GetMethodInfo((Utf8Json.JsonWriter writer, byte[] src) => UnsafeMemory.MemoryCopy(ref writer, src)); public static readonly ConstructorInfo InvalidOperationExceptionConstructor = typeof(InvalidOperationException).GetDeclaredConstructors().First(x => { var p = x.GetParameters(); return p.Length == 1 && p[0].ParameterType == typeof(string); }); public static readonly MethodInfo GetTypeFromHandle = ExpressionUtility.GetMethodInfo(() => Type.GetTypeFromHandle(default(RuntimeTypeHandle))); public static readonly MethodInfo TypeGetProperty = ExpressionUtility.GetMethodInfo((Type t) => t.GetProperty(default(string), default(BindingFlags))); public static readonly MethodInfo TypeGetField = ExpressionUtility.GetMethodInfo((Type t) => t.GetField(default(string), default(BindingFlags))); public static readonly MethodInfo GetCustomAttributeJsonFormatterAttribute = ExpressionUtility.GetMethodInfo(() => CustomAttributeExtensions.GetCustomAttribute(default(MemberInfo), default(bool))); public static readonly MethodInfo ActivatorCreateInstance = ExpressionUtility.GetMethodInfo(() => Activator.CreateInstance(default(Type), default(object[]))); public static readonly MethodInfo GetUninitializedObject = ExpressionUtility.GetMethodInfo(() => System.Runtime.Serialization.FormatterServices.GetUninitializedObject(default(Type))); public static readonly MethodInfo GetTypeMethod = ExpressionUtility.GetMethodInfo((object o) => o.GetType()); public static readonly MethodInfo TypeGetGenericArguments = ExpressionUtility.GetPropertyInfo((Type t) => t.GenericTypeArguments).GetMethod; public static readonly MethodInfo TypeEquals = ExpressionUtility.GetMethodInfo((Type t) => t.Equals(default(Type))); public static readonly MethodInfo MakeGenericType = ExpressionUtility.GetMethodInfo((Type t) => t.MakeGenericType(default(Type[]))); public static readonly MethodInfo NongenericSerialize = ExpressionUtility.GetMethodInfo(writer => JsonSerializer.NonGeneric.Serialize(default(Type), ref writer, default(object), default(IJsonFormatterResolver))); public static MethodInfo Serialize(Type type) => typeof(IJsonFormatter<>).MakeGenericType(type).GetRuntimeMethod(nameof(IJsonFormatter.Serialize), new[] { typeof(Utf8Json.JsonWriter).MakeByRefType(), type, typeof(IJsonFormatterResolver) }); public static MethodInfo Deserialize(Type type) => typeof(IJsonFormatter<>).MakeGenericType(type).GetRuntimeMethod(nameof(IJsonFormatter.Deserialize), new[] { typeof(Utf8Json.JsonReader).MakeByRefType(), typeof(IJsonFormatterResolver) }); public static MethodInfo GetNullableHasValue(Type type) => typeof(Nullable<>).MakeGenericType(type).GetRuntimeProperty(nameof(Nullable.HasValue)).GetMethod; internal static class JsonWriter { public static readonly MethodInfo GetEncodedPropertyNameWithBeginObject = ExpressionUtility.GetMethodInfo(() => Utf8Json.JsonWriter.GetEncodedPropertyNameWithBeginObject(default(string))); public static readonly MethodInfo GetEncodedPropertyNameWithPrefixValueSeparator = ExpressionUtility.GetMethodInfo(() => Utf8Json.JsonWriter.GetEncodedPropertyNameWithPrefixValueSeparator(default(string))); public static readonly MethodInfo GetEncodedPropertyNameWithoutQuotation = ExpressionUtility.GetMethodInfo(() => Utf8Json.JsonWriter.GetEncodedPropertyNameWithoutQuotation(default(string))); public static readonly MethodInfo GetEncodedPropertyName = ExpressionUtility.GetMethodInfo(() => Utf8Json.JsonWriter.GetEncodedPropertyName(default(string))); public static readonly MethodInfo WriteNull = ExpressionUtility.GetMethodInfo((Utf8Json.JsonWriter writer) => writer.WriteNull()); public static readonly MethodInfo WriteRaw = ExpressionUtility.GetMethodInfo((Utf8Json.JsonWriter writer) => writer.WriteRaw(default(byte[]))); public static readonly MethodInfo WriteBeginObject = ExpressionUtility.GetMethodInfo((Utf8Json.JsonWriter writer) => writer.WriteBeginObject()); public static readonly MethodInfo WriteEndObject = ExpressionUtility.GetMethodInfo((Utf8Json.JsonWriter writer) => writer.WriteEndObject()); public static readonly MethodInfo WriteValueSeparator = ExpressionUtility.GetMethodInfo((Utf8Json.JsonWriter writer) => writer.WriteValueSeparator()); static JsonWriter() { } } internal static class JsonReader { public static readonly MethodInfo ReadIsNull = ExpressionUtility.GetMethodInfo((Utf8Json.JsonReader reader) => reader.ReadIsNull()); public static readonly MethodInfo ReadIsBeginObjectWithVerify = ExpressionUtility.GetMethodInfo((Utf8Json.JsonReader reader) => reader.ReadIsBeginObjectWithVerify()); public static readonly MethodInfo ReadIsEndObjectWithSkipValueSeparator = ExpressionUtility.GetMethodInfo((Utf8Json.JsonReader reader, int count) => reader.ReadIsEndObjectWithSkipValueSeparator(ref count)); public static readonly MethodInfo ReadPropertyNameSegmentUnsafe = ExpressionUtility.GetMethodInfo((Utf8Json.JsonReader reader) => reader.ReadPropertyNameSegmentRaw()); public static readonly MethodInfo ReadNextBlock = ExpressionUtility.GetMethodInfo((Utf8Json.JsonReader reader) => reader.ReadNextBlock()); public static readonly MethodInfo GetBufferUnsafe = ExpressionUtility.GetMethodInfo((Utf8Json.JsonReader reader) => reader.GetBufferUnsafe()); public static readonly MethodInfo GetCurrentOffsetUnsafe = ExpressionUtility.GetMethodInfo((Utf8Json.JsonReader reader) => reader.GetCurrentOffsetUnsafe()); static JsonReader() { } } internal static class JsonFormatterAttr { internal static readonly MethodInfo FormatterType = ExpressionUtility.GetPropertyInfo((JsonFormatterAttribute attr) => attr.FormatterType).GetMethod; internal static readonly MethodInfo Arguments = ExpressionUtility.GetPropertyInfo((JsonFormatterAttribute attr) => attr.Arguments).GetMethod; } } } internal delegate void AnonymousJsonSerializeAction(byte[][] stringByteKeysField, object[] customFormatters, ref JsonWriter writer, T value, IJsonFormatterResolver resolver); internal delegate T AnonymousJsonDeserializeFunc(object[] customFormatters, ref JsonReader reader, IJsonFormatterResolver resolver); internal class DynamicMethodAnonymousFormatter : IJsonFormatter { private readonly byte[][] _stringByteKeysField; private readonly object[] _serializeCustomFormatters; private readonly object[] _deserializeCustomFormatters; private readonly AnonymousJsonSerializeAction _serialize; private readonly AnonymousJsonDeserializeFunc _deserialize; public DynamicMethodAnonymousFormatter(byte[][] stringByteKeysField, object[] serializeCustomFormatters, object[] deserializeCustomFormatters, AnonymousJsonSerializeAction serialize, AnonymousJsonDeserializeFunc deserialize) { _stringByteKeysField = stringByteKeysField; _serializeCustomFormatters = serializeCustomFormatters; _deserializeCustomFormatters = deserializeCustomFormatters; _serialize = serialize; _deserialize = deserialize; } public void Serialize(ref JsonWriter writer, T value, IJsonFormatterResolver formatterResolver) { if (_serialize == null) throw new InvalidOperationException(GetType().Name + " does not support Serialize."); _serialize(_stringByteKeysField, _serializeCustomFormatters, ref writer, value, formatterResolver); } public T Deserialize(ref JsonReader reader, IJsonFormatterResolver formatterResolver) { if (_deserialize == null) throw new InvalidOperationException(GetType().Name + " does not support Deserialize."); return _deserialize(_deserializeCustomFormatters, ref reader, formatterResolver); } } }