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.

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