#nullable enable using IPA.Config.Data; using IPA.Config.Stores.Attributes; using IPA.Logging; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Linq.Expressions; using System.Runtime.CompilerServices; using System.IO; using Boolean = IPA.Config.Data.Boolean; using System.Collections; using IPA.Utilities; using System.ComponentModel; using System.Collections.Concurrent; using IPA.Utilities.Async; #if NET3 using Net3_Proxy; using Array = Net3_Proxy.Array; #endif [assembly: InternalsVisibleTo(IPA.Config.Stores.GeneratedStore.AssemblyVisibilityTarget)] namespace IPA.Config.Stores { internal static partial class GeneratedStoreImpl { public static T Create() where T : class => (T)Create(typeof(T)); public static IConfigStore Create(Type type) => Create(type, null); private static readonly MethodInfo CreateGParent = typeof(GeneratedStoreImpl).GetMethod(nameof(Create), BindingFlags.NonPublic | BindingFlags.Static, null, CallingConventions.Any, new[] { typeof(IGeneratedStore) }, Array.Empty()); internal static T Create(IGeneratedStore? parent) where T : class => (T)Create(typeof(T), parent); private static IConfigStore Create(Type type, IGeneratedStore? parent) => GetCreator(type)(parent); private static readonly SingleCreationValueCache generatedCreators = new(); private static (GeneratedStoreCreator ctor, Type type) GetCreatorAndGeneratedType(Type t) => generatedCreators.GetOrAdd(t, MakeCreator); internal static GeneratedStoreCreator GetCreator(Type t) => GetCreatorAndGeneratedType(t).ctor; internal static Type GetGeneratedType(Type t) => GetCreatorAndGeneratedType(t).type; internal const string GeneratedAssemblyName = "IPA.Config.Generated"; private static AssemblyBuilder? assembly; private static AssemblyBuilder Assembly { get { if (assembly == null) { var name = new AssemblyName(GeneratedAssemblyName); assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave); } return assembly; } } internal static void DebugSaveAssembly(string file) { Assembly.Save(file); } private static ModuleBuilder? module; private static ModuleBuilder Module { get { if (module == null) module = Assembly.DefineDynamicModule(Assembly.GetName().Name, Assembly.GetName().Name + ".dll"); return module; } } // TODO: does this need to be a SingleCreationValueCache or similar? private static readonly Dictionary> TypeRequiredConverters = new(); private static void CreateAndInitializeConvertersFor(Type type, IEnumerable structure) { if (!TypeRequiredConverters.TryGetValue(type, out var converters)) { var converterFieldType = Module.DefineType($"{type.FullName}", TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Abstract | TypeAttributes.AnsiClass); // a static class var uniqueConverterTypes = structure.Where(m => m.HasConverter) .Select(m => m.Converter).NonNull().Distinct().ToArray(); converters = new Dictionary(uniqueConverterTypes.Length); foreach (var convType in uniqueConverterTypes) { var field = converterFieldType.DefineField($"_{convType}", convType, FieldAttributes.FamORAssem | FieldAttributes.InitOnly | FieldAttributes.Static); converters.Add(convType, field); } var cctor = converterFieldType.DefineConstructor(MethodAttributes.Static, CallingConventions.Standard, Type.EmptyTypes); { var il = cctor.GetILGenerator(); foreach (var kvp in converters) { var typeCtor = kvp.Key.GetConstructor(Type.EmptyTypes); il.Emit(OpCodes.Newobj, typeCtor); il.Emit(OpCodes.Stsfld, kvp.Value); } il.Emit(OpCodes.Ret); } TypeRequiredConverters.Add(type, converters); _ = converterFieldType.CreateType(); } foreach (var member in structure) { if (!member.HasConverter) continue; member.ConverterField = converters[member.Converter]; } } } }