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.

143 lines
5.3 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.Linq;
  8. using System.Reflection;
  9. using System.Reflection.Emit;
  10. using System.Text;
  11. using System.Threading;
  12. using System.Threading.Tasks;
  13. using System.Linq.Expressions;
  14. using System.Runtime.CompilerServices;
  15. using System.IO;
  16. using Boolean = IPA.Config.Data.Boolean;
  17. using System.Collections;
  18. using IPA.Utilities;
  19. using System.ComponentModel;
  20. using System.Collections.Concurrent;
  21. using IPA.Utilities.Async;
  22. #if NET3
  23. using Net3_Proxy;
  24. using Array = Net3_Proxy.Array;
  25. #endif
  26. [assembly: InternalsVisibleTo(IPA.Config.Stores.GeneratedStore.AssemblyVisibilityTarget)]
  27. namespace IPA.Config.Stores
  28. {
  29. internal static partial class GeneratedStoreImpl
  30. {
  31. public static T Create<T>() where T : class => (T)Create(typeof(T));
  32. public static IConfigStore Create(Type type) => Create(type, null);
  33. private static readonly MethodInfo CreateGParent =
  34. typeof(GeneratedStoreImpl).GetMethod(nameof(Create), BindingFlags.NonPublic | BindingFlags.Static, null,
  35. CallingConventions.Any, new[] { typeof(IGeneratedStore) }, Array.Empty<ParameterModifier>());
  36. internal static T Create<T>(IGeneratedStore? parent) where T : class => (T)Create(typeof(T), parent);
  37. private static IConfigStore Create(Type type, IGeneratedStore? parent)
  38. => GetCreator(type)(parent);
  39. private static readonly SingleCreationValueCache<Type, (GeneratedStoreCreator ctor, Type type)> generatedCreators = new();
  40. private static (GeneratedStoreCreator ctor, Type type) GetCreatorAndGeneratedType(Type t)
  41. => generatedCreators.GetOrAdd(t, MakeCreator);
  42. internal static GeneratedStoreCreator GetCreator(Type t)
  43. => GetCreatorAndGeneratedType(t).ctor;
  44. internal static Type GetGeneratedType(Type t)
  45. => GetCreatorAndGeneratedType(t).type;
  46. internal const string GeneratedAssemblyName = "IPA.Config.Generated";
  47. private static AssemblyBuilder? assembly;
  48. private static AssemblyBuilder Assembly
  49. {
  50. get
  51. {
  52. if (assembly == null)
  53. {
  54. var name = new AssemblyName(GeneratedAssemblyName);
  55. assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave);
  56. }
  57. return assembly;
  58. }
  59. }
  60. internal static void DebugSaveAssembly(string file)
  61. {
  62. try
  63. {
  64. Assembly.Save(file);
  65. }
  66. catch (Exception ex)
  67. {
  68. Logger.Config.Error(ex);
  69. }
  70. }
  71. private static ModuleBuilder? module;
  72. private static ModuleBuilder Module
  73. {
  74. get
  75. {
  76. if (module == null)
  77. module = Assembly.DefineDynamicModule(Assembly.GetName().Name, Assembly.GetName().Name + ".dll");
  78. return module;
  79. }
  80. }
  81. // TODO: does this need to be a SingleCreationValueCache or similar?
  82. private static readonly Dictionary<Type, Dictionary<Type, FieldInfo>> TypeRequiredConverters = new();
  83. private static void CreateAndInitializeConvertersFor(Type type, IEnumerable<SerializedMemberInfo> structure)
  84. {
  85. if (!TypeRequiredConverters.TryGetValue(type, out var converters))
  86. {
  87. var converterFieldType = Module.DefineType($"{type.FullName}<Converters>",
  88. TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Abstract | TypeAttributes.AnsiClass); // a static class
  89. var uniqueConverterTypes = structure.Where(m => m.HasConverter)
  90. .Select(m => m.Converter).NonNull().Distinct().ToArray();
  91. converters = new Dictionary<Type, FieldInfo>(uniqueConverterTypes.Length);
  92. foreach (var convType in uniqueConverterTypes)
  93. {
  94. var field = converterFieldType.DefineField($"<converter>_{convType}", convType,
  95. FieldAttributes.FamORAssem | FieldAttributes.InitOnly | FieldAttributes.Static);
  96. converters.Add(convType, field);
  97. }
  98. var cctor = converterFieldType.DefineConstructor(MethodAttributes.Static, CallingConventions.Standard, Type.EmptyTypes);
  99. {
  100. var il = cctor.GetILGenerator();
  101. foreach (var kvp in converters)
  102. {
  103. var typeCtor = kvp.Key.GetConstructor(Type.EmptyTypes);
  104. il.Emit(OpCodes.Newobj, typeCtor);
  105. il.Emit(OpCodes.Stsfld, kvp.Value);
  106. }
  107. il.Emit(OpCodes.Ret);
  108. }
  109. TypeRequiredConverters.Add(type, converters);
  110. _ = converterFieldType.CreateType();
  111. }
  112. foreach (var member in structure)
  113. {
  114. if (!member.HasConverter) continue;
  115. member.ConverterField = converters[member.Converter];
  116. }
  117. }
  118. }
  119. }