Browse Source

Fixed EventInfo accesses to be compatible with .NET 3.5

pull/46/head
Anairkoen Schno 4 years ago
parent
commit
d37eb488bf
1 changed files with 364 additions and 364 deletions
  1. +364
    -364
      IPA.Loader/Config/Stores/GeneratedStoreImpl.cs

+ 364
- 364
IPA.Loader/Config/Stores/GeneratedStoreImpl.cs View File

@ -15,12 +15,12 @@ using System.IO;
using Boolean = IPA.Config.Data.Boolean; using Boolean = IPA.Config.Data.Boolean;
using System.Collections; using System.Collections;
using IPA.Utilities; using IPA.Utilities;
using System.ComponentModel;
using System.ComponentModel;
#if NET3 #if NET3
using Net3_Proxy; using Net3_Proxy;
using Array = Net3_Proxy.Array; using Array = Net3_Proxy.Array;
#endif #endif
[assembly: InternalsVisibleTo(IPA.Config.Stores.GeneratedStore.AssemblyVisibilityTarget)] [assembly: InternalsVisibleTo(IPA.Config.Stores.GeneratedStore.AssemblyVisibilityTarget)]
namespace IPA.Config.Stores namespace IPA.Config.Stores
@ -107,16 +107,16 @@ namespace IPA.Config.Stores
return ret; return ret;
} }
/// <summary>
/// Creates a generated store outside of the context of the config system.
/// </summary>
/// <remarks>
/// See <see cref="Generated{T}(Config, bool)"/> for more information about how it behaves.
/// </remarks>
/// <typeparam name="T">the type to wrap</typeparam>
/// <summary>
/// Creates a generated store outside of the context of the config system.
/// </summary>
/// <remarks>
/// See <see cref="Generated{T}(Config, bool)"/> for more information about how it behaves.
/// </remarks>
/// <typeparam name="T">the type to wrap</typeparam>
/// <returns>a generated instance of <typeparamref name="T"/> implementing functionality described by <see cref="Generated{T}(Config, bool)"/></returns> /// <returns>a generated instance of <typeparamref name="T"/> implementing functionality described by <see cref="Generated{T}(Config, bool)"/></returns>
/// <seealso cref="Generated{T}(Config, bool)"/> /// <seealso cref="Generated{T}(Config, bool)"/>
public static T Create<T>() where T : class
public static T Create<T>() where T : class
=> GeneratedStoreImpl.Create<T>(); => GeneratedStoreImpl.Create<T>();
} }
@ -139,7 +139,7 @@ namespace IPA.Config.Stores
{ {
void CopyFrom(T source, bool useLock); void CopyFrom(T source, bool useLock);
} }
internal interface IGeneratedPropertyChanged : INotifyPropertyChanged
internal interface IGeneratedPropertyChanged : INotifyPropertyChanged
{ {
PropertyChangedEventHandler PropertyChangedEvent { get; } PropertyChangedEventHandler PropertyChangedEvent { get; }
} }
@ -172,18 +172,18 @@ namespace IPA.Config.Stores
internal static MethodInfo ImplTakeReadMethod = typeof(Impl).GetMethod(nameof(ImplTakeRead)); internal static MethodInfo ImplTakeReadMethod = typeof(Impl).GetMethod(nameof(ImplTakeRead));
public static void ImplTakeRead(IGeneratedStore s) => FindImpl(s).TakeRead(); public static void ImplTakeRead(IGeneratedStore s) => FindImpl(s).TakeRead();
public void TakeRead()
public void TakeRead()
{ {
if (!WriteSyncObject.IsWriteLockHeld) if (!WriteSyncObject.IsWriteLockHeld)
WriteSyncObject.EnterReadLock();
WriteSyncObject.EnterReadLock();
} }
internal static MethodInfo ImplReleaseReadMethod = typeof(Impl).GetMethod(nameof(ImplReleaseRead)); internal static MethodInfo ImplReleaseReadMethod = typeof(Impl).GetMethod(nameof(ImplReleaseRead));
public static void ImplReleaseRead(IGeneratedStore s) => FindImpl(s).ReleaseRead(); public static void ImplReleaseRead(IGeneratedStore s) => FindImpl(s).ReleaseRead();
public void ReleaseRead()
public void ReleaseRead()
{ {
if (!WriteSyncObject.IsWriteLockHeld)
WriteSyncObject.ExitReadLock();
if (!WriteSyncObject.IsWriteLockHeld)
WriteSyncObject.ExitReadLock();
} }
internal static MethodInfo ImplTakeWriteMethod = typeof(Impl).GetMethod(nameof(ImplTakeWrite)); internal static MethodInfo ImplTakeWriteMethod = typeof(Impl).GetMethod(nameof(ImplTakeWrite));
@ -200,57 +200,57 @@ namespace IPA.Config.Stores
public IDisposable ChangeTransaction(IDisposable nest, bool takeWrite = true) public IDisposable ChangeTransaction(IDisposable nest, bool takeWrite = true)
=> GetFreeTransaction().InitWith(this, !inChangeTransaction, nest, takeWrite && !WriteSyncObject.IsWriteLockHeld); => GetFreeTransaction().InitWith(this, !inChangeTransaction, nest, takeWrite && !WriteSyncObject.IsWriteLockHeld);
private ChangeTransactionObj GetFreeTransaction()
=> freeTransactionObjs.Count > 0 ? freeTransactionObjs.Pop()
private ChangeTransactionObj GetFreeTransaction()
=> freeTransactionObjs.Count > 0 ? freeTransactionObjs.Pop()
: new ChangeTransactionObj(); : new ChangeTransactionObj();
// TODO: maybe sometimes clean this? // TODO: maybe sometimes clean this?
private static readonly Stack<ChangeTransactionObj> freeTransactionObjs = new Stack<ChangeTransactionObj>(); private static readonly Stack<ChangeTransactionObj> freeTransactionObjs = new Stack<ChangeTransactionObj>();
private sealed class ChangeTransactionObj : IDisposable
{
private struct Data
{
public readonly Impl impl;
public readonly bool owns;
public readonly bool ownsWrite;
public readonly IDisposable nested;
public Data(Impl impl, bool owning, bool takeWrite, IDisposable nest)
{
this.impl = impl; owns = owning; ownsWrite = takeWrite; nested = nest;
}
}
private Data data;
public ChangeTransactionObj InitWith(Impl impl, bool owning, IDisposable nest, bool takeWrite)
{
data = new Data(impl, owning, takeWrite, nest);
if (data.owns)
impl.inChangeTransaction = true;
if (data.ownsWrite)
impl.TakeWrite();
return this;
}
public void Dispose() => Dispose(true);
private void Dispose(bool addToStore)
{
if (data.owns)
{
data.impl.inChangeTransaction = false;
data.impl.InvokeChanged();
}
data.nested?.Dispose();
if (data.ownsWrite)
data.impl.ReleaseWrite();
if (addToStore)
freeTransactionObjs.Push(this);
}
~ChangeTransactionObj() => Dispose(false);
private sealed class ChangeTransactionObj : IDisposable
{
private struct Data
{
public readonly Impl impl;
public readonly bool owns;
public readonly bool ownsWrite;
public readonly IDisposable nested;
public Data(Impl impl, bool owning, bool takeWrite, IDisposable nest)
{
this.impl = impl; owns = owning; ownsWrite = takeWrite; nested = nest;
}
}
private Data data;
public ChangeTransactionObj InitWith(Impl impl, bool owning, IDisposable nest, bool takeWrite)
{
data = new Data(impl, owning, takeWrite, nest);
if (data.owns)
impl.inChangeTransaction = true;
if (data.ownsWrite)
impl.TakeWrite();
return this;
}
public void Dispose() => Dispose(true);
private void Dispose(bool addToStore)
{
if (data.owns)
{
data.impl.inChangeTransaction = false;
data.impl.InvokeChanged();
}
data.nested?.Dispose();
if (data.ownsWrite)
data.impl.ReleaseWrite();
if (addToStore)
freeTransactionObjs.Push(this);
}
~ChangeTransactionObj() => Dispose(false);
} }
public static Impl FindImpl(IGeneratedStore store) public static Impl FindImpl(IGeneratedStore store)
@ -605,11 +605,11 @@ namespace IPA.Config.Stores
var GetLocal = MakeGetLocal(il); var GetLocal = MakeGetLocal(il);
foreach (var member in structure) foreach (var member in structure)
{
EmitStore(il, member, il =>
{
EmitLoad(il, member); // load the member
EmitCorrectMember(il, member, false, true, GetLocal); // correct it
{
EmitStore(il, member, il =>
{
EmitLoad(il, member); // load the member
EmitCorrectMember(il, member, false, true, GetLocal); // correct it
}); });
} }
@ -623,144 +623,144 @@ namespace IPA.Config.Stores
const MethodAttributes propertyMethodAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig; const MethodAttributes propertyMethodAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
const MethodAttributes virtualPropertyMethodAttr = propertyMethodAttr | MethodAttributes.Virtual | MethodAttributes.Final; const MethodAttributes virtualPropertyMethodAttr = propertyMethodAttr | MethodAttributes.Virtual | MethodAttributes.Final;
const MethodAttributes virtualMemberMethod = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.Final; const MethodAttributes virtualMemberMethod = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.Final;
#region INotifyPropertyChanged
MethodBuilder notifyChanged = null;
if (isINotifyPropertyChanged || hasNotifyAttribute)
{
var INotifyPropertyChanged_t = typeof(INotifyPropertyChanged);
typeBuilder.AddInterfaceImplementation(INotifyPropertyChanged_t);
var INotifyPropertyChanged_PropertyChanged =
INotifyPropertyChanged_t.GetEvent(nameof(INotifyPropertyChanged.PropertyChanged));
var PropertyChangedEventHandler_t = typeof(PropertyChangedEventHandler);
var PropertyChangedEventHander_Invoke = PropertyChangedEventHandler_t.GetMethod(nameof(PropertyChangedEventHandler.Invoke));
var PropertyChangedEventArgs_t = typeof(PropertyChangedEventArgs);
var PropertyChangedEventArgs_ctor = PropertyChangedEventArgs_t.GetConstructor(new[] { typeof(string) });
var Delegate_t = typeof(Delegate);
var Delegate_Combine = Delegate_t.GetMethod(nameof(Delegate.Combine), BindingFlags.Static | BindingFlags.Public, null,
new[] { Delegate_t, Delegate_t }, Array.Empty<ParameterModifier>());
var Delegate_Remove = Delegate_t.GetMethod(nameof(Delegate.Remove), BindingFlags.Static | BindingFlags.Public, null,
new[] { Delegate_t, Delegate_t }, Array.Empty<ParameterModifier>());
var CompareExchange = typeof(Interlocked).GetMethods()
.Where(m => m.Name == nameof(Interlocked.CompareExchange))
.Where(m => m.ContainsGenericParameters)
.Where(m => m.GetParameters().Length == 3).First()
.MakeGenericMethod(PropertyChangedEventHandler_t);
var basePropChangedEvent = type.GetEvents()
.Where(e => e.AddMethod.GetBaseDefinition().DeclaringType == INotifyPropertyChanged_t)
.FirstOrDefault();
var basePropChangedAdd = basePropChangedEvent?.AddMethod;
var basePropChangedRemove = basePropChangedEvent?.RemoveMethod;
var PropertyChanged_backing = typeBuilder.DefineField("<event>PropertyChanged", PropertyChangedEventHandler_t, FieldAttributes.Private);
var add_PropertyChanged = typeBuilder.DefineMethod("<add>PropertyChanged",
MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Final | MethodAttributes.Virtual,
null, new[] { PropertyChangedEventHandler_t });
typeBuilder.DefineMethodOverride(add_PropertyChanged, INotifyPropertyChanged_PropertyChanged.GetAddMethod());
if (basePropChangedAdd != null)
typeBuilder.DefineMethodOverride(add_PropertyChanged, basePropChangedAdd);
{
var il = add_PropertyChanged.GetILGenerator();
var loopLabel = il.DefineLabel();
var delTemp = il.DeclareLocal(PropertyChangedEventHandler_t);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, PropertyChanged_backing);
il.MarkLabel(loopLabel);
il.Emit(OpCodes.Stloc, delTemp);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldflda, PropertyChanged_backing);
il.Emit(OpCodes.Ldloc, delTemp);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Call, Delegate_Combine);
il.Emit(OpCodes.Castclass, PropertyChangedEventHandler_t);
il.Emit(OpCodes.Ldloc, delTemp);
il.Emit(OpCodes.Call, CompareExchange);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldloc, delTemp);
il.Emit(OpCodes.Bne_Un_S, loopLabel);
il.Emit(OpCodes.Ret);
}
var remove_PropertyChanged = typeBuilder.DefineMethod("<remove>PropertyChanged",
MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Final | MethodAttributes.Virtual,
null, new[] { PropertyChangedEventHandler_t });
typeBuilder.DefineMethodOverride(remove_PropertyChanged, INotifyPropertyChanged_PropertyChanged.GetRemoveMethod());
if (basePropChangedRemove != null)
typeBuilder.DefineMethodOverride(remove_PropertyChanged, basePropChangedRemove);
{
var il = remove_PropertyChanged.GetILGenerator();
var loopLabel = il.DefineLabel();
var delTemp = il.DeclareLocal(PropertyChangedEventHandler_t);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, PropertyChanged_backing);
il.MarkLabel(loopLabel);
il.Emit(OpCodes.Stloc, delTemp);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldflda, PropertyChanged_backing);
il.Emit(OpCodes.Ldloc, delTemp);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Call, Delegate_Remove);
il.Emit(OpCodes.Castclass, PropertyChangedEventHandler_t);
il.Emit(OpCodes.Ldloc, delTemp);
il.Emit(OpCodes.Call, CompareExchange);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldloc, delTemp);
il.Emit(OpCodes.Bne_Un_S, loopLabel);
il.Emit(OpCodes.Ret);
}
var PropertyChanged_event = typeBuilder.DefineEvent(nameof(INotifyPropertyChanged.PropertyChanged), EventAttributes.None, PropertyChangedEventHandler_t);
PropertyChanged_event.SetAddOnMethod(add_PropertyChanged);
PropertyChanged_event.SetRemoveOnMethod(remove_PropertyChanged);
notifyChanged = typeBuilder.DefineMethod("<>NotifyChanged",
MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Final, null, new[] { typeof(string) });
{
var il = notifyChanged.GetILGenerator();
var invokeNonNull = il.DefineLabel();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, PropertyChanged_backing);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Brtrue, invokeNonNull);
il.Emit(OpCodes.Pop);
il.Emit(OpCodes.Ret);
il.MarkLabel(invokeNonNull);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Newobj, PropertyChangedEventArgs_ctor);
il.Emit(OpCodes.Call, PropertyChangedEventHander_Invoke);
il.Emit(OpCodes.Ret);
}
}
#region INotifyPropertyChanged
MethodBuilder notifyChanged = null;
if (isINotifyPropertyChanged || hasNotifyAttribute)
{
var INotifyPropertyChanged_t = typeof(INotifyPropertyChanged);
typeBuilder.AddInterfaceImplementation(INotifyPropertyChanged_t);
var INotifyPropertyChanged_PropertyChanged =
INotifyPropertyChanged_t.GetEvent(nameof(INotifyPropertyChanged.PropertyChanged));
var PropertyChangedEventHandler_t = typeof(PropertyChangedEventHandler);
var PropertyChangedEventHander_Invoke = PropertyChangedEventHandler_t.GetMethod(nameof(PropertyChangedEventHandler.Invoke));
var PropertyChangedEventArgs_t = typeof(PropertyChangedEventArgs);
var PropertyChangedEventArgs_ctor = PropertyChangedEventArgs_t.GetConstructor(new[] { typeof(string) });
var Delegate_t = typeof(Delegate);
var Delegate_Combine = Delegate_t.GetMethod(nameof(Delegate.Combine), BindingFlags.Static | BindingFlags.Public, null,
new[] { Delegate_t, Delegate_t }, Array.Empty<ParameterModifier>());
var Delegate_Remove = Delegate_t.GetMethod(nameof(Delegate.Remove), BindingFlags.Static | BindingFlags.Public, null,
new[] { Delegate_t, Delegate_t }, Array.Empty<ParameterModifier>());
var CompareExchange = typeof(Interlocked).GetMethods()
.Where(m => m.Name == nameof(Interlocked.CompareExchange))
.Where(m => m.ContainsGenericParameters)
.Where(m => m.GetParameters().Length == 3).First()
.MakeGenericMethod(PropertyChangedEventHandler_t);
var basePropChangedEvent = type.GetEvents()
.Where(e => e.GetAddMethod().GetBaseDefinition().DeclaringType == INotifyPropertyChanged_t)
.FirstOrDefault();
var basePropChangedAdd = basePropChangedEvent? .GetAddMethod();
var basePropChangedRemove = basePropChangedEvent?.GetRemoveMethod();
var PropertyChanged_backing = typeBuilder.DefineField("<event>PropertyChanged", PropertyChangedEventHandler_t, FieldAttributes.Private);
var add_PropertyChanged = typeBuilder.DefineMethod("<add>PropertyChanged",
MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Final | MethodAttributes.Virtual,
null, new[] { PropertyChangedEventHandler_t });
typeBuilder.DefineMethodOverride(add_PropertyChanged, INotifyPropertyChanged_PropertyChanged.GetAddMethod());
if (basePropChangedAdd != null)
typeBuilder.DefineMethodOverride(add_PropertyChanged, basePropChangedAdd);
{
var il = add_PropertyChanged.GetILGenerator();
var loopLabel = il.DefineLabel();
var delTemp = il.DeclareLocal(PropertyChangedEventHandler_t);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, PropertyChanged_backing);
il.MarkLabel(loopLabel);
il.Emit(OpCodes.Stloc, delTemp);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldflda, PropertyChanged_backing);
il.Emit(OpCodes.Ldloc, delTemp);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Call, Delegate_Combine);
il.Emit(OpCodes.Castclass, PropertyChangedEventHandler_t);
il.Emit(OpCodes.Ldloc, delTemp);
il.Emit(OpCodes.Call, CompareExchange);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldloc, delTemp);
il.Emit(OpCodes.Bne_Un_S, loopLabel);
il.Emit(OpCodes.Ret);
}
var remove_PropertyChanged = typeBuilder.DefineMethod("<remove>PropertyChanged",
MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Final | MethodAttributes.Virtual,
null, new[] { PropertyChangedEventHandler_t });
typeBuilder.DefineMethodOverride(remove_PropertyChanged, INotifyPropertyChanged_PropertyChanged.GetRemoveMethod());
if (basePropChangedRemove != null)
typeBuilder.DefineMethodOverride(remove_PropertyChanged, basePropChangedRemove);
{
var il = remove_PropertyChanged.GetILGenerator();
var loopLabel = il.DefineLabel();
var delTemp = il.DeclareLocal(PropertyChangedEventHandler_t);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, PropertyChanged_backing);
il.MarkLabel(loopLabel);
il.Emit(OpCodes.Stloc, delTemp);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldflda, PropertyChanged_backing);
il.Emit(OpCodes.Ldloc, delTemp);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Call, Delegate_Remove);
il.Emit(OpCodes.Castclass, PropertyChangedEventHandler_t);
il.Emit(OpCodes.Ldloc, delTemp);
il.Emit(OpCodes.Call, CompareExchange);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldloc, delTemp);
il.Emit(OpCodes.Bne_Un_S, loopLabel);
il.Emit(OpCodes.Ret);
}
var PropertyChanged_event = typeBuilder.DefineEvent(nameof(INotifyPropertyChanged.PropertyChanged), EventAttributes.None, PropertyChangedEventHandler_t);
PropertyChanged_event.SetAddOnMethod(add_PropertyChanged);
PropertyChanged_event.SetRemoveOnMethod(remove_PropertyChanged);
notifyChanged = typeBuilder.DefineMethod("<>NotifyChanged",
MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Final, null, new[] { typeof(string) });
{
var il = notifyChanged.GetILGenerator();
var invokeNonNull = il.DefineLabel();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, PropertyChanged_backing);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Brtrue, invokeNonNull);
il.Emit(OpCodes.Pop);
il.Emit(OpCodes.Ret);
il.MarkLabel(invokeNonNull);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Newobj, PropertyChangedEventArgs_ctor);
il.Emit(OpCodes.Call, PropertyChangedEventHander_Invoke);
il.Emit(OpCodes.Ret);
}
}
#endregion #endregion
#region IGeneratedStore #region IGeneratedStore
@ -930,18 +930,18 @@ namespace IPA.Config.Stores
EmitDeserializeMember(il, member, nextLabel, il => il.Emit(OpCodes.Ldloc_S, valueLocal), GetLocal); EmitDeserializeMember(il, member, nextLabel, il => il.Emit(OpCodes.Ldloc_S, valueLocal), GetLocal);
} }
il.MarkLabel(nextLabel);
if (notifyChanged != null)
{
foreach (var member in structure)
{
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldstr, member.Name);
il.Emit(OpCodes.Call, notifyChanged);
}
}
il.MarkLabel(nextLabel);
if (notifyChanged != null)
{
foreach (var member in structure)
{
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldstr, member.Name);
il.Emit(OpCodes.Call, notifyChanged);
}
}
il.Emit(OpCodes.Ret); il.Emit(OpCodes.Ret);
} }
#endregion #endregion
@ -1021,51 +1021,51 @@ namespace IPA.Config.Stores
"<>Changed", "<>Changed",
virtualMemberMethod, virtualMemberMethod,
null, Type.EmptyTypes); null, Type.EmptyTypes);
typeBuilder.DefineMethodOverride(coreChanged, IGeneratedStore_Changed);
if (baseChanged != null)
typeBuilder.DefineMethodOverride(coreChanged, IGeneratedStore_Changed);
if (baseChanged != null)
typeBuilder.DefineMethodOverride(coreChanged, baseChanged); typeBuilder.DefineMethodOverride(coreChanged, baseChanged);
{ {
var il = coreChanged.GetILGenerator(); var il = coreChanged.GetILGenerator();
if (baseChanged != null)
if (baseChanged != null)
{ {
il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, baseChanged); // call base
il.Emit(OpCodes.Call, baseChanged); // call base
} }
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Tailcall); il.Emit(OpCodes.Tailcall);
il.Emit(OpCodes.Call, Impl.ImplSignalChangedMethod); il.Emit(OpCodes.Call, Impl.ImplSignalChangedMethod);
il.Emit(OpCodes.Ret); // simply call our impl's SignalChanged method and return il.Emit(OpCodes.Ret); // simply call our impl's SignalChanged method and return
}
}
#endregion #endregion
#region ChangeTransaction #region ChangeTransaction
var coreChangeTransaction = typeBuilder.DefineMethod(
"<>ChangeTransaction",
virtualMemberMethod,
typeof(IDisposable), Type.EmptyTypes);
typeBuilder.DefineMethodOverride(coreChangeTransaction, IGeneratedStore_ChangeTransaction);
if (baseChangeTransaction != null)
typeBuilder.DefineMethodOverride(coreChangeTransaction, baseChangeTransaction);
{
var il = coreChangeTransaction.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
if (baseChangeTransaction != null)
{
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, baseChangeTransaction);
}
else
il.Emit(OpCodes.Ldnull);
il.Emit(OpCodes.Tailcall);
il.Emit(OpCodes.Call, Impl.ImplChangeTransactionMethod);
il.Emit(OpCodes.Ret);
}
var coreChangeTransaction = typeBuilder.DefineMethod(
"<>ChangeTransaction",
virtualMemberMethod,
typeof(IDisposable), Type.EmptyTypes);
typeBuilder.DefineMethodOverride(coreChangeTransaction, IGeneratedStore_ChangeTransaction);
if (baseChangeTransaction != null)
typeBuilder.DefineMethodOverride(coreChangeTransaction, baseChangeTransaction);
{
var il = coreChangeTransaction.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
if (baseChangeTransaction != null)
{
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, baseChangeTransaction);
}
else
il.Emit(OpCodes.Ldnull);
il.Emit(OpCodes.Tailcall);
il.Emit(OpCodes.Call, Impl.ImplChangeTransactionMethod);
il.Emit(OpCodes.Ret);
}
#endregion #endregion
#region IGeneratedStore<T> #region IGeneratedStore<T>
@ -1085,9 +1085,9 @@ namespace IPA.Config.Stores
var startLock = il.DefineLabel(); var startLock = il.DefineLabel();
il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Ldarg_2);
il.Emit(OpCodes.Brfalse, startLock);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, coreChangeTransaction); // take the write lock
il.Emit(OpCodes.Brfalse, startLock);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, coreChangeTransaction); // take the write lock
il.Emit(OpCodes.Stloc, transactionLocal); il.Emit(OpCodes.Stloc, transactionLocal);
il.MarkLabel(startLock); il.MarkLabel(startLock);
@ -1108,60 +1108,60 @@ namespace IPA.Config.Stores
EmitWarnException(il, $"Error while copying from member {member.Name}"); EmitWarnException(il, $"Error while copying from member {member.Name}");
il.EndExceptionBlock(); il.EndExceptionBlock();
}
if (notifyChanged != null)
{
foreach (var member in structure)
{
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldstr, member.Name);
il.Emit(OpCodes.Call, notifyChanged);
}
}
}
if (notifyChanged != null)
{
foreach (var member in structure)
{
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldstr, member.Name);
il.Emit(OpCodes.Call, notifyChanged);
}
}
var endLock = il.DefineLabel(); var endLock = il.DefineLabel();
il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Ldarg_2);
il.Emit(OpCodes.Brfalse, endLock);
il.Emit(OpCodes.Ldloc, transactionLocal);
il.Emit(OpCodes.Brfalse, endLock);
il.Emit(OpCodes.Ldloc, transactionLocal);
il.Emit(OpCodes.Callvirt, IDisposable_Dispose); il.Emit(OpCodes.Callvirt, IDisposable_Dispose);
il.MarkLabel(endLock); il.MarkLabel(endLock);
il.Emit(OpCodes.Ret); il.Emit(OpCodes.Ret);
} }
#endregion #endregion
#endregion #endregion
#region base.CopyFrom
if (baseCopyFrom != null)
{
var pubCopyFrom = typeBuilder.DefineMethod(
baseCopyFrom.Name,
virtualMemberMethod,
null, new[] { type });
typeBuilder.DefineMethodOverride(pubCopyFrom, baseCopyFrom);
{
var il = pubCopyFrom.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, coreChangeTransaction);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Call, copyFrom); // call internal
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Call, baseCopyFrom); // call base
il.Emit(OpCodes.Tailcall);
il.Emit(OpCodes.Callvirt, IDisposable_Dispose); // dispose transaction (which calls changed)
il.Emit(OpCodes.Ret);
}
}
#endregion
#region base.CopyFrom
if (baseCopyFrom != null)
{
var pubCopyFrom = typeBuilder.DefineMethod(
baseCopyFrom.Name,
virtualMemberMethod,
null, new[] { type });
typeBuilder.DefineMethodOverride(pubCopyFrom, baseCopyFrom);
{
var il = pubCopyFrom.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, coreChangeTransaction);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Call, copyFrom); // call internal
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Call, baseCopyFrom); // call base
il.Emit(OpCodes.Tailcall);
il.Emit(OpCodes.Callvirt, IDisposable_Dispose); // dispose transaction (which calls changed)
il.Emit(OpCodes.Ret);
}
}
#endregion
#region Members #region Members
foreach (var member in structure.Where(m => m.IsVirtual)) foreach (var member in structure.Where(m => m.IsVirtual))
{ // IsVirtual implies !IsField { // IsVirtual implies !IsField
@ -1220,18 +1220,18 @@ namespace IPA.Config.Stores
EmitCorrectMember(il, member, false, false, GetLocal); EmitCorrectMember(il, member, false, false, GetLocal);
il.Emit(OpCodes.Call, set); il.Emit(OpCodes.Call, set);
il.BeginFinallyBlock();
il.BeginFinallyBlock();
il.Emit(OpCodes.Ldloc, transactionLocal); il.Emit(OpCodes.Ldloc, transactionLocal);
il.Emit(OpCodes.Callvirt, IDisposable_Dispose); il.Emit(OpCodes.Callvirt, IDisposable_Dispose);
il.EndExceptionBlock(); il.EndExceptionBlock();
if (notifyChanged != null)
{
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldstr, member.Name);
il.Emit(OpCodes.Call, notifyChanged);
if (notifyChanged != null)
{
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldstr, member.Name);
il.Emit(OpCodes.Call, notifyChanged);
} }
il.Emit(OpCodes.Ret); il.Emit(OpCodes.Ret);
} }
@ -1247,8 +1247,8 @@ namespace IPA.Config.Stores
).Compile(); ).Compile();
return (creatorDel, genType); return (creatorDel, genType);
}
}
#region Logs #region Logs
private static readonly MethodInfo LogErrorMethod = typeof(GeneratedStoreImpl).GetMethod(nameof(LogError), BindingFlags.NonPublic | BindingFlags.Static); private static readonly MethodInfo LogErrorMethod = typeof(GeneratedStoreImpl).GetMethod(nameof(LogError), BindingFlags.NonPublic | BindingFlags.Static);
internal static void LogError(Type expected, Type found, string message) internal static void LogError(Type expected, Type found, string message)
@ -1264,9 +1264,9 @@ namespace IPA.Config.Stores
internal static void LogWarningException(Exception exception) internal static void LogWarningException(Exception exception)
{ {
Logger.config.Warn(exception); Logger.config.Warn(exception);
}
#endregion
}
#endregion
#region Correction #region Correction
private static bool NeedsCorrection(SerializedMemberInfo member) private static bool NeedsCorrection(SerializedMemberInfo member)
{ {
@ -1291,29 +1291,29 @@ namespace IPA.Config.Stores
il.Emit(OpCodes.Call, member.Nullable_HasValue.GetGetMethod()); il.Emit(OpCodes.Call, member.Nullable_HasValue.GetGetMethod());
il.Emit(OpCodes.Brfalse, endLabel); il.Emit(OpCodes.Brfalse, endLabel);
il.Emit(OpCodes.Call, member.Nullable_Value.GetGetMethod()); il.Emit(OpCodes.Call, member.Nullable_Value.GetGetMethod());
}
// TODO: impl the rest of this
// currently the only thing for this is where expect == Map, so do generate shit
}
// TODO: impl the rest of this
// currently the only thing for this is where expect == Map, so do generate shit
var copyFrom = typeof(IGeneratedStore<>).MakeGenericType(member.Type).GetMethod(nameof(IGeneratedStore<Config>.CopyFrom)); var copyFrom = typeof(IGeneratedStore<>).MakeGenericType(member.Type).GetMethod(nameof(IGeneratedStore<Config>.CopyFrom));
var noCreate = il.DefineLabel(); var noCreate = il.DefineLabel();
var valLocal = GetLocal(member.Type);
if (!alwaysNew)
{
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Isinst, typeof(IGeneratedStore));
il.Emit(OpCodes.Brtrue_S, endLabel); // our input is already something we like
var valLocal = GetLocal(member.Type);
if (!alwaysNew)
{
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Isinst, typeof(IGeneratedStore));
il.Emit(OpCodes.Brtrue_S, endLabel); // our input is already something we like
} }
il.Emit(OpCodes.Stloc, valLocal); il.Emit(OpCodes.Stloc, valLocal);
if (!alwaysNew)
{
EmitLoad(il, member, il => il.Emit(OpCodes.Ldarg_0));
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Isinst, typeof(IGeneratedStore));
il.Emit(OpCodes.Brtrue_S, noCreate);
il.Emit(OpCodes.Pop);
if (!alwaysNew)
{
EmitLoad(il, member, il => il.Emit(OpCodes.Ldarg_0));
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Isinst, typeof(IGeneratedStore));
il.Emit(OpCodes.Brtrue_S, noCreate);
il.Emit(OpCodes.Pop);
} }
EmitCreateChildGenerated(il, member.Type); EmitCreateChildGenerated(il, member.Type);
il.MarkLabel(noCreate); il.MarkLabel(noCreate);
@ -1329,11 +1329,11 @@ namespace IPA.Config.Stores
il.Emit(OpCodes.Newobj, member.Nullable_Construct); il.Emit(OpCodes.Newobj, member.Nullable_Construct);
il.MarkLabel(endLabel); il.MarkLabel(endLabel);
}
}
#endregion #endregion
#region Utility
#region Utility
private delegate LocalBuilder GetLocal(Type type, int idx = 0); private delegate LocalBuilder GetLocal(Type type, int idx = 0);
private static GetLocal MakeGetLocal(ILGenerator il) private static GetLocal MakeGetLocal(ILGenerator il)
@ -1615,28 +1615,28 @@ namespace IPA.Config.Stores
// for now, we assume that its a generated type implementing IGeneratedStore // for now, we assume that its a generated type implementing IGeneratedStore
var IGeneratedStore_Serialize = typeof(IGeneratedStore).GetMethod(nameof(IGeneratedStore.Serialize)); var IGeneratedStore_Serialize = typeof(IGeneratedStore).GetMethod(nameof(IGeneratedStore.Serialize));
var IGeneratedStoreT_CopyFrom = typeof(IGeneratedStore<>).MakeGenericType(member.Type)
.GetMethod(nameof(IGeneratedStore<object>.CopyFrom));
if (!member.IsVirtual)
{
var noCreate = il.DefineLabel();
var stlocal = GetLocal(member.Type);
// first check to make sure that this is an IGeneratedStore, because we don't control assignments to it
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Isinst, typeof(IGeneratedStore));
il.Emit(OpCodes.Brtrue_S, noCreate);
il.Emit(OpCodes.Stloc, stlocal);
EmitCreateChildGenerated(il, member.Type);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldloc, stlocal);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Callvirt, IGeneratedStoreT_CopyFrom);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Stloc, stlocal);
EmitStore(il, member, il => il.Emit(OpCodes.Ldloc, stlocal));
il.MarkLabel(noCreate);
var IGeneratedStoreT_CopyFrom = typeof(IGeneratedStore<>).MakeGenericType(member.Type)
.GetMethod(nameof(IGeneratedStore<object>.CopyFrom));
if (!member.IsVirtual)
{
var noCreate = il.DefineLabel();
var stlocal = GetLocal(member.Type);
// first check to make sure that this is an IGeneratedStore, because we don't control assignments to it
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Isinst, typeof(IGeneratedStore));
il.Emit(OpCodes.Brtrue_S, noCreate);
il.Emit(OpCodes.Stloc, stlocal);
EmitCreateChildGenerated(il, member.Type);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldloc, stlocal);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Callvirt, IGeneratedStoreT_CopyFrom);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Stloc, stlocal);
EmitStore(il, member, il => il.Emit(OpCodes.Ldloc, stlocal));
il.MarkLabel(noCreate);
} }
il.Emit(OpCodes.Callvirt, IGeneratedStore_Serialize); il.Emit(OpCodes.Callvirt, IGeneratedStore_Serialize);
} }
@ -1681,11 +1681,11 @@ namespace IPA.Config.Stores
var valuel = GetLocal(srcType, 0); var valuel = GetLocal(srcType, 0);
var noCreate = il.DefineLabel(); var noCreate = il.DefineLabel();
il.Emit(OpCodes.Stloc, valuel);
EmitLoad(il, member, il => il.Emit(OpCodes.Ldarg_0));
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Isinst, typeof(IGeneratedStore));
il.Emit(OpCodes.Brtrue_S, noCreate);
il.Emit(OpCodes.Stloc, valuel);
EmitLoad(il, member, il => il.Emit(OpCodes.Ldarg_0));
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Isinst, typeof(IGeneratedStore));
il.Emit(OpCodes.Brtrue_S, noCreate);
il.Emit(OpCodes.Pop); il.Emit(OpCodes.Pop);
EmitCreateChildGenerated(il, member.Type); EmitCreateChildGenerated(il, member.Type);
il.MarkLabel(noCreate); il.MarkLabel(noCreate);
@ -1760,16 +1760,16 @@ namespace IPA.Config.Stores
if (member.IsGenericConverter) if (member.IsGenericConverter)
{ {
var fromValueBase = member.ConverterBase.GetMethod(nameof(ValueConverter<int>.FromValue), var fromValueBase = member.ConverterBase.GetMethod(nameof(ValueConverter<int>.FromValue),
new[] { typeof(Value), typeof(object) });
var fromValue = member.Converter.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
new[] { typeof(Value), typeof(object) });
var fromValue = member.Converter.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.FirstOrDefault(m => m.GetBaseDefinition() == fromValueBase) ?? fromValueBase; .FirstOrDefault(m => m.GetBaseDefinition() == fromValueBase) ?? fromValueBase;
il.Emit(OpCodes.Call, fromValue); il.Emit(OpCodes.Call, fromValue);
} }
else else
{ {
var fromValueBase = typeof(IValueConverter).GetMethod(nameof(IValueConverter.FromValue), var fromValueBase = typeof(IValueConverter).GetMethod(nameof(IValueConverter.FromValue),
new[] { typeof(Value), typeof(object) });
var fromValue = member.Converter.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
new[] { typeof(Value), typeof(object) });
var fromValue = member.Converter.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.FirstOrDefault(m => m.GetBaseDefinition() == fromValueBase) ?? fromValueBase; .FirstOrDefault(m => m.GetBaseDefinition() == fromValueBase) ?? fromValueBase;
il.Emit(OpCodes.Call, fromValue); il.Emit(OpCodes.Call, fromValue);
if (member.Type.IsValueType) if (member.Type.IsValueType)


Loading…
Cancel
Save