#nullable enable using IPA.Config.Data; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Text; using System.Threading.Tasks; namespace IPA.Config.Stores { internal static partial class GeneratedStoreImpl { internal delegate Value SerializeObject(T obj); internal delegate T DeserializeObject(Value? val, object parent); private static class DelegateStore { public static SerializeObject? Serialize; public static DeserializeObject? Deserialize; } internal static SerializeObject GetSerializerDelegate() => DelegateStore.Serialize ??= GetSerializerDelegateInternal(); private static SerializeObject GetSerializerDelegateInternal() { var type = typeof(T); #if DEBUG var defType = Module.DefineType($"{type.FullName}", TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Abstract); var dynMethod = defType.DefineMethod("SerializeType", MethodAttributes.Public | MethodAttributes.Static, typeof(Value), new[] { type }); #else var dynMethod = new DynamicMethod($"SerializeType>>{type.FullName}", typeof(Value), new[] { type }, Module, true); #endif var structure = ReadObjectMembers(type); //CreateAndInitializeConvertersFor(type, structure); var loadObject = type.IsValueType ? (Action)(il => il.Emit(OpCodes.Ldarga_S, 0)) : il => il.Emit(OpCodes.Ldarg_0); var loadParent = type.IsValueType ? (Action)(il => il.Emit(OpCodes.Ldnull)) : loadObject; { var il = dynMethod.GetILGenerator(); var GetLocal = MakeLocalAllocator(il); if (!type.IsValueType) { var notIGeneratedStore = il.DefineLabel(); var IGeneratedStore_t = typeof(IGeneratedStore); var IGeneratedStore_Serialize = IGeneratedStore_t.GetMethod(nameof(IGeneratedStore.Serialize)); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Isinst, IGeneratedStore_t); il.Emit(OpCodes.Brfalse, notIGeneratedStore); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Castclass, IGeneratedStore_t); il.Emit(OpCodes.Callvirt, IGeneratedStore_Serialize); il.Emit(OpCodes.Ret); il.MarkLabel(notIGeneratedStore); } EmitSerializeStructure(il, structure, GetLocal, loadObject, loadParent); il.Emit(OpCodes.Ret); } #if DEBUG defType.CreateType(); return (SerializeObject)Delegate.CreateDelegate(typeof(SerializeObject), dynMethod); #else return (SerializeObject)dynMethod.CreateDelegate(typeof(SerializeObject)); #endif } internal static DeserializeObject GetDeserializerDelegate() => DelegateStore.Deserialize ??= GetDeserializerDelegateInternal(); private static DeserializeObject GetDeserializerDelegateInternal() { var type = typeof(T); //var dynMethod = new DynamicMethod($"DeserializeType>>{type.FullName}", type, new[] { typeof(Value), typeof(object) }, Module, true); #if DEBUG var defType = Module.DefineType($"{type.FullName}", TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Abstract); var dynMethod = defType.DefineMethod("DeserializeType", MethodAttributes.Public | MethodAttributes.Static, type, new[] { typeof(Value), typeof(object) }); #else var dynMethod = new DynamicMethod($"DeserializeType>>{type.FullName}", type, new[] { typeof(Value), typeof(object) }, Module, true); #endif var structure = ReadObjectMembers(type); //CreateAndInitializeConvertersFor(type, structure); { var il = dynMethod.GetILGenerator(); var GetLocal = MakeLocalAllocator(il); var IGeneratedStore_t = typeof(IGeneratedStore); var IGeneratedStore_Deserialize = IGeneratedStore_t.GetMethod(nameof(IGeneratedStore.Deserialize)); void ParentObj(ILGenerator il) { il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Isinst, IGeneratedStore_t); } if (!type.IsValueType) { EmitCreateChildGenerated(il, type, ParentObj); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Castclass, IGeneratedStore_t); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Callvirt, IGeneratedStore_Deserialize); il.Emit(OpCodes.Ret); } else { var Map_t = typeof(Map); var Map_TryGetValue = Map_t.GetMethod(nameof(Map.TryGetValue)); var Object_GetType = typeof(object).GetMethod(nameof(Object.GetType)); var valueLocal = il.DeclareLocal(typeof(Value)); var mapLocal = il.DeclareLocal(typeof(Map)); var resultLocal = il.DeclareLocal(type); var nonNull = il.DefineLabel(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Brtrue, nonNull); EmitLogError(il, "Attempting to deserialize null", tailcall: false); il.Emit(OpCodes.Ldloc, resultLocal); il.Emit(OpCodes.Ret); il.MarkLabel(nonNull); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Isinst, Map_t); il.Emit(OpCodes.Dup); // duplicate cloned value il.Emit(OpCodes.Stloc, mapLocal); var notMapError = il.DefineLabel(); il.Emit(OpCodes.Brtrue, notMapError); // handle error EmitLogError(il, $"Invalid root for deserializing {type.FullName}", tailcall: false, expected: il => EmitTypeof(il, Map_t), found: il => { il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Callvirt, Object_GetType); }); il.Emit(OpCodes.Ldloc, resultLocal); il.Emit(OpCodes.Ret); il.MarkLabel(notMapError); EmitDeserializeStructure(il, structure, mapLocal, valueLocal, GetLocal, il => il.Emit(OpCodes.Ldloca, resultLocal), ParentObj); il.Emit(OpCodes.Ldloc, resultLocal); il.Emit(OpCodes.Ret); } } #if DEBUG defType.CreateType(); return (DeserializeObject)Delegate.CreateDelegate(typeof(DeserializeObject), dynMethod); #else return (DeserializeObject)dynMethod.CreateDelegate(typeof(DeserializeObject)); #endif } } }