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.

1047 lines
46 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. using System.Runtime.CompilerServices;
  13. using System.IO;
  14. using Boolean = IPA.Config.Data.Boolean;
  15. using System.Collections;
  16. #if NET3
  17. using Net3_Proxy;
  18. using Array = Net3_Proxy.Array;
  19. #endif
  20. [assembly: InternalsVisibleTo(IPA.Config.Stores.GeneratedStore.GeneratedAssemblyName)]
  21. namespace IPA.Config.Stores
  22. {
  23. /// <summary>
  24. /// A class providing an extension for <see cref="Config"/> to make it easy to use generated
  25. /// config stores.
  26. /// </summary>
  27. public static class GeneratedExtension
  28. {
  29. /// <summary>
  30. /// The name of the assembly that internals must be visible to to allow internal protection.
  31. /// </summary>
  32. public const string AssemblyVisibilityTarget = GeneratedStore.GeneratedAssemblyName;
  33. /// <summary>
  34. /// Creates a generated <see cref="IConfigStore"/> of type <typeparamref name="T"/>, registers it to
  35. /// the <see cref="Config"/> object, and returns it. This also forces a synchronous config load via
  36. /// <see cref="Config.LoadSync"/> if <paramref name="loadSync"/> is <see langword="true"/>.
  37. /// </summary>
  38. /// <remarks>
  39. /// <para>
  40. /// <typeparamref name="T"/> must be a public non-<see langword="sealed"/> <see langword="class"/>.
  41. /// It can also be internal, but in that case, then your assembly must have the following attribute
  42. /// to allow the generated code to reference it.
  43. /// <code>
  44. /// [assembly: InternalsVisibleTo(IPA.Config.Stores.GeneratedExtension.AssemblyVisibilityTarget)]
  45. /// </code>
  46. /// </para>
  47. /// <para>
  48. /// If the <typeparamref name="T"/> declares a <see langword="public"/> or <see langword="protected"/>, <see langword="virtual"/>
  49. /// method <c>Changed()</c>, then that method may be called to artificially signal to the runtime that the content of the object
  50. /// has changed. That method will also be called after the write locks are released when a property is set anywhere in the owning
  51. /// tree. This will only be called on the outermost generated object of the config structure, even if the change being signaled
  52. /// is somewhere deep into the tree.
  53. /// </para>
  54. /// <para>
  55. /// Similarly, <typeparamref name="T"/> can declare a <see langword="public"/> or <see langword="protected"/>, <see langword="virtual"/>
  56. /// method <c>OnReload()</c>, which will be called on the filesystem reader thread after the object has been repopulated with new data
  57. /// values. It will be called <i>after</i> the write lock for this object is released. This will only be called on the outermost generated
  58. /// object of the config structure.
  59. /// </para>
  60. /// <para>
  61. /// TODO: describe details of generated stores
  62. /// </para>
  63. /// </remarks>
  64. /// <typeparam name="T">the type to wrap</typeparam>
  65. /// <param name="cfg">the <see cref="Config"/> to register to</param>
  66. /// <param name="loadSync">whether to synchronously load the content, or trigger an async load</param>
  67. /// <returns>a generated instance of <typeparamref name="T"/> as a special <see cref="IConfigStore"/></returns>
  68. public static T Generated<T>(this Config cfg, bool loadSync = true) where T : class
  69. {
  70. var ret = GeneratedStore.Create<T>();
  71. cfg.SetStore(ret as IConfigStore);
  72. if (loadSync)
  73. cfg.LoadSync();
  74. else
  75. cfg.LoadAsync();
  76. return ret;
  77. }
  78. }
  79. internal static class GeneratedStore
  80. {
  81. internal interface IGeneratedStore
  82. {
  83. /// <summary>
  84. /// serializes/deserializes to Value
  85. /// </summary>
  86. Value Values { get; set; }
  87. Type Type { get; }
  88. IGeneratedStore Parent { get; }
  89. Impl Impl { get; }
  90. void OnReload();
  91. }
  92. internal class Impl : IConfigStore
  93. {
  94. private IGeneratedStore generated;
  95. internal static ConstructorInfo Ctor = typeof(Impl).GetConstructor(new[] { typeof(IGeneratedStore) });
  96. public Impl(IGeneratedStore store) => generated = store;
  97. private readonly AutoResetEvent resetEvent = new AutoResetEvent(false);
  98. public WaitHandle SyncObject => resetEvent;
  99. internal static MethodInfo SyncObjectGetMethod = typeof(Impl).GetProperty(nameof(SyncObject)).GetGetMethod();
  100. public ReaderWriterLockSlim WriteSyncObject { get; } = new ReaderWriterLockSlim();
  101. internal static MethodInfo WriteSyncObjectGetMethod = typeof(Impl).GetProperty(nameof(WriteSyncObject)).GetGetMethod();
  102. internal static MethodInfo ImplSignalChangedMethod = typeof(Impl).GetMethod(nameof(ImplSignalChanged));
  103. public static void ImplSignalChanged(IGeneratedStore s) => FindImpl(s).SignalChanged();
  104. public void SignalChanged() => resetEvent.Set();
  105. internal static MethodInfo ImplTakeReadMethod = typeof(Impl).GetMethod(nameof(ImplTakeRead));
  106. public static void ImplTakeRead(IGeneratedStore s) => FindImpl(s).TakeRead();
  107. public void TakeRead() => WriteSyncObject.EnterReadLock();
  108. internal static MethodInfo ImplReleaseReadMethod = typeof(Impl).GetMethod(nameof(ImplReleaseRead));
  109. public static void ImplReleaseRead(IGeneratedStore s) => FindImpl(s).ReleaseRead();
  110. public void ReleaseRead() => WriteSyncObject.ExitReadLock();
  111. internal static MethodInfo ImplTakeWriteMethod = typeof(Impl).GetMethod(nameof(ImplTakeWrite));
  112. public static void ImplTakeWrite(IGeneratedStore s) => FindImpl(s).TakeWrite();
  113. public void TakeWrite() => WriteSyncObject.EnterWriteLock();
  114. internal static MethodInfo ImplReleaseWriteMethod = typeof(Impl).GetMethod(nameof(ImplReleaseWrite));
  115. public static void ImplReleaseWrite(IGeneratedStore s) => FindImpl(s).ReleaseWrite();
  116. public void ReleaseWrite() => WriteSyncObject.ExitWriteLock();
  117. internal static MethodInfo FindImplMethod = typeof(Impl).GetMethod(nameof(FindImpl));
  118. public static Impl FindImpl(IGeneratedStore store)
  119. {
  120. while (store?.Parent != null) store = store.Parent; // walk to the top of the tree
  121. return store?.Impl;
  122. }
  123. internal static MethodInfo ReadFromMethod = typeof(Impl).GetMethod(nameof(ReadFrom));
  124. public void ReadFrom(IConfigProvider provider)
  125. {
  126. var values = provider.Load();
  127. Logger.config.Debug("Generated impl ReadFrom");
  128. Logger.config.Debug($"Read {values}");
  129. generated.Values = values;
  130. ReleaseWrite();
  131. generated.OnReload();
  132. TakeWrite(); // must take again for runtime to be happy (which is unfortunate)
  133. }
  134. internal static MethodInfo WriteToMethod = typeof(Impl).GetMethod(nameof(WriteTo));
  135. public void WriteTo(IConfigProvider provider)
  136. {
  137. var values = generated.Values;
  138. Logger.config.Debug("Generated impl WriteTo");
  139. Logger.config.Debug($"Serialized {values}");
  140. provider.Store(values);
  141. }
  142. }
  143. private static Dictionary<Type, Func<IGeneratedStore, IConfigStore>> generatedCreators = new Dictionary<Type, Func<IGeneratedStore, IConfigStore>>();
  144. private static Dictionary<Type, Dictionary<string, Type>> memberMaps = new Dictionary<Type, Dictionary<string, Type>>();
  145. public static T Create<T>() where T : class => (T)Create(typeof(T));
  146. public static IConfigStore Create(Type type) => Create(type, null);
  147. private static readonly MethodInfo CreateGParent =
  148. typeof(GeneratedStore).GetMethod(nameof(Create), BindingFlags.NonPublic | BindingFlags.Static, null,
  149. CallingConventions.Any, new[] { typeof(IGeneratedStore) }, Array.Empty<ParameterModifier>());
  150. internal static T Create<T>(IGeneratedStore parent) where T : class => (T)Create(typeof(T), parent);
  151. private static IConfigStore Create(Type type, IGeneratedStore parent)
  152. {
  153. if (generatedCreators.TryGetValue(type, out var creator))
  154. return creator(parent);
  155. else
  156. {
  157. creator = MakeCreator(type);
  158. generatedCreators.Add(type, creator);
  159. return creator(parent);
  160. }
  161. }
  162. internal const string GeneratedAssemblyName = "IPA.Config.Generated";
  163. private static AssemblyBuilder assembly = null;
  164. private static AssemblyBuilder Assembly
  165. {
  166. get
  167. {
  168. if (assembly == null)
  169. {
  170. var name = new AssemblyName(GeneratedAssemblyName);
  171. assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave);
  172. }
  173. return assembly;
  174. }
  175. }
  176. internal static void DebugSaveAssembly(string file)
  177. {
  178. Assembly.Save(file);
  179. }
  180. private static ModuleBuilder module = null;
  181. private static ModuleBuilder Module
  182. {
  183. get
  184. {
  185. if (module == null)
  186. module = Assembly.DefineDynamicModule(Assembly.GetName().Name, Assembly.GetName().Name + ".dll");
  187. return module;
  188. }
  189. }
  190. private struct SerializedMemberInfo
  191. {
  192. public string Name;
  193. public MemberInfo Member;
  194. public bool IsVirtual;
  195. public bool IsField;
  196. public Type Type;
  197. }
  198. private static Func<IGeneratedStore, IConfigStore> MakeCreator(Type type)
  199. {
  200. var baseCtor = type.GetConstructor(Type.EmptyTypes); // get a default constructor
  201. if (baseCtor == null)
  202. throw new ArgumentException("Config type does not have a public parameterless constructor");
  203. var typeBuilder = Module.DefineType($"{type.FullName}<Generated>",
  204. TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Class, type);
  205. var typeField = typeBuilder.DefineField("<>_type", typeof(Type), FieldAttributes.Private | FieldAttributes.InitOnly);
  206. var implField = typeBuilder.DefineField("<>_impl", typeof(Impl), FieldAttributes.Private | FieldAttributes.InitOnly);
  207. var parentField = typeBuilder.DefineField("<>_parent", typeof(IGeneratedStore), FieldAttributes.Private | FieldAttributes.InitOnly);
  208. // none of this can be Expressions because CompileToMethod requires a static target method for some dumbass reason
  209. #region Parse base object structure
  210. var baseChanged = type.GetMethod("Changed", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, Array.Empty<ParameterModifier>());
  211. if (baseChanged != null && !baseChanged.IsVirtual) baseChanged = null; // limit this to just the one thing
  212. var baseOnReload = type.GetMethod("OnReload", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, Array.Empty<ParameterModifier>());
  213. if (baseOnReload != null && !baseOnReload.IsVirtual) baseOnReload = null; // limit this to just the one thing
  214. var structure = new List<SerializedMemberInfo>();
  215. // TODO: incorporate attributes/base types
  216. // TODO: ignore probs without setter
  217. // only looks at public properties
  218. foreach (var prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public))
  219. {
  220. var smi = new SerializedMemberInfo
  221. {
  222. Name = prop.Name,
  223. Member = prop,
  224. IsVirtual = (prop.GetGetMethod(true)?.IsVirtual ?? false) ||
  225. (prop.GetSetMethod(true)?.IsVirtual ?? false),
  226. IsField = false,
  227. Type = prop.PropertyType
  228. };
  229. structure.Add(smi);
  230. }
  231. // only look at public fields
  232. foreach (var field in type.GetFields(BindingFlags.Instance | BindingFlags.Public))
  233. {
  234. var smi = new SerializedMemberInfo
  235. {
  236. Name = field.Name,
  237. Member = field,
  238. IsVirtual = false,
  239. IsField = true,
  240. Type = field.FieldType
  241. };
  242. structure.Add(smi);
  243. }
  244. #endregion
  245. #region Constructor
  246. var ctor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new[] { typeof(IGeneratedStore) });
  247. { // because this is a constructor, it has to be raw IL
  248. var il = ctor.GetILGenerator();
  249. il.Emit(OpCodes.Ldarg_0); // keep this at bottom of stack
  250. il.Emit(OpCodes.Dup);
  251. il.Emit(OpCodes.Call, baseCtor);
  252. il.Emit(OpCodes.Dup);
  253. il.Emit(OpCodes.Ldarg_1); // load parent
  254. il.Emit(OpCodes.Stfld, parentField);
  255. il.Emit(OpCodes.Dup);
  256. EmitTypeof(il, type);
  257. il.Emit(OpCodes.Stfld, typeField);
  258. il.Emit(OpCodes.Dup);
  259. il.Emit(OpCodes.Dup);
  260. il.Emit(OpCodes.Newobj, Impl.Ctor);
  261. il.Emit(OpCodes.Stfld, implField);
  262. foreach (var member in structure)
  263. EmitMemberFix(il, member);
  264. il.Emit(OpCodes.Pop);
  265. il.Emit(OpCodes.Ret);
  266. }
  267. #endregion
  268. const MethodAttributes propertyMethodAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
  269. const MethodAttributes virtualPropertyMethodAttr = propertyMethodAttr | MethodAttributes.Virtual | MethodAttributes.Final;
  270. const MethodAttributes virtualMemberMethod = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.Final;
  271. #region IGeneratedStore
  272. typeBuilder.AddInterfaceImplementation(typeof(IGeneratedStore));
  273. var IGeneratedStore_t = typeof(IGeneratedStore);
  274. var IGeneratedStore_GetImpl = IGeneratedStore_t.GetProperty(nameof(IGeneratedStore.Impl)).GetGetMethod();
  275. var IGeneratedStore_GetType = IGeneratedStore_t.GetProperty(nameof(IGeneratedStore.Type)).GetGetMethod();
  276. var IGeneratedStore_GetParent = IGeneratedStore_t.GetProperty(nameof(IGeneratedStore.Parent)).GetGetMethod();
  277. var IGeneratedStore_GetValues = IGeneratedStore_t.GetProperty(nameof(IGeneratedStore.Values)).GetGetMethod();
  278. var IGeneratedStore_SetValues = IGeneratedStore_t.GetProperty(nameof(IGeneratedStore.Values)).GetSetMethod();
  279. var IGeneratedStore_OnReload = IGeneratedStore_t.GetMethod(nameof(IGeneratedStore.OnReload));
  280. #region IGeneratedStore.OnReload
  281. var onReload = typeBuilder.DefineMethod($"<>{nameof(IGeneratedStore.OnReload)}", virtualMemberMethod, null, Type.EmptyTypes);
  282. typeBuilder.DefineMethodOverride(onReload, IGeneratedStore_OnReload);
  283. if (baseOnReload != null) typeBuilder.DefineMethodOverride(onReload, baseOnReload);
  284. {
  285. var il = onReload.GetILGenerator();
  286. if (baseOnReload != null)
  287. {
  288. il.Emit(OpCodes.Ldarg_0); // load this
  289. il.Emit(OpCodes.Tailcall);
  290. il.Emit(OpCodes.Call, baseOnReload); // load impl field
  291. }
  292. il.Emit(OpCodes.Ret);
  293. }
  294. #endregion
  295. #region IGeneratedStore.Impl
  296. var implProp = typeBuilder.DefineProperty(nameof(IGeneratedStore.Impl), PropertyAttributes.None, typeof(Impl), null);
  297. var implPropGet = typeBuilder.DefineMethod($"<g>{nameof(IGeneratedStore.Impl)}", virtualPropertyMethodAttr, implProp.PropertyType, Type.EmptyTypes);
  298. implProp.SetGetMethod(implPropGet);
  299. typeBuilder.DefineMethodOverride(implPropGet, IGeneratedStore_GetImpl);
  300. {
  301. var il = implPropGet.GetILGenerator();
  302. il.Emit(OpCodes.Ldarg_0); // load this
  303. il.Emit(OpCodes.Ldfld, implField); // load impl field
  304. il.Emit(OpCodes.Ret);
  305. }
  306. #endregion
  307. #region IGeneratedStore.Type
  308. var typeProp = typeBuilder.DefineProperty(nameof(IGeneratedStore.Type), PropertyAttributes.None, typeof(Type), null);
  309. var typePropGet = typeBuilder.DefineMethod($"<g>{nameof(IGeneratedStore.Type)}", virtualPropertyMethodAttr, typeProp.PropertyType, Type.EmptyTypes);
  310. typeProp.SetGetMethod(typePropGet);
  311. typeBuilder.DefineMethodOverride(typePropGet, IGeneratedStore_GetType);
  312. {
  313. var il = typePropGet.GetILGenerator();
  314. il.Emit(OpCodes.Ldarg_0); // load this
  315. il.Emit(OpCodes.Ldfld, typeField); // load impl field
  316. il.Emit(OpCodes.Ret);
  317. }
  318. #endregion
  319. #region IGeneratedStore.Parent
  320. var parentProp = typeBuilder.DefineProperty(nameof(IGeneratedStore.Parent), PropertyAttributes.None, typeof(IGeneratedStore), null);
  321. var parentPropGet = typeBuilder.DefineMethod($"<g>{nameof(IGeneratedStore.Parent)}", virtualPropertyMethodAttr, parentProp.PropertyType, Type.EmptyTypes);
  322. parentProp.SetGetMethod(parentPropGet);
  323. typeBuilder.DefineMethodOverride(parentPropGet, IGeneratedStore_GetParent);
  324. {
  325. var il = parentPropGet.GetILGenerator();
  326. il.Emit(OpCodes.Ldarg_0); // load this
  327. il.Emit(OpCodes.Ldfld, parentField); // load impl field
  328. il.Emit(OpCodes.Ret);
  329. }
  330. #endregion
  331. #region IGeneratedStore.Values
  332. var valuesProp = typeBuilder.DefineProperty(nameof(IGeneratedStore.Values), PropertyAttributes.None, typeof(Value), null);
  333. var valuesPropGet = typeBuilder.DefineMethod($"<g>{nameof(IGeneratedStore.Values)}", virtualPropertyMethodAttr, valuesProp.PropertyType, Type.EmptyTypes);
  334. var valuesPropSet = typeBuilder.DefineMethod($"<s>{nameof(IGeneratedStore.Values)}", virtualPropertyMethodAttr, null, new[] { valuesProp.PropertyType });
  335. valuesProp.SetGetMethod(valuesPropGet);
  336. typeBuilder.DefineMethodOverride(valuesPropGet, IGeneratedStore_GetValues);
  337. valuesProp.SetSetMethod(valuesPropSet);
  338. typeBuilder.DefineMethodOverride(valuesPropSet, IGeneratedStore_SetValues);
  339. { // this is non-locking because the only code that will call this will already own the correct lock
  340. var il = valuesPropGet.GetILGenerator();
  341. var Map_Add = typeof(Map).GetMethod(nameof(Map.Add));
  342. il.Emit(OpCodes.Call, typeof(Value).GetMethod(nameof(Value.Map)));
  343. // the map is now at the top of the stack
  344. var locals = new List<LocalBuilder>();
  345. LocalBuilder GetLocal(Type ty, int i = 0)
  346. {
  347. var builder = locals.Where(b => b.LocalType == ty).Skip(i).FirstOrDefault();
  348. if (builder == null)
  349. {
  350. builder = il.DeclareLocal(ty);
  351. locals.Add(builder);
  352. }
  353. return builder;
  354. }
  355. foreach (var member in structure)
  356. {
  357. il.Emit(OpCodes.Dup);
  358. il.Emit(OpCodes.Ldstr, member.Name); // TODO: make this behave with annotations
  359. EmitSerializeMember(il, member, GetLocal);
  360. il.Emit(OpCodes.Call, Map_Add);
  361. }
  362. // the map is still at the top of the stack, return it
  363. il.Emit(OpCodes.Ret);
  364. }
  365. { // this is non-locking because the only code that will call this will already own the correct lock
  366. var il = valuesPropSet.GetILGenerator();
  367. var Map_t = typeof(Map);
  368. var Map_TryGetValue = Map_t.GetMethod(nameof(Map.TryGetValue));
  369. var Object_GetType = typeof(object).GetMethod(nameof(Object.GetType));
  370. var valueLocal = il.DeclareLocal(typeof(Value));
  371. var nonNull = il.DefineLabel();
  372. il.Emit(OpCodes.Ldarg_1);
  373. il.Emit(OpCodes.Brtrue, nonNull);
  374. EmitLogError(il, "Attempting to deserialize null", tailcall: true);
  375. il.Emit(OpCodes.Ret);
  376. il.MarkLabel(nonNull);
  377. il.Emit(OpCodes.Ldarg_1);
  378. il.Emit(OpCodes.Isinst, Map_t);
  379. il.Emit(OpCodes.Dup); // duplicate cloned value
  380. var notMapError = il.DefineLabel();
  381. il.Emit(OpCodes.Brtrue, notMapError);
  382. // handle error
  383. il.Emit(OpCodes.Pop); // removes the duplicate value
  384. EmitLogError(il, $"Invalid root for deserializing {type.FullName}", tailcall: true,
  385. expected: il => EmitTypeof(il, Map_t), found: il =>
  386. {
  387. il.Emit(OpCodes.Ldarg_1);
  388. il.Emit(OpCodes.Callvirt, Object_GetType);
  389. });
  390. il.Emit(OpCodes.Ret);
  391. var nextLabel = notMapError;
  392. var locals = new List<LocalBuilder>();
  393. LocalBuilder GetLocal(Type ty, int i = 0)
  394. {
  395. var builder = locals.Where(b => b.LocalType == ty).Skip(i).FirstOrDefault();
  396. if (builder == null)
  397. {
  398. builder = il.DeclareLocal(ty);
  399. locals.Add(builder);
  400. }
  401. return builder;
  402. }
  403. // head of stack is Map instance
  404. foreach (var member in structure)
  405. {
  406. il.MarkLabel(nextLabel);
  407. nextLabel = il.DefineLabel();
  408. var endErrorLabel = il.DefineLabel();
  409. il.Emit(OpCodes.Dup);
  410. il.Emit(OpCodes.Ldstr, member.Name);
  411. il.Emit(OpCodes.Ldloca_S, valueLocal);
  412. il.Emit(OpCodes.Call, Map_TryGetValue);
  413. il.Emit(OpCodes.Brtrue_S, endErrorLabel);
  414. EmitLogError(il, $"Missing key {member.Name}", tailcall: false);
  415. il.Emit(OpCodes.Br, nextLabel);
  416. il.MarkLabel(endErrorLabel);
  417. il.Emit(OpCodes.Ldloc_S, valueLocal);
  418. EmitDeserializeMember(il, member, nextLabel, il => il.Emit(OpCodes.Ldloc_S, valueLocal), GetLocal);
  419. }
  420. il.MarkLabel(nextLabel);
  421. il.Emit(OpCodes.Pop); // removes the duplicate value
  422. il.Emit(OpCodes.Ret);
  423. }
  424. #endregion
  425. #endregion
  426. #region IConfigStore
  427. typeBuilder.AddInterfaceImplementation(typeof(IConfigStore));
  428. var IConfigStore_t = typeof(IConfigStore);
  429. var IConfigStore_GetSyncObject = IConfigStore_t.GetProperty(nameof(IConfigStore.SyncObject)).GetGetMethod();
  430. var IConfigStore_GetWriteSyncObject = IConfigStore_t.GetProperty(nameof(IConfigStore.WriteSyncObject)).GetGetMethod();
  431. var IConfigStore_WriteTo = IConfigStore_t.GetMethod(nameof(IConfigStore.WriteTo));
  432. var IConfigStore_ReadFrom = IConfigStore_t.GetMethod(nameof(IConfigStore.ReadFrom));
  433. #region IConfigStore.SyncObject
  434. var syncObjProp = typeBuilder.DefineProperty(nameof(IConfigStore.SyncObject), PropertyAttributes.None, typeof(WaitHandle), null);
  435. var syncObjPropGet = typeBuilder.DefineMethod($"<g>{nameof(IConfigStore.SyncObject)}", virtualPropertyMethodAttr, syncObjProp.PropertyType, Type.EmptyTypes);
  436. syncObjProp.SetGetMethod(syncObjPropGet);
  437. typeBuilder.DefineMethodOverride(syncObjPropGet, IConfigStore_GetSyncObject);
  438. {
  439. var il = syncObjPropGet.GetILGenerator();
  440. il.Emit(OpCodes.Ldarg_0);
  441. il.Emit(OpCodes.Call, Impl.FindImplMethod);
  442. il.Emit(OpCodes.Tailcall);
  443. il.Emit(OpCodes.Call, Impl.SyncObjectGetMethod);
  444. il.Emit(OpCodes.Ret);
  445. }
  446. #endregion
  447. #region IConfigStore.WriteSyncObject
  448. var writeSyncObjProp = typeBuilder.DefineProperty(nameof(IConfigStore.WriteSyncObject), PropertyAttributes.None, typeof(WaitHandle), null);
  449. var writeSyncObjPropGet = typeBuilder.DefineMethod($"<g>{nameof(IConfigStore.WriteSyncObject)}", virtualPropertyMethodAttr, writeSyncObjProp.PropertyType, Type.EmptyTypes);
  450. writeSyncObjProp.SetGetMethod(writeSyncObjPropGet);
  451. typeBuilder.DefineMethodOverride(writeSyncObjPropGet, IConfigStore_GetWriteSyncObject);
  452. {
  453. var il = writeSyncObjPropGet.GetILGenerator();
  454. il.Emit(OpCodes.Ldarg_0);
  455. il.Emit(OpCodes.Call, Impl.FindImplMethod);
  456. il.Emit(OpCodes.Tailcall);
  457. il.Emit(OpCodes.Call, Impl.WriteSyncObjectGetMethod);
  458. il.Emit(OpCodes.Ret);
  459. }
  460. #endregion
  461. #region IConfigStore.WriteTo
  462. var writeTo = typeBuilder.DefineMethod($"<>{nameof(IConfigStore.WriteTo)}", virtualMemberMethod, null, new[] { typeof(IConfigProvider) });
  463. typeBuilder.DefineMethodOverride(writeTo, IConfigStore_WriteTo);
  464. {
  465. var il = writeTo.GetILGenerator();
  466. il.Emit(OpCodes.Ldarg_0);
  467. il.Emit(OpCodes.Call, Impl.FindImplMethod);
  468. il.Emit(OpCodes.Ldarg_1);
  469. il.Emit(OpCodes.Tailcall);
  470. il.Emit(OpCodes.Call, Impl.WriteToMethod);
  471. il.Emit(OpCodes.Ret);
  472. }
  473. #endregion
  474. #region IConfigStore.ReadFrom
  475. var readFrom = typeBuilder.DefineMethod($"<>{nameof(IConfigStore.ReadFrom)}", virtualMemberMethod, null, new[] { typeof(IConfigProvider) });
  476. typeBuilder.DefineMethodOverride(readFrom, IConfigStore_ReadFrom);
  477. {
  478. var il = readFrom.GetILGenerator();
  479. il.Emit(OpCodes.Ldarg_0);
  480. il.Emit(OpCodes.Call, Impl.FindImplMethod);
  481. il.Emit(OpCodes.Ldarg_1);
  482. il.Emit(OpCodes.Tailcall);
  483. il.Emit(OpCodes.Call, Impl.ReadFromMethod);
  484. il.Emit(OpCodes.Ret);
  485. }
  486. #endregion
  487. #endregion
  488. #region Changed
  489. var coreChanged = typeBuilder.DefineMethod(
  490. "<>Changed",
  491. MethodAttributes.Public | MethodAttributes.HideBySig,
  492. null, Type.EmptyTypes);
  493. {
  494. var il = coreChanged.GetILGenerator();
  495. il.Emit(OpCodes.Ldarg_0);
  496. il.Emit(OpCodes.Call, Impl.ImplSignalChangedMethod);
  497. il.Emit(OpCodes.Ret); // simply call our impl's SignalChanged method and return
  498. }
  499. if (baseChanged != null) {
  500. var changedMethod = typeBuilder.DefineMethod( // copy to override baseChanged
  501. baseChanged.Name,
  502. MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.HideBySig,
  503. null, Type.EmptyTypes);
  504. typeBuilder.DefineMethodOverride(changedMethod, baseChanged);
  505. {
  506. var il = changedMethod.GetILGenerator();
  507. il.Emit(OpCodes.Ldarg_0);
  508. il.Emit(OpCodes.Call, baseChanged); // call base
  509. il.Emit(OpCodes.Ldarg_0);
  510. il.Emit(OpCodes.Tailcall);
  511. il.Emit(OpCodes.Call, coreChanged); // call back to the core change method
  512. il.Emit(OpCodes.Ret);
  513. }
  514. coreChanged = changedMethod; // switch to calling this version instead of just the default
  515. }
  516. #endregion
  517. // TODO: generate overrides for all the virtual properties
  518. var genType = typeBuilder.CreateType();
  519. var parentParam = Expression.Parameter(typeof(IGeneratedStore), "parent");
  520. var creatorDel = Expression.Lambda<Func<IGeneratedStore, IConfigStore>>(
  521. Expression.New(ctor, parentParam), parentParam
  522. ).Compile();
  523. { // register a member map
  524. var dict = new Dictionary<string, Type>();
  525. foreach (var member in structure)
  526. dict.Add(member.Name, member.Type);
  527. memberMaps.Add(type, dict);
  528. }
  529. return creatorDel;
  530. }
  531. #region Utility
  532. private static void EmitLogError(ILGenerator il, string message, bool tailcall = false, Action<ILGenerator> expected = null, Action<ILGenerator> found = null)
  533. {
  534. if (expected == null) expected = il => il.Emit(OpCodes.Ldnull);
  535. if (found == null) found = il => il.Emit(OpCodes.Ldnull);
  536. expected(il);
  537. found(il);
  538. il.Emit(OpCodes.Ldstr, message);
  539. if (tailcall) il.Emit(OpCodes.Tailcall);
  540. il.Emit(OpCodes.Call, LogErrorMethod);
  541. }
  542. private static readonly MethodInfo Type_GetTypeFromHandle = typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle));
  543. private static void EmitTypeof(ILGenerator il, Type type)
  544. {
  545. il.Emit(OpCodes.Ldtoken, type);
  546. il.Emit(OpCodes.Call, Type_GetTypeFromHandle);
  547. }
  548. private static Type Decimal_t = typeof(decimal);
  549. private static ConstructorInfo Decimal_FromFloat = Decimal_t.GetConstructor(new[] { typeof(float) });
  550. private static ConstructorInfo Decimal_FromDouble = Decimal_t.GetConstructor(new[] { typeof(double) });
  551. private static ConstructorInfo Decimal_FromInt = Decimal_t.GetConstructor(new[] { typeof(int) });
  552. private static ConstructorInfo Decimal_FromUInt = Decimal_t.GetConstructor(new[] { typeof(uint) });
  553. private static ConstructorInfo Decimal_FromLong = Decimal_t.GetConstructor(new[] { typeof(long) });
  554. private static ConstructorInfo Decimal_FromULong = Decimal_t.GetConstructor(new[] { typeof(ulong) });
  555. private static void EmitNumberConvertTo(ILGenerator il, Type to, Type from)
  556. { // WARNING: THIS USES THE NO-OVERFLOW OPCODES
  557. if (to == from) return;
  558. if (to == Decimal_t)
  559. {
  560. if (from == typeof(float)) il.Emit(OpCodes.Newobj, Decimal_FromFloat);
  561. else if (from == typeof(double)) il.Emit(OpCodes.Newobj, Decimal_FromDouble);
  562. else if (from == typeof(long)) il.Emit(OpCodes.Newobj, Decimal_FromLong);
  563. else if (from == typeof(ulong)) il.Emit(OpCodes.Newobj, Decimal_FromULong);
  564. else if (from == typeof(int)) il.Emit(OpCodes.Newobj, Decimal_FromInt);
  565. else if (from == typeof(uint)) il.Emit(OpCodes.Newobj, Decimal_FromUInt);
  566. else if (from == typeof(IntPtr))
  567. {
  568. EmitNumberConvertTo(il, typeof(long), from);
  569. EmitNumberConvertTo(il, to, typeof(long));
  570. }
  571. else if (from == typeof(UIntPtr))
  572. {
  573. EmitNumberConvertTo(il, typeof(ulong), from);
  574. EmitNumberConvertTo(il, to, typeof(ulong));
  575. }
  576. else
  577. { // if the source is anything else, we first convert to int because that can contain all other values
  578. EmitNumberConvertTo(il, typeof(int), from);
  579. EmitNumberConvertTo(il, to, typeof(int));
  580. };
  581. }
  582. else if (from == Decimal_t)
  583. {
  584. if (to == typeof(IntPtr))
  585. {
  586. EmitNumberConvertTo(il, typeof(long), from);
  587. EmitNumberConvertTo(il, to, typeof(long));
  588. }
  589. else if (to == typeof(UIntPtr))
  590. {
  591. EmitNumberConvertTo(il, typeof(ulong), from);
  592. EmitNumberConvertTo(il, to, typeof(ulong));
  593. }
  594. else
  595. {
  596. var method = Decimal_t.GetMethod($"To{to.Name}"); // conveniently, this is the pattern of the to* names
  597. il.Emit(OpCodes.Call, method);
  598. }
  599. }
  600. else if (to == typeof(IntPtr)) il.Emit(OpCodes.Conv_I);
  601. else if (to == typeof(UIntPtr)) il.Emit(OpCodes.Conv_U);
  602. else if (to == typeof(sbyte)) il.Emit(OpCodes.Conv_I1);
  603. else if (to == typeof(byte)) il.Emit(OpCodes.Conv_U1);
  604. else if (to == typeof(short)) il.Emit(OpCodes.Conv_I2);
  605. else if (to == typeof(ushort)) il.Emit(OpCodes.Conv_U2);
  606. else if (to == typeof(int)) il.Emit(OpCodes.Conv_I4);
  607. else if (to == typeof(uint)) il.Emit(OpCodes.Conv_U4);
  608. else if (to == typeof(long)) il.Emit(OpCodes.Conv_I8);
  609. else if (to == typeof(ulong)) il.Emit(OpCodes.Conv_U8);
  610. else if (to == typeof(float))
  611. {
  612. if (from == typeof(byte)
  613. || from == typeof(ushort)
  614. || from == typeof(uint)
  615. || from == typeof(ulong)
  616. || from == typeof(UIntPtr)) il.Emit(OpCodes.Conv_R_Un);
  617. il.Emit(OpCodes.Conv_R4);
  618. }
  619. else if (to == typeof(double))
  620. {
  621. if (from == typeof(byte)
  622. || from == typeof(ushort)
  623. || from == typeof(uint)
  624. || from == typeof(ulong)
  625. || from == typeof(UIntPtr)) il.Emit(OpCodes.Conv_R_Un);
  626. il.Emit(OpCodes.Conv_R8);
  627. }
  628. }
  629. private static void EmitCreateChildGenerated(ILGenerator il, Type childType)
  630. {
  631. var method = CreateGParent.MakeGenericMethod(childType);
  632. il.Emit(OpCodes.Ldarg_0);
  633. il.Emit(OpCodes.Call, method);
  634. }
  635. #endregion
  636. private static readonly MethodInfo LogErrorMethod = typeof(GeneratedStore).GetMethod(nameof(LogError), BindingFlags.NonPublic | BindingFlags.Static);
  637. internal static void LogError(Type expected, Type found, string message)
  638. {
  639. Logger.config.Notice($"{message}{(expected == null ? "" : $" (expected {expected}, found {found?.ToString() ?? "null"})")}");
  640. }
  641. // expects the this param to be on the stack
  642. private static void EmitMemberFix(ILGenerator il, SerializedMemberInfo member)
  643. {
  644. // TODO: impl
  645. }
  646. #region Serialize
  647. // emit takes no args, leaves Value at top of stack
  648. private static void EmitSerializeMember(ILGenerator il, SerializedMemberInfo member, Func<Type, int, LocalBuilder> GetLocal)
  649. {
  650. void EmitLoad()
  651. {
  652. il.Emit(OpCodes.Ldarg_0); // load this
  653. if (member.IsField)
  654. il.Emit(OpCodes.Ldfld, member.Member as FieldInfo);
  655. else
  656. { // member is a property
  657. var prop = member.Member as PropertyInfo;
  658. var getter = prop.GetGetMethod();
  659. if (getter == null) throw new InvalidOperationException($"Property {member.Name} does not have a getter and is not ignored");
  660. il.Emit(OpCodes.Call, getter);
  661. }
  662. }
  663. // TODO: implement Nullable<T>
  664. EmitLoad();
  665. var endSerialize = il.DefineLabel();
  666. if (!member.Type.IsValueType)
  667. {
  668. var passedNull = il.DefineLabel();
  669. il.Emit(OpCodes.Dup);
  670. il.Emit(OpCodes.Brtrue, passedNull);
  671. il.Emit(OpCodes.Pop);
  672. il.Emit(OpCodes.Ldnull);
  673. il.Emit(OpCodes.Br, endSerialize);
  674. il.MarkLabel(passedNull);
  675. }
  676. var targetType = GetExpectedValueTypeForType(member.Type);
  677. if (targetType == typeof(Text))
  678. { // only happens when arg is a string or char
  679. var TextCreate = typeof(Value).GetMethod(nameof(Value.Text));
  680. if (member.Type == typeof(char))
  681. {
  682. var strFromChar = typeof(char).GetMethod(nameof(char.ToString), new[] { typeof(char) });
  683. il.Emit(OpCodes.Call, strFromChar);
  684. }
  685. il.Emit(OpCodes.Call, TextCreate);
  686. }
  687. else if (targetType == typeof(Boolean))
  688. {
  689. var BoolCreate = typeof(Value).GetMethod(nameof(Value.Bool));
  690. il.Emit(OpCodes.Call, BoolCreate);
  691. }
  692. else if (targetType == typeof(Integer))
  693. {
  694. var IntCreate = typeof(Value).GetMethod(nameof(Value.Integer));
  695. EmitNumberConvertTo(il, IntCreate.GetParameters()[0].ParameterType, member.Type);
  696. il.Emit(OpCodes.Call, IntCreate);
  697. }
  698. else if (targetType == typeof(FloatingPoint))
  699. {
  700. var FloatCreate = typeof(Value).GetMethod(nameof(Value.Float));
  701. EmitNumberConvertTo(il, FloatCreate.GetParameters()[0].ParameterType, member.Type);
  702. il.Emit(OpCodes.Call, FloatCreate);
  703. }
  704. else if (targetType == typeof(List))
  705. {
  706. // TODO: impl this
  707. il.Emit(OpCodes.Pop);
  708. il.Emit(OpCodes.Ldnull);
  709. }
  710. else if (targetType == typeof(Map))
  711. {
  712. // TODO: support other aggregate types
  713. // for now, we assume that its a generated type implementing IGeneratedStore
  714. var IGeneratedStore_ValueGet = typeof(IGeneratedStore).GetProperty(nameof(IGeneratedStore.Values)).GetGetMethod();
  715. il.Emit(OpCodes.Callvirt, IGeneratedStore_ValueGet);
  716. }
  717. il.MarkLabel(endSerialize);
  718. // TODO: implement converters
  719. }
  720. #endregion
  721. #region Deserialize
  722. private static Type GetExpectedValueTypeForType(Type valT)
  723. {
  724. if (typeof(Value).IsAssignableFrom(valT)) // this is a Value subtype
  725. return valT;
  726. if (valT == typeof(string)
  727. || valT == typeof(char)) return typeof(Text);
  728. if (valT == typeof(bool)) return typeof(Boolean);
  729. if (valT == typeof(byte)
  730. || valT == typeof(sbyte)
  731. || valT == typeof(short)
  732. || valT == typeof(ushort)
  733. || valT == typeof(int)
  734. || valT == typeof(uint)
  735. || valT == typeof(long)
  736. || valT == typeof(ulong)) return typeof(Integer);
  737. if (valT == typeof(float)
  738. || valT == typeof(double)
  739. || valT == typeof(decimal)) return typeof(FloatingPoint);
  740. if (typeof(IEnumerable).IsAssignableFrom(valT)) return typeof(List);
  741. // TODO: fill this out the rest of the way
  742. // TODO: support converters
  743. return typeof(Map); // default for various objects
  744. }
  745. private static void EmitDeserializeGeneratedValue(ILGenerator il, Type targetType, Type srcType, Func<Type, int, LocalBuilder> GetLocal)
  746. {
  747. var IGeneratedStore_ValueSet = typeof(IGeneratedStore).GetProperty(nameof(IGeneratedStore.Values)).GetSetMethod();
  748. var valuel = GetLocal(srcType, 0);
  749. il.Emit(OpCodes.Stloc, valuel);
  750. EmitCreateChildGenerated(il, targetType);
  751. il.Emit(OpCodes.Dup);
  752. il.Emit(OpCodes.Ldloc, valuel);
  753. il.Emit(OpCodes.Callvirt, IGeneratedStore_ValueSet);
  754. }
  755. // top of stack is the Value to deserialize; the type will be as returned from GetExpectedValueTypeForType
  756. // after, top of stack will be thing to write to field
  757. private static void EmitDeserializeValue(ILGenerator il, Type targetType, Type expected, Func<Type, int, LocalBuilder> GetLocal)
  758. {
  759. if (typeof(Value).IsAssignableFrom(targetType)) return; // do nothing
  760. if (expected == typeof(Text))
  761. {
  762. var getter = expected.GetProperty(nameof(Text.Value)).GetGetMethod();
  763. il.Emit(OpCodes.Call, getter);
  764. if (targetType == typeof(char))
  765. {
  766. var strIndex = typeof(string).GetProperty("Chars").GetGetMethod(); // string's indexer is specially named Chars
  767. il.Emit(OpCodes.Ldc_I4_0);
  768. il.Emit(OpCodes.Call, strIndex);
  769. }
  770. }
  771. else if (expected == typeof(Boolean))
  772. {
  773. var getter = expected.GetProperty(nameof(Boolean.Value)).GetGetMethod();
  774. il.Emit(OpCodes.Call, getter);
  775. }
  776. else if (expected == typeof(Integer))
  777. {
  778. var getter = expected.GetProperty(nameof(Integer.Value)).GetGetMethod();
  779. il.Emit(OpCodes.Call, getter);
  780. EmitNumberConvertTo(il, targetType, getter.ReturnType);
  781. }
  782. else if (expected == typeof(FloatingPoint))
  783. {
  784. var getter = expected.GetProperty(nameof(FloatingPoint.Value)).GetGetMethod();
  785. il.Emit(OpCodes.Call, getter);
  786. EmitNumberConvertTo(il, targetType, getter.ReturnType);
  787. } // TODO: implement stuff for lists and maps of various types (probably call out somewhere else to figure out what to do)
  788. else if (expected == typeof(Map))
  789. {
  790. EmitDeserializeGeneratedValue(il, targetType, expected, GetLocal);
  791. }
  792. else // TODO: support converters
  793. {
  794. il.Emit(OpCodes.Pop);
  795. il.Emit(OpCodes.Ldnull);
  796. }
  797. }
  798. // emit takes the value being deserialized, logs on error, leaves nothing on stack
  799. private static void EmitDeserializeMember(ILGenerator il, SerializedMemberInfo member, Label nextLabel, Action<ILGenerator> getValue, Func<Type, int, LocalBuilder> GetLocal)
  800. {
  801. var Object_GetType = typeof(object).GetMethod(nameof(Object.GetType));
  802. var implLabel = il.DefineLabel();
  803. var passedTypeCheck = il.DefineLabel();
  804. var expectType = GetExpectedValueTypeForType(member.Type);
  805. void EmitStore(Action<ILGenerator> value)
  806. {
  807. il.Emit(OpCodes.Ldarg_0); // load this
  808. value(il);
  809. if (member.IsField)
  810. il.Emit(OpCodes.Stfld, member.Member as FieldInfo);
  811. else
  812. { // member is a property
  813. var prop = member.Member as PropertyInfo;
  814. var setter = prop.GetSetMethod();
  815. if (setter == null) throw new InvalidOperationException($"Property {member.Name} does not have a setter and is not ignored");
  816. il.Emit(OpCodes.Call, setter);
  817. }
  818. }
  819. il.Emit(OpCodes.Dup);
  820. il.Emit(OpCodes.Brtrue_S, implLabel); // null check
  821. // TODO: support Nullable<T>
  822. if (member.Type.IsValueType)
  823. {
  824. il.Emit(OpCodes.Pop);
  825. EmitLogError(il, $"Member {member.Name} ({member.Type}) not nullable", tailcall: false,
  826. expected: il => EmitTypeof(il, expectType));
  827. il.Emit(OpCodes.Br, nextLabel);
  828. }
  829. else
  830. {
  831. il.Emit(OpCodes.Pop);
  832. EmitStore(il => il.Emit(OpCodes.Ldnull));
  833. il.Emit(OpCodes.Br, nextLabel);
  834. }
  835. il.MarkLabel(implLabel);
  836. il.Emit(OpCodes.Isinst, expectType); //replaces on stack
  837. il.Emit(OpCodes.Dup); // duplicate cloned value
  838. il.Emit(OpCodes.Brtrue, passedTypeCheck); // null check
  839. var errorHandle = il.DefineLabel();
  840. // special cases to handle coersion between Float and Int
  841. if (expectType == typeof(FloatingPoint))
  842. {
  843. var specialTypeCheck = il.DefineLabel();
  844. il.Emit(OpCodes.Pop);
  845. getValue(il);
  846. il.Emit(OpCodes.Isinst, typeof(Integer)); //replaces on stack
  847. il.Emit(OpCodes.Dup); // duplicate cloned value
  848. il.Emit(OpCodes.Brfalse, errorHandle); // null check
  849. var Integer_CoerceToFloat = typeof(Integer).GetMethod(nameof(Integer.AsFloat));
  850. il.Emit(OpCodes.Call, Integer_CoerceToFloat);
  851. il.Emit(OpCodes.Br, passedTypeCheck);
  852. }
  853. else if (expectType == typeof(Integer))
  854. {
  855. var specialTypeCheck = il.DefineLabel();
  856. il.Emit(OpCodes.Pop);
  857. getValue(il);
  858. il.Emit(OpCodes.Isinst, typeof(FloatingPoint)); //replaces on stack
  859. il.Emit(OpCodes.Dup); // duplicate cloned value
  860. il.Emit(OpCodes.Brfalse, errorHandle); // null check
  861. var Float_CoerceToInt = typeof(FloatingPoint).GetMethod(nameof(FloatingPoint.AsInteger));
  862. il.Emit(OpCodes.Call, Float_CoerceToInt);
  863. il.Emit(OpCodes.Br, passedTypeCheck);
  864. }
  865. il.MarkLabel(errorHandle);
  866. il.Emit(OpCodes.Pop);
  867. EmitLogError(il, $"Unexpected type deserializing {member.Name}", tailcall: false,
  868. expected: il => EmitTypeof(il, expectType), found: il =>
  869. {
  870. getValue(il);
  871. il.Emit(OpCodes.Callvirt, Object_GetType);
  872. });
  873. il.Emit(OpCodes.Br, nextLabel);
  874. il.MarkLabel(passedTypeCheck);
  875. var local = GetLocal(member.Type, 0);
  876. EmitDeserializeValue(il, member.Type, expectType, GetLocal);
  877. il.Emit(OpCodes.Stloc, local);
  878. EmitStore(il => il.Emit(OpCodes.Ldloc, local));
  879. }
  880. #endregion
  881. }
  882. }