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.

357 lines
15 KiB

  1. using IPA.Config.Data;
  2. using IPA.Logging;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Reflection;
  7. using System.Reflection.Emit;
  8. using System.Text;
  9. using System.Threading;
  10. using System.Threading.Tasks;
  11. namespace IPA.Config.Stores
  12. {
  13. internal static class GeneratedStore
  14. {
  15. private interface IGeneratedStore
  16. {
  17. /// <summary>
  18. /// serializes/deserializes to Value
  19. /// </summary>
  20. Value Values { get; set; }
  21. Type Type { get; }
  22. IGeneratedStore Parent { get; }
  23. Impl Impl { get; }
  24. }
  25. private class Impl : IConfigStore
  26. {
  27. private IGeneratedStore generated;
  28. internal static ConstructorInfo Ctor = typeof(Impl).GetConstructor(new[] { typeof(IGeneratedStore) });
  29. public Impl(IGeneratedStore store) => generated = store;
  30. private readonly AutoResetEvent resetEvent = new AutoResetEvent(false);
  31. public WaitHandle SyncObject => resetEvent;
  32. public ReaderWriterLockSlim WriteSyncObject { get; } = new ReaderWriterLockSlim();
  33. internal static MethodInfo ImplSignalChangedMethod = typeof(Impl).GetMethod(nameof(ImplSignalChanged));
  34. internal static void ImplSignalChanged(IGeneratedStore s) => FindImpl(s).SignalChanged();
  35. internal void SignalChanged() => resetEvent.Set();
  36. internal static MethodInfo ImplTakeReadMethod = typeof(Impl).GetMethod(nameof(ImplTakeRead));
  37. internal static void ImplTakeRead(IGeneratedStore s) => FindImpl(s).TakeRead();
  38. internal void TakeRead() => WriteSyncObject.EnterReadLock();
  39. internal static MethodInfo ImplReleaseReadMethod = typeof(Impl).GetMethod(nameof(ImplReleaseRead));
  40. internal static void ImplReleaseRead(IGeneratedStore s) => FindImpl(s).ReleaseRead();
  41. internal void ReleaseRead() => WriteSyncObject.ExitWriteLock();
  42. internal static MethodInfo ImplTakeWriteMethod = typeof(Impl).GetMethod(nameof(ImplTakeWrite));
  43. internal static void ImplTakeWrite(IGeneratedStore s) => FindImpl(s).TakeWrite();
  44. internal void TakeWrite() => WriteSyncObject.EnterWriteLock();
  45. internal static MethodInfo ImplReleaseWriteMethod = typeof(Impl).GetMethod(nameof(ImplReleaseWrite));
  46. internal static void ImplReleaseWrite(IGeneratedStore s) => FindImpl(s).ReleaseWrite();
  47. internal void ReleaseWrite() => WriteSyncObject.ExitWriteLock();
  48. internal static MethodInfo FindImplMethod = typeof(Impl).GetMethod(nameof(FindImpl));
  49. internal static Impl FindImpl(IGeneratedStore store)
  50. {
  51. while (store != null) store = store.Parent; // walk to the top of the tree
  52. return store?.Impl;
  53. }
  54. internal static MethodInfo ReadFromMethod = typeof(Impl).GetMethod(nameof(ReadFrom));
  55. public void ReadFrom(IConfigProvider provider)
  56. {
  57. // TODO: implement
  58. Logger.config.Debug("Generated impl ReadFrom");
  59. }
  60. internal static MethodInfo WriteToMethod = typeof(Impl).GetMethod(nameof(WriteTo));
  61. public void WriteTo(IConfigProvider provider)
  62. {
  63. var values = generated.Values;
  64. // TODO: implement
  65. Logger.config.Debug("Generated impl WriteTo");
  66. }
  67. }
  68. private static Dictionary<Type, Func<IGeneratedStore, IConfigStore>> generatedCreators = new Dictionary<Type, Func<IGeneratedStore, IConfigStore>>();
  69. private static Dictionary<Type, Dictionary<string, Type>> memberMaps = new Dictionary<Type, Dictionary<string, Type>>();
  70. public static T Create<T>() where T : class => (T)Create(typeof(T));
  71. public static IConfigStore Create(Type type) => Create(type, null);
  72. private static IConfigStore Create(Type type, IGeneratedStore parent)
  73. {
  74. if (generatedCreators.TryGetValue(type, out var creator))
  75. return creator(parent);
  76. else
  77. {
  78. creator = MakeCreator(type);
  79. generatedCreators.Add(type, creator);
  80. return creator(parent);
  81. }
  82. }
  83. private static AssemblyBuilder assembly = null;
  84. private static AssemblyBuilder Assembly
  85. {
  86. get
  87. {
  88. if (assembly == null)
  89. {
  90. var name = new AssemblyName("IPA.Config.Generated");
  91. assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave);
  92. }
  93. return assembly;
  94. }
  95. }
  96. private static ModuleBuilder module = null;
  97. private static ModuleBuilder Module
  98. {
  99. get
  100. {
  101. if (module == null)
  102. module = Assembly.DefineDynamicModule(Assembly.GetName().Name);
  103. return module;
  104. }
  105. }
  106. private struct SerializedMemberInfo
  107. {
  108. public string Name;
  109. public MemberInfo Member;
  110. public bool IsVirtual;
  111. public Type Type;
  112. }
  113. private static Func<IGeneratedStore, IConfigStore> MakeCreator(Type type)
  114. {
  115. var baseCtor = type.GetConstructor(Type.EmptyTypes); // get a default constructor
  116. if (baseCtor == null)
  117. throw new ArgumentException("Config type does not have a public parameterless constructor");
  118. var typeBuilder = Module.DefineType($"{type.FullName}.Generated",
  119. TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Class, type);
  120. var typeField = typeBuilder.DefineField("<>_type", typeof(Type), FieldAttributes.Private | FieldAttributes.InitOnly);
  121. var implField = typeBuilder.DefineField("<>_impl", typeof(Impl), FieldAttributes.Private | FieldAttributes.InitOnly);
  122. var parentField = typeBuilder.DefineField("<>_parent", typeof(IGeneratedStore), FieldAttributes.Private | FieldAttributes.InitOnly);
  123. var GetTypeFromHandle = typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle));
  124. #region Parse base object structure
  125. var baseChanged = type.GetMethod("Changed", BindingFlags.Public | BindingFlags.Instance, null, Type.EmptyTypes, Array.Empty<ParameterModifier>());
  126. if (baseChanged != null && !baseChanged.IsVirtual) baseChanged = null; // limit this to just the one thing
  127. var structure = new Dictionary<string, SerializedMemberInfo>();
  128. // TODO: incorporate attributes
  129. // only looks at public properties
  130. foreach (var prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public))
  131. {
  132. var smi = new SerializedMemberInfo
  133. {
  134. Name = prop.Name,
  135. Member = prop,
  136. IsVirtual = (prop.GetGetMethod(true)?.IsVirtual ?? false) ||
  137. (prop.GetSetMethod(true)?.IsVirtual ?? false),
  138. Type = prop.PropertyType
  139. };
  140. structure.Add(smi.Name, smi);
  141. }
  142. // only look at public fields
  143. foreach (var field in type.GetFields(BindingFlags.Instance | BindingFlags.Public))
  144. {
  145. var smi = new SerializedMemberInfo
  146. {
  147. Name = field.Name,
  148. Member = field,
  149. IsVirtual = false,
  150. Type = field.FieldType
  151. };
  152. structure.Add(smi.Name, smi);
  153. }
  154. #endregion
  155. #region Constructor
  156. // takes its parent
  157. var ctor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new[] { typeof(IGeneratedStore) });
  158. {
  159. var il = ctor.GetILGenerator();
  160. il.Emit(OpCodes.Ldarg_0); // keep this at bottom of stack
  161. il.Emit(OpCodes.Dup);
  162. il.Emit(OpCodes.Call, baseCtor);
  163. il.Emit(OpCodes.Dup);
  164. il.Emit(OpCodes.Ldarg_1); // load parent
  165. il.Emit(OpCodes.Stfld, parentField);
  166. il.Emit(OpCodes.Dup);
  167. il.Emit(OpCodes.Ldtoken, type);
  168. il.Emit(OpCodes.Call, GetTypeFromHandle); // effectively typeof(type)
  169. il.Emit(OpCodes.Stfld, typeField);
  170. il.Emit(OpCodes.Dup);
  171. il.Emit(OpCodes.Dup);
  172. il.Emit(OpCodes.Newobj, Impl.Ctor);
  173. il.Emit(OpCodes.Stfld, implField);
  174. foreach (var kvp in structure)
  175. EmitMemberFix(il, kvp.Value);
  176. il.Emit(OpCodes.Pop);
  177. il.Emit(OpCodes.Ret);
  178. }
  179. #endregion
  180. const MethodAttributes propertyMethodAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
  181. const MethodAttributes virtualPropertyMethodAttr = propertyMethodAttr | MethodAttributes.Virtual | MethodAttributes.Final;
  182. #region IGeneratedStore
  183. typeBuilder.AddInterfaceImplementation(typeof(IGeneratedStore));
  184. var IGeneratedStore_t = typeof(IGeneratedStore);
  185. var IGeneratedStore_GetImpl = IGeneratedStore_t.GetProperty(nameof(IGeneratedStore.Impl)).GetGetMethod();
  186. var IGeneratedStore_GetType = IGeneratedStore_t.GetProperty(nameof(IGeneratedStore.Type)).GetGetMethod();
  187. var IGeneratedStore_GetParent = IGeneratedStore_t.GetProperty(nameof(IGeneratedStore.Parent)).GetGetMethod();
  188. var IGeneratedStore_GetValues = IGeneratedStore_t.GetProperty(nameof(IGeneratedStore.Values)).GetGetMethod();
  189. var IGeneratedStore_SetValues = IGeneratedStore_t.GetProperty(nameof(IGeneratedStore.Values)).GetSetMethod();
  190. #region IGeneratedStore.Impl
  191. var implProp = typeBuilder.DefineProperty(nameof(IGeneratedStore.Impl), PropertyAttributes.None, typeof(Impl), null);
  192. var implPropGet = typeBuilder.DefineMethod($"<g>{nameof(IGeneratedStore.Impl)}", virtualPropertyMethodAttr, implProp.PropertyType, Type.EmptyTypes);
  193. implProp.SetGetMethod(implPropGet);
  194. typeBuilder.DefineMethodOverride(implPropGet, IGeneratedStore_GetImpl);
  195. {
  196. var il = implPropGet.GetILGenerator();
  197. il.Emit(OpCodes.Ldarg_0); // load this
  198. il.Emit(OpCodes.Ldfld, implField); // load impl field
  199. il.Emit(OpCodes.Ret);
  200. }
  201. #endregion
  202. #region IGeneratedStore.Type
  203. var typeProp = typeBuilder.DefineProperty(nameof(IGeneratedStore.Type), PropertyAttributes.None, typeof(Type), null);
  204. var typePropGet = typeBuilder.DefineMethod($"<g>{nameof(IGeneratedStore.Type)}", virtualPropertyMethodAttr, typeProp.PropertyType, Type.EmptyTypes);
  205. typeProp.SetGetMethod(typePropGet);
  206. typeBuilder.DefineMethodOverride(typePropGet, IGeneratedStore_GetType);
  207. {
  208. var il = typePropGet.GetILGenerator();
  209. il.Emit(OpCodes.Ldarg_0); // load this
  210. il.Emit(OpCodes.Ldfld, typeField); // load impl field
  211. il.Emit(OpCodes.Ret);
  212. }
  213. #endregion
  214. #region IGeneratedStore.Parent
  215. var parentProp = typeBuilder.DefineProperty(nameof(IGeneratedStore.Parent), PropertyAttributes.None, typeof(IGeneratedStore), null);
  216. var parentPropGet = typeBuilder.DefineMethod($"<g>{nameof(IGeneratedStore.Parent)}", virtualPropertyMethodAttr, parentProp.PropertyType, Type.EmptyTypes);
  217. parentProp.SetGetMethod(parentPropGet);
  218. typeBuilder.DefineMethodOverride(parentPropGet, IGeneratedStore_GetParent);
  219. {
  220. var il = parentPropGet.GetILGenerator();
  221. il.Emit(OpCodes.Ldarg_0); // load this
  222. il.Emit(OpCodes.Ldfld, parentField); // load impl field
  223. il.Emit(OpCodes.Ret);
  224. }
  225. #endregion
  226. #region IGeneratedStore.Values
  227. var valuesProp = typeBuilder.DefineProperty(nameof(IGeneratedStore.Values), PropertyAttributes.None, typeof(Value), null);
  228. var valuesPropGet = typeBuilder.DefineMethod($"<g>{nameof(IGeneratedStore.Values)}", virtualPropertyMethodAttr, valuesProp.PropertyType, Type.EmptyTypes);
  229. var valuesPropSet = typeBuilder.DefineMethod($"<s>{nameof(IGeneratedStore.Values)}", virtualPropertyMethodAttr, null, new[] { valuesProp.PropertyType });
  230. valuesProp.SetGetMethod(valuesPropGet);
  231. typeBuilder.DefineMethodOverride(valuesPropGet, IGeneratedStore_GetValues);
  232. valuesProp.SetSetMethod(valuesPropSet);
  233. typeBuilder.DefineMethodOverride(valuesPropSet, IGeneratedStore_SetValues);
  234. { // this is non-locking because the only code that will call this will already own the correct lock
  235. var il = valuesPropGet.GetILGenerator();
  236. // TODO: implement get_Values
  237. il.Emit(OpCodes.Ldnull);
  238. il.Emit(OpCodes.Ret);
  239. }
  240. { // this is non-locking because the only code that will call this will already own the correct lock
  241. var il = valuesPropSet.GetILGenerator();
  242. // TODO: implement set_Values
  243. il.Emit(OpCodes.Ret);
  244. }
  245. #endregion
  246. #endregion
  247. #region Changed
  248. var coreChanged = typeBuilder.DefineMethod(
  249. "<>Changed",
  250. MethodAttributes.Public | MethodAttributes.HideBySig,
  251. null, Type.EmptyTypes);
  252. {
  253. var il = coreChanged.GetILGenerator();
  254. il.Emit(OpCodes.Ldarg_0);
  255. il.Emit(OpCodes.Call, Impl.ImplSignalChangedMethod);
  256. il.Emit(OpCodes.Ret); // simply call our impl's SignalChanged method and return
  257. }
  258. if (baseChanged != null) {
  259. var changedMethod = typeBuilder.DefineMethod( // copy to override baseChanged
  260. baseChanged.Name,
  261. MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.HideBySig,
  262. null, Type.EmptyTypes);
  263. typeBuilder.DefineMethodOverride(changedMethod, baseChanged);
  264. {
  265. var il = changedMethod.GetILGenerator();
  266. il.Emit(OpCodes.Ldarg_0);
  267. il.Emit(OpCodes.Call, baseChanged); // call base
  268. il.Emit(OpCodes.Ldarg_0);
  269. il.Emit(OpCodes.Tailcall);
  270. il.Emit(OpCodes.Call, coreChanged); // call back to the core change method
  271. il.Emit(OpCodes.Ret);
  272. }
  273. coreChanged = changedMethod; // switch to calling this version instead of just the default
  274. }
  275. #endregion
  276. // TODO: generate overrides for all the virtual properties
  277. return null;
  278. }
  279. // expects the this param to be on the stack
  280. private static void EmitMemberFix(ILGenerator il, SerializedMemberInfo member)
  281. {
  282. }
  283. }
  284. }