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.

741 lines
35 KiB

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