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.

484 lines
21 KiB

  1. using IPA.Config.Data;
  2. using IPA.Logging;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Reflection;
  7. using System.Reflection.Emit;
  8. using System.Text;
  9. using System.Threading;
  10. using System.Threading.Tasks;
  11. using System.Linq.Expressions;
  12. #if NET3
  13. using Net3_Proxy;
  14. using Array = Net3_Proxy.Array;
  15. #endif
  16. namespace IPA.Config.Stores
  17. {
  18. /// <summary>
  19. /// A class providing an extension for <see cref="Config"/> to make it easy to use generated
  20. /// config stores.
  21. /// </summary>
  22. public static class GeneratedStoreExtensions
  23. {
  24. /// <summary>
  25. /// Creates a generated <see cref="IConfigStore"/> of type <typeparamref name="T"/>, registers it to
  26. /// the <see cref="Config"/> object, and returns it.
  27. /// </summary>
  28. /// <remarks>
  29. /// <para>
  30. /// <typeparamref name="T"/> must be a non-<see langword="sealed"/> <see langword="class"/>.
  31. /// </para>
  32. /// <para>
  33. /// TODO: describe details of generated stores
  34. /// </para>
  35. /// </remarks>
  36. /// <typeparam name="T">the type to wrap</typeparam>
  37. /// <param name="cfg">the <see cref="Config"/> to register to</param>
  38. /// <returns>a generated instance of <typeparamref name="T"/> as a special <see cref="IConfigStore"/></returns>
  39. public static T Generated<T>(this Config cfg) where T : class
  40. {
  41. var ret = GeneratedStore.Create<T>();
  42. cfg.AddStore(ret as IConfigStore);
  43. return ret;
  44. }
  45. }
  46. internal static class GeneratedStore
  47. {
  48. private interface IGeneratedStore
  49. {
  50. /// <summary>
  51. /// serializes/deserializes to Value
  52. /// </summary>
  53. Value Values { get; set; }
  54. Type Type { get; }
  55. IGeneratedStore Parent { get; }
  56. Impl Impl { get; }
  57. }
  58. private class Impl : IConfigStore
  59. {
  60. private IGeneratedStore generated;
  61. internal static ConstructorInfo Ctor = typeof(Impl).GetConstructor(new[] { typeof(IGeneratedStore) });
  62. public Impl(IGeneratedStore store) => generated = store;
  63. private readonly AutoResetEvent resetEvent = new AutoResetEvent(false);
  64. public WaitHandle SyncObject => resetEvent;
  65. internal static MethodInfo SyncObjectGetMethod = typeof(Impl).GetProperty(nameof(SyncObject)).GetGetMethod();
  66. public ReaderWriterLockSlim WriteSyncObject { get; } = new ReaderWriterLockSlim();
  67. internal static MethodInfo WriteSyncObjectGetMethod = typeof(Impl).GetProperty(nameof(WriteSyncObject)).GetGetMethod();
  68. internal static MethodInfo ImplSignalChangedMethod = typeof(Impl).GetMethod(nameof(ImplSignalChanged));
  69. internal static void ImplSignalChanged(IGeneratedStore s) => FindImpl(s).SignalChanged();
  70. internal void SignalChanged() => resetEvent.Set();
  71. internal static MethodInfo ImplTakeReadMethod = typeof(Impl).GetMethod(nameof(ImplTakeRead));
  72. internal static void ImplTakeRead(IGeneratedStore s) => FindImpl(s).TakeRead();
  73. internal void TakeRead() => WriteSyncObject.EnterReadLock();
  74. internal static MethodInfo ImplReleaseReadMethod = typeof(Impl).GetMethod(nameof(ImplReleaseRead));
  75. internal static void ImplReleaseRead(IGeneratedStore s) => FindImpl(s).ReleaseRead();
  76. internal void ReleaseRead() => WriteSyncObject.ExitWriteLock();
  77. internal static MethodInfo ImplTakeWriteMethod = typeof(Impl).GetMethod(nameof(ImplTakeWrite));
  78. internal static void ImplTakeWrite(IGeneratedStore s) => FindImpl(s).TakeWrite();
  79. internal void TakeWrite() => WriteSyncObject.EnterWriteLock();
  80. internal static MethodInfo ImplReleaseWriteMethod = typeof(Impl).GetMethod(nameof(ImplReleaseWrite));
  81. internal static void ImplReleaseWrite(IGeneratedStore s) => FindImpl(s).ReleaseWrite();
  82. internal void ReleaseWrite() => WriteSyncObject.ExitWriteLock();
  83. internal static MethodInfo FindImplMethod = typeof(Impl).GetMethod(nameof(FindImpl));
  84. internal static Impl FindImpl(IGeneratedStore store)
  85. {
  86. while (store != null) store = store.Parent; // walk to the top of the tree
  87. return store?.Impl;
  88. }
  89. internal static MethodInfo ReadFromMethod = typeof(Impl).GetMethod(nameof(ReadFrom));
  90. public void ReadFrom(IConfigProvider provider)
  91. {
  92. // TODO: implement
  93. Logger.config.Debug("Generated impl ReadFrom");
  94. Logger.config.Debug($"Read {provider.Load()}");
  95. }
  96. internal static MethodInfo WriteToMethod = typeof(Impl).GetMethod(nameof(WriteTo));
  97. public void WriteTo(IConfigProvider provider)
  98. {
  99. var values = generated.Values;
  100. // TODO: implement
  101. Logger.config.Debug("Generated impl WriteTo");
  102. }
  103. }
  104. private static Dictionary<Type, Func<IGeneratedStore, IConfigStore>> generatedCreators = new Dictionary<Type, Func<IGeneratedStore, IConfigStore>>();
  105. private static Dictionary<Type, Dictionary<string, Type>> memberMaps = new Dictionary<Type, Dictionary<string, Type>>();
  106. public static T Create<T>() where T : class => (T)Create(typeof(T));
  107. public static IConfigStore Create(Type type) => Create(type, null);
  108. private static IConfigStore Create(Type type, IGeneratedStore parent)
  109. {
  110. if (generatedCreators.TryGetValue(type, out var creator))
  111. return creator(parent);
  112. else
  113. {
  114. creator = MakeCreator(type);
  115. generatedCreators.Add(type, creator);
  116. return creator(parent);
  117. }
  118. }
  119. private static AssemblyBuilder assembly = null;
  120. private static AssemblyBuilder Assembly
  121. {
  122. get
  123. {
  124. if (assembly == null)
  125. {
  126. var name = new AssemblyName("IPA.Config.Generated");
  127. assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave);
  128. }
  129. return assembly;
  130. }
  131. }
  132. private static ModuleBuilder module = null;
  133. private static ModuleBuilder Module
  134. {
  135. get
  136. {
  137. if (module == null)
  138. module = Assembly.DefineDynamicModule(Assembly.GetName().Name);
  139. return module;
  140. }
  141. }
  142. private struct SerializedMemberInfo
  143. {
  144. public string Name;
  145. public MemberInfo Member;
  146. public bool IsVirtual;
  147. public Type Type;
  148. }
  149. private static Func<IGeneratedStore, IConfigStore> MakeCreator(Type type)
  150. {
  151. var baseCtor = type.GetConstructor(Type.EmptyTypes); // get a default constructor
  152. if (baseCtor == null)
  153. throw new ArgumentException("Config type does not have a public parameterless constructor");
  154. var typeBuilder = Module.DefineType($"{type.FullName}.Generated",
  155. TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Class, type);
  156. var typeField = typeBuilder.DefineField("<>_type", typeof(Type), FieldAttributes.Private | FieldAttributes.InitOnly);
  157. var implField = typeBuilder.DefineField("<>_impl", typeof(Impl), FieldAttributes.Private | FieldAttributes.InitOnly);
  158. var parentField = typeBuilder.DefineField("<>_parent", typeof(IGeneratedStore), FieldAttributes.Private | FieldAttributes.InitOnly);
  159. var GetTypeFromHandle = typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle));
  160. // TODO: possibly move all of this manual IL over to Linq.Expressions
  161. #region Parse base object structure
  162. var baseChanged = type.GetMethod("Changed", BindingFlags.Public | BindingFlags.Instance, null, Type.EmptyTypes, Array.Empty<ParameterModifier>());
  163. if (baseChanged != null && !baseChanged.IsVirtual) baseChanged = null; // limit this to just the one thing
  164. var structure = new Dictionary<string, SerializedMemberInfo>();
  165. // TODO: incorporate attributes
  166. // only looks at public properties
  167. foreach (var prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public))
  168. {
  169. var smi = new SerializedMemberInfo
  170. {
  171. Name = prop.Name,
  172. Member = prop,
  173. IsVirtual = (prop.GetGetMethod(true)?.IsVirtual ?? false) ||
  174. (prop.GetSetMethod(true)?.IsVirtual ?? false),
  175. Type = prop.PropertyType
  176. };
  177. structure.Add(smi.Name, smi);
  178. }
  179. // only look at public fields
  180. foreach (var field in type.GetFields(BindingFlags.Instance | BindingFlags.Public))
  181. {
  182. var smi = new SerializedMemberInfo
  183. {
  184. Name = field.Name,
  185. Member = field,
  186. IsVirtual = false,
  187. Type = field.FieldType
  188. };
  189. structure.Add(smi.Name, smi);
  190. }
  191. #endregion
  192. #region Constructor
  193. // takes its parent
  194. var ctor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new[] { typeof(IGeneratedStore) });
  195. {
  196. var il = ctor.GetILGenerator();
  197. il.Emit(OpCodes.Ldarg_0); // keep this at bottom of stack
  198. il.Emit(OpCodes.Dup);
  199. il.Emit(OpCodes.Call, baseCtor);
  200. il.Emit(OpCodes.Dup);
  201. il.Emit(OpCodes.Ldarg_1); // load parent
  202. il.Emit(OpCodes.Stfld, parentField);
  203. il.Emit(OpCodes.Dup);
  204. il.Emit(OpCodes.Ldtoken, type);
  205. il.Emit(OpCodes.Call, GetTypeFromHandle); // effectively typeof(type)
  206. il.Emit(OpCodes.Stfld, typeField);
  207. il.Emit(OpCodes.Dup);
  208. il.Emit(OpCodes.Dup);
  209. il.Emit(OpCodes.Newobj, Impl.Ctor);
  210. il.Emit(OpCodes.Stfld, implField);
  211. foreach (var kvp in structure)
  212. EmitMemberFix(il, kvp.Value);
  213. il.Emit(OpCodes.Pop);
  214. il.Emit(OpCodes.Ret);
  215. }
  216. #endregion
  217. const MethodAttributes propertyMethodAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
  218. const MethodAttributes virtualPropertyMethodAttr = propertyMethodAttr | MethodAttributes.Virtual | MethodAttributes.Final;
  219. const MethodAttributes virtualMemberMethod = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.Final;
  220. #region IGeneratedStore
  221. typeBuilder.AddInterfaceImplementation(typeof(IGeneratedStore));
  222. var IGeneratedStore_t = typeof(IGeneratedStore);
  223. var IGeneratedStore_GetImpl = IGeneratedStore_t.GetProperty(nameof(IGeneratedStore.Impl)).GetGetMethod();
  224. var IGeneratedStore_GetType = IGeneratedStore_t.GetProperty(nameof(IGeneratedStore.Type)).GetGetMethod();
  225. var IGeneratedStore_GetParent = IGeneratedStore_t.GetProperty(nameof(IGeneratedStore.Parent)).GetGetMethod();
  226. var IGeneratedStore_GetValues = IGeneratedStore_t.GetProperty(nameof(IGeneratedStore.Values)).GetGetMethod();
  227. var IGeneratedStore_SetValues = IGeneratedStore_t.GetProperty(nameof(IGeneratedStore.Values)).GetSetMethod();
  228. #region IGeneratedStore.Impl
  229. var implProp = typeBuilder.DefineProperty(nameof(IGeneratedStore.Impl), PropertyAttributes.None, typeof(Impl), null);
  230. var implPropGet = typeBuilder.DefineMethod($"<g>{nameof(IGeneratedStore.Impl)}", virtualPropertyMethodAttr, implProp.PropertyType, Type.EmptyTypes);
  231. implProp.SetGetMethod(implPropGet);
  232. typeBuilder.DefineMethodOverride(implPropGet, IGeneratedStore_GetImpl);
  233. {
  234. var il = implPropGet.GetILGenerator();
  235. il.Emit(OpCodes.Ldarg_0); // load this
  236. il.Emit(OpCodes.Ldfld, implField); // load impl field
  237. il.Emit(OpCodes.Ret);
  238. }
  239. #endregion
  240. #region IGeneratedStore.Type
  241. var typeProp = typeBuilder.DefineProperty(nameof(IGeneratedStore.Type), PropertyAttributes.None, typeof(Type), null);
  242. var typePropGet = typeBuilder.DefineMethod($"<g>{nameof(IGeneratedStore.Type)}", virtualPropertyMethodAttr, typeProp.PropertyType, Type.EmptyTypes);
  243. typeProp.SetGetMethod(typePropGet);
  244. typeBuilder.DefineMethodOverride(typePropGet, IGeneratedStore_GetType);
  245. {
  246. var il = typePropGet.GetILGenerator();
  247. il.Emit(OpCodes.Ldarg_0); // load this
  248. il.Emit(OpCodes.Ldfld, typeField); // load impl field
  249. il.Emit(OpCodes.Ret);
  250. }
  251. #endregion
  252. #region IGeneratedStore.Parent
  253. var parentProp = typeBuilder.DefineProperty(nameof(IGeneratedStore.Parent), PropertyAttributes.None, typeof(IGeneratedStore), null);
  254. var parentPropGet = typeBuilder.DefineMethod($"<g>{nameof(IGeneratedStore.Parent)}", virtualPropertyMethodAttr, parentProp.PropertyType, Type.EmptyTypes);
  255. parentProp.SetGetMethod(parentPropGet);
  256. typeBuilder.DefineMethodOverride(parentPropGet, IGeneratedStore_GetParent);
  257. {
  258. var il = parentPropGet.GetILGenerator();
  259. il.Emit(OpCodes.Ldarg_0); // load this
  260. il.Emit(OpCodes.Ldfld, parentField); // load impl field
  261. il.Emit(OpCodes.Ret);
  262. }
  263. #endregion
  264. #region IGeneratedStore.Values
  265. var valuesProp = typeBuilder.DefineProperty(nameof(IGeneratedStore.Values), PropertyAttributes.None, typeof(Value), null);
  266. var valuesPropGet = typeBuilder.DefineMethod($"<g>{nameof(IGeneratedStore.Values)}", virtualPropertyMethodAttr, valuesProp.PropertyType, Type.EmptyTypes);
  267. var valuesPropSet = typeBuilder.DefineMethod($"<s>{nameof(IGeneratedStore.Values)}", virtualPropertyMethodAttr, null, new[] { valuesProp.PropertyType });
  268. valuesProp.SetGetMethod(valuesPropGet);
  269. typeBuilder.DefineMethodOverride(valuesPropGet, IGeneratedStore_GetValues);
  270. valuesProp.SetSetMethod(valuesPropSet);
  271. typeBuilder.DefineMethodOverride(valuesPropSet, IGeneratedStore_SetValues);
  272. { // this is non-locking because the only code that will call this will already own the correct lock
  273. var il = valuesPropGet.GetILGenerator();
  274. // TODO: implement get_Values
  275. il.Emit(OpCodes.Ldnull);
  276. il.Emit(OpCodes.Ret);
  277. }
  278. { // this is non-locking because the only code that will call this will already own the correct lock
  279. var il = valuesPropSet.GetILGenerator();
  280. // TODO: implement set_Values
  281. il.Emit(OpCodes.Ret);
  282. }
  283. #endregion
  284. #endregion
  285. #region IConfigStore
  286. typeBuilder.AddInterfaceImplementation(typeof(IConfigStore));
  287. var IConfigStore_t = typeof(IConfigStore);
  288. var IConfigStore_GetSyncObject = IConfigStore_t.GetProperty(nameof(IConfigStore.SyncObject)).GetGetMethod();
  289. var IConfigStore_GetWriteSyncObject = IConfigStore_t.GetProperty(nameof(IConfigStore.WriteSyncObject)).GetGetMethod();
  290. var IConfigStore_WriteTo = IConfigStore_t.GetMethod(nameof(IConfigStore.WriteTo));
  291. var IConfigStore_ReadFrom = IConfigStore_t.GetMethod(nameof(IConfigStore.ReadFrom));
  292. #region IConfigStore.SyncObject
  293. var syncObjProp = typeBuilder.DefineProperty(nameof(IConfigStore.SyncObject), PropertyAttributes.None, typeof(WaitHandle), null);
  294. var syncObjPropGet = typeBuilder.DefineMethod($"<g>{nameof(IConfigStore.SyncObject)}", virtualPropertyMethodAttr, syncObjProp.PropertyType, Type.EmptyTypes);
  295. syncObjProp.SetGetMethod(syncObjPropGet);
  296. typeBuilder.DefineMethodOverride(syncObjPropGet, IConfigStore_GetSyncObject);
  297. {
  298. var il = syncObjPropGet.GetILGenerator();
  299. il.Emit(OpCodes.Ldarg_0);
  300. il.Emit(OpCodes.Call, Impl.FindImplMethod);
  301. il.Emit(OpCodes.Tailcall);
  302. il.Emit(OpCodes.Call, Impl.SyncObjectGetMethod);
  303. il.Emit(OpCodes.Ret);
  304. }
  305. #endregion
  306. #region IConfigStore.WriteSyncObject
  307. var writeSyncObjProp = typeBuilder.DefineProperty(nameof(IConfigStore.WriteSyncObject), PropertyAttributes.None, typeof(WaitHandle), null);
  308. var writeSyncObjPropGet = typeBuilder.DefineMethod($"<g>{nameof(IConfigStore.WriteSyncObject)}", virtualPropertyMethodAttr, writeSyncObjProp.PropertyType, Type.EmptyTypes);
  309. writeSyncObjProp.SetGetMethod(writeSyncObjPropGet);
  310. typeBuilder.DefineMethodOverride(writeSyncObjPropGet, IConfigStore_GetWriteSyncObject);
  311. {
  312. var il = writeSyncObjPropGet.GetILGenerator();
  313. il.Emit(OpCodes.Ldarg_0);
  314. il.Emit(OpCodes.Call, Impl.FindImplMethod);
  315. il.Emit(OpCodes.Tailcall);
  316. il.Emit(OpCodes.Call, Impl.WriteSyncObjectGetMethod);
  317. il.Emit(OpCodes.Ret);
  318. }
  319. #endregion
  320. #region IConfigStore.WriteTo
  321. var writeTo = typeBuilder.DefineMethod($"<>{nameof(IConfigStore.WriteTo)}", virtualMemberMethod, null, new[] { typeof(IConfigProvider) });
  322. typeBuilder.DefineMethodOverride(writeTo, IConfigStore_WriteTo);
  323. {
  324. var il = writeTo.GetILGenerator();
  325. il.Emit(OpCodes.Ldarg_0);
  326. il.Emit(OpCodes.Call, Impl.FindImplMethod);
  327. il.Emit(OpCodes.Ldarg_1);
  328. il.Emit(OpCodes.Tailcall);
  329. il.Emit(OpCodes.Call, Impl.WriteToMethod);
  330. il.Emit(OpCodes.Ret);
  331. }
  332. #endregion
  333. #region IConfigStore.ReadFrom
  334. var readFrom = typeBuilder.DefineMethod($"<>{nameof(IConfigStore.ReadFrom)}", virtualMemberMethod, null, new[] { typeof(IConfigProvider) });
  335. typeBuilder.DefineMethodOverride(readFrom, IConfigStore_ReadFrom);
  336. {
  337. var il = writeTo.GetILGenerator();
  338. il.Emit(OpCodes.Ldarg_0);
  339. il.Emit(OpCodes.Call, Impl.FindImplMethod);
  340. il.Emit(OpCodes.Ldarg_1);
  341. il.Emit(OpCodes.Tailcall);
  342. il.Emit(OpCodes.Call, Impl.ReadFromMethod);
  343. il.Emit(OpCodes.Ret);
  344. }
  345. #endregion
  346. #endregion
  347. #region Changed
  348. var coreChanged = typeBuilder.DefineMethod(
  349. "<>Changed",
  350. MethodAttributes.Public | MethodAttributes.HideBySig,
  351. null, Type.EmptyTypes);
  352. {
  353. var il = coreChanged.GetILGenerator();
  354. il.Emit(OpCodes.Ldarg_0);
  355. il.Emit(OpCodes.Call, Impl.ImplSignalChangedMethod);
  356. il.Emit(OpCodes.Ret); // simply call our impl's SignalChanged method and return
  357. }
  358. if (baseChanged != null) {
  359. var changedMethod = typeBuilder.DefineMethod( // copy to override baseChanged
  360. baseChanged.Name,
  361. MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.HideBySig,
  362. null, Type.EmptyTypes);
  363. typeBuilder.DefineMethodOverride(changedMethod, baseChanged);
  364. {
  365. var il = changedMethod.GetILGenerator();
  366. il.Emit(OpCodes.Ldarg_0);
  367. il.Emit(OpCodes.Call, baseChanged); // call base
  368. il.Emit(OpCodes.Ldarg_0);
  369. il.Emit(OpCodes.Tailcall);
  370. il.Emit(OpCodes.Call, coreChanged); // call back to the core change method
  371. il.Emit(OpCodes.Ret);
  372. }
  373. coreChanged = changedMethod; // switch to calling this version instead of just the default
  374. }
  375. #endregion
  376. // TODO: generate overrides for all the virtual properties
  377. var genType = typeBuilder.CreateType();
  378. var parentParam = Expression.Parameter(typeof(IGeneratedStore), "parent");
  379. var creatorDel = Expression.Lambda<Func<IGeneratedStore, IConfigStore>>(
  380. Expression.New(ctor, parentParam), parentParam
  381. ).Compile();
  382. { // register a member map
  383. var dict = new Dictionary<string, Type>();
  384. foreach (var kvp in structure)
  385. dict.Add(kvp.Key, kvp.Value.Type);
  386. memberMaps.Add(type, dict);
  387. }
  388. return creatorDel;
  389. }
  390. // expects the this param to be on the stack
  391. private static void EmitMemberFix(ILGenerator il, SerializedMemberInfo member)
  392. {
  393. }
  394. }
  395. }