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.

672 lines
29 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. #if NET3
  15. using Net3_Proxy;
  16. using Array = Net3_Proxy.Array;
  17. #endif
  18. [assembly: InternalsVisibleTo(IPA.Config.Stores.GeneratedStore.GeneratedAssemblyName)]
  19. namespace IPA.Config.Stores
  20. {
  21. /// <summary>
  22. /// A class providing an extension for <see cref="Config"/> to make it easy to use generated
  23. /// config stores.
  24. /// </summary>
  25. public static class GeneratedStoreExtensions
  26. {
  27. /// <summary>
  28. /// Creates a generated <see cref="IConfigStore"/> of type <typeparamref name="T"/>, registers it to
  29. /// the <see cref="Config"/> object, and returns it. This also forces a synchronous config load via
  30. /// <see cref="Config.LoadSync"/> if <paramref name="loadSync"/> is <see langword="true"/>.
  31. /// </summary>
  32. /// <remarks>
  33. /// <para>
  34. /// <typeparamref name="T"/> must be a non-<see langword="sealed"/> <see langword="class"/>.
  35. /// </para>
  36. /// <para>
  37. /// TODO: describe details of generated stores
  38. /// </para>
  39. /// </remarks>
  40. /// <typeparam name="T">the type to wrap</typeparam>
  41. /// <param name="cfg">the <see cref="Config"/> to register to</param>
  42. /// <param name="loadSync">whether to synchronously load the content, or trigger an async load</param>
  43. /// <returns>a generated instance of <typeparamref name="T"/> as a special <see cref="IConfigStore"/></returns>
  44. public static T Generated<T>(this Config cfg, bool loadSync = true) where T : class
  45. {
  46. var ret = GeneratedStore.Create<T>();
  47. cfg.SetStore(ret as IConfigStore);
  48. if (loadSync)
  49. cfg.LoadSync();
  50. else
  51. cfg.LoadAsync();
  52. return ret;
  53. }
  54. }
  55. internal static class GeneratedStore
  56. {
  57. internal interface IGeneratedStore
  58. {
  59. /// <summary>
  60. /// serializes/deserializes to Value
  61. /// </summary>
  62. Value Values { get; set; }
  63. Type Type { get; }
  64. IGeneratedStore Parent { get; }
  65. Impl Impl { get; }
  66. }
  67. internal class Impl : IConfigStore
  68. {
  69. private IGeneratedStore generated;
  70. internal static ConstructorInfo Ctor = typeof(Impl).GetConstructor(new[] { typeof(IGeneratedStore) });
  71. public Impl(IGeneratedStore store) => generated = store;
  72. private readonly AutoResetEvent resetEvent = new AutoResetEvent(false);
  73. public WaitHandle SyncObject => resetEvent;
  74. internal static MethodInfo SyncObjectGetMethod = typeof(Impl).GetProperty(nameof(SyncObject)).GetGetMethod();
  75. public ReaderWriterLockSlim WriteSyncObject { get; } = new ReaderWriterLockSlim();
  76. internal static MethodInfo WriteSyncObjectGetMethod = typeof(Impl).GetProperty(nameof(WriteSyncObject)).GetGetMethod();
  77. internal static MethodInfo ImplSignalChangedMethod = typeof(Impl).GetMethod(nameof(ImplSignalChanged));
  78. public static void ImplSignalChanged(IGeneratedStore s) => FindImpl(s).SignalChanged();
  79. public void SignalChanged() => resetEvent.Set();
  80. internal static MethodInfo ImplTakeReadMethod = typeof(Impl).GetMethod(nameof(ImplTakeRead));
  81. public static void ImplTakeRead(IGeneratedStore s) => FindImpl(s).TakeRead();
  82. public void TakeRead() => WriteSyncObject.EnterReadLock();
  83. internal static MethodInfo ImplReleaseReadMethod = typeof(Impl).GetMethod(nameof(ImplReleaseRead));
  84. public static void ImplReleaseRead(IGeneratedStore s) => FindImpl(s).ReleaseRead();
  85. public void ReleaseRead() => WriteSyncObject.ExitWriteLock();
  86. internal static MethodInfo ImplTakeWriteMethod = typeof(Impl).GetMethod(nameof(ImplTakeWrite));
  87. public static void ImplTakeWrite(IGeneratedStore s) => FindImpl(s).TakeWrite();
  88. public void TakeWrite() => WriteSyncObject.EnterWriteLock();
  89. internal static MethodInfo ImplReleaseWriteMethod = typeof(Impl).GetMethod(nameof(ImplReleaseWrite));
  90. public static void ImplReleaseWrite(IGeneratedStore s) => FindImpl(s).ReleaseWrite();
  91. public void ReleaseWrite() => WriteSyncObject.ExitWriteLock();
  92. internal static MethodInfo FindImplMethod = typeof(Impl).GetMethod(nameof(FindImpl));
  93. public static Impl FindImpl(IGeneratedStore store)
  94. {
  95. while (store?.Parent != null) store = store.Parent; // walk to the top of the tree
  96. return store?.Impl;
  97. }
  98. internal static MethodInfo ReadFromMethod = typeof(Impl).GetMethod(nameof(ReadFrom));
  99. public void ReadFrom(IConfigProvider provider)
  100. {
  101. var values = provider.Load();
  102. Logger.config.Debug("Generated impl ReadFrom");
  103. Logger.config.Debug($"Read {values}");
  104. generated.Values = values;
  105. }
  106. internal static MethodInfo WriteToMethod = typeof(Impl).GetMethod(nameof(WriteTo));
  107. public void WriteTo(IConfigProvider provider)
  108. {
  109. var values = generated.Values;
  110. Logger.config.Debug("Generated impl WriteTo");
  111. Logger.config.Debug($"Serialized {values}");
  112. provider.Store(values);
  113. }
  114. }
  115. private static Dictionary<Type, Func<IGeneratedStore, IConfigStore>> generatedCreators = new Dictionary<Type, Func<IGeneratedStore, IConfigStore>>();
  116. private static Dictionary<Type, Dictionary<string, Type>> memberMaps = new Dictionary<Type, Dictionary<string, Type>>();
  117. public static T Create<T>() where T : class => (T)Create(typeof(T));
  118. public static IConfigStore Create(Type type) => Create(type, null);
  119. private static IConfigStore Create(Type type, IGeneratedStore parent)
  120. {
  121. if (generatedCreators.TryGetValue(type, out var creator))
  122. return creator(parent);
  123. else
  124. {
  125. creator = MakeCreator(type);
  126. generatedCreators.Add(type, creator);
  127. return creator(parent);
  128. }
  129. }
  130. internal const string GeneratedAssemblyName = "IPA.Config.Generated";
  131. private static AssemblyBuilder assembly = null;
  132. private static AssemblyBuilder Assembly
  133. {
  134. get
  135. {
  136. if (assembly == null)
  137. {
  138. var name = new AssemblyName(GeneratedAssemblyName);
  139. assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave);
  140. }
  141. return assembly;
  142. }
  143. }
  144. internal static void DebugSaveAssembly(string file)
  145. {
  146. Assembly.Save(file);
  147. }
  148. private static ModuleBuilder module = null;
  149. private static ModuleBuilder Module
  150. {
  151. get
  152. {
  153. if (module == null)
  154. module = Assembly.DefineDynamicModule(Assembly.GetName().Name, Assembly.GetName().Name + ".dll");
  155. return module;
  156. }
  157. }
  158. private struct SerializedMemberInfo
  159. {
  160. public string Name;
  161. public MemberInfo Member;
  162. public bool IsVirtual;
  163. public bool IsField;
  164. public Type Type;
  165. }
  166. private static Func<IGeneratedStore, IConfigStore> MakeCreator(Type type)
  167. {
  168. var baseCtor = type.GetConstructor(Type.EmptyTypes); // get a default constructor
  169. if (baseCtor == null)
  170. throw new ArgumentException("Config type does not have a public parameterless constructor");
  171. var typeBuilder = Module.DefineType($"{type.FullName}.Generated",
  172. TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Class, type);
  173. var typeField = typeBuilder.DefineField("<>_type", typeof(Type), FieldAttributes.Private | FieldAttributes.InitOnly);
  174. var implField = typeBuilder.DefineField("<>_impl", typeof(Impl), FieldAttributes.Private | FieldAttributes.InitOnly);
  175. var parentField = typeBuilder.DefineField("<>_parent", typeof(IGeneratedStore), FieldAttributes.Private | FieldAttributes.InitOnly);
  176. // none of this can be Expressions because CompileToMethod requires a static target method for some dumbass reason
  177. #region Parse base object structure
  178. var baseChanged = type.GetMethod("Changed", BindingFlags.Public | BindingFlags.Instance, null, Type.EmptyTypes, Array.Empty<ParameterModifier>());
  179. if (baseChanged != null && !baseChanged.IsVirtual) baseChanged = null; // limit this to just the one thing
  180. var structure = new List<SerializedMemberInfo>();
  181. // TODO: incorporate attributes/base types
  182. // only looks at public properties
  183. foreach (var prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public))
  184. {
  185. var smi = new SerializedMemberInfo
  186. {
  187. Name = prop.Name,
  188. Member = prop,
  189. IsVirtual = (prop.GetGetMethod(true)?.IsVirtual ?? false) ||
  190. (prop.GetSetMethod(true)?.IsVirtual ?? false),
  191. IsField = false,
  192. Type = prop.PropertyType
  193. };
  194. structure.Add(smi);
  195. }
  196. // only look at public fields
  197. foreach (var field in type.GetFields(BindingFlags.Instance | BindingFlags.Public))
  198. {
  199. var smi = new SerializedMemberInfo
  200. {
  201. Name = field.Name,
  202. Member = field,
  203. IsVirtual = false,
  204. IsField = true,
  205. Type = field.FieldType
  206. };
  207. structure.Add(smi);
  208. }
  209. #endregion
  210. #region Constructor
  211. var ctor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new[] { typeof(IGeneratedStore) });
  212. { // because this is a constructor, it has to be raw IL
  213. var il = ctor.GetILGenerator();
  214. il.Emit(OpCodes.Ldarg_0); // keep this at bottom of stack
  215. il.Emit(OpCodes.Dup);
  216. il.Emit(OpCodes.Call, baseCtor);
  217. il.Emit(OpCodes.Dup);
  218. il.Emit(OpCodes.Ldarg_1); // load parent
  219. il.Emit(OpCodes.Stfld, parentField);
  220. il.Emit(OpCodes.Dup);
  221. EmitTypeof(il, type);
  222. il.Emit(OpCodes.Stfld, typeField);
  223. il.Emit(OpCodes.Dup);
  224. il.Emit(OpCodes.Dup);
  225. il.Emit(OpCodes.Newobj, Impl.Ctor);
  226. il.Emit(OpCodes.Stfld, implField);
  227. foreach (var member in structure)
  228. EmitMemberFix(il, member);
  229. il.Emit(OpCodes.Pop);
  230. il.Emit(OpCodes.Ret);
  231. }
  232. #endregion
  233. const MethodAttributes propertyMethodAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
  234. const MethodAttributes virtualPropertyMethodAttr = propertyMethodAttr | MethodAttributes.Virtual | MethodAttributes.Final;
  235. const MethodAttributes virtualMemberMethod = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.Final;
  236. #region IGeneratedStore
  237. typeBuilder.AddInterfaceImplementation(typeof(IGeneratedStore));
  238. var IGeneratedStore_t = typeof(IGeneratedStore);
  239. var IGeneratedStore_GetImpl = IGeneratedStore_t.GetProperty(nameof(IGeneratedStore.Impl)).GetGetMethod();
  240. var IGeneratedStore_GetType = IGeneratedStore_t.GetProperty(nameof(IGeneratedStore.Type)).GetGetMethod();
  241. var IGeneratedStore_GetParent = IGeneratedStore_t.GetProperty(nameof(IGeneratedStore.Parent)).GetGetMethod();
  242. var IGeneratedStore_GetValues = IGeneratedStore_t.GetProperty(nameof(IGeneratedStore.Values)).GetGetMethod();
  243. var IGeneratedStore_SetValues = IGeneratedStore_t.GetProperty(nameof(IGeneratedStore.Values)).GetSetMethod();
  244. #region IGeneratedStore.Impl
  245. var implProp = typeBuilder.DefineProperty(nameof(IGeneratedStore.Impl), PropertyAttributes.None, typeof(Impl), null);
  246. var implPropGet = typeBuilder.DefineMethod($"<g>{nameof(IGeneratedStore.Impl)}", virtualPropertyMethodAttr, implProp.PropertyType, Type.EmptyTypes);
  247. implProp.SetGetMethod(implPropGet);
  248. typeBuilder.DefineMethodOverride(implPropGet, IGeneratedStore_GetImpl);
  249. {
  250. var il = implPropGet.GetILGenerator();
  251. il.Emit(OpCodes.Ldarg_0); // load this
  252. il.Emit(OpCodes.Ldfld, implField); // load impl field
  253. il.Emit(OpCodes.Ret);
  254. }
  255. #endregion
  256. #region IGeneratedStore.Type
  257. var typeProp = typeBuilder.DefineProperty(nameof(IGeneratedStore.Type), PropertyAttributes.None, typeof(Type), null);
  258. var typePropGet = typeBuilder.DefineMethod($"<g>{nameof(IGeneratedStore.Type)}", virtualPropertyMethodAttr, typeProp.PropertyType, Type.EmptyTypes);
  259. typeProp.SetGetMethod(typePropGet);
  260. typeBuilder.DefineMethodOverride(typePropGet, IGeneratedStore_GetType);
  261. {
  262. var il = typePropGet.GetILGenerator();
  263. il.Emit(OpCodes.Ldarg_0); // load this
  264. il.Emit(OpCodes.Ldfld, typeField); // load impl field
  265. il.Emit(OpCodes.Ret);
  266. }
  267. #endregion
  268. #region IGeneratedStore.Parent
  269. var parentProp = typeBuilder.DefineProperty(nameof(IGeneratedStore.Parent), PropertyAttributes.None, typeof(IGeneratedStore), null);
  270. var parentPropGet = typeBuilder.DefineMethod($"<g>{nameof(IGeneratedStore.Parent)}", virtualPropertyMethodAttr, parentProp.PropertyType, Type.EmptyTypes);
  271. parentProp.SetGetMethod(parentPropGet);
  272. typeBuilder.DefineMethodOverride(parentPropGet, IGeneratedStore_GetParent);
  273. {
  274. var il = parentPropGet.GetILGenerator();
  275. il.Emit(OpCodes.Ldarg_0); // load this
  276. il.Emit(OpCodes.Ldfld, parentField); // load impl field
  277. il.Emit(OpCodes.Ret);
  278. }
  279. #endregion
  280. #region IGeneratedStore.Values
  281. var valuesProp = typeBuilder.DefineProperty(nameof(IGeneratedStore.Values), PropertyAttributes.None, typeof(Value), null);
  282. var valuesPropGet = typeBuilder.DefineMethod($"<g>{nameof(IGeneratedStore.Values)}", virtualPropertyMethodAttr, valuesProp.PropertyType, Type.EmptyTypes);
  283. var valuesPropSet = typeBuilder.DefineMethod($"<s>{nameof(IGeneratedStore.Values)}", virtualPropertyMethodAttr, null, new[] { valuesProp.PropertyType });
  284. valuesProp.SetGetMethod(valuesPropGet);
  285. typeBuilder.DefineMethodOverride(valuesPropGet, IGeneratedStore_GetValues);
  286. valuesProp.SetSetMethod(valuesPropSet);
  287. typeBuilder.DefineMethodOverride(valuesPropSet, IGeneratedStore_SetValues);
  288. { // this is non-locking because the only code that will call this will already own the correct lock
  289. var il = valuesPropGet.GetILGenerator();
  290. var Map_Add = typeof(Map).GetMethod(nameof(Map.Add));
  291. il.Emit(OpCodes.Call, typeof(Value).GetMethod(nameof(Value.Map)));
  292. // the map is now at the top of the stack
  293. foreach (var member in structure)
  294. {
  295. il.Emit(OpCodes.Dup);
  296. il.Emit(OpCodes.Ldstr, member.Name); // TODO: make this behave with annotations
  297. EmitSerializeMember(il, member);
  298. il.Emit(OpCodes.Call, Map_Add);
  299. }
  300. // the map is still at the top of the stack, return it
  301. il.Emit(OpCodes.Ret);
  302. }
  303. { // this is non-locking because the only code that will call this will already own the correct lock
  304. var il = valuesPropSet.GetILGenerator();
  305. var Map_t = typeof(Map);
  306. var Map_TryGetValue = Map_t.GetMethod(nameof(Map.TryGetValue));
  307. var Object_GetType = typeof(object).GetMethod(nameof(Object.GetType));
  308. var valueLocal = il.DeclareLocal(typeof(Value));
  309. var nonNull = il.DefineLabel();
  310. il.Emit(OpCodes.Ldarg_1);
  311. il.Emit(OpCodes.Brtrue, nonNull);
  312. EmitLogError(il, "Attempting to deserialize null", tailcall: true);
  313. il.Emit(OpCodes.Ret);
  314. il.MarkLabel(nonNull);
  315. il.Emit(OpCodes.Ldarg_1);
  316. il.Emit(OpCodes.Isinst, Map_t);
  317. il.Emit(OpCodes.Dup); // duplicate cloned value
  318. var notMapError = il.DefineLabel();
  319. il.Emit(OpCodes.Brtrue, notMapError);
  320. // handle error
  321. il.Emit(OpCodes.Pop); // removes the duplicate value
  322. EmitLogError(il, $"Invalid root for deserializing {type.FullName}", tailcall: true,
  323. expected: il => EmitTypeof(il, Map_t), found: il =>
  324. {
  325. il.Emit(OpCodes.Ldarg_1);
  326. il.Emit(OpCodes.Callvirt, Object_GetType);
  327. });
  328. il.Emit(OpCodes.Ret);
  329. var nextLabel = notMapError;
  330. // head of stack is Map instance
  331. foreach (var member in structure)
  332. {
  333. il.MarkLabel(nextLabel);
  334. nextLabel = il.DefineLabel();
  335. var endErrorLabel = il.DefineLabel();
  336. il.Emit(OpCodes.Dup);
  337. il.Emit(OpCodes.Ldstr, member.Name);
  338. il.Emit(OpCodes.Ldloca_S, valueLocal);
  339. il.Emit(OpCodes.Call, Map_TryGetValue);
  340. il.Emit(OpCodes.Brtrue_S, endErrorLabel);
  341. EmitLogError(il, $"Missing key {member.Name}", tailcall: false);
  342. il.Emit(OpCodes.Br, nextLabel);
  343. il.MarkLabel(endErrorLabel);
  344. il.Emit(OpCodes.Ldloc_S, valueLocal);
  345. EmitDeserializeMember(il, member, nextLabel, il => il.Emit(OpCodes.Ldloc_S, valueLocal));
  346. }
  347. il.MarkLabel(nextLabel);
  348. il.Emit(OpCodes.Pop); // removes the duplicate value
  349. il.Emit(OpCodes.Ret);
  350. }
  351. #endregion
  352. #endregion
  353. #region IConfigStore
  354. typeBuilder.AddInterfaceImplementation(typeof(IConfigStore));
  355. var IConfigStore_t = typeof(IConfigStore);
  356. var IConfigStore_GetSyncObject = IConfigStore_t.GetProperty(nameof(IConfigStore.SyncObject)).GetGetMethod();
  357. var IConfigStore_GetWriteSyncObject = IConfigStore_t.GetProperty(nameof(IConfigStore.WriteSyncObject)).GetGetMethod();
  358. var IConfigStore_WriteTo = IConfigStore_t.GetMethod(nameof(IConfigStore.WriteTo));
  359. var IConfigStore_ReadFrom = IConfigStore_t.GetMethod(nameof(IConfigStore.ReadFrom));
  360. #region IConfigStore.SyncObject
  361. var syncObjProp = typeBuilder.DefineProperty(nameof(IConfigStore.SyncObject), PropertyAttributes.None, typeof(WaitHandle), null);
  362. var syncObjPropGet = typeBuilder.DefineMethod($"<g>{nameof(IConfigStore.SyncObject)}", virtualPropertyMethodAttr, syncObjProp.PropertyType, Type.EmptyTypes);
  363. syncObjProp.SetGetMethod(syncObjPropGet);
  364. typeBuilder.DefineMethodOverride(syncObjPropGet, IConfigStore_GetSyncObject);
  365. {
  366. var il = syncObjPropGet.GetILGenerator();
  367. il.Emit(OpCodes.Ldarg_0);
  368. il.Emit(OpCodes.Call, Impl.FindImplMethod);
  369. il.Emit(OpCodes.Tailcall);
  370. il.Emit(OpCodes.Call, Impl.SyncObjectGetMethod);
  371. il.Emit(OpCodes.Ret);
  372. }
  373. #endregion
  374. #region IConfigStore.WriteSyncObject
  375. var writeSyncObjProp = typeBuilder.DefineProperty(nameof(IConfigStore.WriteSyncObject), PropertyAttributes.None, typeof(WaitHandle), null);
  376. var writeSyncObjPropGet = typeBuilder.DefineMethod($"<g>{nameof(IConfigStore.WriteSyncObject)}", virtualPropertyMethodAttr, writeSyncObjProp.PropertyType, Type.EmptyTypes);
  377. writeSyncObjProp.SetGetMethod(writeSyncObjPropGet);
  378. typeBuilder.DefineMethodOverride(writeSyncObjPropGet, IConfigStore_GetWriteSyncObject);
  379. {
  380. var il = writeSyncObjPropGet.GetILGenerator();
  381. il.Emit(OpCodes.Ldarg_0);
  382. il.Emit(OpCodes.Call, Impl.FindImplMethod);
  383. il.Emit(OpCodes.Tailcall);
  384. il.Emit(OpCodes.Call, Impl.WriteSyncObjectGetMethod);
  385. il.Emit(OpCodes.Ret);
  386. }
  387. #endregion
  388. #region IConfigStore.WriteTo
  389. var writeTo = typeBuilder.DefineMethod($"<>{nameof(IConfigStore.WriteTo)}", virtualMemberMethod, null, new[] { typeof(IConfigProvider) });
  390. typeBuilder.DefineMethodOverride(writeTo, IConfigStore_WriteTo);
  391. {
  392. var il = writeTo.GetILGenerator();
  393. il.Emit(OpCodes.Ldarg_0);
  394. il.Emit(OpCodes.Call, Impl.FindImplMethod);
  395. il.Emit(OpCodes.Ldarg_1);
  396. il.Emit(OpCodes.Tailcall);
  397. il.Emit(OpCodes.Call, Impl.WriteToMethod);
  398. il.Emit(OpCodes.Ret);
  399. }
  400. #endregion
  401. #region IConfigStore.ReadFrom
  402. var readFrom = typeBuilder.DefineMethod($"<>{nameof(IConfigStore.ReadFrom)}", virtualMemberMethod, null, new[] { typeof(IConfigProvider) });
  403. typeBuilder.DefineMethodOverride(readFrom, IConfigStore_ReadFrom);
  404. {
  405. var il = readFrom.GetILGenerator();
  406. il.Emit(OpCodes.Ldarg_0);
  407. il.Emit(OpCodes.Call, Impl.FindImplMethod);
  408. il.Emit(OpCodes.Ldarg_1);
  409. il.Emit(OpCodes.Tailcall);
  410. il.Emit(OpCodes.Call, Impl.ReadFromMethod);
  411. il.Emit(OpCodes.Ret);
  412. }
  413. #endregion
  414. #endregion
  415. #region Changed
  416. var coreChanged = typeBuilder.DefineMethod(
  417. "<>Changed",
  418. MethodAttributes.Public | MethodAttributes.HideBySig,
  419. null, Type.EmptyTypes);
  420. {
  421. var il = coreChanged.GetILGenerator();
  422. il.Emit(OpCodes.Ldarg_0);
  423. il.Emit(OpCodes.Call, Impl.ImplSignalChangedMethod);
  424. il.Emit(OpCodes.Ret); // simply call our impl's SignalChanged method and return
  425. }
  426. if (baseChanged != null) {
  427. var changedMethod = typeBuilder.DefineMethod( // copy to override baseChanged
  428. baseChanged.Name,
  429. MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.HideBySig,
  430. null, Type.EmptyTypes);
  431. typeBuilder.DefineMethodOverride(changedMethod, baseChanged);
  432. {
  433. var il = changedMethod.GetILGenerator();
  434. il.Emit(OpCodes.Ldarg_0);
  435. il.Emit(OpCodes.Call, baseChanged); // call base
  436. il.Emit(OpCodes.Ldarg_0);
  437. il.Emit(OpCodes.Tailcall);
  438. il.Emit(OpCodes.Call, coreChanged); // call back to the core change method
  439. il.Emit(OpCodes.Ret);
  440. }
  441. coreChanged = changedMethod; // switch to calling this version instead of just the default
  442. }
  443. #endregion
  444. // TODO: generate overrides for all the virtual properties
  445. var genType = typeBuilder.CreateType();
  446. var parentParam = Expression.Parameter(typeof(IGeneratedStore), "parent");
  447. var creatorDel = Expression.Lambda<Func<IGeneratedStore, IConfigStore>>(
  448. Expression.New(ctor, parentParam), parentParam
  449. ).Compile();
  450. { // register a member map
  451. var dict = new Dictionary<string, Type>();
  452. foreach (var member in structure)
  453. dict.Add(member.Name, member.Type);
  454. memberMaps.Add(type, dict);
  455. }
  456. return creatorDel;
  457. }
  458. private static void EmitLogError(ILGenerator il, string message, bool tailcall = false, Action<ILGenerator> expected = null, Action<ILGenerator> found = null)
  459. {
  460. if (expected == null) expected = il => il.Emit(OpCodes.Ldnull);
  461. if (found == null) found = il => il.Emit(OpCodes.Ldnull);
  462. expected(il);
  463. found(il);
  464. il.Emit(OpCodes.Ldstr, message);
  465. if (tailcall) il.Emit(OpCodes.Tailcall);
  466. il.Emit(OpCodes.Call, LogErrorMethod);
  467. }
  468. private static readonly MethodInfo LogErrorMethod = typeof(GeneratedStore).GetMethod(nameof(LogError), BindingFlags.NonPublic | BindingFlags.Static);
  469. internal static void LogError(Type expected, Type found, string message)
  470. {
  471. Logger.config.Notice($"{message}{(expected == null ? "" : $" (expected {expected}, found {found?.ToString() ?? "null"})")}");
  472. }
  473. // expects the this param to be on the stack
  474. private static void EmitMemberFix(ILGenerator il, SerializedMemberInfo member)
  475. {
  476. // TODO: impl
  477. }
  478. private static readonly MethodInfo Type_GetTypeFromHandle = typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle));
  479. private static void EmitTypeof(ILGenerator il, Type type)
  480. {
  481. il.Emit(OpCodes.Ldtoken, type);
  482. il.Emit(OpCodes.Call, Type_GetTypeFromHandle);
  483. }
  484. // emit takes no args, leaves Value at top of stack
  485. private static void EmitSerializeMember(ILGenerator il, SerializedMemberInfo member)
  486. {
  487. // TODO: impl
  488. il.Emit(OpCodes.Ldnull);
  489. }
  490. private static Type GetExpectedValueTypeForType(Type valT)
  491. {
  492. if (typeof(Value).IsAssignableFrom(valT)) // this is a Value subtype
  493. return valT;
  494. // TODO: fill this out the rest of the way
  495. return typeof(string); // something that will always fail
  496. }
  497. internal static class Deserializers
  498. {
  499. }
  500. // emit takes the value being deserialized, logs on error, leaves nothing on stack
  501. private static void EmitDeserializeMember(ILGenerator il, SerializedMemberInfo member, Label nextLabel, Action<ILGenerator> getValue)
  502. {
  503. var Object_GetType = typeof(object).GetMethod(nameof(Object.GetType));
  504. var implLabel = il.DefineLabel();
  505. var passedTypeCheck = il.DefineLabel();
  506. var expectType = GetExpectedValueTypeForType(member.Type);
  507. il.Emit(OpCodes.Dup);
  508. il.Emit(OpCodes.Brtrue_S, implLabel); // null check
  509. if (member.Type.IsValueType)
  510. {
  511. il.Emit(OpCodes.Pop);
  512. EmitLogError(il, $"Member {member.Name} ({member.Type}) not nullable", tailcall: false,
  513. expected: il => EmitTypeof(il, expectType));
  514. il.Emit(OpCodes.Br, nextLabel);
  515. }
  516. else
  517. {
  518. // TODO: deserialize null sanely
  519. il.Emit(OpCodes.Nop);
  520. il.Emit(OpCodes.Pop);
  521. il.Emit(OpCodes.Br, nextLabel);
  522. }
  523. il.MarkLabel(implLabel);
  524. il.Emit(OpCodes.Isinst, expectType); //replaces on stack
  525. il.Emit(OpCodes.Dup); // duplicate cloned value
  526. il.Emit(OpCodes.Brtrue, passedTypeCheck); // null check
  527. il.Emit(OpCodes.Pop);
  528. EmitLogError(il, $"Unexpected type deserializing {member.Name}; type not nullable", tailcall: false,
  529. expected: il => EmitTypeof(il, expectType), found: il =>
  530. {
  531. getValue(il);
  532. il.Emit(OpCodes.Callvirt, Object_GetType);
  533. });
  534. il.Emit(OpCodes.Br, nextLabel);
  535. il.MarkLabel(passedTypeCheck);
  536. {
  537. // TODO: actually write the value
  538. il.Emit(OpCodes.Nop);
  539. }
  540. il.Emit(OpCodes.Pop); // this is just so the stack is balanced (currently removing the result of Isinst)
  541. }
  542. }
  543. }