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

#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<T>() 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<ParameterModifier>());
internal static T Create<T>(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<Type, (GeneratedStoreCreator ctor, Type type)> 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)
{
try
{
Assembly.Save(file);
}
catch (Exception ex)
{
Logger.Config.Error(ex);
}
}
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<Type, Dictionary<Type, FieldInfo>> TypeRequiredConverters = new();
private static void CreateAndInitializeConvertersFor(Type type, IEnumerable<SerializedMemberInfo> structure)
{
if (!TypeRequiredConverters.TryGetValue(type, out var converters))
{
var converterFieldType = Module.DefineType($"{type.FullName}<Converters>",
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<Type, FieldInfo>(uniqueConverterTypes.Length);
foreach (var convType in uniqueConverterTypes)
{
var field = converterFieldType.DefineField($"<converter>_{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];
}
}
}
}