Browse Source

Improved codegen for generated stores

pull/46/head
Anairkoen Schno 4 years ago
parent
commit
f3c4cc5a50
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); var GetLocal = MakeGetLocal(il);
foreach (var member in structure) 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); il.Emit(OpCodes.Pop);
@ -716,7 +720,7 @@ namespace IPA.Config.Stores
EmitStore(il, member, il => EmitStore(il, member, il =>
{ {
EmitLoad(il, member, il => il.Emit(OpCodes.Ldarg_1)); EmitLoad(il, member, il => il.Emit(OpCodes.Ldarg_1));
EmitCorrectMember(il, member, false, GetLocal);
EmitCorrectMember(il, member, false, false, GetLocal);
}); });
il.BeginCatchBlock(typeof(Exception)); il.BeginCatchBlock(typeof(Exception));
@ -930,7 +934,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, false, GetLocal);
EmitCorrectMember(il, member, false, false, GetLocal);
il.Emit(OpCodes.Call, set); il.Emit(OpCodes.Call, set);
il.BeginFinallyBlock(); il.BeginFinallyBlock();
@ -1006,7 +1010,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, GetLocal GetLocal)
private static void EmitCorrectMember(ILGenerator il, SerializedMemberInfo member, bool shouldLock, bool alwaysNew, GetLocal GetLocal)
{ {
if (!NeedsCorrection(member)) return; if (!NeedsCorrection(member)) return;
@ -1018,44 +1022,46 @@ namespace IPA.Config.Stores
il.Emit(OpCodes.Call, member.Nullable_HasValue.GetGetMethod()); il.Emit(OpCodes.Call, member.Nullable_HasValue.GetGetMethod());
il.Emit(OpCodes.Brfalse, endLabel); il.Emit(OpCodes.Brfalse, endLabel);
il.Emit(OpCodes.Call, member.Nullable_Value.GetGetMethod()); 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 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); 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); EmitCreateChildGenerated(il, member.Type);
il.MarkLabel(noCreate);
il.Emit(OpCodes.Dup); il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldloc, valLocal); il.Emit(OpCodes.Ldloc, valLocal);
il.Emit(shouldLock ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); il.Emit(shouldLock ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Callvirt, copyFrom); il.Emit(OpCodes.Callvirt, copyFrom);
// TODO: impl the rest of this
if (member.IsNullable) if (member.IsNullable)
il.Emit(OpCodes.Newobj, member.Nullable_Construct); il.Emit(OpCodes.Newobj, member.Nullable_Construct);
il.MarkLabel(endLabel); 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 #region Utility
private static void EmitLoad(ILGenerator il, SerializedMemberInfo member, Action<ILGenerator> thisarg = null) 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 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 IGeneratedStore_Deserialize = typeof(IGeneratedStore).GetMethod(nameof(IGeneratedStore.Deserialize));
var valuel = GetLocal(srcType, 0); 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.Dup);
il.Emit(OpCodes.Ldloc, valuel); il.Emit(OpCodes.Ldloc, valuel);
il.Emit(OpCodes.Callvirt, IGeneratedStore_Deserialize); 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) 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); il.Emit(OpCodes.Newobj, member.Nullable_Construct);
} }
// 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, 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 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) } // 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)) else if (expected == typeof(Map))
{ {
EmitDeserializeGeneratedValue(il, targetType, expected, GetLocal);
EmitDeserializeGeneratedValue(il, member, expected, GetLocal);
} }
else else
{ {
@ -1540,7 +1555,7 @@ namespace IPA.Config.Stores
var local = GetLocal(member.Type, 0); var local = GetLocal(member.Type, 0);
if (member.HasConverter) EmitDeserializeConverter(il, member, nextLabel, GetLocal); if (member.HasConverter) EmitDeserializeConverter(il, member, nextLabel, GetLocal);
else if (member.IsNullable) EmitDeserializeNullable(il, member, expectType, 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); il.Emit(OpCodes.Stloc, local);
EmitStore(il, member, il => il.Emit(OpCodes.Ldloc, local)); EmitStore(il, member, il => il.Emit(OpCodes.Ldloc, local));
} }


Loading…
Cancel
Save