internal static Config GetConfigFor(string modName, ParameterInfo info) { var prefs = Array.Empty <string>(); if (info.GetCustomAttribute <PreferAttribute>() is PreferAttribute prefer) { prefs = prefer.PreferenceOrder; } if (info.GetCustomAttribute <NameAttribute>() is NameAttribute name) { modName = name.Name; } return(GetConfigFor(modName, prefs)); }
private static void InstallPendingModUpdates() { var pendingDir = Path.Combine(UnityGame.InstallPath, "IPA", "Pending"); if (!Directory.Exists(pendingDir)) { return; } // there are pending updates, install updater.Info("Installing pending updates"); var toDelete = Array.Empty <string>(); var delFn = Path.Combine(pendingDir, DeleteFileName); if (File.Exists(delFn)) { toDelete = File.ReadAllLines(delFn); File.Delete(delFn); } foreach (var file in toDelete) { try { File.Delete(Path.Combine(UnityGame.InstallPath, file)); } catch (Exception e) { updater.Error("While trying to install pending updates: Error deleting file marked for deletion"); updater.Error(e); } } #region Self Protection string path; if (Directory.Exists(path = Path.Combine(pendingDir, "IPA"))) { var dirs = new Stack <string>(20); dirs.Push(path); while (dirs.Count > 0) { var currentDir = dirs.Pop(); string[] subDirs; string[] files; try { subDirs = Directory.GetDirectories(currentDir); files = Directory.GetFiles(currentDir); } catch (UnauthorizedAccessException e) { updater.Error(e); continue; } catch (DirectoryNotFoundException e) { updater.Error(e); continue; } foreach (var file in files) { try { if (!Utils.GetRelativePath(file, path).Split(Path.PathSeparator).Contains("Pending")) { File.Delete(file); } } catch (FileNotFoundException e) { updater.Error(e); } } foreach (var str in subDirs) { dirs.Push(str); } } } if (File.Exists(path = Path.Combine(pendingDir, "IPA.exe"))) { File.Delete(path); if (File.Exists(path = Path.Combine(pendingDir, "Mono.Cecil.dll"))) { File.Delete(path); } } #endregion try { Utils.CopyAll(new DirectoryInfo(pendingDir), new DirectoryInfo(UnityGame.InstallPath), onCopyException: (e, f) => { updater.Error($"Error copying file {Utils.GetRelativePath(f.FullName, pendingDir)} from Pending:"); updater.Error(e); return(true); }); } catch (Exception e) { updater.Error("While trying to install pending updates: Error copying files in"); updater.Error(e); } try { Directory.Delete(pendingDir, true); } catch (Exception e) { updater.Error("Something went wrong performing an operation that should never fail!"); updater.Error(e); } }
/// <inheritdoc /> /// <summary> /// Constructs the attribute with a specific preference list. Each entry is the extension without a '.' /// </summary> /// <param name="preference">The preferences in order of preference.</param> public PreferAttribute(params string[] preference) { PreferenceOrder = preference; }
private static (GeneratedStoreCreator ctor, Type type) MakeCreator(Type type) { // note that this does not and should not use converters by default for everything if (!type.IsClass) { throw new ArgumentException("Config type is not a class"); } var baseCtor = type.GetConstructor(Type.EmptyTypes); // get a default constructor if (baseCtor == null) { throw new ArgumentException("Config type does not have a public parameterless constructor"); } #region Parse base object structure const BindingFlags overrideMemberFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; var baseChanged = type.GetMethod("Changed", overrideMemberFlags, null, Type.EmptyTypes, Array.Empty <ParameterModifier>()); if (baseChanged != null && IsMethodInvalid(baseChanged, typeof(void))) { baseChanged = null; } var baseOnReload = type.GetMethod("OnReload", overrideMemberFlags, null, Type.EmptyTypes, Array.Empty <ParameterModifier>()); if (baseOnReload != null && IsMethodInvalid(baseOnReload, typeof(void))) { baseOnReload = null; } var baseCopyFrom = type.GetMethod("CopyFrom", overrideMemberFlags, null, new[] { type }, Array.Empty <ParameterModifier>()); if (baseCopyFrom != null && IsMethodInvalid(baseCopyFrom, typeof(void))) { baseCopyFrom = null; } var baseChangeTransaction = type.GetMethod("ChangeTransaction", overrideMemberFlags, null, Type.EmptyTypes, Array.Empty <ParameterModifier>()); if (baseChangeTransaction != null && IsMethodInvalid(baseChangeTransaction, typeof(IDisposable))) { baseChangeTransaction = null; } var isINotifyPropertyChanged = type.FindInterfaces((i, t) => i == (Type)t, typeof(INotifyPropertyChanged)).Length != 0; var hasNotifyAttribute = type.GetCustomAttribute <NotifyPropertyChangesAttribute>() != null; var structure = ReadObjectMembers(type); if (!structure.Any()) { Logger.config.Warn($"Custom type {type.FullName} has no accessible members"); } #endregion 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); /*#region Converter fields * var uniqueConverterTypes = structure.Where(m => m.HasConverter).Select(m => m.Converter).Distinct().ToArray(); * var converterFields = new Dictionary<Type, FieldInfo>(uniqueConverterTypes.Length); * * foreach (var convType in uniqueConverterTypes) * { * var field = typeBuilder.DefineField($"<converter>_{convType}", convType, * FieldAttributes.Private | FieldAttributes.InitOnly | FieldAttributes.Static); * converterFields.Add(convType, field); * * foreach (var member in structure.Where(m => m.HasConverter && m.Converter == convType)) * member.ConverterField = field; * } #endregion * #region Static constructor * var cctor = typeBuilder.DefineConstructor(MethodAttributes.Static, CallingConventions.Standard, Type.EmptyTypes); * { * var il = cctor.GetILGenerator(); * * foreach (var kvp in converterFields) * { * var typeCtor = kvp.Key.GetConstructor(Type.EmptyTypes); * il.Emit(OpCodes.Newobj, typeCtor); * il.Emit(OpCodes.Stsfld, kvp.Value); * } * * il.Emit(OpCodes.Ret); * } #endregion*/ //CreateAndInitializeConvertersFor(type, structure); #region Constructor var ctor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new[] { typeof(IGeneratedStore) }); { var il = ctor.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); // keep this at bottom of stack il.Emit(OpCodes.Dup); il.Emit(OpCodes.Call, baseCtor); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Ldarg_1); // load parent il.Emit(OpCodes.Stfld, parentField); il.Emit(OpCodes.Dup); EmitTypeof(il, type); il.Emit(OpCodes.Stfld, typeField); var noImplLabel = il.DefineLabel(); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Brtrue, noImplLabel); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Newobj, Impl.Ctor); il.Emit(OpCodes.Stfld, implField); il.MarkLabel(noImplLabel); var GetLocal = MakeLocalAllocator(il); foreach (var member in structure) { if (NeedsCorrection(member)) { EmitLoadCorrectStore(il, member, false, true, GetLocal, GetMethodThis, GetMethodThis, GetMethodThis); } } il.Emit(OpCodes.Pop); il.Emit(OpCodes.Ret); } #endregion const MethodAttributes propertyMethodAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig; const MethodAttributes virtualPropertyMethodAttr = propertyMethodAttr | MethodAttributes.Virtual | 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.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 #region IGeneratedStore typeBuilder.AddInterfaceImplementation(typeof(IGeneratedStore)); var IGeneratedStore_t = typeof(IGeneratedStore); var IGeneratedStore_GetImpl = IGeneratedStore_t.GetProperty(nameof(IGeneratedStore.Impl)).GetGetMethod(); var IGeneratedStore_GetType = IGeneratedStore_t.GetProperty(nameof(IGeneratedStore.Type)).GetGetMethod(); var IGeneratedStore_GetParent = IGeneratedStore_t.GetProperty(nameof(IGeneratedStore.Parent)).GetGetMethod(); var IGeneratedStore_Serialize = IGeneratedStore_t.GetMethod(nameof(IGeneratedStore.Serialize)); var IGeneratedStore_Deserialize = IGeneratedStore_t.GetMethod(nameof(IGeneratedStore.Deserialize)); var IGeneratedStore_OnReload = IGeneratedStore_t.GetMethod(nameof(IGeneratedStore.OnReload)); var IGeneratedStore_Changed = IGeneratedStore_t.GetMethod(nameof(IGeneratedStore.Changed)); var IGeneratedStore_ChangeTransaction = IGeneratedStore_t.GetMethod(nameof(IGeneratedStore.ChangeTransaction)); #region IGeneratedStore.OnReload var onReload = typeBuilder.DefineMethod($"<>{nameof(IGeneratedStore.OnReload)}", virtualMemberMethod, null, Type.EmptyTypes); typeBuilder.DefineMethodOverride(onReload, IGeneratedStore_OnReload); if (baseOnReload != null) { typeBuilder.DefineMethodOverride(onReload, baseOnReload); } { var il = onReload.GetILGenerator(); if (baseOnReload != null) { il.Emit(OpCodes.Ldarg_0); // load this il.Emit(OpCodes.Tailcall); il.Emit(OpCodes.Call, baseOnReload); // load impl field } il.Emit(OpCodes.Ret); } #endregion #region IGeneratedStore.Impl var implProp = typeBuilder.DefineProperty(nameof(IGeneratedStore.Impl), PropertyAttributes.None, typeof(Impl), null); var implPropGet = typeBuilder.DefineMethod($"<g>{nameof(IGeneratedStore.Impl)}", virtualPropertyMethodAttr, implProp.PropertyType, Type.EmptyTypes); implProp.SetGetMethod(implPropGet); typeBuilder.DefineMethodOverride(implPropGet, IGeneratedStore_GetImpl); { 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($"<g>{nameof(IGeneratedStore.Type)}", virtualPropertyMethodAttr, typeProp.PropertyType, Type.EmptyTypes); typeProp.SetGetMethod(typePropGet); typeBuilder.DefineMethodOverride(typePropGet, IGeneratedStore_GetType); { 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($"<g>{nameof(IGeneratedStore.Parent)}", virtualPropertyMethodAttr, parentProp.PropertyType, Type.EmptyTypes); parentProp.SetGetMethod(parentPropGet); typeBuilder.DefineMethodOverride(parentPropGet, IGeneratedStore_GetParent); { 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.Serialize var serializeGen = typeBuilder.DefineMethod($"<>{nameof(IGeneratedStore.Serialize)}", virtualPropertyMethodAttr, IGeneratedStore_Serialize.ReturnType, Type.EmptyTypes); typeBuilder.DefineMethodOverride(serializeGen, IGeneratedStore_Serialize); { // this is non-locking because the only code that will call this will already own the correct lock var il = serializeGen.GetILGenerator(); var GetLocal = MakeLocalAllocator(il); EmitSerializeStructure(il, structure, GetLocal, GetMethodThis, GetMethodThis); il.Emit(OpCodes.Ret); } #endregion #region IGeneratedStore.Deserialize var deserializeGen = typeBuilder.DefineMethod($"<>{nameof(IGeneratedStore.Deserialize)}", virtualPropertyMethodAttr, null, new[] { IGeneratedStore_Deserialize.GetParameters()[0].ParameterType }); typeBuilder.DefineMethodOverride(deserializeGen, IGeneratedStore_Deserialize); { // this is non-locking because the only code that will call this will already own the correct lock var il = deserializeGen.GetILGenerator(); var Map_t = typeof(Map); var Map_TryGetValue = Map_t.GetMethod(nameof(Map.TryGetValue)); var Object_GetType = typeof(object).GetMethod(nameof(Object.GetType)); var valueLocal = il.DeclareLocal(typeof(Value)); var mapLocal = il.DeclareLocal(typeof(Map)); var nonNull = il.DefineLabel(); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Brtrue, nonNull); EmitLogError(il, "Attempting to deserialize null", tailcall: true); il.Emit(OpCodes.Ret); il.MarkLabel(nonNull); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Isinst, Map_t); il.Emit(OpCodes.Dup); // duplicate cloned value il.Emit(OpCodes.Stloc, mapLocal); var notMapError = il.DefineLabel(); il.Emit(OpCodes.Brtrue, notMapError); // handle error EmitLogError(il, $"Invalid root for deserializing {type.FullName}", tailcall: true, expected: il => EmitTypeof(il, Map_t), found: il => { il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Callvirt, Object_GetType); }); il.Emit(OpCodes.Ret); il.MarkLabel(notMapError); var GetLocal = MakeLocalAllocator(il); // head of stack is Map instance EmitDeserializeStructure(il, structure, mapLocal, valueLocal, GetLocal, GetMethodThis, GetMethodThis); 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); } #endregion #endregion #region IConfigStore typeBuilder.AddInterfaceImplementation(typeof(IConfigStore)); var IConfigStore_t = typeof(IConfigStore); var IConfigStore_GetSyncObject = IConfigStore_t.GetProperty(nameof(IConfigStore.SyncObject)).GetGetMethod(); var IConfigStore_GetWriteSyncObject = IConfigStore_t.GetProperty(nameof(IConfigStore.WriteSyncObject)).GetGetMethod(); var IConfigStore_WriteTo = IConfigStore_t.GetMethod(nameof(IConfigStore.WriteTo)); var IConfigStore_ReadFrom = IConfigStore_t.GetMethod(nameof(IConfigStore.ReadFrom)); #region IConfigStore.SyncObject var syncObjProp = typeBuilder.DefineProperty(nameof(IConfigStore.SyncObject), PropertyAttributes.None, typeof(WaitHandle), null); var syncObjPropGet = typeBuilder.DefineMethod($"<g>{nameof(IConfigStore.SyncObject)}", virtualPropertyMethodAttr, syncObjProp.PropertyType, Type.EmptyTypes); syncObjProp.SetGetMethod(syncObjPropGet); typeBuilder.DefineMethodOverride(syncObjPropGet, IConfigStore_GetSyncObject); { var il = syncObjPropGet.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Tailcall); il.Emit(OpCodes.Call, Impl.ImplGetSyncObjectMethod); il.Emit(OpCodes.Ret); } #endregion #region IConfigStore.WriteSyncObject var writeSyncObjProp = typeBuilder.DefineProperty(nameof(IConfigStore.WriteSyncObject), PropertyAttributes.None, typeof(WaitHandle), null); var writeSyncObjPropGet = typeBuilder.DefineMethod($"<g>{nameof(IConfigStore.WriteSyncObject)}", virtualPropertyMethodAttr, writeSyncObjProp.PropertyType, Type.EmptyTypes); writeSyncObjProp.SetGetMethod(writeSyncObjPropGet); typeBuilder.DefineMethodOverride(writeSyncObjPropGet, IConfigStore_GetWriteSyncObject); { var il = writeSyncObjPropGet.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Tailcall); il.Emit(OpCodes.Call, Impl.ImplGetWriteSyncObjectMethod); il.Emit(OpCodes.Ret); } #endregion #region IConfigStore.WriteTo var writeTo = typeBuilder.DefineMethod($"<>{nameof(IConfigStore.WriteTo)}", virtualMemberMethod, null, new[] { typeof(ConfigProvider) }); typeBuilder.DefineMethodOverride(writeTo, IConfigStore_WriteTo); { var il = writeTo.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Tailcall); il.Emit(OpCodes.Call, Impl.ImplWriteToMethod); il.Emit(OpCodes.Ret); } #endregion #region IConfigStore.ReadFrom var readFrom = typeBuilder.DefineMethod($"<>{nameof(IConfigStore.ReadFrom)}", virtualMemberMethod, null, new[] { typeof(ConfigProvider) }); typeBuilder.DefineMethodOverride(readFrom, IConfigStore_ReadFrom); { var il = readFrom.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Tailcall); il.Emit(OpCodes.Call, Impl.ImplReadFromMethod); il.Emit(OpCodes.Ret); } #endregion #endregion #region Changed var coreChanged = typeBuilder.DefineMethod( "<>Changed", virtualMemberMethod, null, Type.EmptyTypes); typeBuilder.DefineMethodOverride(coreChanged, IGeneratedStore_Changed); if (baseChanged != null) { typeBuilder.DefineMethodOverride(coreChanged, baseChanged); } { var il = coreChanged.GetILGenerator(); if (baseChanged != null) { il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Call, baseChanged); // call base } il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Tailcall); il.Emit(OpCodes.Call, Impl.ImplSignalChangedMethod); il.Emit(OpCodes.Ret); // simply call our impl's SignalChanged method and return } #endregion #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); } #endregion #region IGeneratedStore<T> var IGeneratedStore_T_t = typeof(IGeneratedStore <>).MakeGenericType(type); typeBuilder.AddInterfaceImplementation(IGeneratedStore_T_t); var IGeneratedStore_T_CopyFrom = IGeneratedStore_T_t.GetMethod(nameof(IGeneratedStore <Config> .CopyFrom)); #region IGeneratedStore<T>.CopyFrom var copyFrom = typeBuilder.DefineMethod($"<>{nameof(IGeneratedStore<Config>.CopyFrom)}", virtualMemberMethod, null, new[] { type, typeof(bool) }); typeBuilder.DefineMethodOverride(copyFrom, IGeneratedStore_T_CopyFrom); { var il = copyFrom.GetILGenerator(); var transactionLocal = il.DeclareLocal(IDisposable_t); var startLock = il.DefineLabel(); 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.Stloc, transactionLocal); il.MarkLabel(startLock); var GetLocal = MakeLocalAllocator(il); foreach (var member in structure) { il.BeginExceptionBlock(); EmitLoadCorrectStore(il, member, false, false, GetLocal, il => il.Emit(OpCodes.Ldarg_1), GetMethodThis, GetMethodThis); il.BeginCatchBlock(typeof(Exception)); EmitWarnException(il, $"Error while copying from member {member.Name}"); 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); } } var endLock = il.DefineLabel(); il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Brfalse, endLock); il.Emit(OpCodes.Ldloc, transactionLocal); il.Emit(OpCodes.Callvirt, IDisposable_Dispose); il.MarkLabel(endLock); il.Emit(OpCodes.Ret); } #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 Members foreach (var member in structure.Where(m => m.IsVirtual)) { // IsVirtual implies !IsField var prop = member.Member as PropertyInfo; var get = prop.GetGetMethod(true); var set = prop.GetSetMethod(true); var propBuilder = typeBuilder.DefineProperty($"{member.Name}#", PropertyAttributes.None, member.Type, null); var propGet = typeBuilder.DefineMethod($"<g>{propBuilder.Name}", virtualPropertyMethodAttr, member.Type, Type.EmptyTypes); propBuilder.SetGetMethod(propGet); typeBuilder.DefineMethodOverride(propGet, get); { var il = propGet.GetILGenerator(); var local = il.DeclareLocal(member.Type); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Call, Impl.ImplTakeReadMethod); // take the read lock il.BeginExceptionBlock(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Call, get); // call base getter il.Emit(OpCodes.Stloc, local); il.BeginFinallyBlock(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Call, Impl.ImplReleaseReadMethod); // release the read lock il.EndExceptionBlock(); il.Emit(OpCodes.Ldloc, local); il.Emit(OpCodes.Ret); } var propSet = typeBuilder.DefineMethod($"<s>{propBuilder.Name}", virtualPropertyMethodAttr, null, new[] { member.Type }); propBuilder.SetSetMethod(propSet); typeBuilder.DefineMethodOverride(propSet, set); { var il = propSet.GetILGenerator(); var transactionLocal = il.DeclareLocal(IDisposable_t); var GetLocal = MakeLocalAllocator(il); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Call, coreChangeTransaction); // take the write lock il.Emit(OpCodes.Stloc, transactionLocal); il.BeginExceptionBlock(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); EmitCorrectMember(il, member, false, false, GetLocal, GetMethodThis, GetMethodThis); il.Emit(OpCodes.Call, set); il.BeginFinallyBlock(); il.Emit(OpCodes.Ldloc, transactionLocal); il.Emit(OpCodes.Callvirt, IDisposable_Dispose); il.EndExceptionBlock(); if (notifyChanged != null) { il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldstr, member.Name); il.Emit(OpCodes.Call, notifyChanged); } il.Emit(OpCodes.Ret); } } #endregion var genType = typeBuilder.CreateType(); var parentParam = Expression.Parameter(typeof(IGeneratedStore), "parent"); var creatorDel = Expression.Lambda <GeneratedStoreCreator>( Expression.New(ctor, parentParam), parentParam ).Compile(); return(creatorDel, genType); }
private static TDelegate MakeDelegate(string name) { var delParams = AccessorDelegateInfo <TDelegate> .Parameters; var method = typeof(T).GetMethod(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly, null, delParams.Skip(1).Select(p => p.ParameterType).ToArray(), Array.Empty <ParameterModifier>()); if (method == null) { throw new MissingMethodException(typeof(T).FullName, name); } var retType = AccessorDelegateInfo <TDelegate> .Invoke.ReturnType; if (!retType.IsAssignableFrom(method.ReturnType)) { throw new ArgumentException($"The method found returns a type incompatable with the return type of {typeof(TDelegate)}"); } return((TDelegate)Delegate.CreateDelegate(AccessorDelegateInfo <TDelegate> .Type, method, true)); }