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/46/head
Anairkoen Schno 4 years ago
parent
commit
d18d055a5b
16 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/Unity.TextMeshPro.dll
  15. BIN
      Refs/UnityEngine.CoreModule.Net4.dll
  16. 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.Threading.Tasks;
using IPA.Config.Data;
using IPA.Logging;
namespace IPA.Config.Stores.Converters
{
/// <summary>
@ -53,6 +54,7 @@ namespace IPA.Config.Stores.Converters
/// <seealso cref="ValueConverter{T}.FromValue(Value, object)"/>
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)
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>
/// <returns>the <see cref="List"/> that <paramref name="obj"/> was serialized into</returns>
/// <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)));
}
/// <summary>


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

@ -1,5 +1,6 @@
using IPA.Config.Data;
using IPA.Config.Stores.Attributes;
using IPA.Logging;
using System;
using System.Collections.Generic;
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>();
public ValueConverter<T> Get() => Impl.Get();
ValueConverter<T> IValConv<T>.Get()
=> null; // default to null
=> new CustomValueTypeConverter<T>();
}
private class ValConvImpls : IValConv<char>,
IValConv<IntPtr>, IValConv<UIntPtr>,
@ -89,24 +90,29 @@ namespace IPA.Config.Stores.Converters
private static ValueConverter<T> MakeDefault()
{
var t = typeof(T);
var t = typeof(T);
//Logger.log.Debug($"Converter<{t}>.MakeDefault()");
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
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>;
}
}
//Logger.log.Debug($"gives converter for value type {t}");
var valConv = Activator.CreateInstance(typeof(Converter.ValConv<>).MakeGenericType(t)) as Converter.IValConv<T>;
return valConv.Get();
}
else if (t == typeof(string))
{
{
//Logger.log.Debug($"gives StringConverter");
return new StringConverter() as ValueConverter<T>;
}
else
{
{
//Logger.log.Debug($"gives CustomObjectConverter<{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);
}
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
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)
{
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
var copyFrom = typeof(IGeneratedStore<>).MakeGenericType(convType).GetMethod(nameof(IGeneratedStore<Config>.CopyFrom));
var noCreate = il.DefineLabel();
var valLocal = GetLocal(convType);
using var valLocal = GetLocal.Allocate(convType);
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
var structure = ReadObjectMembers(convType);
var valueLocal = GetLocal(convType);
using var valueLocal = GetLocal.Allocate(convType);
il.Emit(OpCodes.Stloc, valueLocal);
void LdlocaValueLocal(ILGenerator il)
@ -116,7 +116,7 @@ namespace IPA.Config.Stores
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)
{
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
{
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)
{
var IGeneratedStore_Deserialize = typeof(IGeneratedStore).GetMethod(nameof(IGeneratedStore.Deserialize));
var valuel = GetLocal(srcType, 0);
using var valuel = GetLocal.Allocate(srcType);
var noCreate = il.DefineLabel();
il.Emit(OpCodes.Stloc, valuel);
@ -40,7 +40,7 @@ namespace IPA.Config.Stores
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)
{
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
// 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)
{
if (typeof(Value).IsAssignableFrom(targetType)) return; // do nothing
@ -93,9 +93,9 @@ namespace IPA.Config.Stores
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);
if (!structure.Any())
@ -129,7 +129,7 @@ namespace IPA.Config.Stores
private static void EmitDeserializeStructure(ILGenerator il, IEnumerable<SerializedMemberInfo> structure,
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));
@ -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)
{
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.BeginExceptionBlock();
@ -198,7 +198,7 @@ namespace IPA.Config.Stores
}
// 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)
{
var Object_GetType = typeof(object).GetMethod(nameof(Object.GetType));
@ -220,7 +220,7 @@ namespace IPA.Config.Stores
else if (member.IsNullable)
{
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.Initobj, member.Type);
EmitStore(il, member, il => il.Emit(OpCodes.Ldloc, valTLocal), thisobj);
@ -291,7 +291,7 @@ namespace IPA.Config.Stores
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);
else if (member.IsNullable) EmitDeserializeNullable(il, member, 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;
}
}
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));
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));
@ -142,22 +142,22 @@ namespace IPA.Config.Stores
public void Dispose() => Dispose(true);
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();
try
{
if (data.ownsWrite)
try
{
if (data.ownsWrite)
data.impl.ReleaseWrite();
}
catch
{
}
if (addToStore)
catch
{
}
if (addToStore)
freeTransactionObjs.Push(this);
}
@ -175,8 +175,8 @@ namespace IPA.Config.Stores
public static void ImplReadFrom(IGeneratedStore s, ConfigProvider provider) => FindImpl(s).ReadFrom(provider);
public void ReadFrom(ConfigProvider provider)
{
Logger.config.Debug($"Generated impl ReadFrom {generated.GetType()}");
var values = provider.Load();
Logger.config.Debug("Generated impl ReadFrom");
Logger.config.Debug($"Read {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 void WriteTo(ConfigProvider provider)
{
Logger.config.Debug($"Generated impl WriteTo {generated.GetType()}");
var values = generated.Serialize();
Logger.config.Debug("Generated impl WriteTo");
Logger.config.Debug($"Serialized {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 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 converterFields = new Dictionary<Type, FieldInfo>(uniqueConverterTypes.Length);
@ -91,7 +91,9 @@ namespace IPA.Config.Stores
il.Emit(OpCodes.Ret);
}
#endregion
#endregion*/
//CreateAndInitializeConvertersFor(type, structure);
#region Constructor
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.MarkLabel(noImplLabel);
var GetLocal = MakeGetLocal(il);
var GetLocal = MakeLocalAllocator(il);
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
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);
}
#endregion
@ -421,7 +407,7 @@ namespace IPA.Config.Stores
il.MarkLabel(notMapError);
var GetLocal = MakeGetLocal(il);
var GetLocal = MakeLocalAllocator(il);
// head of stack is Map instance
EmitDeserializeStructure(il, structure, mapLocal, valueLocal, GetLocal, GetMethodThis, GetMethodThis);
@ -585,7 +571,7 @@ namespace IPA.Config.Stores
il.Emit(OpCodes.Stloc, transactionLocal);
il.MarkLabel(startLock);
var GetLocal = MakeGetLocal(il);
var GetLocal = MakeLocalAllocator(il);
foreach (var member in structure)
{
@ -697,7 +683,7 @@ namespace IPA.Config.Stores
var il = propSet.GetILGenerator();
var transactionLocal = il.DeclareLocal(IDisposable_t);
var GetLocal = MakeGetLocal(il);
var GetLocal = MakeLocalAllocator(il);
il.Emit(OpCodes.Ldarg_0);
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);
}
CreateAndInitializeConvertersFor(type, 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
{
// 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);
@ -47,8 +47,8 @@ namespace IPA.Config.Stores
var targetType = GetExpectedValueTypeForType(memberConversionType);
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.BeginExceptionBlock();
@ -130,7 +130,7 @@ namespace IPA.Config.Stores
if (!member.IsVirtual)
{
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
il.Emit(OpCodes.Dup);
@ -151,10 +151,7 @@ namespace IPA.Config.Stores
}
else
{ // 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);
if (!structure.Any())
@ -168,19 +165,35 @@ namespace IPA.Config.Stores
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);
}
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.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Boolean = IPA.Config.Data.Boolean;
@ -36,24 +38,56 @@ namespace IPA.Config.Stores
}
#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)


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

@ -103,6 +103,7 @@
<Compile Include="Config\Stores\Converters.cs" />
<Compile Include="Config\Stores\CustomObjectConverter.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\Deserialization.cs" />
<Compile Include="Config\Stores\GeneratedStoreImpl\GeneratedStoreImpl.cs" />


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