diff --git a/Torch.Server/packages.lock.json b/Torch.Server/packages.lock.json index 2703515..1595433 100644 --- a/Torch.Server/packages.lock.json +++ b/Torch.Server/packages.lock.json @@ -162,12 +162,10 @@ }, "HarmonyX": { "type": "Transitive", - "resolved": "2.10.2-prerelease.1", - "contentHash": "5hbH0ENhQ+JV7tk63fQ2ab7MtplRHKJYH1JfIjG39rlltHNXxAcGJh05an+SQbVRV4EoP/+Qm9w9BrLc3RwRMA==", + "resolved": "2.10.2-prerelease.2", + "contentHash": "JCoFKWQx90PqF3iztNWCLLYaPnNjMyet4/pJZaSpX8zQGIhk8pWLomUD3JFLyqP+NpUzFxLbOYMxiQZYjDVpFw==", "dependencies": { - "MonoModReorg.RuntimeDetour": "22.11.21-prerelease.2", - "System.Reflection.Emit": "4.7.0", - "System.Reflection.Emit.Lightweight": "4.7.0" + "MonoModReorg.RuntimeDetour": "23.1.2-prerelease.1" } }, "JorgeSerrano.Json.JsonSnakeCaseNamingPolicy": { @@ -320,48 +318,48 @@ }, "MonoModReorg.Backports": { "type": "Transitive", - "resolved": "22.11.21-prerelease.2", - "contentHash": "69T6jjA5nx29jLkdqtfXKlJ8sMqIlc6czNDTomy0rbM68W0xo2JRJBgsu2mroBuqx7nvUdX+zIU6k1edS/pPbw==", + "resolved": "23.1.2-prerelease.1", + "contentHash": "m1wlCgVjZTFJs3mUxmC1aE/O0RIvsNbSFBI/g93Bqzz1tHa+LhXFyrHzL60PeZMQBIPVy3CeDX4um/UrqLOn/g==", "dependencies": { - "MonoModReorg.ILHelpers": "22.11.21-prerelease.2" + "MonoModReorg.ILHelpers": "23.1.2-prerelease.1" } }, "MonoModReorg.Core": { "type": "Transitive", - "resolved": "22.11.21-prerelease.2", - "contentHash": "gDoxu4aAF6TeOo8rsrj5prq2X36i12ch6NeRHu/Ct0H3qoPDHuEQ6JMJN/Eiy45YrLNEN7C5+Ku4BrNX4nwVQg==", + "resolved": "23.1.2-prerelease.1", + "contentHash": "t1Y89M0rbwUx2VjDMCJOWgtSdsi1F5KNu0O6JAMOtwo2EWJ0HfYj9nS8UWWPwrgRpsquGjqbmYA8jhb59F2a/A==", "dependencies": { "Mono.Cecil": "0.11.4", - "MonoModReorg.Backports": "22.11.21-prerelease.2", - "MonoModReorg.ILHelpers": "22.11.21-prerelease.2", - "MonoModReorg.Utils": "22.11.21-prerelease.2" + "MonoModReorg.Backports": "23.1.2-prerelease.1", + "MonoModReorg.ILHelpers": "23.1.2-prerelease.1", + "MonoModReorg.Utils": "23.1.2-prerelease.1" } }, "MonoModReorg.ILHelpers": { "type": "Transitive", - "resolved": "22.11.21-prerelease.2", - "contentHash": "JtOKHJR4DEyq3HxmdEVXIxhqNQnu1KmjGFXuEQrNHoPbzi8Yr9465VKVXdsoAF0Lm8StdyJHQ03efjv3+OlonA==" + "resolved": "23.1.2-prerelease.1", + "contentHash": "GVh1cmrTCAK0zHr3t8aHnKsyKIlDFiDERn++lCZomHcYc8dgcOAhpkZ7KmaKgZCTJuBIrc44RjpKFr/4ScQnGA==" }, "MonoModReorg.RuntimeDetour": { "type": "Transitive", - "resolved": "22.11.21-prerelease.2", - "contentHash": "Qv1h4rW03LrHwxwVuw5R6hbL8X78l8Lfnxe5tMlyVAe+AK0HnwsRzjsTwzFF57wxWUwq12NbLflkzV6T+hIhJw==", + "resolved": "23.1.2-prerelease.1", + "contentHash": "UZyJ7OIbLCIBg+dzLejWq2paL1s11koUrq1noSLGCP9uNmFjwDPK+lRmGs0X4qg+Alfq6VsOpI45pGqmaAvP+Q==", "dependencies": { "Mono.Cecil": "0.11.4", - "MonoModReorg.Backports": "22.11.21-prerelease.2", - "MonoModReorg.Core": "22.11.21-prerelease.2", - "MonoModReorg.ILHelpers": "22.11.21-prerelease.2", - "MonoModReorg.Utils": "22.11.21-prerelease.2" + "MonoModReorg.Backports": "23.1.2-prerelease.1", + "MonoModReorg.Core": "23.1.2-prerelease.1", + "MonoModReorg.ILHelpers": "23.1.2-prerelease.1", + "MonoModReorg.Utils": "23.1.2-prerelease.1" } }, "MonoModReorg.Utils": { "type": "Transitive", - "resolved": "22.11.21-prerelease.2", - "contentHash": "TX+vlgg2/x8rzEOqwiAy2qv61FjlJsr4u10WGTekCkulZVmmC+xxDmK+4Do9noXF/4RlgFN6sR3m9/W8KvJq3g==", + "resolved": "23.1.2-prerelease.1", + "contentHash": "6N4LNG+x4RVPLOc8QWL7dc5sqWdl0gxR+4ASRd1CvvappsK84ISgD9qgeYHgQQtTgE+h6Cuqr3Om4Ly0roLfoA==", "dependencies": { "Mono.Cecil": "0.11.4", - "MonoModReorg.Backports": "22.11.21-prerelease.2", - "MonoModReorg.ILHelpers": "22.11.21-prerelease.2" + "MonoModReorg.Backports": "23.1.2-prerelease.1", + "MonoModReorg.ILHelpers": "23.1.2-prerelease.1" } }, "Newtonsoft.Json": { @@ -517,16 +515,6 @@ "resolved": "4.5.5", "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" }, - "System.Reflection.Emit": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "VR4kk8XLKebQ4MZuKuIni/7oh+QGFmZW3qORd1GvBq/8026OpW501SzT/oypwiQl4TvT8ErnReh/NzY9u+C6wQ==" - }, - "System.Reflection.Emit.Lightweight": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "a4OLB4IITxAXJeV74MDx49Oq2+PsF6Sml54XAFv+2RyWwtDBcabzoxiiJRhdhx+gaohLh4hEGCLQyBozXoQPqA==" - }, "System.Reflection.Metadata": { "type": "Transitive", "resolved": "5.0.0", @@ -597,11 +585,11 @@ "type": "Project", "dependencies": { "ControlzEx": "[5.0.2, )", - "HarmonyX": "[2.10.2-prerelease.1, )", + "HarmonyX": "[2.10.2-prerelease.2, )", "MahApps.Metro": "[2.4.9, )", "Microsoft.CodeAnalysis.CSharp": "[4.4.0, )", "Microsoft.CodeAnalysis.Common": "[4.4.0, )", - "MonoModReorg.RuntimeDetour": "[22.11.21-prerelease.2, )", + "MonoModReorg.RuntimeDetour": "[23.1.2-prerelease.1, )", "NLog": "[5.1.0, )", "System.ComponentModel.Annotations": "[5.0.0, )", "Torch.API": "[1.0.0, )", diff --git a/Torch/Managers/PatchManager/DecoratedMethod.cs b/Torch/Managers/PatchManager/DecoratedMethod.cs index e880059..6d37104 100644 --- a/Torch/Managers/PatchManager/DecoratedMethod.cs +++ b/Torch/Managers/PatchManager/DecoratedMethod.cs @@ -1,40 +1,34 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; -using System.ComponentModel.Design; -using System.Diagnostics; -using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using MonoMod.Cil; -using MonoMod.RuntimeDetour; +using HarmonyLib; using MonoMod.Utils; -using MonoMod.Utils.Cil; using NLog; using Torch.Managers.PatchManager.MSIL; using Torch.Managers.PatchManager.Transpile; -using Torch.Utils; namespace Torch.Managers.PatchManager { internal class DecoratedMethod : MethodRewritePattern { - [ReflectedMethodInfo(typeof(MethodBase), nameof(MethodBase.GetMethodFromHandle), Parameters = new[] {typeof(RuntimeMethodHandle)})] - private static MethodInfo _getMethodFromHandle = null!; - - [ReflectedMethodInfo(typeof(MethodBase), nameof(MethodBase.GetMethodFromHandle), Parameters = new[] {typeof(RuntimeMethodHandle), typeof(RuntimeTypeHandle)})] - private static MethodInfo _getMethodFromHandleGeneric = null!; + private static readonly ConcurrentDictionary Methods = new(); private static readonly Logger _log = LogManager.GetCurrentClassLogger(); private readonly MethodBase _method; + private readonly Harmony _harmony; - private ILHook _hook; + private readonly PatchProcessor _processor; + private bool _hasRan; - internal DecoratedMethod(MethodBase method) : base(null) + internal DecoratedMethod(MethodBase method, Harmony harmony) : base(null) { _method = method; + _harmony = harmony; + _processor = harmony.CreateProcessor(method); + Methods[method] = this; } internal bool HasChanged() @@ -56,29 +50,25 @@ namespace Torch.Managers.PatchManager _log.Log(PrintMode != 0 ? LogLevel.Info : LogLevel.Debug, $"Begin patching {_method.DeclaringType?.FullName}#{_method.Name}({string.Join(", ", _method.GetParameters().Select(x => x.ParameterType.Name))})"); - _hook ??= new ILHook(_method, Manipulator, false); - try + foreach (var prefix in Prefixes) { - _hook.Apply(); + _processor.AddPrefix(prefix); } - catch (InvalidProgramException e) + + foreach (var suffix in Suffixes) { - _hook.Undo(); - PrintMode = PrintModeEnum.Emitted | PrintModeEnum.Original; - try - { - _hook.Apply(); - } - catch - { - // Ignore, we are already know there is an error in IL - } + _processor.AddPostfix(suffix); + } - throw; - } + if (Transpilers.Any() || PostTranspilers.Any()) + _processor.AddTranspiler(SymbolExtensions.GetMethodInfo(() => TranspilerProxy(null, null, null))); + + _processor.Patch(); _log.Log(PrintMode != 0 ? LogLevel.Info : LogLevel.Debug, $"Done patching {_method.GetID()})"); + + _hasRan = true; } catch (Exception exception) { @@ -87,332 +77,40 @@ namespace Torch.Managers.PatchManager } } + private static IEnumerable TranspilerProxy(IEnumerable instructions, + MethodBase __originalMethod, + ILGenerator generator) + { + if (!Methods.TryGetValue(__originalMethod, out var decoratedMethod)) + throw new Exception($"Unknown method {__originalMethod.GetID()}"); + + var loggingGenerator = new LoggingIlGenerator(generator, decoratedMethod.PrintMode != 0 ? LogLevel.Info : LogLevel.Debug); + + MsilLocal LocalFactory(Type type) => new(loggingGenerator.DeclareLocal(type)); + + foreach (var transpiler in decoratedMethod.Transpilers.Concat(decoratedMethod.PostTranspilers)) + { + var ins = (IEnumerable) transpiler.Invoke(null, transpiler.GetParameters().Select(b => b switch + { + _ when b.ParameterType.IsAssignableTo(typeof(MethodBase)) => __originalMethod, + _ when b.ParameterType.IsAssignableTo(typeof(IEnumerable)) => instructions.Select(c => new MsilInstruction(c)), + _ when b.ParameterType.IsAssignableTo(typeof(Func)) => new Func(LocalFactory), + _ => null + }).ToArray()); + + instructions = ins!.Select(b => b.ToCodeIns(loggingGenerator)).ToList(); + } + + return instructions; + } + internal void Revert() { - if (_hook == null) + if (!_hasRan) return; + _log.Debug($"Revert {_method.GetID()}"); - _hook.Dispose(); - _hook = null; + _processor.Unpatch(HarmonyPatchType.All, _harmony.Id); } - - #region Create - - public const string INSTANCE_PARAMETER = "__instance"; - public const string RESULT_PARAMETER = "__result"; - public const string PREFIX_SKIPPED_PARAMETER = "__prefixSkipped"; - public const string ORIGINAL_PARAMETER = "__original"; - public const string LOCAL_PARAMETER = "__local"; - - private void SavePatchedMethod(string target) - { - throw new NotSupportedException(); - // var asmBuilder = - // AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("SomeName"), AssemblyBuilderAccess.RunAndSave, Path.GetDirectoryName(target)); - // var moduleBuilder = asmBuilder.DefineDynamicModule(Path.GetFileNameWithoutExtension(target), Path.GetFileName(target)); - // var typeBuilder = moduleBuilder.DefineType("Test", TypeAttributes.Public); - // - // - // var methodName = _method.Name + $"_{_patchSalt}"; - // var returnType = _method is MethodInfo meth ? meth.ReturnType : typeof(void); - // var parameters = _method.GetParameters(); - // var parameterTypes = (_method.IsStatic ? Enumerable.Empty() : new[] {_method.DeclaringType}) - // .Concat(parameters.Select(x => x.ParameterType)).ToArray(); - // - // var patchMethod = typeBuilder.DefineMethod(methodName, MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, - // returnType, parameterTypes); - // if (!_method.IsStatic) - // patchMethod.DefineParameter(0, ParameterAttributes.None, INSTANCE_PARAMETER); - // for (var i = 0; i < parameters.Length; i++) - // patchMethod.DefineParameter((patchMethod.IsStatic ? 0 : 1) + i, parameters[i].Attributes, parameters[i].Name); - // - // var generator = new LoggingIlGenerator(patchMethod.GetILGenerator(), LogLevel.Trace); - // List il = EmitPatched((type, pinned) => new MsilLocal(generator.DeclareLocal(type, pinned))).ToList(); - // - // MethodTranspiler.EmitMethod(il, generator); - // - // Type res = typeBuilder.CreateType(); - // asmBuilder.Save(Path.GetFileName(target)); - // foreach (var method in res.GetMethods(BindingFlags.Public | BindingFlags.Static)) - // _log.Info($"Information " + method); - } - - public void Manipulator(ILContext context) - { - context.IL.Clear(); - var generator = new LoggingIlGenerator(new CecilILGenerator(context.IL), - PrintMode.HasFlag(PrintModeEnum.EmittedReflection) ? LogLevel.Info : LogLevel.Trace); - List il = EmitPatched((type, pinned) => new MsilLocal(generator.DeclareLocal(type, pinned))).ToList(); - - var dumpTarget = DumpTarget != null ? File.CreateText(DumpTarget) : null; - try - { - const string gap = "\n\n\n\n\n"; - - void LogTarget(PrintModeEnum mode, bool err, string msg) - { - if (DumpMode.HasFlag(mode)) - dumpTarget?.WriteLine((err ? "ERROR " : "") + msg); - if (!PrintMode.HasFlag(mode)) return; - if (err) - _log.Error(msg); - else - _log.Info(msg); - } - -#pragma warning disable CS0612 - if (PrintMsil || DumpTarget != null) -#pragma warning restore CS0612 - { - lock (_log) - { - var ctx = new MethodContext(_method); - ctx.Read(); - LogTarget(PrintModeEnum.Original, false, "========== Original method =========="); - MethodTranspiler.IntegrityAnalysis((a, b) => LogTarget(PrintModeEnum.Original, a, b), ctx.Instructions, true); - LogTarget(PrintModeEnum.Original, false, gap); - - LogTarget(PrintModeEnum.Emitted, false, "========== Desired method =========="); - MethodTranspiler.IntegrityAnalysis((a, b) => LogTarget(PrintModeEnum.Emitted, a, b), il); - LogTarget(PrintModeEnum.Emitted, false, gap); - // If the method is invalid the program is likely to hard crash in EmitMethod or Compile, so flush the log - LogManager.Flush(); - } - } - - MethodTranspiler.EmitMethod(il, generator); - -#pragma warning disable CS0612 - if (PrintMsil || DumpTarget != null) -#pragma warning restore CS0612 - { - lock (_log) - { - var instructions = context.Body.Instructions - .Select(b => b.ToMsilInstruction()).ToList(); - LogTarget(PrintModeEnum.Patched, false, "========== Patched method =========="); - MethodTranspiler.IntegrityAnalysis((a, b) => LogTarget(PrintModeEnum.Patched, a, b), instructions, true); - LogTarget(PrintModeEnum.Patched, false, gap); - } - } - } - finally - { - dumpTarget?.Close(); - } - } - - #endregion - - #region Emit - - private IEnumerable EmitPatched(Func declareLocal) - { - var methodBody = _method.GetMethodBody(); - Debug.Assert(methodBody != null, "Method body is null"); - foreach (var localVar in methodBody.LocalVariables) - { - Debug.Assert(localVar.LocalType != null); - declareLocal(localVar.LocalType, localVar.IsPinned); - } - - var instructions = new List(); - var specialVariables = new Dictionary(); - - var labelAfterOriginalContent = new MsilLabel(); - var labelSkipMethodContent = new MsilLabel(); - - - Type returnType = _method is MethodInfo meth ? meth.ReturnType : typeof(void); - MsilLocal resultVariable = null; - if (returnType != typeof(void)) - { - if (Prefixes.Concat(Suffixes).SelectMany(x => x.GetParameters()).Any(x => x.Name == RESULT_PARAMETER) - || Prefixes.Any(x => x.ReturnType == typeof(bool))) - resultVariable = declareLocal(returnType, false); - } - - if (resultVariable != null) - instructions.AddRange(resultVariable.SetToDefault()); - MsilLocal prefixSkippedVariable = null; - if (Prefixes.Count > 0 && Suffixes.Any(x => x.GetParameters() - .Any(y => y.Name.Equals(PREFIX_SKIPPED_PARAMETER)))) - { - prefixSkippedVariable = declareLocal(typeof(bool), false); - specialVariables.Add(PREFIX_SKIPPED_PARAMETER, prefixSkippedVariable); - } - - if (resultVariable != null) - specialVariables.Add(RESULT_PARAMETER, resultVariable); - - // Create special variables - foreach (var m in Prefixes.Concat(Suffixes)) - foreach (var param in m.GetParameters()) - if (param.Name.StartsWith(LOCAL_PARAMETER)) - { - var requiredType = param.ParameterType.IsByRef ? param.ParameterType.GetElementType() : param.ParameterType; - if (specialVariables.TryGetValue(param.Name, out var existingParam)) - { - if (existingParam.Type != requiredType) - throw new ArgumentException( - $"Trying to use injected local {param.Name} for {m.DeclaringType?.FullName}#{m.ToString()} with type {requiredType} but a local with the same name already exists with type {existingParam.Type}", - param.Name); - } - else - specialVariables.Add(param.Name, declareLocal(requiredType, false)); - } - - foreach (MethodInfo prefix in Prefixes) - { - instructions.AddRange(EmitMonkeyCall(prefix, specialVariables)); - if (prefix.ReturnType == typeof(bool)) - instructions.Add(new MsilInstruction(OpCodes.Brfalse).InlineTarget(labelSkipMethodContent)); - else if (prefix.ReturnType != typeof(void)) - throw new PatchException( - $"Prefixes must return void or bool. {prefix.DeclaringType?.FullName}.{prefix.Name} returns {prefix.ReturnType}", prefix); - } - - instructions.AddRange(MethodTranspiler.Transpile(_method, (x) => declareLocal(x, false), Transpilers, labelAfterOriginalContent)); - - instructions.Add(new MsilInstruction(OpCodes.Nop).LabelWith(labelAfterOriginalContent)); - if (resultVariable != null) - instructions.Add(new MsilInstruction(OpCodes.Stloc).InlineValue(resultVariable)); - var notSkip = new MsilLabel(); - instructions.Add(new MsilInstruction(OpCodes.Br).InlineTarget(notSkip)); - instructions.Add(new MsilInstruction(OpCodes.Nop).LabelWith(labelSkipMethodContent)); - if (prefixSkippedVariable != null) - { - instructions.Add(new MsilInstruction(OpCodes.Ldc_I4_1)); - instructions.Add(new MsilInstruction(OpCodes.Stloc).InlineValue(prefixSkippedVariable)); - } - - instructions.Add(new MsilInstruction(OpCodes.Nop).LabelWith(notSkip)); - - foreach (MethodInfo suffix in Suffixes) - { - instructions.AddRange(EmitMonkeyCall(suffix, specialVariables)); - if (suffix.ReturnType != typeof(void)) - throw new PatchException($"Suffixes must return void. {suffix.DeclaringType?.FullName}.{suffix.Name} returns {suffix.ReturnType}", suffix); - } - - if (resultVariable != null) - instructions.Add(new MsilInstruction(OpCodes.Ldloc).InlineValue(resultVariable)); - instructions.Add(new MsilInstruction(OpCodes.Ret)); - - var result = MethodTranspiler.Transpile(_method, instructions, (x) => declareLocal(x, false), PostTranspilers, null).ToList(); - if (result.Last().OpCode != OpCodes.Ret) - result.Add(new MsilInstruction(OpCodes.Ret)); - return result; - } - - private IEnumerable EmitMonkeyCall(MethodInfo patch, - IReadOnlyDictionary specialVariables) - { - foreach (var param in patch.GetParameters()) - { - switch (param.Name) - { - case INSTANCE_PARAMETER: - { - if (_method.IsStatic) - throw new PatchException("Can't use an instance parameter for a static method", _method); - yield return new MsilInstruction(OpCodes.Ldarg_0); - break; - } - case ORIGINAL_PARAMETER: - { - if (!typeof(MethodBase).IsAssignableFrom(param.ParameterType)) - throw new PatchException($"Original parameter should be assignable to {nameof(MethodBase)}", - _method); - yield return new MsilInstruction(OpCodes.Ldtoken).InlineValue(_method); - if (_method.DeclaringType!.ContainsGenericParameters) - { - yield return new MsilInstruction(OpCodes.Ldtoken).InlineValue(_method.DeclaringType); - yield return new MsilInstruction(OpCodes.Call).InlineValue(_getMethodFromHandleGeneric); - } - else - yield return new MsilInstruction(OpCodes.Call).InlineValue(_getMethodFromHandle); - - if (param.ParameterType != typeof(MethodBase)) - yield return new MsilInstruction(OpCodes.Castclass).InlineValue(param.ParameterType); - break; - } - case PREFIX_SKIPPED_PARAMETER: - { - if (param.ParameterType != typeof(bool)) - throw new PatchException($"Prefix skipped parameter {param.ParameterType} must be of type bool", _method); - if (param.ParameterType.IsByRef || param.IsOut) - throw new PatchException($"Prefix skipped parameter {param.ParameterType} can't be a reference type", _method); - if (specialVariables.TryGetValue(PREFIX_SKIPPED_PARAMETER, out MsilLocal prefixSkip)) - yield return new MsilInstruction(OpCodes.Ldloc).InlineValue(prefixSkip); - else - yield return new MsilInstruction(OpCodes.Ldc_I4_0); - break; - } - case RESULT_PARAMETER: - { - var retType = param.ParameterType.IsByRef - ? param.ParameterType.GetElementType() - : param.ParameterType; - if (retType == null || !retType.IsAssignableFrom(specialVariables[RESULT_PARAMETER].Type)) - throw new PatchException( - $"Return type {specialVariables[RESULT_PARAMETER].Type} can't be assigned to result parameter type {retType}", _method); - yield return new MsilInstruction(param.ParameterType.IsByRef ? OpCodes.Ldloca : OpCodes.Ldloc) - .InlineValue(specialVariables[RESULT_PARAMETER]); - break; - } - default: - { - if (specialVariables.TryGetValue(param.Name, out var specialVar)) - { - yield return new MsilInstruction(param.ParameterType.IsByRef ? OpCodes.Ldloca : OpCodes.Ldloc) - .InlineValue(specialVar); - break; - } - - if (param.Name.StartsWith("__field_")) - { - var fieldName = param.Name.Substring(8); - var fieldDef = _method.DeclaringType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static).FirstOrDefault(x => x.Name == fieldName); - if (fieldDef == null) throw new PatchException($"Could not find field {fieldName}", _method); - if (fieldDef.IsStatic) - yield return new MsilInstruction(param.ParameterType.IsByRef ? OpCodes.Ldsflda : OpCodes.Ldsfld) - .InlineValue(fieldDef); - else - { - yield return new MsilInstruction(OpCodes.Ldarg_0); - yield return new MsilInstruction(param.ParameterType.IsByRef ? OpCodes.Ldflda : OpCodes.Ldfld) - .InlineValue(fieldDef); - } - break; - } - - ParameterInfo declParam = _method.GetParameters().FirstOrDefault(x => x.Name == param.Name); - - if (declParam == null) - throw new PatchException($"Parameter name {param.Name} not found", _method); - int paramIdx = (_method.IsStatic ? 0 : 1) + declParam.Position; - - bool patchByRef = param.IsOut || param.ParameterType.IsByRef; - bool declByRef = declParam.IsOut || declParam.ParameterType.IsByRef; - if (patchByRef == declByRef) - yield return new MsilInstruction(OpCodes.Ldarg).InlineValue(new MsilArgument(paramIdx)); - else if (patchByRef) - yield return new MsilInstruction(OpCodes.Ldarga).InlineValue(new MsilArgument(paramIdx)); - else - { - yield return new MsilInstruction(OpCodes.Ldarg).InlineValue(new MsilArgument(paramIdx)); - yield return EmitExtensions.EmitDereference(declParam.ParameterType); - } - - break; - } - } - } - - yield return new MsilInstruction(OpCodes.Call).InlineValue(patch); - } - - #endregion } } \ No newline at end of file diff --git a/Torch/Managers/PatchManager/MSIL/MsilInstruction.cs b/Torch/Managers/PatchManager/MSIL/MsilInstruction.cs index cc0af95..bc9c53f 100644 --- a/Torch/Managers/PatchManager/MSIL/MsilInstruction.cs +++ b/Torch/Managers/PatchManager/MSIL/MsilInstruction.cs @@ -7,6 +7,7 @@ using System.Reflection; using System.Reflection.Emit; using System.Text; using System.Windows.Documents; +using HarmonyLib; using Mono.Cecil; using Mono.Cecil.Cil; using MonoMod.Utils; @@ -88,6 +89,85 @@ namespace Torch.Managers.PatchManager.MSIL throw new ArgumentOutOfRangeException(); } } + + public MsilInstruction(CodeInstruction instruction) : this(instruction.opcode) + { + switch (instruction.operand) + { + case LocalBuilder builder when Operand is MsilOperandInline.MsilOperandLocal operandLocal: + operandLocal.Value = new(builder); + break; + case MethodBase methodBase when Operand is MsilOperandInline.MsilOperandReflected operandMethod: + operandMethod.Value = methodBase; + break; + case Type type when Operand is MsilOperandInline.MsilOperandReflected operandType: + operandType.Value = type; + break; + case MemberInfo info when Operand is MsilOperandInline.MsilOperandReflected operandMember: + operandMember.Value = info; + break; + case IEnumerable