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.

830 lines
38 KiB

  1. using IPA.Config.Data;
  2. using IPA.Config.Stores.Attributes;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.ComponentModel;
  6. using System.Linq;
  7. using System.Linq.Expressions;
  8. using System.Reflection;
  9. using System.Reflection.Emit;
  10. using System.Text;
  11. using System.Threading;
  12. using System.Threading.Tasks;
  13. namespace IPA.Config.Stores
  14. {
  15. internal static partial class GeneratedStoreImpl
  16. {
  17. internal delegate IConfigStore GeneratedStoreCreator(IGeneratedStore parent);
  18. private static (GeneratedStoreCreator ctor, Type type) MakeCreator(Type type)
  19. { // note that this does not and should not use converters by default for everything
  20. if (!type.IsClass) throw new ArgumentException("Config type is not a class");
  21. var baseCtor = type.GetConstructor(Type.EmptyTypes); // get a default constructor
  22. if (baseCtor == null)
  23. throw new ArgumentException("Config type does not have a public parameterless constructor");
  24. #region Parse base object structure
  25. const BindingFlags overrideMemberFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
  26. var baseChanged = type.GetMethod("Changed", overrideMemberFlags, null, Type.EmptyTypes, Array.Empty<ParameterModifier>());
  27. if (baseChanged != null && IsMethodInvalid(baseChanged, typeof(void))) baseChanged = null;
  28. var baseOnReload = type.GetMethod("OnReload", overrideMemberFlags, null, Type.EmptyTypes, Array.Empty<ParameterModifier>());
  29. if (baseOnReload != null && IsMethodInvalid(baseOnReload, typeof(void))) baseOnReload = null;
  30. var baseCopyFrom = type.GetMethod("CopyFrom", overrideMemberFlags, null, new[] { type }, Array.Empty<ParameterModifier>());
  31. if (baseCopyFrom != null && IsMethodInvalid(baseCopyFrom, typeof(void))) baseCopyFrom = null;
  32. var baseChangeTransaction = type.GetMethod("ChangeTransaction", overrideMemberFlags, null, Type.EmptyTypes, Array.Empty<ParameterModifier>());
  33. if (baseChangeTransaction != null && IsMethodInvalid(baseChangeTransaction, typeof(IDisposable))) baseChangeTransaction = null;
  34. var isINotifyPropertyChanged = type.FindInterfaces((i, t) => i == (Type)t, typeof(INotifyPropertyChanged)).Length != 0;
  35. var hasNotifyAttribute = type.GetCustomAttribute<NotifyPropertyChangesAttribute>() != null;
  36. var structure = ReadObjectMembers(type, throwOnPrivateProperty: false, throwOnPrivateField: false);
  37. #endregion
  38. var typeBuilder = Module.DefineType($"{type.FullName}<Generated>",
  39. TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Class, type);
  40. var typeField = typeBuilder.DefineField("<>_type", typeof(Type), FieldAttributes.Private | FieldAttributes.InitOnly);
  41. var implField = typeBuilder.DefineField("<>_impl", typeof(Impl), FieldAttributes.Private | FieldAttributes.InitOnly);
  42. var parentField = typeBuilder.DefineField("<>_parent", typeof(IGeneratedStore), FieldAttributes.Private | FieldAttributes.InitOnly);
  43. #region Converter fields
  44. var uniqueConverterTypes = structure.Where(m => m.HasConverter).Select(m => m.Converter).Distinct().ToArray();
  45. var converterFields = new Dictionary<Type, FieldInfo>(uniqueConverterTypes.Length);
  46. foreach (var convType in uniqueConverterTypes)
  47. {
  48. var field = typeBuilder.DefineField($"<converter>_{convType}", convType,
  49. FieldAttributes.Private | FieldAttributes.InitOnly | FieldAttributes.Static);
  50. converterFields.Add(convType, field);
  51. foreach (var member in structure.Where(m => m.HasConverter && m.Converter == convType))
  52. member.ConverterField = field;
  53. }
  54. #endregion
  55. #region Static constructor
  56. var cctor = typeBuilder.DefineConstructor(MethodAttributes.Static, CallingConventions.Standard, Type.EmptyTypes);
  57. {
  58. var il = cctor.GetILGenerator();
  59. foreach (var kvp in converterFields)
  60. {
  61. var typeCtor = kvp.Key.GetConstructor(Type.EmptyTypes);
  62. il.Emit(OpCodes.Newobj, typeCtor);
  63. il.Emit(OpCodes.Stsfld, kvp.Value);
  64. }
  65. il.Emit(OpCodes.Ret);
  66. }
  67. #endregion
  68. #region Constructor
  69. var ctor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new[] { typeof(IGeneratedStore) });
  70. {
  71. var il = ctor.GetILGenerator();
  72. il.Emit(OpCodes.Ldarg_0); // keep this at bottom of stack
  73. il.Emit(OpCodes.Dup);
  74. il.Emit(OpCodes.Call, baseCtor);
  75. il.Emit(OpCodes.Dup);
  76. il.Emit(OpCodes.Ldarg_1); // load parent
  77. il.Emit(OpCodes.Stfld, parentField);
  78. il.Emit(OpCodes.Dup);
  79. EmitTypeof(il, type);
  80. il.Emit(OpCodes.Stfld, typeField);
  81. var noImplLabel = il.DefineLabel();
  82. il.Emit(OpCodes.Ldarg_1);
  83. il.Emit(OpCodes.Brtrue, noImplLabel);
  84. il.Emit(OpCodes.Dup);
  85. il.Emit(OpCodes.Dup);
  86. il.Emit(OpCodes.Newobj, Impl.Ctor);
  87. il.Emit(OpCodes.Stfld, implField);
  88. il.MarkLabel(noImplLabel);
  89. var GetLocal = MakeGetLocal(il);
  90. foreach (var member in structure)
  91. {
  92. EmitStore(il, member, il =>
  93. {
  94. EmitLoad(il, member); // load the member
  95. EmitCorrectMember(il, member, false, true, GetLocal); // correct it
  96. });
  97. }
  98. il.Emit(OpCodes.Pop);
  99. il.Emit(OpCodes.Ret);
  100. }
  101. #endregion
  102. const MethodAttributes propertyMethodAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
  103. const MethodAttributes virtualPropertyMethodAttr = propertyMethodAttr | MethodAttributes.Virtual | MethodAttributes.Final;
  104. const MethodAttributes virtualMemberMethod = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.Final;
  105. #region INotifyPropertyChanged
  106. MethodBuilder notifyChanged = null;
  107. if (isINotifyPropertyChanged || hasNotifyAttribute)
  108. {
  109. var INotifyPropertyChanged_t = typeof(INotifyPropertyChanged);
  110. typeBuilder.AddInterfaceImplementation(INotifyPropertyChanged_t);
  111. var INotifyPropertyChanged_PropertyChanged =
  112. INotifyPropertyChanged_t.GetEvent(nameof(INotifyPropertyChanged.PropertyChanged));
  113. var PropertyChangedEventHandler_t = typeof(PropertyChangedEventHandler);
  114. var PropertyChangedEventHander_Invoke = PropertyChangedEventHandler_t.GetMethod(nameof(PropertyChangedEventHandler.Invoke));
  115. var PropertyChangedEventArgs_t = typeof(PropertyChangedEventArgs);
  116. var PropertyChangedEventArgs_ctor = PropertyChangedEventArgs_t.GetConstructor(new[] { typeof(string) });
  117. var Delegate_t = typeof(Delegate);
  118. var Delegate_Combine = Delegate_t.GetMethod(nameof(Delegate.Combine), BindingFlags.Static | BindingFlags.Public, null,
  119. new[] { Delegate_t, Delegate_t }, Array.Empty<ParameterModifier>());
  120. var Delegate_Remove = Delegate_t.GetMethod(nameof(Delegate.Remove), BindingFlags.Static | BindingFlags.Public, null,
  121. new[] { Delegate_t, Delegate_t }, Array.Empty<ParameterModifier>());
  122. var CompareExchange = typeof(Interlocked).GetMethods()
  123. .Where(m => m.Name == nameof(Interlocked.CompareExchange))
  124. .Where(m => m.ContainsGenericParameters)
  125. .Where(m => m.GetParameters().Length == 3).First()
  126. .MakeGenericMethod(PropertyChangedEventHandler_t);
  127. var basePropChangedEvent = type.GetEvents()
  128. .Where(e => e.GetAddMethod().GetBaseDefinition().DeclaringType == INotifyPropertyChanged_t)
  129. .FirstOrDefault();
  130. var basePropChangedAdd = basePropChangedEvent?.GetAddMethod();
  131. var basePropChangedRemove = basePropChangedEvent?.GetRemoveMethod();
  132. var PropertyChanged_backing = typeBuilder.DefineField("<event>PropertyChanged", PropertyChangedEventHandler_t, FieldAttributes.Private);
  133. var add_PropertyChanged = typeBuilder.DefineMethod("<add>PropertyChanged",
  134. MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Final | MethodAttributes.Virtual,
  135. null, new[] { PropertyChangedEventHandler_t });
  136. typeBuilder.DefineMethodOverride(add_PropertyChanged, INotifyPropertyChanged_PropertyChanged.GetAddMethod());
  137. if (basePropChangedAdd != null)
  138. typeBuilder.DefineMethodOverride(add_PropertyChanged, basePropChangedAdd);
  139. {
  140. var il = add_PropertyChanged.GetILGenerator();
  141. var loopLabel = il.DefineLabel();
  142. var delTemp = il.DeclareLocal(PropertyChangedEventHandler_t);
  143. il.Emit(OpCodes.Ldarg_0);
  144. il.Emit(OpCodes.Ldfld, PropertyChanged_backing);
  145. il.MarkLabel(loopLabel);
  146. il.Emit(OpCodes.Stloc, delTemp);
  147. il.Emit(OpCodes.Ldarg_0);
  148. il.Emit(OpCodes.Ldflda, PropertyChanged_backing);
  149. il.Emit(OpCodes.Ldloc, delTemp);
  150. il.Emit(OpCodes.Ldarg_1);
  151. il.Emit(OpCodes.Call, Delegate_Combine);
  152. il.Emit(OpCodes.Castclass, PropertyChangedEventHandler_t);
  153. il.Emit(OpCodes.Ldloc, delTemp);
  154. il.Emit(OpCodes.Call, CompareExchange);
  155. il.Emit(OpCodes.Dup);
  156. il.Emit(OpCodes.Ldloc, delTemp);
  157. il.Emit(OpCodes.Bne_Un_S, loopLabel);
  158. il.Emit(OpCodes.Ret);
  159. }
  160. var remove_PropertyChanged = typeBuilder.DefineMethod("<remove>PropertyChanged",
  161. MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Final | MethodAttributes.Virtual,
  162. null, new[] { PropertyChangedEventHandler_t });
  163. typeBuilder.DefineMethodOverride(remove_PropertyChanged, INotifyPropertyChanged_PropertyChanged.GetRemoveMethod());
  164. if (basePropChangedRemove != null)
  165. typeBuilder.DefineMethodOverride(remove_PropertyChanged, basePropChangedRemove);
  166. {
  167. var il = remove_PropertyChanged.GetILGenerator();
  168. var loopLabel = il.DefineLabel();
  169. var delTemp = il.DeclareLocal(PropertyChangedEventHandler_t);
  170. il.Emit(OpCodes.Ldarg_0);
  171. il.Emit(OpCodes.Ldfld, PropertyChanged_backing);
  172. il.MarkLabel(loopLabel);
  173. il.Emit(OpCodes.Stloc, delTemp);
  174. il.Emit(OpCodes.Ldarg_0);
  175. il.Emit(OpCodes.Ldflda, PropertyChanged_backing);
  176. il.Emit(OpCodes.Ldloc, delTemp);
  177. il.Emit(OpCodes.Ldarg_1);
  178. il.Emit(OpCodes.Call, Delegate_Remove);
  179. il.Emit(OpCodes.Castclass, PropertyChangedEventHandler_t);
  180. il.Emit(OpCodes.Ldloc, delTemp);
  181. il.Emit(OpCodes.Call, CompareExchange);
  182. il.Emit(OpCodes.Dup);
  183. il.Emit(OpCodes.Ldloc, delTemp);
  184. il.Emit(OpCodes.Bne_Un_S, loopLabel);
  185. il.Emit(OpCodes.Ret);
  186. }
  187. var PropertyChanged_event = typeBuilder.DefineEvent(nameof(INotifyPropertyChanged.PropertyChanged), EventAttributes.None, PropertyChangedEventHandler_t);
  188. PropertyChanged_event.SetAddOnMethod(add_PropertyChanged);
  189. PropertyChanged_event.SetRemoveOnMethod(remove_PropertyChanged);
  190. notifyChanged = typeBuilder.DefineMethod("<>NotifyChanged",
  191. MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Final, null, new[] { typeof(string) });
  192. {
  193. var il = notifyChanged.GetILGenerator();
  194. var invokeNonNull = il.DefineLabel();
  195. il.Emit(OpCodes.Ldarg_0);
  196. il.Emit(OpCodes.Ldfld, PropertyChanged_backing);
  197. il.Emit(OpCodes.Dup);
  198. il.Emit(OpCodes.Brtrue, invokeNonNull);
  199. il.Emit(OpCodes.Pop);
  200. il.Emit(OpCodes.Ret);
  201. il.MarkLabel(invokeNonNull);
  202. il.Emit(OpCodes.Ldarg_0);
  203. il.Emit(OpCodes.Ldarg_1);
  204. il.Emit(OpCodes.Newobj, PropertyChangedEventArgs_ctor);
  205. il.Emit(OpCodes.Call, PropertyChangedEventHander_Invoke);
  206. il.Emit(OpCodes.Ret);
  207. }
  208. }
  209. #endregion
  210. #region IGeneratedStore
  211. typeBuilder.AddInterfaceImplementation(typeof(IGeneratedStore));
  212. var IGeneratedStore_t = typeof(IGeneratedStore);
  213. var IGeneratedStore_GetImpl = IGeneratedStore_t.GetProperty(nameof(IGeneratedStore.Impl)).GetGetMethod();
  214. var IGeneratedStore_GetType = IGeneratedStore_t.GetProperty(nameof(IGeneratedStore.Type)).GetGetMethod();
  215. var IGeneratedStore_GetParent = IGeneratedStore_t.GetProperty(nameof(IGeneratedStore.Parent)).GetGetMethod();
  216. var IGeneratedStore_Serialize = IGeneratedStore_t.GetMethod(nameof(IGeneratedStore.Serialize));
  217. var IGeneratedStore_Deserialize = IGeneratedStore_t.GetMethod(nameof(IGeneratedStore.Deserialize));
  218. var IGeneratedStore_OnReload = IGeneratedStore_t.GetMethod(nameof(IGeneratedStore.OnReload));
  219. var IGeneratedStore_Changed = IGeneratedStore_t.GetMethod(nameof(IGeneratedStore.Changed));
  220. var IGeneratedStore_ChangeTransaction = IGeneratedStore_t.GetMethod(nameof(IGeneratedStore.ChangeTransaction));
  221. #region IGeneratedStore.OnReload
  222. var onReload = typeBuilder.DefineMethod($"<>{nameof(IGeneratedStore.OnReload)}", virtualMemberMethod, null, Type.EmptyTypes);
  223. typeBuilder.DefineMethodOverride(onReload, IGeneratedStore_OnReload);
  224. if (baseOnReload != null) typeBuilder.DefineMethodOverride(onReload, baseOnReload);
  225. {
  226. var il = onReload.GetILGenerator();
  227. if (baseOnReload != null)
  228. {
  229. il.Emit(OpCodes.Ldarg_0); // load this
  230. il.Emit(OpCodes.Tailcall);
  231. il.Emit(OpCodes.Call, baseOnReload); // load impl field
  232. }
  233. il.Emit(OpCodes.Ret);
  234. }
  235. #endregion
  236. #region IGeneratedStore.Impl
  237. var implProp = typeBuilder.DefineProperty(nameof(IGeneratedStore.Impl), PropertyAttributes.None, typeof(Impl), null);
  238. var implPropGet = typeBuilder.DefineMethod($"<g>{nameof(IGeneratedStore.Impl)}", virtualPropertyMethodAttr, implProp.PropertyType, Type.EmptyTypes);
  239. implProp.SetGetMethod(implPropGet);
  240. typeBuilder.DefineMethodOverride(implPropGet, IGeneratedStore_GetImpl);
  241. {
  242. var il = implPropGet.GetILGenerator();
  243. il.Emit(OpCodes.Ldarg_0); // load this
  244. il.Emit(OpCodes.Ldfld, implField); // load impl field
  245. il.Emit(OpCodes.Ret);
  246. }
  247. #endregion
  248. #region IGeneratedStore.Type
  249. var typeProp = typeBuilder.DefineProperty(nameof(IGeneratedStore.Type), PropertyAttributes.None, typeof(Type), null);
  250. var typePropGet = typeBuilder.DefineMethod($"<g>{nameof(IGeneratedStore.Type)}", virtualPropertyMethodAttr, typeProp.PropertyType, Type.EmptyTypes);
  251. typeProp.SetGetMethod(typePropGet);
  252. typeBuilder.DefineMethodOverride(typePropGet, IGeneratedStore_GetType);
  253. {
  254. var il = typePropGet.GetILGenerator();
  255. il.Emit(OpCodes.Ldarg_0); // load this
  256. il.Emit(OpCodes.Ldfld, typeField); // load impl field
  257. il.Emit(OpCodes.Ret);
  258. }
  259. #endregion
  260. #region IGeneratedStore.Parent
  261. var parentProp = typeBuilder.DefineProperty(nameof(IGeneratedStore.Parent), PropertyAttributes.None, typeof(IGeneratedStore), null);
  262. var parentPropGet = typeBuilder.DefineMethod($"<g>{nameof(IGeneratedStore.Parent)}", virtualPropertyMethodAttr, parentProp.PropertyType, Type.EmptyTypes);
  263. parentProp.SetGetMethod(parentPropGet);
  264. typeBuilder.DefineMethodOverride(parentPropGet, IGeneratedStore_GetParent);
  265. {
  266. var il = parentPropGet.GetILGenerator();
  267. il.Emit(OpCodes.Ldarg_0); // load this
  268. il.Emit(OpCodes.Ldfld, parentField); // load impl field
  269. il.Emit(OpCodes.Ret);
  270. }
  271. #endregion
  272. #region IGeneratedStore.Serialize
  273. var serializeGen = typeBuilder.DefineMethod($"<>{nameof(IGeneratedStore.Serialize)}", virtualPropertyMethodAttr, IGeneratedStore_Serialize.ReturnType, Type.EmptyTypes);
  274. typeBuilder.DefineMethodOverride(serializeGen, IGeneratedStore_Serialize);
  275. { // this is non-locking because the only code that will call this will already own the correct lock
  276. var il = serializeGen.GetILGenerator();
  277. var Map_Add = typeof(Map).GetMethod(nameof(Map.Add));
  278. var mapLocal = il.DeclareLocal(typeof(Map));
  279. var GetLocal = MakeGetLocal(il);
  280. var valLocal = GetLocal(typeof(Value));
  281. il.Emit(OpCodes.Call, typeof(Value).GetMethod(nameof(Value.Map)));
  282. il.Emit(OpCodes.Stloc, mapLocal);
  283. foreach (var member in structure)
  284. {
  285. EmitSerializeMember(il, member, GetLocal);
  286. il.Emit(OpCodes.Stloc, valLocal);
  287. il.Emit(OpCodes.Ldloc, mapLocal);
  288. il.Emit(OpCodes.Ldstr, member.Name);
  289. il.Emit(OpCodes.Ldloc, valLocal);
  290. il.Emit(OpCodes.Call, Map_Add);
  291. }
  292. il.Emit(OpCodes.Ldloc, mapLocal);
  293. il.Emit(OpCodes.Ret);
  294. }
  295. #endregion
  296. #region IGeneratedStore.Deserialize
  297. var deserializeGen = typeBuilder.DefineMethod($"<>{nameof(IGeneratedStore.Deserialize)}", virtualPropertyMethodAttr, null,
  298. new[] { IGeneratedStore_Deserialize.GetParameters()[0].ParameterType });
  299. typeBuilder.DefineMethodOverride(deserializeGen, IGeneratedStore_Deserialize);
  300. { // this is non-locking because the only code that will call this will already own the correct lock
  301. var il = deserializeGen.GetILGenerator();
  302. var Map_t = typeof(Map);
  303. var Map_TryGetValue = Map_t.GetMethod(nameof(Map.TryGetValue));
  304. var Object_GetType = typeof(object).GetMethod(nameof(Object.GetType));
  305. var valueLocal = il.DeclareLocal(typeof(Value));
  306. var mapLocal = il.DeclareLocal(typeof(Map));
  307. var nonNull = il.DefineLabel();
  308. il.Emit(OpCodes.Ldarg_1);
  309. il.Emit(OpCodes.Brtrue, nonNull);
  310. EmitLogError(il, "Attempting to deserialize null", tailcall: true);
  311. il.Emit(OpCodes.Ret);
  312. il.MarkLabel(nonNull);
  313. il.Emit(OpCodes.Ldarg_1);
  314. il.Emit(OpCodes.Isinst, Map_t);
  315. il.Emit(OpCodes.Dup); // duplicate cloned value
  316. il.Emit(OpCodes.Stloc, mapLocal);
  317. var notMapError = il.DefineLabel();
  318. il.Emit(OpCodes.Brtrue, notMapError);
  319. // handle error
  320. EmitLogError(il, $"Invalid root for deserializing {type.FullName}", tailcall: true,
  321. expected: il => EmitTypeof(il, Map_t), found: il =>
  322. {
  323. il.Emit(OpCodes.Ldarg_1);
  324. il.Emit(OpCodes.Callvirt, Object_GetType);
  325. });
  326. il.Emit(OpCodes.Ret);
  327. var nextLabel = notMapError;
  328. var GetLocal = MakeGetLocal(il);
  329. // head of stack is Map instance
  330. foreach (var member in structure)
  331. {
  332. il.MarkLabel(nextLabel);
  333. nextLabel = il.DefineLabel();
  334. var endErrorLabel = il.DefineLabel();
  335. il.Emit(OpCodes.Ldloc, mapLocal);
  336. il.Emit(OpCodes.Ldstr, member.Name);
  337. il.Emit(OpCodes.Ldloca_S, valueLocal);
  338. il.Emit(OpCodes.Call, Map_TryGetValue);
  339. il.Emit(OpCodes.Brtrue_S, endErrorLabel);
  340. EmitLogError(il, $"Missing key {member.Name}", tailcall: false);
  341. il.Emit(OpCodes.Br, nextLabel);
  342. il.MarkLabel(endErrorLabel);
  343. il.Emit(OpCodes.Ldloc_S, valueLocal);
  344. EmitDeserializeMember(il, member, nextLabel, il => il.Emit(OpCodes.Ldloc_S, valueLocal), GetLocal);
  345. }
  346. il.MarkLabel(nextLabel);
  347. if (notifyChanged != null)
  348. {
  349. foreach (var member in structure)
  350. {
  351. il.Emit(OpCodes.Ldarg_0);
  352. il.Emit(OpCodes.Ldstr, member.Name);
  353. il.Emit(OpCodes.Call, notifyChanged);
  354. }
  355. }
  356. il.Emit(OpCodes.Ret);
  357. }
  358. #endregion
  359. #endregion
  360. #region IConfigStore
  361. typeBuilder.AddInterfaceImplementation(typeof(IConfigStore));
  362. var IConfigStore_t = typeof(IConfigStore);
  363. var IConfigStore_GetSyncObject = IConfigStore_t.GetProperty(nameof(IConfigStore.SyncObject)).GetGetMethod();
  364. var IConfigStore_GetWriteSyncObject = IConfigStore_t.GetProperty(nameof(IConfigStore.WriteSyncObject)).GetGetMethod();
  365. var IConfigStore_WriteTo = IConfigStore_t.GetMethod(nameof(IConfigStore.WriteTo));
  366. var IConfigStore_ReadFrom = IConfigStore_t.GetMethod(nameof(IConfigStore.ReadFrom));
  367. #region IConfigStore.SyncObject
  368. var syncObjProp = typeBuilder.DefineProperty(nameof(IConfigStore.SyncObject), PropertyAttributes.None, typeof(WaitHandle), null);
  369. var syncObjPropGet = typeBuilder.DefineMethod($"<g>{nameof(IConfigStore.SyncObject)}", virtualPropertyMethodAttr, syncObjProp.PropertyType, Type.EmptyTypes);
  370. syncObjProp.SetGetMethod(syncObjPropGet);
  371. typeBuilder.DefineMethodOverride(syncObjPropGet, IConfigStore_GetSyncObject);
  372. {
  373. var il = syncObjPropGet.GetILGenerator();
  374. il.Emit(OpCodes.Ldarg_0);
  375. il.Emit(OpCodes.Tailcall);
  376. il.Emit(OpCodes.Call, Impl.ImplGetSyncObjectMethod);
  377. il.Emit(OpCodes.Ret);
  378. }
  379. #endregion
  380. #region IConfigStore.WriteSyncObject
  381. var writeSyncObjProp = typeBuilder.DefineProperty(nameof(IConfigStore.WriteSyncObject), PropertyAttributes.None, typeof(WaitHandle), null);
  382. var writeSyncObjPropGet = typeBuilder.DefineMethod($"<g>{nameof(IConfigStore.WriteSyncObject)}", virtualPropertyMethodAttr, writeSyncObjProp.PropertyType, Type.EmptyTypes);
  383. writeSyncObjProp.SetGetMethod(writeSyncObjPropGet);
  384. typeBuilder.DefineMethodOverride(writeSyncObjPropGet, IConfigStore_GetWriteSyncObject);
  385. {
  386. var il = writeSyncObjPropGet.GetILGenerator();
  387. il.Emit(OpCodes.Ldarg_0);
  388. il.Emit(OpCodes.Tailcall);
  389. il.Emit(OpCodes.Call, Impl.ImplGetWriteSyncObjectMethod);
  390. il.Emit(OpCodes.Ret);
  391. }
  392. #endregion
  393. #region IConfigStore.WriteTo
  394. var writeTo = typeBuilder.DefineMethod($"<>{nameof(IConfigStore.WriteTo)}", virtualMemberMethod, null, new[] { typeof(ConfigProvider) });
  395. typeBuilder.DefineMethodOverride(writeTo, IConfigStore_WriteTo);
  396. {
  397. var il = writeTo.GetILGenerator();
  398. il.Emit(OpCodes.Ldarg_0);
  399. il.Emit(OpCodes.Ldarg_1);
  400. il.Emit(OpCodes.Tailcall);
  401. il.Emit(OpCodes.Call, Impl.ImplWriteToMethod);
  402. il.Emit(OpCodes.Ret);
  403. }
  404. #endregion
  405. #region IConfigStore.ReadFrom
  406. var readFrom = typeBuilder.DefineMethod($"<>{nameof(IConfigStore.ReadFrom)}", virtualMemberMethod, null, new[] { typeof(ConfigProvider) });
  407. typeBuilder.DefineMethodOverride(readFrom, IConfigStore_ReadFrom);
  408. {
  409. var il = readFrom.GetILGenerator();
  410. il.Emit(OpCodes.Ldarg_0);
  411. il.Emit(OpCodes.Ldarg_1);
  412. il.Emit(OpCodes.Tailcall);
  413. il.Emit(OpCodes.Call, Impl.ImplReadFromMethod);
  414. il.Emit(OpCodes.Ret);
  415. }
  416. #endregion
  417. #endregion
  418. #region Changed
  419. var coreChanged = typeBuilder.DefineMethod(
  420. "<>Changed",
  421. virtualMemberMethod,
  422. null, Type.EmptyTypes);
  423. typeBuilder.DefineMethodOverride(coreChanged, IGeneratedStore_Changed);
  424. if (baseChanged != null)
  425. typeBuilder.DefineMethodOverride(coreChanged, baseChanged);
  426. {
  427. var il = coreChanged.GetILGenerator();
  428. if (baseChanged != null)
  429. {
  430. il.Emit(OpCodes.Ldarg_0);
  431. il.Emit(OpCodes.Call, baseChanged); // call base
  432. }
  433. il.Emit(OpCodes.Ldarg_0);
  434. il.Emit(OpCodes.Tailcall);
  435. il.Emit(OpCodes.Call, Impl.ImplSignalChangedMethod);
  436. il.Emit(OpCodes.Ret); // simply call our impl's SignalChanged method and return
  437. }
  438. #endregion
  439. #region ChangeTransaction
  440. var coreChangeTransaction = typeBuilder.DefineMethod(
  441. "<>ChangeTransaction",
  442. virtualMemberMethod,
  443. typeof(IDisposable), Type.EmptyTypes);
  444. typeBuilder.DefineMethodOverride(coreChangeTransaction, IGeneratedStore_ChangeTransaction);
  445. if (baseChangeTransaction != null)
  446. typeBuilder.DefineMethodOverride(coreChangeTransaction, baseChangeTransaction);
  447. {
  448. var il = coreChangeTransaction.GetILGenerator();
  449. il.Emit(OpCodes.Ldarg_0);
  450. if (baseChangeTransaction != null)
  451. {
  452. il.Emit(OpCodes.Ldarg_0);
  453. il.Emit(OpCodes.Call, baseChangeTransaction);
  454. }
  455. else
  456. il.Emit(OpCodes.Ldnull);
  457. il.Emit(OpCodes.Tailcall);
  458. il.Emit(OpCodes.Call, Impl.ImplChangeTransactionMethod);
  459. il.Emit(OpCodes.Ret);
  460. }
  461. #endregion
  462. #region IGeneratedStore<T>
  463. var IGeneratedStore_T_t = typeof(IGeneratedStore<>).MakeGenericType(type);
  464. typeBuilder.AddInterfaceImplementation(IGeneratedStore_T_t);
  465. var IGeneratedStore_T_CopyFrom = IGeneratedStore_T_t.GetMethod(nameof(IGeneratedStore<Config>.CopyFrom));
  466. #region IGeneratedStore<T>.CopyFrom
  467. var copyFrom = typeBuilder.DefineMethod($"<>{nameof(IGeneratedStore<Config>.CopyFrom)}", virtualMemberMethod, null, new[] { type, typeof(bool) });
  468. typeBuilder.DefineMethodOverride(copyFrom, IGeneratedStore_T_CopyFrom);
  469. {
  470. var il = copyFrom.GetILGenerator();
  471. var transactionLocal = il.DeclareLocal(IDisposable_t);
  472. var startLock = il.DefineLabel();
  473. il.Emit(OpCodes.Ldarg_2);
  474. il.Emit(OpCodes.Brfalse, startLock);
  475. il.Emit(OpCodes.Ldarg_0);
  476. il.Emit(OpCodes.Call, coreChangeTransaction); // take the write lock
  477. il.Emit(OpCodes.Stloc, transactionLocal);
  478. il.MarkLabel(startLock);
  479. var GetLocal = MakeGetLocal(il);
  480. foreach (var member in structure)
  481. {
  482. il.BeginExceptionBlock();
  483. EmitStore(il, member, il =>
  484. {
  485. EmitLoad(il, member, il => il.Emit(OpCodes.Ldarg_1));
  486. EmitCorrectMember(il, member, false, false, GetLocal);
  487. });
  488. il.BeginCatchBlock(typeof(Exception));
  489. EmitWarnException(il, $"Error while copying from member {member.Name}");
  490. il.EndExceptionBlock();
  491. }
  492. if (notifyChanged != null)
  493. {
  494. foreach (var member in structure)
  495. {
  496. il.Emit(OpCodes.Ldarg_0);
  497. il.Emit(OpCodes.Ldstr, member.Name);
  498. il.Emit(OpCodes.Call, notifyChanged);
  499. }
  500. }
  501. var endLock = il.DefineLabel();
  502. il.Emit(OpCodes.Ldarg_2);
  503. il.Emit(OpCodes.Brfalse, endLock);
  504. il.Emit(OpCodes.Ldloc, transactionLocal);
  505. il.Emit(OpCodes.Callvirt, IDisposable_Dispose);
  506. il.MarkLabel(endLock);
  507. il.Emit(OpCodes.Ret);
  508. }
  509. #endregion
  510. #endregion
  511. #region base.CopyFrom
  512. if (baseCopyFrom != null)
  513. {
  514. var pubCopyFrom = typeBuilder.DefineMethod(
  515. baseCopyFrom.Name,
  516. virtualMemberMethod,
  517. null, new[] { type });
  518. typeBuilder.DefineMethodOverride(pubCopyFrom, baseCopyFrom);
  519. {
  520. var il = pubCopyFrom.GetILGenerator();
  521. il.Emit(OpCodes.Ldarg_0);
  522. il.Emit(OpCodes.Call, coreChangeTransaction);
  523. il.Emit(OpCodes.Ldarg_0);
  524. il.Emit(OpCodes.Ldarg_1);
  525. il.Emit(OpCodes.Ldc_I4_0);
  526. il.Emit(OpCodes.Call, copyFrom); // call internal
  527. il.Emit(OpCodes.Ldarg_0);
  528. il.Emit(OpCodes.Ldarg_1);
  529. il.Emit(OpCodes.Call, baseCopyFrom); // call base
  530. il.Emit(OpCodes.Tailcall);
  531. il.Emit(OpCodes.Callvirt, IDisposable_Dispose); // dispose transaction (which calls changed)
  532. il.Emit(OpCodes.Ret);
  533. }
  534. }
  535. #endregion
  536. #region Members
  537. foreach (var member in structure.Where(m => m.IsVirtual))
  538. { // IsVirtual implies !IsField
  539. var prop = member.Member as PropertyInfo;
  540. var get = prop.GetGetMethod(true);
  541. var set = prop.GetSetMethod(true);
  542. var propBuilder = typeBuilder.DefineProperty($"{member.Name}#", PropertyAttributes.None, member.Type, null);
  543. var propGet = typeBuilder.DefineMethod($"<g>{propBuilder.Name}", virtualPropertyMethodAttr, member.Type, Type.EmptyTypes);
  544. propBuilder.SetGetMethod(propGet);
  545. typeBuilder.DefineMethodOverride(propGet, get);
  546. {
  547. var il = propGet.GetILGenerator();
  548. var local = il.DeclareLocal(member.Type);
  549. il.Emit(OpCodes.Ldarg_0);
  550. il.Emit(OpCodes.Call, Impl.ImplTakeReadMethod); // take the read lock
  551. il.BeginExceptionBlock();
  552. il.Emit(OpCodes.Ldarg_0);
  553. il.Emit(OpCodes.Call, get); // call base getter
  554. il.Emit(OpCodes.Stloc, local);
  555. il.BeginFinallyBlock();
  556. il.Emit(OpCodes.Ldarg_0);
  557. il.Emit(OpCodes.Call, Impl.ImplReleaseReadMethod); // release the read lock
  558. il.EndExceptionBlock();
  559. il.Emit(OpCodes.Ldloc, local);
  560. il.Emit(OpCodes.Ret);
  561. }
  562. var propSet = typeBuilder.DefineMethod($"<s>{propBuilder.Name}", virtualPropertyMethodAttr, null, new[] { member.Type });
  563. propBuilder.SetSetMethod(propSet);
  564. typeBuilder.DefineMethodOverride(propSet, set);
  565. { // TODO: decide if i want to correct the value before or after i take the write lock
  566. var il = propSet.GetILGenerator();
  567. var transactionLocal = il.DeclareLocal(IDisposable_t);
  568. var GetLocal = MakeGetLocal(il);
  569. il.Emit(OpCodes.Ldarg_0);
  570. il.Emit(OpCodes.Call, coreChangeTransaction); // take the write lock
  571. il.Emit(OpCodes.Stloc, transactionLocal);
  572. il.BeginExceptionBlock();
  573. il.Emit(OpCodes.Ldarg_0);
  574. il.Emit(OpCodes.Ldarg_1);
  575. EmitCorrectMember(il, member, false, false, GetLocal);
  576. il.Emit(OpCodes.Call, set);
  577. il.BeginFinallyBlock();
  578. il.Emit(OpCodes.Ldloc, transactionLocal);
  579. il.Emit(OpCodes.Callvirt, IDisposable_Dispose);
  580. il.EndExceptionBlock();
  581. if (notifyChanged != null)
  582. {
  583. il.Emit(OpCodes.Ldarg_0);
  584. il.Emit(OpCodes.Ldstr, member.Name);
  585. il.Emit(OpCodes.Call, notifyChanged);
  586. }
  587. il.Emit(OpCodes.Ret);
  588. }
  589. }
  590. #endregion
  591. var genType = typeBuilder.CreateType();
  592. var parentParam = Expression.Parameter(typeof(IGeneratedStore), "parent");
  593. var creatorDel = Expression.Lambda<GeneratedStoreCreator>(
  594. Expression.New(ctor, parentParam), parentParam
  595. ).Compile();
  596. return (creatorDel, genType);
  597. }
  598. #region Correction
  599. private static bool NeedsCorrection(SerializedMemberInfo member)
  600. {
  601. if (member.HasConverter) return false;
  602. var memberType = member.ConversionType;
  603. var expectType = GetExpectedValueTypeForType(memberType);
  604. // if we expect a map, and the type is *not* a value type, it can be converted
  605. if (expectType == typeof(Map) && !memberType.IsValueType) // TODO: make this slightly saner
  606. return true;
  607. return false;
  608. }
  609. // expects start value on stack, exits with final value on stack
  610. private static void EmitCorrectMember(ILGenerator il, SerializedMemberInfo member, bool shouldLock, bool alwaysNew, GetLocal GetLocal)
  611. {
  612. if (!NeedsCorrection(member)) return;
  613. // this will never be called for a custom value type
  614. var endLabel = il.DefineLabel();
  615. if (member.IsNullable)
  616. {
  617. il.Emit(OpCodes.Dup);
  618. il.Emit(OpCodes.Call, member.Nullable_HasValue.GetGetMethod());
  619. il.Emit(OpCodes.Brfalse, endLabel);
  620. il.Emit(OpCodes.Call, member.Nullable_Value.GetGetMethod());
  621. }
  622. // TODO: impl the rest of this
  623. // currently the only thing for this is where expect == Map, so do generate shit
  624. var copyFrom = typeof(IGeneratedStore<>).MakeGenericType(member.Type).GetMethod(nameof(IGeneratedStore<Config>.CopyFrom));
  625. var noCreate = il.DefineLabel();
  626. var valLocal = GetLocal(member.Type);
  627. if (!alwaysNew)
  628. {
  629. il.Emit(OpCodes.Dup);
  630. il.Emit(OpCodes.Isinst, typeof(IGeneratedStore));
  631. il.Emit(OpCodes.Brtrue_S, endLabel); // our input is already something we like
  632. }
  633. il.Emit(OpCodes.Stloc, valLocal);
  634. if (!alwaysNew)
  635. {
  636. EmitLoad(il, member, il => il.Emit(OpCodes.Ldarg_0));
  637. il.Emit(OpCodes.Dup);
  638. il.Emit(OpCodes.Isinst, typeof(IGeneratedStore));
  639. il.Emit(OpCodes.Brtrue_S, noCreate);
  640. il.Emit(OpCodes.Pop);
  641. }
  642. EmitCreateChildGenerated(il, member.Type);
  643. il.MarkLabel(noCreate);
  644. il.Emit(OpCodes.Dup);
  645. il.Emit(OpCodes.Ldloc, valLocal);
  646. il.Emit(shouldLock ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
  647. il.Emit(OpCodes.Callvirt, copyFrom);
  648. // TODO: impl the rest of this
  649. if (member.IsNullable)
  650. il.Emit(OpCodes.Newobj, member.Nullable_Construct);
  651. il.MarkLabel(endLabel);
  652. }
  653. #endregion
  654. }
  655. }