using HarmonyLib.Public.Patching; using HarmonyLib; using KFCommonUtilityLib.Scripts.Attributes; using Mono.Cecil; using Mono.Cecil.Cil; using MonoMod.Cil; using MonoMod.Utils; using System; using System.Collections.Generic; using UniLinq; using System.Reflection; using KFCommonUtilityLib.Harmony; using UnityEngine.Scripting; using TypeAttributes = Mono.Cecil.TypeAttributes; using MethodAttributes = Mono.Cecil.MethodAttributes; using FieldAttributes = Mono.Cecil.FieldAttributes; using Mono.Cecil.Rocks; namespace KFCommonUtilityLib { public interface IModuleContainerFor where T : class { T Instance { get; } } public class TranspilerTarget { public TranspilerTarget(Type type_action, MethodInfo mtdinf_original, PatchInfo patchinf_harmony) { this.type_action = type_action; this.mtdinf_original = mtdinf_original; this.patchinf_harmony = patchinf_harmony; } public Type type_action; public MethodInfo mtdinf_original; public PatchInfo patchinf_harmony; } public class MethodPatchInfo { public readonly MethodDefinition Method; public Instruction PrefixBegin; public Instruction PostfixBegin; public Instruction PostfixEnd; public MethodPatchInfo(MethodDefinition mtddef, Instruction postfixEnd, Instruction prefixBegin) { Method = mtddef; PostfixEnd = postfixEnd; PrefixBegin = prefixBegin; } } internal struct MethodOverrideInfo { public MethodInfo mtdinf_target; public MethodInfo mtdinf_base; public MethodReference mtdref_base; public Type prefType; public MethodOverrideInfo(MethodInfo mtdinf_target, MethodInfo mtdinf_base, MethodReference mtddef_base, Type prefType) { this.mtdinf_target = mtdinf_target; this.mtdinf_base = mtdinf_base; this.mtdref_base = mtddef_base; this.prefType = prefType; } } public class ModuleManipulator { public ModuleDefinition module; public IModuleProcessor processor; public Type targetType; public Type baseType; public Type[] moduleTypes; public Type[][] moduleExtensionTypes; public TypeDefinition typedef_newTarget; public TypeReference typeref_interface; public FieldDefinition[] arr_flddef_modules; private static MethodInfo mtdinf = AccessTools.Method(typeof(ModuleManagers), nameof(ModuleManagers.GetModuleExtensions)); public ModuleManipulator(AssemblyDefinition workingAssembly, IModuleProcessor processor, Type targetType, Type baseType, params Type[] moduleTypes) { module = workingAssembly.MainModule; this.processor = processor; this.targetType = targetType; this.baseType = baseType; this.moduleTypes = moduleTypes; moduleExtensionTypes = moduleTypes.Select(t => (Type[])mtdinf.MakeGenericMethod(t).Invoke(null, null)).ToArray(); Patch(); } private void Patch() { typeref_interface = module.ImportReference(typeof(IModuleContainerFor<>)); //Create new override type TypeReference typeref_target = module.ImportReference(targetType); typedef_newTarget = new TypeDefinition(null, ModuleUtils.CreateTypeName(targetType, moduleTypes), TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.Public | TypeAttributes.Sealed, typeref_target); typedef_newTarget.CustomAttributes.Add(new CustomAttribute(module.ImportReference(typeof(PreserveAttribute).GetConstructor(Array.Empty())))); module.Types.Add(typedef_newTarget); //Create fields arr_flddef_modules = new FieldDefinition[moduleTypes.Length]; for (int i = 0; i < moduleTypes.Length; i++) { //Create ItemAction field Type type_module = moduleTypes[i]; MakeContainerFor(typedef_newTarget, type_module, out var flddef_module); arr_flddef_modules[i] = flddef_module; } //Create ItemAction constructor MethodDefinition mtddef_ctor = new MethodDefinition(".ctor", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, module.TypeSystem.Void); if (processor == null || !processor.BuildConstructor(this, mtddef_ctor)) { var il = mtddef_ctor.Body.GetILProcessor(); il.Append(il.Create(OpCodes.Ldarg_0)); il.Append(il.Create(OpCodes.Call, module.ImportReference(targetType.GetConstructor(Array.Empty())))); il.Append(il.Create(OpCodes.Nop)); for (int i = 0; i < arr_flddef_modules.Length; i++) { il.Append(il.Create(OpCodes.Ldarg_0)); il.Append(il.Create(OpCodes.Newobj, module.ImportReference(moduleTypes[i].GetConstructor(Array.Empty())))); il.Append(il.Create(OpCodes.Stfld, arr_flddef_modules[i])); il.Append(il.Create(OpCodes.Nop)); } il.Append(il.Create(OpCodes.Ret)); } typedef_newTarget.Methods.Add(mtddef_ctor); processor?.InitModules(this, targetType, baseType, moduleTypes); // Dictionary dict_overrides = new Dictionary(); // //TODO: USE TREE INSTEAD OF LIST Dictionary> dict_transpilers = new Dictionary>(); //> Dictionary> dict_all_states = new Dictionary>(); //Get all transpilers for (int i = 0; i < moduleTypes.Length; i++) { Type moduleType = moduleTypes[i]; const BindingFlags searchFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; foreach (var mtd in moduleType.GetMethods(searchFlags).Concat(moduleExtensionTypes[i].SelectMany(t => t.GetMethods(searchFlags)))) { var attr = mtd.GetCustomAttribute(); foreach (var hp in mtd.GetCustomAttributes()) { //make sure the transpiler has a target method to apply, otherwise skip it if (attr != null && hp != null && hp.info.declaringType != null) { var hm = hp.info; hm.methodType = hm.methodType ?? MethodType.Normal; var mtdinf_target = hm.GetOriginalMethod() as MethodInfo; if (mtdinf_target == null || mtdinf_target.IsAbstract || !mtdinf_target.IsVirtual) { continue; } string id = hm.GetTargetMethodIdentifier(); if (!dict_transpilers.TryGetValue(id, out var list)) { dict_transpilers[id] = (list = new List()); Type nextType = targetType; TranspilerTarget curNode = null; var hm_next = hm.Clone(); while (hm.declaringType.IsAssignableFrom(nextType)) { hm_next.declaringType = nextType; var mtdinfo_cur = hm_next.GetOriginalMethod() as MethodInfo; if (mtdinfo_cur != null) { var patchinf_harmony = mtdinfo_cur.ToPatchInfoDontAdd().Copy(); curNode = new TranspilerTarget(mtdinfo_cur.DeclaringType, mtdinfo_cur, patchinf_harmony); list.Add(curNode); } nextType = nextType.BaseType; } if (curNode != null) { curNode.patchinf_harmony.AddTranspilers(CommonUtilityLibInit.HarmonyInstance.Id, new HarmonyMethod(mtd)); Log.Out($"Adding transpiler {mtd.FullDescription()}\nCurrent transpilers:\n{string.Join('\n', curNode.patchinf_harmony.transpilers.Select(p => p.PatchMethod.FullDescription()))}"); } } else { bool childFound = false; foreach (var node in ((IEnumerable)list).Reverse()) { if (node.type_action.Equals(hm.declaringType)) { childFound = true; node.patchinf_harmony.AddTranspilers(CommonUtilityLibInit.HarmonyInstance.Id, mtd); Log.Out($"Adding transpiler {mtd.FullDescription()}\nCurrent transpilers:\n{string.Join('\n', node.patchinf_harmony.transpilers.Select(p => p.PatchMethod.FullDescription()))}"); break; } } if (!childFound) { Type nextType = list[list.Count - 1].type_action.BaseType; TranspilerTarget curNode = null; var hm_next = hm.Clone(); while (hm.declaringType.IsAssignableFrom(nextType)) { hm_next.declaringType = nextType; var mtdinfo_cur = hm_next.GetOriginalMethod() as MethodInfo; if (mtdinfo_cur != null) { var patchinf_harmony = mtdinfo_cur.ToPatchInfoDontAdd().Copy(); curNode = new TranspilerTarget(mtdinfo_cur.DeclaringType, mtdinfo_cur, patchinf_harmony); list.Add(curNode); } nextType = nextType.BaseType; } if (curNode != null) { curNode.patchinf_harmony.AddTranspilers(CommonUtilityLibInit.HarmonyInstance.Id, new HarmonyMethod(mtd)); Log.Out($"Adding transpiler {mtd.FullDescription()}\nCurrent transpilers:\n{string.Join('\n', curNode.patchinf_harmony.transpilers.Select(p => p.PatchMethod.FullDescription()))}"); } } } } } } } //apply transpilers and replace method calls on base methods with patched ones Dictionary dict_replacers = new Dictionary(); foreach (var pair in dict_transpilers) { List list = pair.Value; //the top copy to call in the override method MethodDefinition mtddef_override_copy = null; MethodReference mtdref_override_base = null; for (int i = list.Count - 1; i >= 0; i--) { TranspilerTarget curNode = list[i]; MethodPatcher patcher = curNode.mtdinf_original.GetMethodPatcher(); DynamicMethodDefinition dmd = patcher.CopyOriginal(); ILContext context = new ILContext(dmd.Definition); HarmonyManipulator.Manipulate(curNode.mtdinf_original, curNode.patchinf_harmony, context); var mtdref_original = module.ImportReference(curNode.mtdinf_original); var mtddef_copy = mtdref_original.Resolve().CloneToModuleAsStatic(context.Body, module.ImportReference(curNode.type_action), module); dmd.Dispose(); context.Dispose(); if (mtddef_override_copy != null && mtdref_override_base != null) { //replace calls to the base foreach (var ins in mtddef_copy.Body.Instructions) { if (ins.OpCode == OpCodes.Call && ((MethodReference)ins.Operand).FullName.Equals(mtdref_override_base.FullName)) { Log.Out($"replacing call to {mtdref_override_base.FullName} to {mtddef_override_copy.FullName}"); ins.Operand = mtddef_override_copy; } } } //add patched copy to the class typedef_newTarget.Methods.Add(mtddef_copy); //the iteration is reversed so make sure we grab the latest method mtddef_override_copy = mtddef_copy; mtdref_override_base = mtdref_original; } //create the method override that calls the patched copy if (mtddef_override_copy != null && mtdref_override_base != null) { GetOrCreateOverride(dict_overrides, pair.Key, mtdref_override_base, mtddef_override_copy); } } //Apply Postfixes first so that Prefixes can jump to the right instruction for (int i = 0; i < moduleTypes.Length; i++) { Type moduleType = moduleTypes[i]; Dictionary dict_targets = GetMethodOverrideTargets(i); string moduleID = ModuleUtils.CreateFieldName(moduleType); foreach (var pair in dict_targets) { MethodDefinition mtddef_root = module.ImportReference(pair.Value.mtdinf_base.GetBaseDefinition()).Resolve(); MethodDefinition mtddef_target = module.ImportReference(pair.Value.mtdinf_target).Resolve(); MethodPatchInfo mtdpinf_derived = GetOrCreateOverride(dict_overrides, pair.Key, pair.Value.mtdref_base); MethodDefinition mtddef_derived = mtdpinf_derived.Method; if (!dict_all_states.TryGetValue(pair.Key, out var dict_states)) { dict_states = new Dictionary(); dict_all_states.Add(pair.Key, dict_states); } var list_inst_pars = MatchArguments(mtddef_root, mtdpinf_derived, mtddef_target, i, true, dict_states, moduleID); //insert invocation var il = mtddef_derived.Body.GetILProcessor(); foreach (var ins in list_inst_pars) { il.InsertBefore(mtdpinf_derived.PostfixEnd, ins); } il.InsertBefore(mtdpinf_derived.PostfixEnd, il.Create(OpCodes.Call, module.ImportReference(mtddef_target))); if (mtdpinf_derived.PostfixBegin == null) mtdpinf_derived.PostfixBegin = list_inst_pars[0]; } } //Apply Prefixes for (int i = moduleTypes.Length - 1; i >= 0; i--) { Type moduleType = moduleTypes[i]; Dictionary dict_targets = GetMethodOverrideTargets(i); string moduleID = ModuleUtils.CreateFieldName(moduleType); foreach (var pair in dict_targets) { MethodDefinition mtddef_root = module.ImportReference(pair.Value.mtdinf_base.GetBaseDefinition()).Resolve(); MethodDefinition mtddef_target = module.ImportReference(pair.Value.mtdinf_target).Resolve(); MethodPatchInfo mtdpinf_derived = GetOrCreateOverride(dict_overrides, pair.Key, pair.Value.mtdref_base); MethodDefinition mtddef_derived = mtdpinf_derived.Method; dict_all_states.TryGetValue(pair.Key, out var dict_states); var list_inst_pars = MatchArguments(mtddef_root, mtdpinf_derived, mtddef_target, i, false, dict_states, moduleID); //insert invocation var il = mtdpinf_derived.Method.Body.GetILProcessor(); Instruction ins_insert = mtdpinf_derived.PrefixBegin; foreach (var ins in list_inst_pars) { il.InsertBefore(ins_insert, ins); } il.InsertBefore(ins_insert, il.Create(OpCodes.Call, module.ImportReference(mtddef_target))); il.InsertBefore(ins_insert, il.Create(OpCodes.Brfalse_S, mtdpinf_derived.PostfixBegin ?? mtdpinf_derived.PostfixEnd)); } } foreach (var pair in dict_all_states) { var dict_states = pair.Value; if (dict_states.Count > 0) { Log.Error($"__state variable count does not match in prefixes and postfixes for {pair.Key}! check following modules:\n" + string.Join("\n", dict_states.Keys)); throw new Exception(); } } //Add all overrides to new type foreach (var mtd in dict_overrides.Values) { typedef_newTarget.Methods.Add(mtd.Method); //Log.Out($"Add method override to new action: {mtd.Method.Name}"); } } /// /// /// /// /// /// /// /// private Dictionary GetMethodOverrideTargets(int moduleIndex) where T : Attribute, IMethodTarget { Type moduleType = moduleTypes[moduleIndex]; Dictionary dict_overrides = new Dictionary(); const BindingFlags searchFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; const BindingFlags extensionFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; foreach (var mtd in moduleType.GetMethods(searchFlags).Concat(moduleExtensionTypes[moduleIndex].SelectMany(t => t.GetMethods(extensionFlags)))) { if (mtd.GetCustomAttribute() != null) { foreach (HarmonyPatch hp in mtd.GetCustomAttributes()) { if (hp != null && (hp.info.declaringType == null || hp.info.declaringType.IsAssignableFrom(targetType))) { var hm = hp.info; hm.methodType = hm.methodType ?? MethodType.Normal; var hmclone = hm.Clone(); hmclone.declaringType = targetType; string id = hm.GetTargetMethodIdentifier(); MethodInfo mtdinf_base = hmclone.GetBaseMethod() as MethodInfo; if (mtdinf_base == null || !mtdinf_base.IsVirtual || mtdinf_base.IsFinal) { Log.Error($"Method not found: {moduleType.FullName} on {hmclone.methodName}\n{hmclone.ToString()}"); continue; } MethodReference mtdref_base = module.ImportReference(mtdinf_base); //Find preferred patch if (dict_overrides.TryGetValue(id, out var info)) { if (hm.declaringType == null) continue; //cur action type is sub or same class of cur preferred type //cur preferred type is sub class of previous preferred type //means cur preferred type is closer to the action type in inheritance hierachy than the previous one if (hm.declaringType.IsAssignableFrom(targetType) && (info.prefType == null || hm.declaringType.IsSubclassOf(info.prefType))) { dict_overrides[id] = new MethodOverrideInfo(mtd, mtdinf_base, mtdref_base, hm.declaringType); } } else { dict_overrides[id] = new MethodOverrideInfo(mtd, mtdinf_base, mtdref_base, hm.declaringType); } //Log.Out($"Add method override: {id} for {mtdref_base.FullName}/{mtdinf_base.Name}, action type: {itemActionType.Name}"); } else { //Log.Out($"No override target found or preferred type not match on {mtd.Name}"); } } } } return dict_overrides; } /// /// Get or create override MethodDefinition of mtdref_base. /// /// /// /// /// /// private MethodPatchInfo GetOrCreateOverride(Dictionary dict_overrides, string id, MethodReference mtdref_base, MethodDefinition mtddef_base_override = null) { //if (mtddef_base.FullName == "CreateModifierData") // throw new MethodAccessException($"YOU SHOULD NOT MANUALLY MODIFY CreateModifierData!"); if (dict_overrides.TryGetValue(id, out var mtdpinf_derived)) { return mtdpinf_derived; } //when overriding, retain attributes of base but make sure to remove the 'new' keyword which presents if you are overriding the root method MethodDefinition mtddef_base = mtdref_base.Resolve(); MethodDefinition mtddef_derived = new MethodDefinition(mtddef_base.Name, (mtddef_base.Attributes | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.ReuseSlot) & ~MethodAttributes.NewSlot, module.ImportReference(mtddef_base.ReturnType)); //Log.Out($"Create method override: {id} for {mtdref_base.FullName}"); foreach (var par in mtddef_base_override?.Parameters?.Skip(1) ?? mtddef_base.Parameters) { ParameterDefinition pardef = new ParameterDefinition(par.Name, par.Attributes, module.ImportReference(par.ParameterType)); if (par.HasConstant) pardef.Constant = par.Constant; mtddef_derived.Parameters.Add(pardef); } mtddef_derived.Body.Variables.Clear(); mtddef_derived.Body.InitLocals = true; mtddef_derived.Body.Variables.Add(new VariableDefinition(module.TypeSystem.Boolean)); bool hasReturnVal = mtddef_derived.ReturnType.MetadataType != MetadataType.Void; if (hasReturnVal) { mtddef_derived.Body.Variables.Add(new VariableDefinition(module.ImportReference(mtddef_base.ReturnType))); } var il = mtddef_derived.Body.GetILProcessor(); if (hasReturnVal) { il.Emit(OpCodes.Ldloca_S, mtddef_derived.Body.Variables[1]); il.Emit(OpCodes.Initobj, module.ImportReference(mtddef_derived.ReturnType)); } il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Stloc_S, mtddef_derived.Body.Variables[0]); Instruction prefixBegin = il.Create(OpCodes.Ldc_I4_1); il.Append(prefixBegin); il.Emit(OpCodes.Stloc_S, mtddef_derived.Body.Variables[0]); il.Emit(OpCodes.Ldarg_0); for (int i = 0; i < mtddef_derived.Parameters.Count; i++) { var par = mtddef_derived.Parameters[i]; il.Emit(par.ParameterType.IsByReference ? OpCodes.Ldarga_S : OpCodes.Ldarg_S, par); } il.Emit(OpCodes.Call, mtddef_base_override ?? module.ImportReference(mtdref_base)); if (hasReturnVal) { il.Emit(OpCodes.Stloc_S, mtddef_derived.Body.Variables[1]); il.Emit(OpCodes.Ldloc_S, mtddef_derived.Body.Variables[1]); } il.Emit(OpCodes.Ret); mtdpinf_derived = new MethodPatchInfo(mtddef_derived, mtddef_derived.Body.Instructions[mtddef_derived.Body.Instructions.Count - (hasReturnVal ? 2 : 1)], prefixBegin); dict_overrides.Add(id, mtdpinf_derived); return mtdpinf_derived; } /// /// Create a List that loads all arguments required to call the method onto stack. /// /// The root definition of this method. /// The override method. /// The patch method to be called. /// The injected module field. /// The injected data field. /// The assembly's main module. /// The base ItemAction type. /// /// /// /// private List MatchArguments(MethodDefinition mtddef_root, MethodPatchInfo mtdpinf_derived, MethodDefinition mtddef_target, int moduleIndex, bool isPostfix, Dictionary dict_states, string moduleID) { FieldDefinition flddef_module = arr_flddef_modules[moduleIndex]; var mtddef_derived = mtdpinf_derived.Method; var il = mtddef_derived.Body.GetILProcessor(); //Match parameters List list_inst_pars = new List(); list_inst_pars.Add(il.Create(OpCodes.Ldarg_0)); list_inst_pars.Add(il.Create(OpCodes.Ldfld, flddef_module)); foreach (var par in mtddef_target.IsStatic ? mtddef_target.Parameters.Skip(1) : mtddef_target.Parameters) { if (par.Name.StartsWith("___")) { //___ means non public fields string str_fldname = par.Name.Substring(3); FieldDefinition flddef_target = module.ImportReference(targetType.GetField(str_fldname, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)).Resolve(); if (flddef_target == null) throw new MissingFieldException($"Field with name \"{str_fldname}\" not found! Patch method: {mtddef_target.DeclaringType.FullName}.{mtddef_target.Name}"); if (flddef_target.IsStatic) { list_inst_pars.Add(il.Create((par.ParameterType.IsByReference) ? OpCodes.Ldsflda : OpCodes.Ldsfld, module.ImportReference(flddef_target))); } else { list_inst_pars.Add(il.Create(OpCodes.Ldarg_0)); list_inst_pars.Add(il.Create(par.ParameterType.IsByReference ? OpCodes.Ldflda : OpCodes.Ldfld, module.ImportReference(flddef_target))); } } else if (!MatchSpecialParameters(par, mtddef_target, mtdpinf_derived, moduleIndex, list_inst_pars, il, isPostfix, dict_states, moduleID)) { //match param by name int index = -1; for (int j = 0; j < mtddef_root.Parameters.Count; j++) { if (mtddef_root.Parameters[j].Name == par.Name) { index = mtddef_root.Parameters[j].Index; break; } } if (index < 0) throw new ArgumentException($"Parameter \"{par.Name}\" not found! Patch method: {mtddef_target.DeclaringType.FullName}.{mtddef_target.Name}"); try { //Log.Out($"Match Parameter {par.Name} to {mtddef_derived.Parameters[index].Name}/{mtddef_root.Parameters[index].Name} index: {index}"); } catch (ArgumentOutOfRangeException e) { Log.Error($"index {index} parameter {par.Name}" + $"root pars: {{{string.Join(",", mtddef_root.Parameters.Select(p => p.Name + "/" + p.Index).ToArray())}}}" + $"derived pars: {{{string.Join(",", mtddef_derived.Parameters.Select(p => p.Name + "/" + p.Index).ToArray())}}}"); throw e; } if (!mtddef_derived.Parameters[index].ParameterType.IsByReference) { list_inst_pars.Add(il.Create(par.ParameterType.IsByReference ? OpCodes.Ldarga_S : OpCodes.Ldarg_S, mtddef_derived.Parameters[index])); } else { list_inst_pars.Add(il.Create(OpCodes.Ldarg_S, mtddef_derived.Parameters[index])); if (!par.ParameterType.IsByReference) { list_inst_pars.Add(il.Create(OpCodes.Ldind_Ref)); } } } } return list_inst_pars; } private bool MatchSpecialParameters(ParameterDefinition par, MethodDefinition mtddef_target, MethodPatchInfo mtdpinf_derived, int moduleIndex, List list_inst_pars, ILProcessor il, bool isPostfix, Dictionary dict_states, string moduleID) { MethodDefinition mtddef_derived = mtdpinf_derived.Method; switch (par.Name) { //load ItemAction instance case "__instance": list_inst_pars.Add(il.Create(OpCodes.Ldarg_0)); break; //load return value case "__result": list_inst_pars.Add(il.Create(par.ParameterType.IsByReference ? OpCodes.Ldloca_S : OpCodes.Ldloc_S, mtddef_derived.Body.Variables[1])); break; //for postfix only, indicates whether original method is executed case "__runOriginal": if (par.ParameterType.IsByReference) throw new ArgumentException($"__runOriginal is readonly! Patch method: {mtddef_target.DeclaringType.FullName}.{mtddef_target.Name}"); list_inst_pars.Add(il.Create(OpCodes.Ldloc_S, mtddef_derived.Body.Variables[0])); break; case "__state": if (dict_states == null) { throw new ArgumentNullException($"__state is found in prefix but no matching postfix exists! Patch method: {mtddef_target.DeclaringType.FullName}.{mtddef_target.Name}"); } if (!isPostfix && !dict_states.TryGetValue(moduleID, out var vardef)) { throw new KeyNotFoundException($"__state is found in prefix but not found in corresponding postfix! Patch method: {mtddef_target.DeclaringType.FullName}.{mtddef_target.Name}"); } if (par.IsOut && isPostfix) { throw new ArgumentException($"__state is marked as out parameter in postfix! Patch method: {mtddef_target.DeclaringType.FullName}.{mtddef_target.Name}"); } if (!par.IsOut && !isPostfix) { throw new ArgumentException($"__state is not marked as out in prefix! Patch method: {mtddef_target.DeclaringType.FullName}.{mtddef_target.Name}"); } if (isPostfix) { vardef = new VariableDefinition(module.ImportReference(par.ParameterType)); mtddef_derived.Body.Variables.Add(vardef); dict_states.Add(moduleID, vardef); var ins = mtddef_derived.Body.Instructions[0]; il.InsertBefore(ins, il.Create(OpCodes.Ldloca_S, vardef)); il.InsertBefore(ins, il.Create(OpCodes.Initobj, module.ImportReference(par.ParameterType))); list_inst_pars.Add(il.Create(OpCodes.Ldloc_S, vardef)); } else { vardef = dict_states[moduleID]; dict_states.Remove(moduleID); list_inst_pars.Add(il.Create(OpCodes.Ldloca_S, vardef)); } break; default: return processor != null ? processor.MatchSpecialArgs(par, mtddef_target, mtdpinf_derived, moduleIndex, list_inst_pars, il) : false; } return true; } public void MakeContainerFor(TypeDefinition typedef_container, Type type_module, out FieldDefinition flddef_module) { var typeref_module = module.ImportReference(type_module); flddef_module = new FieldDefinition(ModuleUtils.CreateFieldName(type_module), FieldAttributes.Public, typeref_module); typedef_container.Fields.Add(flddef_module); typedef_container.Interfaces.Add(new InterfaceImplementation(typeref_interface.MakeGenericInstanceType(typeref_module))); PropertyDefinition propdef_instance = new PropertyDefinition("Instance", Mono.Cecil.PropertyAttributes.None, typeref_module); MethodDefinition mtddef_instance_getter = new MethodDefinition("get_Instance", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final, typeref_module); mtddef_instance_getter.Overrides.Add(module.ImportReference(AccessTools.Method(typeof(IModuleContainerFor<>).MakeGenericType(type_module), "get_Instance"))); typedef_container.Methods.Add(mtddef_instance_getter); mtddef_instance_getter.Body = new Mono.Cecil.Cil.MethodBody(mtddef_instance_getter); var generator = mtddef_instance_getter.Body.GetILProcessor(); generator.Emit(OpCodes.Ldarg_0); generator.Emit(OpCodes.Ldfld, flddef_module); generator.Emit(OpCodes.Ret); propdef_instance.GetMethod = mtddef_instance_getter; typedef_container.Properties.Add(propdef_instance); } } }