#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>(T obj);
|
|
internal delegate T DeserializeObject<T>(Value? val, object parent);
|
|
|
|
private static class DelegateStore<T>
|
|
{
|
|
public static SerializeObject<T>? Serialize;
|
|
public static DeserializeObject<T>? Deserialize;
|
|
}
|
|
|
|
internal static SerializeObject<T> GetSerializerDelegate<T>()
|
|
=> DelegateStore<T>.Serialize ??= GetSerializerDelegateInternal<T>();
|
|
|
|
private static SerializeObject<T> GetSerializerDelegateInternal<T>()
|
|
{
|
|
var type = typeof(T);
|
|
#if DEBUG
|
|
var defType = Module.DefineType($"{type.FullName}<SerializeTypeContainer>", 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<ILGenerator>)(il => il.Emit(OpCodes.Ldarga_S, 0))
|
|
: il => il.Emit(OpCodes.Ldarg_0);
|
|
var loadParent = type.IsValueType
|
|
? (Action<ILGenerator>)(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<T>)Delegate.CreateDelegate(typeof(SerializeObject<T>), dynMethod);
|
|
#else
|
|
return (SerializeObject<T>)dynMethod.CreateDelegate(typeof(SerializeObject<T>));
|
|
#endif
|
|
}
|
|
|
|
internal static DeserializeObject<T> GetDeserializerDelegate<T>()
|
|
=> DelegateStore<T>.Deserialize ??= GetDeserializerDelegateInternal<T>();
|
|
|
|
private static DeserializeObject<T> GetDeserializerDelegateInternal<T>()
|
|
{
|
|
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}<DeserializeTypeContainer>", 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<T>)Delegate.CreateDelegate(typeof(DeserializeObject<T>), dynMethod);
|
|
#else
|
|
return (DeserializeObject<T>)dynMethod.CreateDelegate(typeof(DeserializeObject<T>));
|
|
#endif
|
|
}
|
|
}
|
|
}
|