You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

209 lines
8.9 KiB

  1. using IPA.Config.Data;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Reflection;
  6. using System.Reflection.Emit;
  7. using System.Text;
  8. using System.Threading;
  9. using System.Threading.Tasks;
  10. namespace IPA.Config.Stores
  11. {
  12. internal static class GeneratedStore
  13. {
  14. private interface IGeneratedStore
  15. {
  16. /// <summary>
  17. /// serializes/deserializes to Value
  18. /// </summary>
  19. Value Values { get; set; }
  20. Type Type { get; }
  21. IGeneratedStore Parent { get; }
  22. Impl Impl { get; }
  23. }
  24. private class Impl : IConfigStore
  25. {
  26. private IGeneratedStore generated;
  27. internal static ConstructorInfo Ctor = typeof(Impl).GetConstructor(new[] { typeof(IGeneratedStore) });
  28. public Impl(IGeneratedStore store) => generated = store;
  29. private readonly AutoResetEvent resetEvent = new AutoResetEvent(false);
  30. public WaitHandle SyncObject => resetEvent;
  31. public ReaderWriterLockSlim WriteSyncObject { get; } = new ReaderWriterLockSlim();
  32. internal static MethodInfo ImplSignalChangedMethod = typeof(Impl).GetMethod(nameof(ImplSignalChanged));
  33. internal static void ImplSignalChanged(IGeneratedStore s) => FindImpl(s).SignalChanged();
  34. internal void SignalChanged() => resetEvent.Set();
  35. internal static MethodInfo ImplTakeReadMethod = typeof(Impl).GetMethod(nameof(ImplTakeRead));
  36. internal static void ImplTakeRead(IGeneratedStore s) => FindImpl(s).TakeRead();
  37. internal void TakeRead() => WriteSyncObject.EnterReadLock();
  38. internal static MethodInfo ImplReleaseReadMethod = typeof(Impl).GetMethod(nameof(ImplReleaseRead));
  39. internal static void ImplReleaseRead(IGeneratedStore s) => FindImpl(s).ReleaseRead();
  40. internal void ReleaseRead() => WriteSyncObject.ExitWriteLock();
  41. internal static MethodInfo ImplTakeWriteMethod = typeof(Impl).GetMethod(nameof(ImplTakeWrite));
  42. internal static void ImplTakeWrite(IGeneratedStore s) => FindImpl(s).TakeWrite();
  43. internal void TakeWrite() => WriteSyncObject.EnterWriteLock();
  44. internal static MethodInfo ImplReleaseWriteMethod = typeof(Impl).GetMethod(nameof(ImplReleaseWrite));
  45. internal static void ImplReleaseWrite(IGeneratedStore s) => FindImpl(s).ReleaseWrite();
  46. internal void ReleaseWrite() => WriteSyncObject.ExitWriteLock();
  47. internal static MethodInfo FindImplMethod = typeof(Impl).GetMethod(nameof(FindImpl));
  48. internal static Impl FindImpl(IGeneratedStore store)
  49. {
  50. while (store != null) store = store.Parent; // walk to the top of the tree
  51. return store?.Impl;
  52. }
  53. internal static MethodInfo ReadFromMethod = typeof(Impl).GetMethod(nameof(ReadFrom));
  54. public void ReadFrom(IConfigProvider provider)
  55. {
  56. // TODO: implement
  57. }
  58. internal static MethodInfo WriteToMethod = typeof(Impl).GetMethod(nameof(WriteTo));
  59. public void WriteTo(IConfigProvider provider)
  60. {
  61. var values = generated.Values;
  62. // TODO: implement
  63. }
  64. }
  65. private static Dictionary<Type, Func<IGeneratedStore, IConfigStore>> generatedCreators = new Dictionary<Type, Func<IGeneratedStore, IConfigStore>>();
  66. private static Dictionary<Type, Dictionary<string, Type>> memberMaps = new Dictionary<Type, Dictionary<string, Type>>();
  67. public static T Create<T>() where T : class => (T)Create(typeof(T));
  68. public static IConfigStore Create(Type type) => Create(type, null);
  69. private static IConfigStore Create(Type type, IGeneratedStore parent)
  70. {
  71. if (generatedCreators.TryGetValue(type, out var creator))
  72. return creator(parent);
  73. else
  74. {
  75. creator = MakeCreator(type);
  76. generatedCreators.Add(type, creator);
  77. return creator(parent);
  78. }
  79. }
  80. private static AssemblyBuilder assembly = null;
  81. private static AssemblyBuilder Assembly
  82. {
  83. get
  84. {
  85. if (assembly == null)
  86. {
  87. var name = new AssemblyName("IPA.Config.Generated");
  88. assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run);
  89. }
  90. return assembly;
  91. }
  92. }
  93. private static ModuleBuilder module = null;
  94. private static ModuleBuilder Module
  95. {
  96. get
  97. {
  98. if (module == null)
  99. module = Assembly.DefineDynamicModule(Assembly.GetName().Name);
  100. return module;
  101. }
  102. }
  103. private static Func<IGeneratedStore, IConfigStore> MakeCreator(Type type)
  104. {
  105. var typeBuilder = Module.DefineType($"{type.FullName}.Generated",
  106. TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Class, type);
  107. var typeField = typeBuilder.DefineField("<>_type", typeof(Type), FieldAttributes.Private | FieldAttributes.InitOnly);
  108. var implField = typeBuilder.DefineField("<>_impl", typeof(Impl), FieldAttributes.Private | FieldAttributes.InitOnly);
  109. var parentField = typeBuilder.DefineField("<>_parent", typeof(IGeneratedStore), FieldAttributes.Private | FieldAttributes.InitOnly);
  110. const MethodAttributes propertyMethodAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
  111. #region IGeneratedStore
  112. typeBuilder.AddInterfaceImplementation(typeof(IGeneratedStore));
  113. #region IGeneratedStore.Impl
  114. var implProp = typeBuilder.DefineProperty(nameof(IGeneratedStore.Impl), PropertyAttributes.None, typeof(Impl), null);
  115. var implPropGet = typeBuilder.DefineMethod($"get_{nameof(IGeneratedStore.Impl)}", propertyMethodAttr, implProp.PropertyType, Type.EmptyTypes);
  116. implProp.SetGetMethod(implPropGet);
  117. {
  118. var il = implPropGet.GetILGenerator();
  119. il.Emit(OpCodes.Ldarg_0); // load this
  120. il.Emit(OpCodes.Ldfld, implField); // load impl field
  121. il.Emit(OpCodes.Ret);
  122. }
  123. #endregion
  124. #region IGeneratedStore.Type
  125. var typeProp = typeBuilder.DefineProperty(nameof(IGeneratedStore.Type), PropertyAttributes.None, typeof(Type), null);
  126. var typePropGet = typeBuilder.DefineMethod($"get_{nameof(IGeneratedStore.Type)}", propertyMethodAttr, typeProp.PropertyType, Type.EmptyTypes);
  127. typeProp.SetGetMethod(typePropGet);
  128. {
  129. var il = typePropGet.GetILGenerator();
  130. il.Emit(OpCodes.Ldarg_0); // load this
  131. il.Emit(OpCodes.Ldfld, typeField); // load impl field
  132. il.Emit(OpCodes.Ret);
  133. }
  134. #endregion
  135. #region IGeneratedStore.Parent
  136. var parentProp = typeBuilder.DefineProperty(nameof(IGeneratedStore.Parent), PropertyAttributes.None, typeof(IGeneratedStore), null);
  137. var parentPropGet = typeBuilder.DefineMethod($"get_{nameof(IGeneratedStore.Parent)}", propertyMethodAttr, parentProp.PropertyType, Type.EmptyTypes);
  138. parentProp.SetGetMethod(parentPropGet);
  139. {
  140. var il = parentPropGet.GetILGenerator();
  141. il.Emit(OpCodes.Ldarg_0); // load this
  142. il.Emit(OpCodes.Ldfld, parentField); // load impl field
  143. il.Emit(OpCodes.Ret);
  144. }
  145. #endregion
  146. #region IGeneratedStore.Values
  147. var valuesProp = typeBuilder.DefineProperty(nameof(IGeneratedStore.Values), PropertyAttributes.None, typeof(Value), null);
  148. var valuesPropGet = typeBuilder.DefineMethod($"get_{nameof(IGeneratedStore.Values)}", propertyMethodAttr, valuesProp.PropertyType, Type.EmptyTypes);
  149. var valuesPropSet = typeBuilder.DefineMethod($"set_{nameof(IGeneratedStore.Values)}", propertyMethodAttr, null, new[] { valuesProp.PropertyType });
  150. valuesProp.SetGetMethod(valuesPropGet);
  151. valuesProp.SetSetMethod(valuesPropSet);
  152. { // this is non-locking because the only code that will call this will already own the correct lock
  153. var il = valuesPropGet.GetILGenerator();
  154. // TODO: implement get_Values
  155. il.Emit(OpCodes.Ldnull);
  156. il.Emit(OpCodes.Ret);
  157. }
  158. { // this is non-locking because the only code that will call this will already own the correct lock
  159. var il = valuesPropSet.GetILGenerator();
  160. // TODO: implement set_Values
  161. il.Emit(OpCodes.Ret);
  162. }
  163. #endregion
  164. #endregion
  165. return null;
  166. }
  167. }
  168. }