From fa645445b5c972d9c3a16b2cb46709f5af054484 Mon Sep 17 00:00:00 2001 From: Anairkoen Schno Date: Sat, 11 Jan 2020 17:57:50 -0600 Subject: [PATCH] Added the ability to expose CopyFrom in config --- IPA.Loader/Config/Stores/GeneratedStore.cs | 76 +++++++++++++++++----- 1 file changed, 58 insertions(+), 18 deletions(-) diff --git a/IPA.Loader/Config/Stores/GeneratedStore.cs b/IPA.Loader/Config/Stores/GeneratedStore.cs index 6b739e35..1e344d21 100644 --- a/IPA.Loader/Config/Stores/GeneratedStore.cs +++ b/IPA.Loader/Config/Stores/GeneratedStore.cs @@ -72,6 +72,11 @@ namespace IPA.Config.Stores /// object of the config structure. /// /// + /// Similarly, can declare a public or protected, + /// method CopyFrom(ConfigType) (the first parameter is the type it is defined on), which may be called to copy the properties from + /// another object of its type easily, and more importantly, as only one change. Its body will be executed after the values have been copied. + /// + /// /// TODO: describe details of generated stores /// /// @@ -120,10 +125,12 @@ namespace IPA.Config.Stores private readonly AutoResetEvent resetEvent = new AutoResetEvent(false); public WaitHandle SyncObject => resetEvent; - internal static MethodInfo SyncObjectGetMethod = typeof(Impl).GetProperty(nameof(SyncObject)).GetGetMethod(); + public static WaitHandle ImplGetSyncObject(IGeneratedStore s) => FindImpl(s).SyncObject; + internal static MethodInfo ImplGetSyncObjectMethod = typeof(Impl).GetMethod(nameof(ImplGetSyncObject)); public ReaderWriterLockSlim WriteSyncObject { get; } = new ReaderWriterLockSlim(); - internal static MethodInfo WriteSyncObjectGetMethod = typeof(Impl).GetProperty(nameof(WriteSyncObject)).GetGetMethod(); + public static ReaderWriterLockSlim ImplGetWriteSyncObject(IGeneratedStore s) => FindImpl(s)?.WriteSyncObject; + internal static MethodInfo ImplGetWriteSyncObjectMethod = typeof(Impl).GetMethod(nameof(ImplGetWriteSyncObject)); internal static MethodInfo ImplSignalChangedMethod = typeof(Impl).GetMethod(nameof(ImplSignalChanged)); public static void ImplSignalChanged(IGeneratedStore s) => FindImpl(s).SignalChanged(); @@ -149,7 +156,7 @@ namespace IPA.Config.Stores public static void ImplReleaseWrite(IGeneratedStore s) => FindImpl(s).ReleaseWrite(); public void ReleaseWrite() => WriteSyncObject.ExitWriteLock(); - internal static MethodInfo FindImplMethod = typeof(Impl).GetMethod(nameof(FindImpl)); + public static Impl FindImpl(IGeneratedStore store) { while (store?.Parent != null) store = store.Parent; // walk to the top of the tree @@ -157,8 +164,8 @@ namespace IPA.Config.Stores } - - internal static MethodInfo ReadFromMethod = typeof(Impl).GetMethod(nameof(ReadFrom)); + internal static MethodInfo ImplReadFromMethod = typeof(Impl).GetMethod(nameof(ImplReadFrom)); + public static void ImplReadFrom(IGeneratedStore s, ConfigProvider provider) => FindImpl(s).ReadFrom(provider); public void ReadFrom(ConfigProvider provider) { var values = provider.Load(); @@ -171,7 +178,8 @@ namespace IPA.Config.Stores TakeWrite(); // must take again for runtime to be happy (which is unfortunate) } - internal static MethodInfo WriteToMethod = typeof(Impl).GetMethod(nameof(WriteTo)); + internal static MethodInfo ImplWriteToMethod = typeof(Impl).GetMethod(nameof(ImplWriteTo)); + public static void ImplWriteTo(IGeneratedStore s, ConfigProvider provider) => FindImpl(s).WriteTo(provider); public void WriteTo(ConfigProvider provider) { var values = generated.Serialize(); @@ -292,9 +300,11 @@ namespace IPA.Config.Stores #region Parse base object structure var baseChanged = type.GetMethod("Changed", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, Array.Empty()); - if (baseChanged != null && !baseChanged.IsVirtual) baseChanged = null; // limit this to just the one thing + if (baseChanged != null && !baseChanged.IsVirtual) baseChanged = null; var baseOnReload = type.GetMethod("OnReload", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, Array.Empty()); - if (baseOnReload != null && !baseOnReload.IsVirtual) baseOnReload = null; // limit this to just the one thing + if (baseOnReload != null && !baseOnReload.IsVirtual) baseOnReload = null; + var baseCopyFrom = type.GetMethod("CopyFrom", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { type }, Array.Empty()); + if (baseCopyFrom != null && !baseCopyFrom.IsVirtual) baseCopyFrom = null; var structure = new List(); @@ -477,10 +487,14 @@ namespace IPA.Config.Stores EmitTypeof(il, type); il.Emit(OpCodes.Stfld, typeField); + var noImplLabel = il.DefineLabel(); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Brtrue, noImplLabel); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Newobj, Impl.Ctor); il.Emit(OpCodes.Stfld, implField); + il.MarkLabel(noImplLabel); var GetLocal = MakeGetLocal(il); @@ -735,9 +749,8 @@ namespace IPA.Config.Stores var il = syncObjPropGet.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Call, Impl.FindImplMethod); il.Emit(OpCodes.Tailcall); - il.Emit(OpCodes.Call, Impl.SyncObjectGetMethod); + il.Emit(OpCodes.Call, Impl.ImplGetSyncObjectMethod); il.Emit(OpCodes.Ret); } #endregion @@ -751,9 +764,8 @@ namespace IPA.Config.Stores var il = writeSyncObjPropGet.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Call, Impl.FindImplMethod); il.Emit(OpCodes.Tailcall); - il.Emit(OpCodes.Call, Impl.WriteSyncObjectGetMethod); + il.Emit(OpCodes.Call, Impl.ImplGetWriteSyncObjectMethod); il.Emit(OpCodes.Ret); } #endregion @@ -765,10 +777,9 @@ namespace IPA.Config.Stores var il = writeTo.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Call, Impl.FindImplMethod); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Tailcall); - il.Emit(OpCodes.Call, Impl.WriteToMethod); + il.Emit(OpCodes.Call, Impl.ImplWriteToMethod); il.Emit(OpCodes.Ret); } #endregion @@ -780,10 +791,9 @@ namespace IPA.Config.Stores var il = readFrom.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Call, Impl.FindImplMethod); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Tailcall); - il.Emit(OpCodes.Call, Impl.ReadFromMethod); + il.Emit(OpCodes.Call, Impl.ImplReadFromMethod); il.Emit(OpCodes.Ret); } #endregion @@ -826,9 +836,39 @@ namespace IPA.Config.Stores coreChanged = changedMethod; // switch to calling this version instead of just the default } - typeBuilder.DefineMethodOverride(coreChanged, IGeneratedStore_Changed); + typeBuilder.DefineMethodOverride(coreChanged, IGeneratedStore_Changed); #endregion - + + #region base.CopyFrom + if (baseCopyFrom != null) + { + var pubCopyFrom = typeBuilder.DefineMethod( + baseCopyFrom.Name, + virtualMemberMethod, + null, new[] { type }); + typeBuilder.DefineMethodOverride(pubCopyFrom, baseCopyFrom); + + { + var il = pubCopyFrom.GetILGenerator(); + + // TODO: use transactional changes + + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Call, copyFrom); // call internal + + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Call, baseCopyFrom); // call base + + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Tailcall); + il.Emit(OpCodes.Call, coreChanged); // call changed + il.Emit(OpCodes.Ret); + } + } + #endregion + #region Members foreach (var member in structure.Where(m => m.IsVirtual)) { // IsVirtual implies !IsField