Browse Source

Added converter support for value types

In the process, to prevent an infinite recursion bug, transitioned to a proper local allocation system
pull/44/head
Anairkoen Schno 4 years ago
parent
commit
580b702398
17 changed files with 387 additions and 106 deletions
  1. +4
    -2
      IPA.Loader/Config/Stores/CollectionConverter.cs
  2. +13
    -7
      IPA.Loader/Config/Stores/Converters.cs
  3. +14
    -0
      IPA.Loader/Config/Stores/CustomObjectConverter.cs
  4. +183
    -0
      IPA.Loader/Config/Stores/GeneratedStoreImpl/ConversionDelegates.cs
  5. +4
    -4
      IPA.Loader/Config/Stores/GeneratedStoreImpl/Correction.cs
  6. +14
    -14
      IPA.Loader/Config/Stores/GeneratedStoreImpl/Deserialization.cs
  7. +41
    -0
      IPA.Loader/Config/Stores/GeneratedStoreImpl/GeneratedStoreImpl.cs
  8. +25
    -25
      IPA.Loader/Config/Stores/GeneratedStoreImpl/IGeneratedStore.cs
  9. +10
    -24
      IPA.Loader/Config/Stores/GeneratedStoreImpl/MakeCreator.cs
  10. +1
    -0
      IPA.Loader/Config/Stores/GeneratedStoreImpl/ObjectStructure.cs
  11. +30
    -17
      IPA.Loader/Config/Stores/GeneratedStoreImpl/Serialization.cs
  12. +47
    -13
      IPA.Loader/Config/Stores/GeneratedStoreImpl/Utility.cs
  13. +1
    -0
      IPA.Loader/IPA.Loader.csproj
  14. BIN
      Refs/Main.dll
  15. BIN
      Refs/Unity.TextMeshPro.dll
  16. BIN
      Refs/UnityEngine.CoreModule.Net4.dll
  17. BIN
      Refs/UnityEngine.UI.dll

+ 4
- 2
IPA.Loader/Config/Stores/CollectionConverter.cs View File

@ -4,7 +4,8 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using IPA.Config.Data; using IPA.Config.Data;
using IPA.Logging;
namespace IPA.Config.Stores.Converters namespace IPA.Config.Stores.Converters
{ {
/// <summary> /// <summary>
@ -53,6 +54,7 @@ namespace IPA.Config.Stores.Converters
/// <seealso cref="ValueConverter{T}.FromValue(Value, object)"/> /// <seealso cref="ValueConverter{T}.FromValue(Value, object)"/>
protected void PopulateFromValue(TCollection col, List list, object parent) protected void PopulateFromValue(TCollection col, List list, object parent)
{ {
//Logger.log.Debug($"CollectionConverter<{typeof(T)}, {typeof(TCollection)}>({BaseConverter.GetType()}).PopulateFromValue([object], {list}, {parent.GetType()})");
foreach (var it in list) foreach (var it in list)
col.Add(BaseConverter.FromValue(it, parent)); col.Add(BaseConverter.FromValue(it, parent));
} }
@ -79,7 +81,7 @@ namespace IPA.Config.Stores.Converters
/// <param name="parent">the object owning <paramref name="obj"/></param> /// <param name="parent">the object owning <paramref name="obj"/></param>
/// <returns>the <see cref="List"/> that <paramref name="obj"/> was serialized into</returns> /// <returns>the <see cref="List"/> that <paramref name="obj"/> was serialized into</returns>
/// <seealso cref="ValueConverter{T}.ToValue(T, object)"/> /// <seealso cref="ValueConverter{T}.ToValue(T, object)"/>
public override Value ToValue(TCollection obj, object parent)
public override Value ToValue(TCollection obj, object parent)
=> Value.From(obj.Select(t => BaseConverter.ToValue(t, parent))); => Value.From(obj.Select(t => BaseConverter.ToValue(t, parent)));
} }
/// <summary> /// <summary>


+ 13
- 7
IPA.Loader/Config/Stores/Converters.cs View File

@ -1,5 +1,6 @@
using IPA.Config.Data; using IPA.Config.Data;
using IPA.Config.Stores.Attributes; using IPA.Config.Stores.Attributes;
using IPA.Logging;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -44,7 +45,7 @@ namespace IPA.Config.Stores.Converters
private static readonly IValConv<T> Impl = ValConvImpls.Impl as IValConv<T> ?? new ValConv<T>(); private static readonly IValConv<T> Impl = ValConvImpls.Impl as IValConv<T> ?? new ValConv<T>();
public ValueConverter<T> Get() => Impl.Get(); public ValueConverter<T> Get() => Impl.Get();
ValueConverter<T> IValConv<T>.Get() ValueConverter<T> IValConv<T>.Get()
=> null; // default to null
=> new CustomValueTypeConverter<T>();
} }
private class ValConvImpls : IValConv<char>, private class ValConvImpls : IValConv<char>,
IValConv<IntPtr>, IValConv<UIntPtr>, IValConv<IntPtr>, IValConv<UIntPtr>,
@ -89,24 +90,29 @@ namespace IPA.Config.Stores.Converters
private static ValueConverter<T> MakeDefault() private static ValueConverter<T> MakeDefault()
{ {
var t = typeof(T);
var t = typeof(T);
//Logger.log.Debug($"Converter<{t}>.MakeDefault()");
if (t.IsValueType) if (t.IsValueType)
{ // we have to do this garbo to make it accept the thing that we know is a value type at instantiation time { // we have to do this garbo to make it accept the thing that we know is a value type at instantiation time
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)) if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>))
{ // this is a Nullable
{ // this is a Nullable
//Logger.log.Debug($"gives NullableConverter<{Nullable.GetUnderlyingType(t)}>");
return Activator.CreateInstance(typeof(NullableConverter<>).MakeGenericType(Nullable.GetUnderlyingType(t))) as ValueConverter<T>; return Activator.CreateInstance(typeof(NullableConverter<>).MakeGenericType(Nullable.GetUnderlyingType(t))) as ValueConverter<T>;
}
}
//Logger.log.Debug($"gives converter for value type {t}");
var valConv = Activator.CreateInstance(typeof(Converter.ValConv<>).MakeGenericType(t)) as Converter.IValConv<T>; var valConv = Activator.CreateInstance(typeof(Converter.ValConv<>).MakeGenericType(t)) as Converter.IValConv<T>;
return valConv.Get(); return valConv.Get();
} }
else if (t == typeof(string)) else if (t == typeof(string))
{
{
//Logger.log.Debug($"gives StringConverter");
return new StringConverter() as ValueConverter<T>; return new StringConverter() as ValueConverter<T>;
} }
else else
{
{
//Logger.log.Debug($"gives CustomObjectConverter<{t}>");
return Activator.CreateInstance(typeof(CustomObjectConverter<>).MakeGenericType(t)) as ValueConverter<T>; return Activator.CreateInstance(typeof(CustomObjectConverter<>).MakeGenericType(t)) as ValueConverter<T>;
} }
} }


+ 14
- 0
IPA.Loader/Config/Stores/CustomObjectConverter.cs View File

@ -85,4 +85,18 @@ namespace IPA.Config.Stores.Converters
=> impl.ToValue(obj, parent); => impl.ToValue(obj, parent);
} }
public class CustomValueTypeConverter<T> : ValueConverter<T> where T : struct
{
private static readonly GeneratedStoreImpl.SerializeObject<T> serialize
= GeneratedStoreImpl.GetSerializerDelegate<T>();
private static readonly GeneratedStoreImpl.DeserializeObject<T> deserialize
= GeneratedStoreImpl.GetDeserializerDelegate<T>();
public override T FromValue(Value value, object parent)
=> deserialize(value, parent);
public override Value ToValue(T obj, object parent)
=> serialize(obj);
}
} }

+ 183
- 0
IPA.Loader/Config/Stores/GeneratedStoreImpl/ConversionDelegates.cs View File

@ -0,0 +1,183 @@
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 il = dynMethod.GetILGenerator();
var GetLocal = MakeLocalAllocator(il);
EmitLogError(il, $"Entered SerializeType delegate for type {type}");
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);
}
EmitLogError(il, $"Serializing structure of {type}");
EmitSerializeStructure(il, structure, GetLocal, loadObject);
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);
}
EmitLogError(il, $"Entered DeserializeType delegate for type {type}");
if (!type.IsValueType)
{
EmitLogError(il, $"Forwarding to created type serialization");
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();
EmitLogError(il, $"Deserializing structure of {type}");
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
}
}
}

+ 4
- 4
IPA.Loader/Config/Stores/GeneratedStoreImpl/Correction.cs View File

@ -38,7 +38,7 @@ namespace IPA.Config.Stores
} }
// expects start value on stack, exits with final value on stack // expects start value on stack, exits with final value on stack
private static void EmitCorrectMember(ILGenerator il, SerializedMemberInfo member, bool shouldLock, bool alwaysNew, GetLocal GetLocal,
private static void EmitCorrectMember(ILGenerator il, SerializedMemberInfo member, bool shouldLock, bool alwaysNew, LocalAllocator GetLocal,
Action<ILGenerator> thisobj, Action<ILGenerator> parentobj) Action<ILGenerator> thisobj, Action<ILGenerator> parentobj)
{ {
if (!NeedsCorrection(member)) return; if (!NeedsCorrection(member)) return;
@ -59,7 +59,7 @@ namespace IPA.Config.Stores
// currently the only thing for this is where expect == Map, so do generate shit // currently the only thing for this is where expect == Map, so do generate shit
var copyFrom = typeof(IGeneratedStore<>).MakeGenericType(convType).GetMethod(nameof(IGeneratedStore<Config>.CopyFrom)); var copyFrom = typeof(IGeneratedStore<>).MakeGenericType(convType).GetMethod(nameof(IGeneratedStore<Config>.CopyFrom));
var noCreate = il.DefineLabel(); var noCreate = il.DefineLabel();
var valLocal = GetLocal(convType);
using var valLocal = GetLocal.Allocate(convType);
if (member.AllowNull) if (member.AllowNull)
{ {
@ -94,7 +94,7 @@ namespace IPA.Config.Stores
// for special value types, we'll go ahead and correct each of their members // for special value types, we'll go ahead and correct each of their members
var structure = ReadObjectMembers(convType); var structure = ReadObjectMembers(convType);
var valueLocal = GetLocal(convType);
using var valueLocal = GetLocal.Allocate(convType);
il.Emit(OpCodes.Stloc, valueLocal); il.Emit(OpCodes.Stloc, valueLocal);
void LdlocaValueLocal(ILGenerator il) void LdlocaValueLocal(ILGenerator il)
@ -116,7 +116,7 @@ namespace IPA.Config.Stores
il.MarkLabel(endLabel); il.MarkLabel(endLabel);
} }
private static void EmitLoadCorrectStore(ILGenerator il, SerializedMemberInfo member, bool shouldLock, bool alwaysNew, GetLocal GetLocal,
private static void EmitLoadCorrectStore(ILGenerator il, SerializedMemberInfo member, bool shouldLock, bool alwaysNew, LocalAllocator GetLocal,
Action<ILGenerator> loadFrom, Action<ILGenerator> storeTo, Action<ILGenerator> parentobj) Action<ILGenerator> loadFrom, Action<ILGenerator> storeTo, Action<ILGenerator> parentobj)
{ {
EmitStore(il, member, il => EmitStore(il, member, il =>


+ 14
- 14
IPA.Loader/Config/Stores/GeneratedStoreImpl/Deserialization.cs View File

@ -18,12 +18,12 @@ namespace IPA.Config.Stores
{ {
internal static partial class GeneratedStoreImpl internal static partial class GeneratedStoreImpl
{ {
private static void EmitDeserializeGeneratedValue(ILGenerator il, SerializedMemberInfo member, Type srcType, GetLocal GetLocal,
private static void EmitDeserializeGeneratedValue(ILGenerator il, SerializedMemberInfo member, Type srcType, LocalAllocator GetLocal,
Action<ILGenerator> thisarg, Action<ILGenerator> parentobj) Action<ILGenerator> thisarg, Action<ILGenerator> parentobj)
{ {
var IGeneratedStore_Deserialize = typeof(IGeneratedStore).GetMethod(nameof(IGeneratedStore.Deserialize)); var IGeneratedStore_Deserialize = typeof(IGeneratedStore).GetMethod(nameof(IGeneratedStore.Deserialize));
var valuel = GetLocal(srcType, 0);
using var valuel = GetLocal.Allocate(srcType);
var noCreate = il.DefineLabel(); var noCreate = il.DefineLabel();
il.Emit(OpCodes.Stloc, valuel); il.Emit(OpCodes.Stloc, valuel);
@ -40,7 +40,7 @@ namespace IPA.Config.Stores
il.Emit(OpCodes.Callvirt, IGeneratedStore_Deserialize); il.Emit(OpCodes.Callvirt, IGeneratedStore_Deserialize);
} }
private static void EmitDeserializeNullable(ILGenerator il, SerializedMemberInfo member, Type expected, GetLocal GetLocal,
private static void EmitDeserializeNullable(ILGenerator il, SerializedMemberInfo member, Type expected, LocalAllocator GetLocal,
Action<ILGenerator> thisarg, Action<ILGenerator> parentobj) Action<ILGenerator> thisarg, Action<ILGenerator> parentobj)
{ {
thisarg ??= il => il.Emit(OpCodes.Ldarg_0); thisarg ??= il => il.Emit(OpCodes.Ldarg_0);
@ -51,7 +51,7 @@ namespace IPA.Config.Stores
// top of stack is the Value to deserialize; the type will be as returned from GetExpectedValueTypeForType // top of stack is the Value to deserialize; the type will be as returned from GetExpectedValueTypeForType
// after, top of stack will be thing to write to field // after, top of stack will be thing to write to field
private static void EmitDeserializeValue(ILGenerator il, SerializedMemberInfo member, Type targetType, Type expected, GetLocal GetLocal,
private static void EmitDeserializeValue(ILGenerator il, SerializedMemberInfo member, Type targetType, Type expected, LocalAllocator GetLocal,
Action<ILGenerator> thisarg, Action<ILGenerator> parentobj) Action<ILGenerator> thisarg, Action<ILGenerator> parentobj)
{ {
if (typeof(Value).IsAssignableFrom(targetType)) return; // do nothing if (typeof(Value).IsAssignableFrom(targetType)) return; // do nothing
@ -93,9 +93,9 @@ namespace IPA.Config.Stores
else else
{ {
var mapLocal = GetLocal(typeof(Map));
var resultLocal = GetLocal(targetType, 1);
var valueLocal = GetLocal(typeof(Value));
using var mapLocal = GetLocal.Allocate(typeof(Map));
using var resultLocal = GetLocal.Allocate(targetType);
using var valueLocal = GetLocal.Allocate(typeof(Value));
var structure = ReadObjectMembers(targetType); var structure = ReadObjectMembers(targetType);
if (!structure.Any()) if (!structure.Any())
@ -129,7 +129,7 @@ namespace IPA.Config.Stores
private static void EmitDeserializeStructure(ILGenerator il, IEnumerable<SerializedMemberInfo> structure, private static void EmitDeserializeStructure(ILGenerator il, IEnumerable<SerializedMemberInfo> structure,
LocalBuilder mapLocal, LocalBuilder valueLocal, LocalBuilder mapLocal, LocalBuilder valueLocal,
GetLocal GetLocal, Action<ILGenerator> thisobj, Action<ILGenerator> parentobj)
LocalAllocator GetLocal, Action<ILGenerator> thisobj, Action<ILGenerator> parentobj)
{ {
var Map_TryGetValue = typeof(Map).GetMethod(nameof(Map.TryGetValue)); var Map_TryGetValue = typeof(Map).GetMethod(nameof(Map.TryGetValue));
@ -158,11 +158,11 @@ namespace IPA.Config.Stores
} }
} }
private static void EmitDeserializeConverter(ILGenerator il, SerializedMemberInfo member, Label nextLabel, GetLocal GetLocal,
private static void EmitDeserializeConverter(ILGenerator il, SerializedMemberInfo member, Label nextLabel, LocalAllocator GetLocal,
Action<ILGenerator> thisobj, Action<ILGenerator> parentobj) Action<ILGenerator> thisobj, Action<ILGenerator> parentobj)
{ {
var stlocal = GetLocal(typeof(Value));
var valLocal = GetLocal(member.Type);
using var stlocal = GetLocal.Allocate(typeof(Value));
using var valLocal = GetLocal.Allocate(member.Type);
il.Emit(OpCodes.Stloc, stlocal); il.Emit(OpCodes.Stloc, stlocal);
il.BeginExceptionBlock(); il.BeginExceptionBlock();
@ -198,7 +198,7 @@ namespace IPA.Config.Stores
} }
// emit takes the value being deserialized, logs on error, leaves nothing on stack // emit takes the value being deserialized, logs on error, leaves nothing on stack
private static void EmitDeserializeMember(ILGenerator il, SerializedMemberInfo member, Label nextLabel, Action<ILGenerator> getValue, GetLocal GetLocal,
private static void EmitDeserializeMember(ILGenerator il, SerializedMemberInfo member, Label nextLabel, Action<ILGenerator> getValue, LocalAllocator GetLocal,
Action<ILGenerator> thisobj, Action<ILGenerator> parentobj) Action<ILGenerator> thisobj, Action<ILGenerator> parentobj)
{ {
var Object_GetType = typeof(object).GetMethod(nameof(Object.GetType)); var Object_GetType = typeof(object).GetMethod(nameof(Object.GetType));
@ -220,7 +220,7 @@ namespace IPA.Config.Stores
else if (member.IsNullable) else if (member.IsNullable)
{ {
il.Emit(OpCodes.Pop); il.Emit(OpCodes.Pop);
var valTLocal = GetLocal(member.Type, 0);
using var valTLocal = GetLocal.Allocate(member.Type);
il.Emit(OpCodes.Ldloca, valTLocal); il.Emit(OpCodes.Ldloca, valTLocal);
il.Emit(OpCodes.Initobj, member.Type); il.Emit(OpCodes.Initobj, member.Type);
EmitStore(il, member, il => il.Emit(OpCodes.Ldloc, valTLocal), thisobj); EmitStore(il, member, il => il.Emit(OpCodes.Ldloc, valTLocal), thisobj);
@ -291,7 +291,7 @@ namespace IPA.Config.Stores
il.MarkLabel(passedTypeCheck); il.MarkLabel(passedTypeCheck);
var local = GetLocal(member.Type, 0);
using var local = GetLocal.Allocate(member.Type);
if (member.HasConverter) EmitDeserializeConverter(il, member, nextLabel, GetLocal, thisobj, parentobj); if (member.HasConverter) EmitDeserializeConverter(il, member, nextLabel, GetLocal, thisobj, parentobj);
else if (member.IsNullable) EmitDeserializeNullable(il, member, expectType, GetLocal, thisobj, parentobj); else if (member.IsNullable) EmitDeserializeNullable(il, member, expectType, GetLocal, thisobj, parentobj);
else EmitDeserializeValue(il, member, member.Type, expectType, GetLocal, thisobj, parentobj); else EmitDeserializeValue(il, member, member.Type, expectType, GetLocal, thisobj, parentobj);


+ 41
- 0
IPA.Loader/Config/Stores/GeneratedStoreImpl/GeneratedStoreImpl.cs View File

@ -97,6 +97,47 @@ namespace IPA.Config.Stores
return module; return module;
} }
}
private static readonly Dictionary<Type, Dictionary<Type, FieldInfo>> TypeRequiredConverters = new Dictionary<Type, Dictionary<Type, FieldInfo>>();
private static void CreateAndInitializeConvertersFor(Type type, IEnumerable<SerializedMemberInfo> structure)
{
if (!TypeRequiredConverters.TryGetValue(type, out var converters))
{
var converterFieldType = Module.DefineType($"{type.FullName}<Converters>",
TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Abstract | TypeAttributes.AnsiClass); // a static class
var uniqueConverterTypes = structure.Where(m => m.HasConverter).Select(m => m.Converter).Distinct().ToArray();
converters = new Dictionary<Type, FieldInfo>(uniqueConverterTypes.Length);
foreach (var convType in uniqueConverterTypes)
{
var field = converterFieldType.DefineField($"<converter>_{convType}", convType,
FieldAttributes.FamORAssem | FieldAttributes.InitOnly | FieldAttributes.Static);
converters.Add(convType, field);
}
var cctor = converterFieldType.DefineConstructor(MethodAttributes.Static, CallingConventions.Standard, Type.EmptyTypes);
{
var il = cctor.GetILGenerator();
foreach (var kvp in converters)
{
var typeCtor = kvp.Key.GetConstructor(Type.EmptyTypes);
il.Emit(OpCodes.Newobj, typeCtor);
il.Emit(OpCodes.Stsfld, kvp.Value);
}
il.Emit(OpCodes.Ret);
}
TypeRequiredConverters.Add(type, converters);
converterFieldType.CreateType();
}
foreach (var member in structure.Where(m => m.HasConverter))
member.ConverterField = converters[member.Converter];
} }
} }
} }

+ 25
- 25
IPA.Loader/Config/Stores/GeneratedStoreImpl/IGeneratedStore.cs View File

@ -59,17 +59,17 @@ namespace IPA.Config.Stores
internal static MethodInfo ImplSignalChangedMethod = typeof(Impl).GetMethod(nameof(ImplSignalChanged)); internal static MethodInfo ImplSignalChangedMethod = typeof(Impl).GetMethod(nameof(ImplSignalChanged));
public static void ImplSignalChanged(IGeneratedStore s) => FindImpl(s).SignalChanged(); public static void ImplSignalChanged(IGeneratedStore s) => FindImpl(s).SignalChanged();
public void SignalChanged()
public void SignalChanged()
{ {
try
{
resetEvent.Set();
}
catch (ObjectDisposedException e)
{
Logger.config.Error($"ObjectDisposedException while signalling a change for generated store {generated?.GetType()}");
Logger.config.Error(e);
}
try
{
resetEvent.Set();
}
catch (ObjectDisposedException e)
{
Logger.config.Error($"ObjectDisposedException while signalling a change for generated store {generated?.GetType()}");
Logger.config.Error(e);
}
} }
internal static MethodInfo ImplInvokeChangedMethod = typeof(Impl).GetMethod(nameof(ImplInvokeChanged)); internal static MethodInfo ImplInvokeChangedMethod = typeof(Impl).GetMethod(nameof(ImplInvokeChanged));
@ -142,22 +142,22 @@ namespace IPA.Config.Stores
public void Dispose() => Dispose(true); public void Dispose() => Dispose(true);
private void Dispose(bool addToStore) private void Dispose(bool addToStore)
{
if (data.owns)
{
data.impl.inChangeTransaction = false;
data.impl.InvokeChanged();
}
{
if (data.owns)
{
data.impl.inChangeTransaction = false;
data.impl.InvokeChanged();
}
data.nested?.Dispose(); data.nested?.Dispose();
try
{
if (data.ownsWrite)
try
{
if (data.ownsWrite)
data.impl.ReleaseWrite(); data.impl.ReleaseWrite();
} }
catch
{
}
if (addToStore)
catch
{
}
if (addToStore)
freeTransactionObjs.Push(this); freeTransactionObjs.Push(this);
} }
@ -175,8 +175,8 @@ namespace IPA.Config.Stores
public static void ImplReadFrom(IGeneratedStore s, ConfigProvider provider) => FindImpl(s).ReadFrom(provider); public static void ImplReadFrom(IGeneratedStore s, ConfigProvider provider) => FindImpl(s).ReadFrom(provider);
public void ReadFrom(ConfigProvider provider) public void ReadFrom(ConfigProvider provider)
{ {
Logger.config.Debug($"Generated impl ReadFrom {generated.GetType()}");
var values = provider.Load(); var values = provider.Load();
Logger.config.Debug("Generated impl ReadFrom");
Logger.config.Debug($"Read {values}"); Logger.config.Debug($"Read {values}");
generated.Deserialize(values); generated.Deserialize(values);
@ -188,8 +188,8 @@ namespace IPA.Config.Stores
public static void ImplWriteTo(IGeneratedStore s, ConfigProvider provider) => FindImpl(s).WriteTo(provider); public static void ImplWriteTo(IGeneratedStore s, ConfigProvider provider) => FindImpl(s).WriteTo(provider);
public void WriteTo(ConfigProvider provider) public void WriteTo(ConfigProvider provider)
{ {
Logger.config.Debug($"Generated impl WriteTo {generated.GetType()}");
var values = generated.Serialize(); var values = generated.Serialize();
Logger.config.Debug("Generated impl WriteTo");
Logger.config.Debug($"Serialized {values}"); Logger.config.Debug($"Serialized {values}");
provider.Store(values); provider.Store(values);
} }


+ 10
- 24
IPA.Loader/Config/Stores/GeneratedStoreImpl/MakeCreator.cs View File

@ -62,7 +62,7 @@ namespace IPA.Config.Stores
var implField = typeBuilder.DefineField("<>_impl", typeof(Impl), FieldAttributes.Private | FieldAttributes.InitOnly); var implField = typeBuilder.DefineField("<>_impl", typeof(Impl), FieldAttributes.Private | FieldAttributes.InitOnly);
var parentField = typeBuilder.DefineField("<>_parent", typeof(IGeneratedStore), FieldAttributes.Private | FieldAttributes.InitOnly); var parentField = typeBuilder.DefineField("<>_parent", typeof(IGeneratedStore), FieldAttributes.Private | FieldAttributes.InitOnly);
#region Converter fields
/*#region Converter fields
var uniqueConverterTypes = structure.Where(m => m.HasConverter).Select(m => m.Converter).Distinct().ToArray(); var uniqueConverterTypes = structure.Where(m => m.HasConverter).Select(m => m.Converter).Distinct().ToArray();
var converterFields = new Dictionary<Type, FieldInfo>(uniqueConverterTypes.Length); var converterFields = new Dictionary<Type, FieldInfo>(uniqueConverterTypes.Length);
@ -91,7 +91,9 @@ namespace IPA.Config.Stores
il.Emit(OpCodes.Ret); il.Emit(OpCodes.Ret);
} }
#endregion
#endregion*/
//CreateAndInitializeConvertersFor(type, structure);
#region Constructor #region Constructor
var ctor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new[] { typeof(IGeneratedStore) }); var ctor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new[] { typeof(IGeneratedStore) });
@ -120,7 +122,7 @@ namespace IPA.Config.Stores
il.Emit(OpCodes.Stfld, implField); il.Emit(OpCodes.Stfld, implField);
il.MarkLabel(noImplLabel); il.MarkLabel(noImplLabel);
var GetLocal = MakeGetLocal(il);
var GetLocal = MakeLocalAllocator(il);
foreach (var member in structure) foreach (var member in structure)
{ {
@ -357,26 +359,10 @@ namespace IPA.Config.Stores
{ // this is non-locking because the only code that will call this will already own the correct lock { // this is non-locking because the only code that will call this will already own the correct lock
var il = serializeGen.GetILGenerator(); var il = serializeGen.GetILGenerator();
var Map_Add = typeof(Map).GetMethod(nameof(Map.Add));
var mapLocal = il.DeclareLocal(typeof(Map));
var GetLocal = MakeLocalAllocator(il);
var GetLocal = MakeGetLocal(il);
var valLocal = GetLocal(typeof(Value));
il.Emit(OpCodes.Call, typeof(Value).GetMethod(nameof(Value.Map)));
il.Emit(OpCodes.Stloc, mapLocal);
foreach (var member in structure)
{
EmitSerializeMember(il, member, GetLocal, GetMethodThis);
il.Emit(OpCodes.Stloc, valLocal);
il.Emit(OpCodes.Ldloc, mapLocal);
il.Emit(OpCodes.Ldstr, member.Name);
il.Emit(OpCodes.Ldloc, valLocal);
il.Emit(OpCodes.Call, Map_Add);
}
EmitSerializeStructure(il, structure, GetLocal, GetMethodThis);
il.Emit(OpCodes.Ldloc, mapLocal);
il.Emit(OpCodes.Ret); il.Emit(OpCodes.Ret);
} }
#endregion #endregion
@ -421,7 +407,7 @@ namespace IPA.Config.Stores
il.MarkLabel(notMapError); il.MarkLabel(notMapError);
var GetLocal = MakeGetLocal(il);
var GetLocal = MakeLocalAllocator(il);
// head of stack is Map instance // head of stack is Map instance
EmitDeserializeStructure(il, structure, mapLocal, valueLocal, GetLocal, GetMethodThis, GetMethodThis); EmitDeserializeStructure(il, structure, mapLocal, valueLocal, GetLocal, GetMethodThis, GetMethodThis);
@ -585,7 +571,7 @@ namespace IPA.Config.Stores
il.Emit(OpCodes.Stloc, transactionLocal); il.Emit(OpCodes.Stloc, transactionLocal);
il.MarkLabel(startLock); il.MarkLabel(startLock);
var GetLocal = MakeGetLocal(il);
var GetLocal = MakeLocalAllocator(il);
foreach (var member in structure) foreach (var member in structure)
{ {
@ -697,7 +683,7 @@ namespace IPA.Config.Stores
var il = propSet.GetILGenerator(); var il = propSet.GetILGenerator();
var transactionLocal = il.DeclareLocal(IDisposable_t); var transactionLocal = il.DeclareLocal(IDisposable_t);
var GetLocal = MakeGetLocal(il);
var GetLocal = MakeLocalAllocator(il);
il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, coreChangeTransaction); // take the write lock il.Emit(OpCodes.Call, coreChangeTransaction); // take the write lock


+ 1
- 0
IPA.Loader/Config/Stores/GeneratedStoreImpl/ObjectStructure.cs View File

@ -181,6 +181,7 @@ namespace IPA.Config.Stores
structure.Add(smi); structure.Add(smi);
} }
CreateAndInitializeConvertersFor(type, structure);
return structure; return structure;
} }
} }


+ 30
- 17
IPA.Loader/Config/Stores/GeneratedStoreImpl/Serialization.cs View File

@ -18,7 +18,7 @@ namespace IPA.Config.Stores
internal static partial class GeneratedStoreImpl internal static partial class GeneratedStoreImpl
{ {
// emit takes no args, leaves Value at top of stack // emit takes no args, leaves Value at top of stack
private static void EmitSerializeMember(ILGenerator il, SerializedMemberInfo member, GetLocal GetLocal, Action<ILGenerator> thisarg)
private static void EmitSerializeMember(ILGenerator il, SerializedMemberInfo member, LocalAllocator GetLocal, Action<ILGenerator> thisarg)
{ {
EmitLoad(il, member, thisarg); EmitLoad(il, member, thisarg);
@ -47,8 +47,8 @@ namespace IPA.Config.Stores
var targetType = GetExpectedValueTypeForType(memberConversionType); var targetType = GetExpectedValueTypeForType(memberConversionType);
if (member.HasConverter) if (member.HasConverter)
{ {
var stlocal = GetLocal(member.Type);
var valLocal = GetLocal(typeof(Value));
using var stlocal = GetLocal.Allocate(member.Type);
using var valLocal = GetLocal.Allocate(typeof(Value));
il.Emit(OpCodes.Stloc, stlocal); il.Emit(OpCodes.Stloc, stlocal);
il.BeginExceptionBlock(); il.BeginExceptionBlock();
@ -130,7 +130,7 @@ namespace IPA.Config.Stores
if (!member.IsVirtual) if (!member.IsVirtual)
{ {
var noCreate = il.DefineLabel(); var noCreate = il.DefineLabel();
var stlocal = GetLocal(member.Type);
using var stlocal = GetLocal.Allocate(member.Type);
// first check to make sure that this is an IGeneratedStore, because we don't control assignments to it // first check to make sure that this is an IGeneratedStore, because we don't control assignments to it
il.Emit(OpCodes.Dup); il.Emit(OpCodes.Dup);
@ -151,10 +151,7 @@ namespace IPA.Config.Stores
} }
else else
{ // generate serialization for value types { // generate serialization for value types
var MapCreate = typeof(Value).GetMethod(nameof(Value.Map));
var MapAdd = typeof(Map).GetMethod(nameof(Map.Add));
var valueLocal = GetLocal(memberConversionType);
using var valueLocal = GetLocal.Allocate(memberConversionType);
var structure = ReadObjectMembers(memberConversionType); var structure = ReadObjectMembers(memberConversionType);
if (!structure.Any()) if (!structure.Any())
@ -168,19 +165,35 @@ namespace IPA.Config.Stores
il.Emit(OpCodes.Stloc, valueLocal); il.Emit(OpCodes.Stloc, valueLocal);
} }
il.Emit(OpCodes.Call, MapCreate);
foreach (var mem in structure)
{
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldstr, mem.Name);
EmitSerializeMember(il, mem, GetLocal, il => il.Emit(OpCodes.Ldloca, valueLocal));
il.Emit(OpCodes.Call, MapAdd);
}
EmitSerializeStructure(il, structure, GetLocal, il => il.Emit(OpCodes.Ldloca, valueLocal));
} }
} }
il.MarkLabel(endSerialize); il.MarkLabel(endSerialize);
} }
private static void EmitSerializeStructure(ILGenerator il, IEnumerable<SerializedMemberInfo> structure, LocalAllocator GetLocal, Action<ILGenerator> thisarg)
{
var MapCreate = typeof(Value).GetMethod(nameof(Value.Map));
var MapAdd = typeof(Map).GetMethod(nameof(Map.Add));
using var mapLocal = GetLocal.Allocate(typeof(Map));
using var valueLocal = GetLocal.Allocate(typeof(Value));
il.Emit(OpCodes.Call, MapCreate);
il.Emit(OpCodes.Stloc, mapLocal);
foreach (var mem in structure)
{
EmitSerializeMember(il, mem, GetLocal, thisarg);
il.Emit(OpCodes.Stloc, valueLocal);
il.Emit(OpCodes.Ldloc, mapLocal);
il.Emit(OpCodes.Ldstr, mem.Name);
il.Emit(OpCodes.Ldloc, valueLocal);
il.Emit(OpCodes.Call, MapAdd);
}
il.Emit(OpCodes.Ldloc, mapLocal);
}
} }
} }

+ 47
- 13
IPA.Loader/Config/Stores/GeneratedStoreImpl/Utility.cs View File

@ -3,9 +3,11 @@ using IPA.Logging;
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Reflection.Emit; using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Boolean = IPA.Config.Data.Boolean; using Boolean = IPA.Config.Data.Boolean;
@ -36,24 +38,56 @@ namespace IPA.Config.Stores
} }
#endregion #endregion
private delegate LocalBuilder GetLocal(Type type, int idx = 0);
//private delegate LocalBuilder LocalAllocator(Type type, int idx = 0);
private static GetLocal MakeGetLocal(ILGenerator il)
{ // TODO: improve this shit a bit so that i can release a hold of a variable and do more auto managing
var locals = new List<LocalBuilder>();
private static LocalAllocator MakeLocalAllocator(ILGenerator il)
=> new LocalAllocator(il);
LocalBuilder GetLocal(Type ty, int i = 0)
private struct AllocatedLocal : IDisposable
{
internal readonly LocalAllocator allocator;
public LocalBuilder Local { get; }
public AllocatedLocal(LocalAllocator alloc, LocalBuilder builder)
{ {
var builder = locals.Where(b => b.LocalType == ty).Skip(i).FirstOrDefault();
if (builder == null)
{
builder = il.DeclareLocal(ty);
locals.Add(builder);
}
return builder;
allocator = alloc;
Local = builder;
} }
return GetLocal;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator LocalBuilder(AllocatedLocal loc) => loc.Local;
public void Dealloc() => allocator.Deallocate(this);
public void Dispose() => Dealloc();
}
private sealed class LocalAllocator
{
private readonly ILGenerator ilSource;
private readonly Dictionary<Type, Stack<LocalBuilder>> unallocatedLocals = new Dictionary<Type, Stack<LocalBuilder>>();
public LocalAllocator(ILGenerator il)
=> ilSource = il;
private Stack<LocalBuilder> GetLocalListForType(Type type)
{
if (!unallocatedLocals.TryGetValue(type, out var list))
unallocatedLocals.Add(type, list = new Stack<LocalBuilder>());
return list;
}
public AllocatedLocal Allocate(Type type)
{
var list = GetLocalListForType(type);
if (list.Count < 1) list.Push(ilSource.DeclareLocal(type));
return new AllocatedLocal(this, list.Pop());
}
public void Deallocate(AllocatedLocal loc)
{
Debug.Assert(loc.allocator == this);
var list = GetLocalListForType(loc.Local.LocalType);
list.Push(loc.Local);
}
} }
private static void EmitLoad(ILGenerator il, SerializedMemberInfo member, Action<ILGenerator> thisarg) private static void EmitLoad(ILGenerator il, SerializedMemberInfo member, Action<ILGenerator> thisarg)


+ 1
- 0
IPA.Loader/IPA.Loader.csproj View File

@ -103,6 +103,7 @@
<Compile Include="Config\Stores\Converters.cs" /> <Compile Include="Config\Stores\Converters.cs" />
<Compile Include="Config\Stores\CustomObjectConverter.cs" /> <Compile Include="Config\Stores\CustomObjectConverter.cs" />
<Compile Include="Config\Stores\CollectionConverter.cs" /> <Compile Include="Config\Stores\CollectionConverter.cs" />
<Compile Include="Config\Stores\GeneratedStoreImpl\ConversionDelegates.cs" />
<Compile Include="Config\Stores\GeneratedStoreImpl\Correction.cs" /> <Compile Include="Config\Stores\GeneratedStoreImpl\Correction.cs" />
<Compile Include="Config\Stores\GeneratedStoreImpl\Deserialization.cs" /> <Compile Include="Config\Stores\GeneratedStoreImpl\Deserialization.cs" />
<Compile Include="Config\Stores\GeneratedStoreImpl\GeneratedStoreImpl.cs" /> <Compile Include="Config\Stores\GeneratedStoreImpl\GeneratedStoreImpl.cs" />


BIN
Refs/Main.dll View File


BIN
Refs/Unity.TextMeshPro.dll View File


BIN
Refs/UnityEngine.CoreModule.Net4.dll View File


BIN
Refs/UnityEngine.UI.dll View File


Loading…
Cancel
Save