Browse Source

Added sane handling of non-generated objects being passed into CustomObjectConverter

pull/46/head
Anairkoen Schno 4 years ago
parent
commit
14ef4a3923
3 changed files with 104 additions and 13 deletions
  1. +9
    -3
      IPA.Loader/Config/Stores/CustomObjectConverter.cs
  2. +95
    -10
      IPA.Loader/Config/Stores/GeneratedStore.cs
  3. BIN
      Refs/UnityEngine.CoreModule.Net4.dll

+ 9
- 3
IPA.Loader/Config/Stores/CustomObjectConverter.cs View File

@ -15,13 +15,15 @@ namespace IPA.Config.Stores.Converters
T FromValue(Value value, object parent); T FromValue(Value value, object parent);
Value ToValue(T obj, object parent); Value ToValue(T obj, object parent);
} }
private class Impl<U> : IImpl where U : class, GeneratedStore.IGeneratedStore, T
private class Impl<U> : IImpl where U : class, GeneratedStore.IGeneratedStore<T>, T
{ {
private static readonly GeneratedStore.GeneratedStoreCreator creator = GeneratedStore.GetCreator(typeof(T)); private static readonly GeneratedStore.GeneratedStoreCreator creator = GeneratedStore.GetCreator(typeof(T));
private static U Create(GeneratedStore.IGeneratedStore parent)
=> creator(parent) as U;
public T FromValue(Value value, object parent) public T FromValue(Value value, object parent)
{ // lots of casting here, but it works i promise (probably) (parent can be a non-IGeneratedStore, however it won't necessarily behave then) { // lots of casting here, but it works i promise (probably) (parent can be a non-IGeneratedStore, however it won't necessarily behave then)
var obj = creator(parent as GeneratedStore.IGeneratedStore) as U;
var obj = Create(parent as GeneratedStore.IGeneratedStore);
obj.Deserialize(value); obj.Deserialize(value);
return obj; return obj;
} }
@ -31,7 +33,11 @@ namespace IPA.Config.Stores.Converters
if (obj is GeneratedStore.IGeneratedStore store) if (obj is GeneratedStore.IGeneratedStore store)
return store.Serialize(); return store.Serialize();
else else
return null; // TODO: make this behave sanely instead of just giving null
{
var newObj = Create(null);
newObj.CopyFrom(obj);
return newObj.Serialize();
}
} }
} }


+ 95
- 10
IPA.Loader/Config/Stores/GeneratedStore.cs View File

@ -106,10 +106,14 @@ namespace IPA.Config.Stores
Value Serialize(); Value Serialize();
void Deserialize(Value val); void Deserialize(Value val);
} }
internal interface IGeneratedStore<T> : IGeneratedStore where T : class
{
void CopyFrom(T source);
}
internal class Impl : IConfigStore internal class Impl : IConfigStore
{ {
private IGeneratedStore generated;
private readonly IGeneratedStore generated;
internal static ConstructorInfo Ctor = typeof(Impl).GetConstructor(new[] { typeof(IGeneratedStore) }); internal static ConstructorInfo Ctor = typeof(Impl).GetConstructor(new[] { typeof(IGeneratedStore) });
public Impl(IGeneratedStore store) => generated = store; public Impl(IGeneratedStore store) => generated = store;
@ -672,6 +676,49 @@ namespace IPA.Config.Stores
#endregion #endregion
#endregion #endregion
#region IGeneratedStore<T>
var IGeneratedStore_T_t = typeof(IGeneratedStore<>).MakeGenericType(type);
typeBuilder.AddInterfaceImplementation(IGeneratedStore_T_t);
var IGeneratedStore_T_CopyFrom = IGeneratedStore_T_t.GetMethod(nameof(IGeneratedStore<Config>.CopyFrom));
#region IGeneratedStore<T>.CopyFrom
var copyFrom = typeBuilder.DefineMethod($"<>{nameof(IGeneratedStore<Config>.CopyFrom)}", virtualMemberMethod, null, new[] { type });
typeBuilder.DefineMethodOverride(copyFrom, IGeneratedStore_T_CopyFrom);
{
var il = copyFrom.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, Impl.ImplTakeWriteMethod); // take the write lock
var GetLocal = MakeGetLocal(il);
foreach (var member in structure)
{
il.BeginExceptionBlock();
EmitStore(il, member, il =>
{
EmitLoad(il, member, il => il.Emit(OpCodes.Ldarg_1));
EmitCorrectMember(il, member, GetLocal);
});
il.BeginCatchBlock(typeof(Exception));
EmitWarnException(il, $"Error while copying from member {member.Name}");
il.EndExceptionBlock();
}
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, Impl.ImplReleaseWriteMethod); // release write lock
il.Emit(OpCodes.Ret);
}
#endregion
#endregion
#region IConfigStore #region IConfigStore
typeBuilder.AddInterfaceImplementation(typeof(IConfigStore)); typeBuilder.AddInterfaceImplementation(typeof(IConfigStore));
@ -829,6 +876,8 @@ namespace IPA.Config.Stores
{ // TODO: decide if i want to correct the value before or after i take the write lock { // TODO: decide if i want to correct the value before or after i take the write lock
var il = propSet.GetILGenerator(); var il = propSet.GetILGenerator();
var GetLocal = MakeGetLocal(il);
il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, Impl.ImplTakeWriteMethod); // take the write lock il.Emit(OpCodes.Call, Impl.ImplTakeWriteMethod); // take the write lock
@ -836,7 +885,7 @@ namespace IPA.Config.Stores
il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldarg_1);
EmitCorrectMember(il, member);
EmitCorrectMember(il, member, GetLocal);
il.Emit(OpCodes.Call, set); il.Emit(OpCodes.Call, set);
il.BeginFinallyBlock(); il.BeginFinallyBlock();
@ -904,15 +953,47 @@ namespace IPA.Config.Stores
private static bool NeedsCorrection(SerializedMemberInfo member) private static bool NeedsCorrection(SerializedMemberInfo member)
{ {
var expectType = GetExpectedValueTypeForType(member.IsNullable ? member.NullableWrappedType : member.Type);
if (expectType == typeof(Map)) // TODO: make this slightly saner
return true;
return false; return false;
} }
// 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)
private static void EmitCorrectMember(ILGenerator il, SerializedMemberInfo member, GetLocal GetLocal)
{ {
if (!NeedsCorrection(member)) return; if (!NeedsCorrection(member)) return;
var endLabel = il.DefineLabel();
if (member.IsNullable)
{
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Call, member.Nullable_HasValue.GetGetMethod());
il.Emit(OpCodes.Brfalse, endLabel);
il.Emit(OpCodes.Call, member.Nullable_Value.GetGetMethod());
}
// TODO: impl // TODO: impl
// currently the only thing for this is where expect == Map, so do generate shit
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Isinst, typeof(IGeneratedStore));
il.Emit(OpCodes.Brtrue_S, endLabel); // our input is already something we like
var copyFrom = typeof(IGeneratedStore<>).MakeGenericType(member.Type).GetMethod(nameof(IGeneratedStore<Config>.CopyFrom));
var valLocal = GetLocal(member.Type);
il.Emit(OpCodes.Stloc, valLocal);
EmitCreateChildGenerated(il, member.Type);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldloc, valLocal);
il.Emit(OpCodes.Callvirt, copyFrom);
if (member.IsNullable)
il.Emit(OpCodes.Newobj, member.Nullable_Construct);
il.MarkLabel(endLabel);
} }
// expects the this param to be on the stack // expects the this param to be on the stack
@ -920,18 +1001,22 @@ namespace IPA.Config.Stores
{ {
if (!NeedsCorrection(member)) return; if (!NeedsCorrection(member)) return;
var local = GetLocal(member.Type);
//var local = GetLocal(member.Type);
EmitLoad(il, member); // load the member
EmitCorrectMember(il, member); // correct it
il.Emit(OpCodes.Stloc, local);
EmitStore(il, member, il => il.Emit(OpCodes.Ldloc, local));
EmitStore(il, member, il =>
{
EmitLoad(il, member); // load the member
EmitCorrectMember(il, member, GetLocal); // correct it
});
} }
#region Utility #region Utility
private static void EmitLoad(ILGenerator il, SerializedMemberInfo member)
private static void EmitLoad(ILGenerator il, SerializedMemberInfo member, Action<ILGenerator> thisarg = null)
{ {
il.Emit(OpCodes.Ldarg_0); // load this
if (thisarg == null)
thisarg = il => il.Emit(OpCodes.Ldarg_0);
thisarg(il); // load this
if (member.IsField) if (member.IsField)
il.Emit(OpCodes.Ldfld, member.Member as FieldInfo); il.Emit(OpCodes.Ldfld, member.Member as FieldInfo);


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


Loading…
Cancel
Save