using IPA.Config.Data;
|
|
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;
|
|
|
|
namespace IPA.Config.Stores
|
|
{
|
|
internal static class GeneratedStore
|
|
{
|
|
private interface IGeneratedStore
|
|
{
|
|
/// <summary>
|
|
/// serializes/deserializes to Value
|
|
/// </summary>
|
|
Value Values { get; set; }
|
|
Type Type { get; }
|
|
IGeneratedStore Parent { get; }
|
|
Impl Impl { get; }
|
|
}
|
|
|
|
private class Impl : IConfigStore
|
|
{
|
|
private IGeneratedStore generated;
|
|
|
|
internal static ConstructorInfo Ctor = typeof(Impl).GetConstructor(new[] { typeof(IGeneratedStore) });
|
|
public Impl(IGeneratedStore store) => generated = store;
|
|
|
|
private readonly AutoResetEvent resetEvent = new AutoResetEvent(false);
|
|
public WaitHandle SyncObject => resetEvent;
|
|
|
|
public ReaderWriterLockSlim WriteSyncObject { get; } = new ReaderWriterLockSlim();
|
|
|
|
internal static MethodInfo ImplSignalChangedMethod = typeof(Impl).GetMethod(nameof(ImplSignalChanged));
|
|
internal static void ImplSignalChanged(IGeneratedStore s) => FindImpl(s).SignalChanged();
|
|
internal void SignalChanged() => resetEvent.Set();
|
|
|
|
internal static MethodInfo ImplTakeReadMethod = typeof(Impl).GetMethod(nameof(ImplTakeRead));
|
|
internal static void ImplTakeRead(IGeneratedStore s) => FindImpl(s).TakeRead();
|
|
internal void TakeRead() => WriteSyncObject.EnterReadLock();
|
|
|
|
internal static MethodInfo ImplReleaseReadMethod = typeof(Impl).GetMethod(nameof(ImplReleaseRead));
|
|
internal static void ImplReleaseRead(IGeneratedStore s) => FindImpl(s).ReleaseRead();
|
|
internal void ReleaseRead() => WriteSyncObject.ExitWriteLock();
|
|
|
|
internal static MethodInfo ImplTakeWriteMethod = typeof(Impl).GetMethod(nameof(ImplTakeWrite));
|
|
internal static void ImplTakeWrite(IGeneratedStore s) => FindImpl(s).TakeWrite();
|
|
internal void TakeWrite() => WriteSyncObject.EnterWriteLock();
|
|
|
|
internal static MethodInfo ImplReleaseWriteMethod = typeof(Impl).GetMethod(nameof(ImplReleaseWrite));
|
|
internal static void ImplReleaseWrite(IGeneratedStore s) => FindImpl(s).ReleaseWrite();
|
|
internal void ReleaseWrite() => WriteSyncObject.ExitWriteLock();
|
|
|
|
internal static MethodInfo FindImplMethod = typeof(Impl).GetMethod(nameof(FindImpl));
|
|
internal static Impl FindImpl(IGeneratedStore store)
|
|
{
|
|
while (store != null) store = store.Parent; // walk to the top of the tree
|
|
return store?.Impl;
|
|
}
|
|
|
|
|
|
|
|
internal static MethodInfo ReadFromMethod = typeof(Impl).GetMethod(nameof(ReadFrom));
|
|
public void ReadFrom(IConfigProvider provider)
|
|
{
|
|
// TODO: implement
|
|
}
|
|
|
|
internal static MethodInfo WriteToMethod = typeof(Impl).GetMethod(nameof(WriteTo));
|
|
public void WriteTo(IConfigProvider provider)
|
|
{
|
|
var values = generated.Values;
|
|
// TODO: implement
|
|
}
|
|
}
|
|
|
|
private static Dictionary<Type, Func<IGeneratedStore, IConfigStore>> generatedCreators = new Dictionary<Type, Func<IGeneratedStore, IConfigStore>>();
|
|
private static Dictionary<Type, Dictionary<string, Type>> memberMaps = new Dictionary<Type, Dictionary<string, Type>>();
|
|
|
|
public static T Create<T>() where T : class => (T)Create(typeof(T));
|
|
|
|
public static IConfigStore Create(Type type) => Create(type, null);
|
|
|
|
private static IConfigStore Create(Type type, IGeneratedStore parent)
|
|
{
|
|
if (generatedCreators.TryGetValue(type, out var creator))
|
|
return creator(parent);
|
|
else
|
|
{
|
|
creator = MakeCreator(type);
|
|
generatedCreators.Add(type, creator);
|
|
return creator(parent);
|
|
}
|
|
}
|
|
|
|
private static AssemblyBuilder assembly = null;
|
|
private static AssemblyBuilder Assembly
|
|
{
|
|
get
|
|
{
|
|
if (assembly == null)
|
|
{
|
|
var name = new AssemblyName("IPA.Config.Generated");
|
|
assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run);
|
|
}
|
|
|
|
return assembly;
|
|
}
|
|
}
|
|
private static ModuleBuilder module = null;
|
|
private static ModuleBuilder Module
|
|
{
|
|
get
|
|
{
|
|
if (module == null)
|
|
module = Assembly.DefineDynamicModule(Assembly.GetName().Name);
|
|
|
|
return module;
|
|
}
|
|
}
|
|
|
|
private static Func<IGeneratedStore, IConfigStore> MakeCreator(Type type)
|
|
{
|
|
var typeBuilder = Module.DefineType($"{type.FullName}.Generated",
|
|
TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Class, type);
|
|
|
|
var typeField = typeBuilder.DefineField("<>_type", typeof(Type), FieldAttributes.Private | FieldAttributes.InitOnly);
|
|
var implField = typeBuilder.DefineField("<>_impl", typeof(Impl), FieldAttributes.Private | FieldAttributes.InitOnly);
|
|
var parentField = typeBuilder.DefineField("<>_parent", typeof(IGeneratedStore), FieldAttributes.Private | FieldAttributes.InitOnly);
|
|
|
|
const MethodAttributes propertyMethodAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
|
|
|
|
#region IGeneratedStore
|
|
typeBuilder.AddInterfaceImplementation(typeof(IGeneratedStore));
|
|
|
|
#region IGeneratedStore.Impl
|
|
var implProp = typeBuilder.DefineProperty(nameof(IGeneratedStore.Impl), PropertyAttributes.None, typeof(Impl), null);
|
|
var implPropGet = typeBuilder.DefineMethod($"get_{nameof(IGeneratedStore.Impl)}", propertyMethodAttr, implProp.PropertyType, Type.EmptyTypes);
|
|
implProp.SetGetMethod(implPropGet);
|
|
|
|
{
|
|
var il = implPropGet.GetILGenerator();
|
|
|
|
il.Emit(OpCodes.Ldarg_0); // load this
|
|
il.Emit(OpCodes.Ldfld, implField); // load impl field
|
|
il.Emit(OpCodes.Ret);
|
|
}
|
|
#endregion
|
|
#region IGeneratedStore.Type
|
|
var typeProp = typeBuilder.DefineProperty(nameof(IGeneratedStore.Type), PropertyAttributes.None, typeof(Type), null);
|
|
var typePropGet = typeBuilder.DefineMethod($"get_{nameof(IGeneratedStore.Type)}", propertyMethodAttr, typeProp.PropertyType, Type.EmptyTypes);
|
|
typeProp.SetGetMethod(typePropGet);
|
|
|
|
{
|
|
var il = typePropGet.GetILGenerator();
|
|
|
|
il.Emit(OpCodes.Ldarg_0); // load this
|
|
il.Emit(OpCodes.Ldfld, typeField); // load impl field
|
|
il.Emit(OpCodes.Ret);
|
|
}
|
|
#endregion
|
|
#region IGeneratedStore.Parent
|
|
var parentProp = typeBuilder.DefineProperty(nameof(IGeneratedStore.Parent), PropertyAttributes.None, typeof(IGeneratedStore), null);
|
|
var parentPropGet = typeBuilder.DefineMethod($"get_{nameof(IGeneratedStore.Parent)}", propertyMethodAttr, parentProp.PropertyType, Type.EmptyTypes);
|
|
parentProp.SetGetMethod(parentPropGet);
|
|
|
|
{
|
|
var il = parentPropGet.GetILGenerator();
|
|
|
|
il.Emit(OpCodes.Ldarg_0); // load this
|
|
il.Emit(OpCodes.Ldfld, parentField); // load impl field
|
|
il.Emit(OpCodes.Ret);
|
|
}
|
|
#endregion
|
|
#region IGeneratedStore.Values
|
|
var valuesProp = typeBuilder.DefineProperty(nameof(IGeneratedStore.Values), PropertyAttributes.None, typeof(Value), null);
|
|
var valuesPropGet = typeBuilder.DefineMethod($"get_{nameof(IGeneratedStore.Values)}", propertyMethodAttr, valuesProp.PropertyType, Type.EmptyTypes);
|
|
var valuesPropSet = typeBuilder.DefineMethod($"set_{nameof(IGeneratedStore.Values)}", propertyMethodAttr, null, new[] { valuesProp.PropertyType });
|
|
valuesProp.SetGetMethod(valuesPropGet);
|
|
valuesProp.SetSetMethod(valuesPropSet);
|
|
|
|
{ // this is non-locking because the only code that will call this will already own the correct lock
|
|
var il = valuesPropGet.GetILGenerator();
|
|
|
|
// TODO: implement get_Values
|
|
il.Emit(OpCodes.Ldnull);
|
|
|
|
il.Emit(OpCodes.Ret);
|
|
}
|
|
|
|
{ // this is non-locking because the only code that will call this will already own the correct lock
|
|
var il = valuesPropSet.GetILGenerator();
|
|
|
|
// TODO: implement set_Values
|
|
|
|
il.Emit(OpCodes.Ret);
|
|
}
|
|
#endregion
|
|
#endregion
|
|
|
|
return null;
|
|
}
|
|
|
|
}
|
|
}
|