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.

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