Browse Source

Improved codegen for generated stores

4.0.0-beta
Anairkoen Schno 5 years ago
parent
commit
07fa7c6968
1 changed files with 51 additions and 36 deletions
  1. +51
    -36
      IPA.Loader/Config/Stores/GeneratedStore.cs

+ 51
- 36
IPA.Loader/Config/Stores/GeneratedStore.cs View File

@ -499,8 +499,12 @@ namespace IPA.Config.Stores
var GetLocal = MakeGetLocal(il);
foreach (var member in structure)
{
EmitMemberFix(il, member, false, GetLocal);
{
EmitStore(il, member, il =>
{
EmitLoad(il, member); // load the member
EmitCorrectMember(il, member, false, true, GetLocal); // correct it
});
}
il.Emit(OpCodes.Pop);
@ -716,7 +720,7 @@ namespace IPA.Config.Stores
EmitStore(il, member, il =>
{
EmitLoad(il, member, il => il.Emit(OpCodes.Ldarg_1));
EmitCorrectMember(il, member, false, GetLocal);
EmitCorrectMember(il, member, false, false, GetLocal);
});
il.BeginCatchBlock(typeof(Exception));
@ -930,7 +934,7 @@ namespace IPA.Config.Stores
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
EmitCorrectMember(il, member, false, GetLocal);
EmitCorrectMember(il, member, false, false, GetLocal);
il.Emit(OpCodes.Call, set);
il.BeginFinallyBlock();
@ -1006,7 +1010,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, GetLocal GetLocal)
private static void EmitCorrectMember(ILGenerator il, SerializedMemberInfo member, bool shouldLock, bool alwaysNew, GetLocal GetLocal)
{
if (!NeedsCorrection(member)) return;
@ -1018,44 +1022,46 @@ namespace IPA.Config.Stores
il.Emit(OpCodes.Call, member.Nullable_HasValue.GetGetMethod());
il.Emit(OpCodes.Brfalse, endLabel);
il.Emit(OpCodes.Call, member.Nullable_Value.GetGetMethod());
}
// TODO: impl the rest of this
// 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
}
// TODO: impl the rest of this
// currently the only thing for this is where expect == Map, so do generate shit
var copyFrom = typeof(IGeneratedStore<>).MakeGenericType(member.Type).GetMethod(nameof(IGeneratedStore<Config>.CopyFrom));
var valLocal = GetLocal(member.Type);
var noCreate = il.DefineLabel();
var valLocal = GetLocal(member.Type);
if (!alwaysNew)
{
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Isinst, typeof(IGeneratedStore));
il.Emit(OpCodes.Brtrue_S, endLabel); // our input is already something we like
}
il.Emit(OpCodes.Stloc, valLocal);
if (!alwaysNew)
{
EmitLoad(il, member, il => il.Emit(OpCodes.Ldarg_0));
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Isinst, typeof(IGeneratedStore));
il.Emit(OpCodes.Brtrue_S, noCreate);
il.Emit(OpCodes.Pop);
}
EmitCreateChildGenerated(il, member.Type);
il.MarkLabel(noCreate);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldloc, valLocal);
il.Emit(shouldLock ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Callvirt, copyFrom);
// TODO: impl the rest of this
if (member.IsNullable)
il.Emit(OpCodes.Newobj, member.Nullable_Construct);
il.MarkLabel(endLabel);
}
// expects the this param to be on the stack
private static void EmitMemberFix(ILGenerator il, SerializedMemberInfo member, bool shouldLock, GetLocal GetLocal)
{
if (!NeedsCorrection(member)) return;
//var local = GetLocal(member.Type);
EmitStore(il, member, il =>
{
EmitLoad(il, member); // load the member
EmitCorrectMember(il, member, shouldLock, GetLocal); // correct it
});
}
#region Utility
private static void EmitLoad(ILGenerator il, SerializedMemberInfo member, Action<ILGenerator> thisarg = null)
{
@ -1347,13 +1353,22 @@ namespace IPA.Config.Stores
return typeof(Map); // default for various objects
}
private static void EmitDeserializeGeneratedValue(ILGenerator il, Type targetType, Type srcType, GetLocal GetLocal)
private static void EmitDeserializeGeneratedValue(ILGenerator il, SerializedMemberInfo member, Type srcType, GetLocal GetLocal)
{
var IGeneratedStore_Deserialize = typeof(IGeneratedStore).GetMethod(nameof(IGeneratedStore.Deserialize));
var valuel = GetLocal(srcType, 0);
il.Emit(OpCodes.Stloc, valuel);
EmitCreateChildGenerated(il, targetType);
var noCreate = il.DefineLabel();
il.Emit(OpCodes.Stloc, valuel);
EmitLoad(il, member, il => il.Emit(OpCodes.Ldarg_0));
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Isinst, typeof(IGeneratedStore));
il.Emit(OpCodes.Brtrue_S, noCreate);
il.Emit(OpCodes.Pop);
EmitCreateChildGenerated(il, member.Type);
il.MarkLabel(noCreate);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldloc, valuel);
il.Emit(OpCodes.Callvirt, IGeneratedStore_Deserialize);
@ -1361,13 +1376,13 @@ namespace IPA.Config.Stores
private static void EmitDeserializeNullable(ILGenerator il, SerializedMemberInfo member, Type expected, GetLocal GetLocal)
{
EmitDeserializeValue(il, member.NullableWrappedType, expected, GetLocal);
EmitDeserializeValue(il, member, member.NullableWrappedType, expected, GetLocal);
il.Emit(OpCodes.Newobj, member.Nullable_Construct);
}
// 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, Type targetType, Type expected, GetLocal GetLocal)
private static void EmitDeserializeValue(ILGenerator il, SerializedMemberInfo member, Type targetType, Type expected, GetLocal GetLocal)
{
if (typeof(Value).IsAssignableFrom(targetType)) return; // do nothing
@ -1401,7 +1416,7 @@ namespace IPA.Config.Stores
} // TODO: implement stuff for lists and maps of various types (probably call out somewhere else to figure out what to do)
else if (expected == typeof(Map))
{
EmitDeserializeGeneratedValue(il, targetType, expected, GetLocal);
EmitDeserializeGeneratedValue(il, member, expected, GetLocal);
}
else
{
@ -1540,7 +1555,7 @@ namespace IPA.Config.Stores
var local = GetLocal(member.Type, 0);
if (member.HasConverter) EmitDeserializeConverter(il, member, nextLabel, GetLocal);
else if (member.IsNullable) EmitDeserializeNullable(il, member, expectType, GetLocal);
else EmitDeserializeValue(il, member.Type, expectType, GetLocal);
else EmitDeserializeValue(il, member, member.Type, expectType, GetLocal);
il.Emit(OpCodes.Stloc, local);
EmitStore(il, member, il => il.Emit(OpCodes.Ldloc, local));
}


Loading…
Cancel
Save