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.

755 lines
36 KiB

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