From 600e73ad43f5e7ddd442bfb6052a56e0128cab46 Mon Sep 17 00:00:00 2001 From: Westin Miller Date: Sat, 4 Nov 2017 10:09:22 -0700 Subject: [PATCH] Post transpilers (happen after prefix and suffix) More debugging options More robust inline writing and reading --- .../Managers/PatchManager/DecoratedMethod.cs | 153 ++++++---- Torch/Managers/PatchManager/EmitExtensions.cs | 77 ++--- .../PatchManager/MSIL/ITokenResolver.cs | 2 +- .../PatchManager/MSIL/MsilInstruction.cs | 10 +- .../MSIL/MsilInstructionExtensions.cs | 34 ++- Torch/Managers/PatchManager/MSIL/MsilLocal.cs | 1 + .../PatchManager/MSIL/MsilOperandBrTarget.cs | 35 ++- .../PatchManager/MSIL/MsilOperandInline.cs | 286 +++++++++++++----- .../PatchManager/MethodRewritePattern.cs | 10 +- Torch/Managers/PatchManager/PatchManager.cs | 4 +- .../Transpile/LoggingILGenerator.cs | 65 ++-- .../PatchManager/Transpile/MethodContext.cs | 27 +- .../Transpile/MethodTranspiler.cs | 30 +- 13 files changed, 498 insertions(+), 236 deletions(-) diff --git a/Torch/Managers/PatchManager/DecoratedMethod.cs b/Torch/Managers/PatchManager/DecoratedMethod.cs index 6171a13..22e3073 100644 --- a/Torch/Managers/PatchManager/DecoratedMethod.cs +++ b/Torch/Managers/PatchManager/DecoratedMethod.cs @@ -29,20 +29,21 @@ namespace Torch.Managers.PatchManager internal bool HasChanged() { - return Prefixes.HasChanges() || Suffixes.HasChanges() || Transpilers.HasChanges(); + return Prefixes.HasChanges() || Suffixes.HasChanges() || Transpilers.HasChanges() || PostTranspilers.HasChanges(); } internal void Commit() { try { - if (!Prefixes.HasChanges(true) && !Suffixes.HasChanges(true) && !Transpilers.HasChanges(true)) + // non-greedy so they are all reset + if (!Prefixes.HasChanges(true) & !Suffixes.HasChanges(true) & !Transpilers.HasChanges(true) & !PostTranspilers.HasChanges(true)) return; Revert(); - if (Prefixes.Count == 0 && Suffixes.Count == 0 && Transpilers.Count == 0) + if (Prefixes.Count == 0 && Suffixes.Count == 0 && Transpilers.Count == 0 && PostTranspilers.Count == 0) return; - _log.Debug( + _log.Log(PrintMsil ? LogLevel.Info : LogLevel.Debug, $"Begin patching {_method.DeclaringType?.FullName}#{_method.Name}({string.Join(", ", _method.GetParameters().Select(x => x.ParameterType.Name))})"); var patch = ComposePatchedMethod(); @@ -50,7 +51,7 @@ namespace Torch.Managers.PatchManager var newAddress = AssemblyMemory.GetMethodBodyStart(patch); _revertData = AssemblyMemory.WriteJump(_revertAddress, newAddress); _pinnedPatch = GCHandle.Alloc(patch); - _log.Debug( + _log.Log(PrintMsil ? LogLevel.Info : LogLevel.Debug, $"Done patching {_method.DeclaringType?.FullName}#{_method.Name}({string.Join(", ", _method.GetParameters().Select(x => x.ParameterType.Name))})"); } catch (Exception exception) @@ -110,100 +111,119 @@ namespace Torch.Managers.PatchManager public DynamicMethod ComposePatchedMethod() { DynamicMethod method = AllocatePatchMethod(); - var generator = new LoggingIlGenerator(method.GetILGenerator()); - EmitPatched(generator); + var generator = new LoggingIlGenerator(method.GetILGenerator(), PrintMsil ? LogLevel.Info : LogLevel.Trace); + List il = EmitPatched((type, pinned) => new MsilLocal(generator.DeclareLocal(type, pinned))).ToList(); + if (PrintMsil) + { + lock (_log) + { + MethodTranspiler.IntegrityAnalysis(LogLevel.Info, il); + } + } + MethodTranspiler.EmitMethod(il, generator); - // Force it to compile - RuntimeMethodHandle handle = _getMethodHandle.Invoke(method); - object runtimeMethodInfo = _getMethodInfo.Invoke(handle); - _compileDynamicMethod.Invoke(runtimeMethodInfo); + try + { + // Force it to compile + RuntimeMethodHandle handle = _getMethodHandle.Invoke(method); + object runtimeMethodInfo = _getMethodInfo.Invoke(handle); + _compileDynamicMethod.Invoke(runtimeMethodInfo); + } + catch + { + lock (_log) + { + var ctx = new MethodContext(method); + ctx.Read(); + MethodTranspiler.IntegrityAnalysis(LogLevel.Warn, ctx.Instructions); + } + throw; + } return method; } #endregion #region Emit - private void EmitPatched(LoggingIlGenerator target) + private IEnumerable EmitPatched(Func declareLocal) { - var originalLocalVariables = _method.GetMethodBody().LocalVariables - .Select(x => - { - Debug.Assert(x.LocalType != null); - return target.DeclareLocal(x.LocalType, x.IsPinned); - }).ToArray(); + 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 specialVariables = new Dictionary(); - - Label labelAfterOriginalContent = target.DefineLabel(); - Label labelSkipMethodContent = target.DefineLabel(); + var labelAfterOriginalContent = new MsilLabel(); + var labelSkipMethodContent = new MsilLabel(); Type returnType = _method is MethodInfo meth ? meth.ReturnType : typeof(void); - LocalBuilder resultVariable = null; + MsilLocal resultVariable = null; if (returnType != typeof(void)) { - if (Prefixes.Concat(Suffixes).SelectMany(x => x.GetParameters()).Any(x => x.Name == RESULT_PARAMETER)) - resultVariable = target.DeclareLocal(returnType); - else if (Prefixes.Any(x => x.ReturnType == typeof(bool))) - resultVariable = target.DeclareLocal(returnType); + 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); } - resultVariable?.SetToDefault(target); - LocalBuilder prefixSkippedVariable = null; + 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 = target.DeclareLocal(typeof(bool)); + prefixSkippedVariable = declareLocal(typeof(bool), false); specialVariables.Add(PREFIX_SKIPPED_PARAMETER, prefixSkippedVariable); } if (resultVariable != null) specialVariables.Add(RESULT_PARAMETER, resultVariable); - target.EmitComment("Prefixes Begin"); foreach (MethodInfo prefix in Prefixes) { - EmitMonkeyCall(target, prefix, specialVariables); + instructions.AddRange(EmitMonkeyCall(prefix, specialVariables)); if (prefix.ReturnType == typeof(bool)) - target.Emit(OpCodes.Brfalse, labelSkipMethodContent); + instructions.Add(new MsilInstruction(OpCodes.Brfalse).InlineTarget(labelSkipMethodContent)); else if (prefix.ReturnType != typeof(void)) throw new Exception( $"Prefixes must return void or bool. {prefix.DeclaringType?.FullName}.{prefix.Name} returns {prefix.ReturnType}"); } - target.EmitComment("Prefixes End"); + instructions.AddRange(MethodTranspiler.Transpile(_method, (x) => declareLocal(x, false), Transpilers, labelAfterOriginalContent)); - target.EmitComment("Original Begin"); - MethodTranspiler.Transpile(_method, (type) => new MsilLocal(target.DeclareLocal(type)), Transpilers, target, labelAfterOriginalContent, PrintMsil); - target.EmitComment("Original End"); - - target.MarkLabel(labelAfterOriginalContent); + instructions.Add(new MsilInstruction(OpCodes.Nop).LabelWith(labelAfterOriginalContent)); if (resultVariable != null) - target.Emit(OpCodes.Stloc, resultVariable); - Label notSkip = target.DefineLabel(); - target.Emit(OpCodes.Br, notSkip); - target.MarkLabel(labelSkipMethodContent); + 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) { - target.Emit(OpCodes.Ldc_I4_1); - target.Emit(OpCodes.Stloc, prefixSkippedVariable); + instructions.Add(new MsilInstruction(OpCodes.Ldc_I4_1)); + instructions.Add(new MsilInstruction(OpCodes.Stloc).InlineValue(prefixSkippedVariable)); } - target.MarkLabel(notSkip); + instructions.Add(new MsilInstruction(OpCodes.Nop).LabelWith(notSkip)); - target.EmitComment("Suffixes Begin"); foreach (MethodInfo suffix in Suffixes) { - EmitMonkeyCall(target, suffix, specialVariables); + instructions.AddRange(EmitMonkeyCall(suffix, specialVariables)); if (suffix.ReturnType != typeof(void)) throw new Exception($"Suffixes must return void. {suffix.DeclaringType?.FullName}.{suffix.Name} returns {suffix.ReturnType}"); } - target.EmitComment("Suffixes End"); if (resultVariable != null) - target.Emit(OpCodes.Ldloc, resultVariable); - target.Emit(OpCodes.Ret); + 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 void EmitMonkeyCall(LoggingIlGenerator target, MethodInfo patch, - IReadOnlyDictionary specialVariables) + private IEnumerable EmitMonkeyCall(MethodInfo patch, + IReadOnlyDictionary specialVariables) { - target.EmitComment($"Call {patch.DeclaringType?.FullName}#{patch.Name}"); foreach (var param in patch.GetParameters()) { switch (param.Name) @@ -211,25 +231,26 @@ namespace Torch.Managers.PatchManager case INSTANCE_PARAMETER: if (_method.IsStatic) throw new Exception("Can't use an instance parameter for a static method"); - target.Emit(OpCodes.Ldarg_0); + yield return new MsilInstruction(OpCodes.Ldarg_0); break; case PREFIX_SKIPPED_PARAMETER: if (param.ParameterType != typeof(bool)) throw new Exception($"Prefix skipped parameter {param.ParameterType} must be of type bool"); if (param.ParameterType.IsByRef || param.IsOut) throw new Exception($"Prefix skipped parameter {param.ParameterType} can't be a reference type"); - if (specialVariables.TryGetValue(PREFIX_SKIPPED_PARAMETER, out LocalBuilder prefixSkip)) - target.Emit(OpCodes.Ldloc, prefixSkip); + if (specialVariables.TryGetValue(PREFIX_SKIPPED_PARAMETER, out MsilLocal prefixSkip)) + yield return new MsilInstruction(OpCodes.Ldloc).InlineValue(prefixSkip); else - target.Emit(OpCodes.Ldc_I4_0); + yield return new MsilInstruction(OpCodes.Ldc_I4_0); break; case RESULT_PARAMETER: Type retType = param.ParameterType.IsByRef ? param.ParameterType.GetElementType() : param.ParameterType; - if (retType == null || !retType.IsAssignableFrom(specialVariables[RESULT_PARAMETER].LocalType)) - throw new Exception($"Return type {specialVariables[RESULT_PARAMETER].LocalType} can't be assigned to result parameter type {retType}"); - target.Emit(param.ParameterType.IsByRef ? OpCodes.Ldloca : OpCodes.Ldloc, specialVariables[RESULT_PARAMETER]); + if (retType == null || !retType.IsAssignableFrom(specialVariables[RESULT_PARAMETER].Type)) + throw new Exception($"Return type {specialVariables[RESULT_PARAMETER].Type} can't be assigned to result parameter type {retType}"); + yield return new MsilInstruction(param.ParameterType.IsByRef ? OpCodes.Ldloca : OpCodes.Ldloc) + .InlineValue(specialVariables[RESULT_PARAMETER]); break; default: ParameterInfo declParam = _method.GetParameters().FirstOrDefault(x => x.Name == param.Name); @@ -240,18 +261,18 @@ namespace Torch.Managers.PatchManager bool patchByRef = param.IsOut || param.ParameterType.IsByRef; bool declByRef = declParam.IsOut || declParam.ParameterType.IsByRef; if (patchByRef == declByRef) - target.Emit(OpCodes.Ldarg, paramIdx); + yield return new MsilInstruction(OpCodes.Ldarg).InlineValue(new MsilArgument(paramIdx)); else if (patchByRef) - target.Emit(OpCodes.Ldarga, paramIdx); + yield return new MsilInstruction(OpCodes.Ldarga).InlineValue(new MsilArgument(paramIdx)); else { - target.Emit(OpCodes.Ldarg, paramIdx); - target.EmitDereference(declParam.ParameterType); + yield return new MsilInstruction(OpCodes.Ldarg).InlineValue(new MsilArgument(paramIdx)); + yield return EmitExtensions.EmitDereference(declParam.ParameterType); } break; } } - target.Emit(OpCodes.Call, patch); + yield return new MsilInstruction(OpCodes.Call).InlineValue(patch); } #endregion } diff --git a/Torch/Managers/PatchManager/EmitExtensions.cs b/Torch/Managers/PatchManager/EmitExtensions.cs index 90f1ffd..dad1a1a 100644 --- a/Torch/Managers/PatchManager/EmitExtensions.cs +++ b/Torch/Managers/PatchManager/EmitExtensions.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Reflection.Emit; +using Torch.Managers.PatchManager.MSIL; using Torch.Managers.PatchManager.Transpile; namespace Torch.Managers.PatchManager @@ -11,65 +13,64 @@ namespace Torch.Managers.PatchManager /// Sets the given local to its default value in the given IL generator. /// /// Local to set to default - /// The IL generator - public static void SetToDefault(this LocalBuilder local, LoggingIlGenerator target) + /// Instructions + public static IEnumerable SetToDefault(this MsilLocal local) { - Debug.Assert(local.LocalType != null); - if (local.LocalType.IsEnum || local.LocalType.IsPrimitive) + Debug.Assert(local.Type != null); + if (local.Type.IsEnum || local.Type.IsPrimitive) { - if (local.LocalType == typeof(float)) - target.Emit(OpCodes.Ldc_R4, 0f); - else if (local.LocalType == typeof(double)) - target.Emit(OpCodes.Ldc_R8, 0d); - else if (local.LocalType == typeof(long) || local.LocalType == typeof(ulong)) - target.Emit(OpCodes.Ldc_I8, 0L); + if (local.Type == typeof(float)) + yield return new MsilInstruction(OpCodes.Ldc_R4).InlineValue(0f); + else if (local.Type == typeof(double)) + yield return new MsilInstruction(OpCodes.Ldc_R8).InlineValue(0d); + else if (local.Type == typeof(long) || local.Type == typeof(ulong)) + yield return new MsilInstruction(OpCodes.Ldc_I8).InlineValue(0L); else - target.Emit(OpCodes.Ldc_I4, 0); - target.Emit(OpCodes.Stloc, local); + yield return new MsilInstruction(OpCodes.Ldc_I4).InlineValue(0); + yield return new MsilInstruction(OpCodes.Stloc).InlineValue(local); } - else if (local.LocalType.IsValueType) // struct + else if (local.Type.IsValueType) // struct { - target.Emit(OpCodes.Ldloca, local); - target.Emit(OpCodes.Initobj, local.LocalType); + yield return new MsilInstruction(OpCodes.Ldloca).InlineValue(local); + yield return new MsilInstruction(OpCodes.Initobj).InlineValue(local.Type); } else // class { - target.Emit(OpCodes.Ldnull); - target.Emit(OpCodes.Stloc, local); + yield return new MsilInstruction(OpCodes.Ldnull); + yield return new MsilInstruction(OpCodes.Stloc).InlineValue(local); } } /// /// Emits a dereference for the given type. /// - /// IL Generator to emit on /// Type to dereference - public static void EmitDereference(this LoggingIlGenerator target, Type type) + /// Derference instruction + public static MsilInstruction EmitDereference(Type type) { if (type.IsByRef) type = type.GetElementType(); Debug.Assert(type != null); if (type == typeof(float)) - target.Emit(OpCodes.Ldind_R4); - else if (type == typeof(double)) - target.Emit(OpCodes.Ldind_R8); - else if (type == typeof(byte)) - target.Emit(OpCodes.Ldind_U1); - else if (type == typeof(ushort) || type == typeof(char)) - target.Emit(OpCodes.Ldind_U2); - else if (type == typeof(uint)) - target.Emit(OpCodes.Ldind_U4); - else if (type == typeof(sbyte)) - target.Emit(OpCodes.Ldind_I1); - else if (type == typeof(short)) - target.Emit(OpCodes.Ldind_I2); - else if (type == typeof(int) || type.IsEnum) - target.Emit(OpCodes.Ldind_I4); - else if (type == typeof(long) || type == typeof(ulong)) - target.Emit(OpCodes.Ldind_I8); - else - target.Emit(OpCodes.Ldind_Ref); + return new MsilInstruction(OpCodes.Ldind_R4); + if (type == typeof(double)) + return new MsilInstruction(OpCodes.Ldind_R8); + if (type == typeof(byte)) + return new MsilInstruction(OpCodes.Ldind_U1); + if (type == typeof(ushort) || type == typeof(char)) + return new MsilInstruction(OpCodes.Ldind_U2); + if (type == typeof(uint)) + return new MsilInstruction(OpCodes.Ldind_U4); + if (type == typeof(sbyte)) + return new MsilInstruction(OpCodes.Ldind_I1); + if (type == typeof(short)) + return new MsilInstruction(OpCodes.Ldind_I2); + if (type == typeof(int) || type.IsEnum) + return new MsilInstruction(OpCodes.Ldind_I4); + if (type == typeof(long) || type == typeof(ulong)) + return new MsilInstruction(OpCodes.Ldind_I8); + return new MsilInstruction(OpCodes.Ldind_Ref); } } } diff --git a/Torch/Managers/PatchManager/MSIL/ITokenResolver.cs b/Torch/Managers/PatchManager/MSIL/ITokenResolver.cs index 5b85c93..b8403fb 100644 --- a/Torch/Managers/PatchManager/MSIL/ITokenResolver.cs +++ b/Torch/Managers/PatchManager/MSIL/ITokenResolver.cs @@ -62,7 +62,7 @@ namespace Torch.Managers.PatchManager.MSIL { internal static readonly NullTokenResolver Instance = new NullTokenResolver(); - private NullTokenResolver() + internal NullTokenResolver() { } diff --git a/Torch/Managers/PatchManager/MSIL/MsilInstruction.cs b/Torch/Managers/PatchManager/MSIL/MsilInstruction.cs index 85fa4a6..879343d 100644 --- a/Torch/Managers/PatchManager/MSIL/MsilInstruction.cs +++ b/Torch/Managers/PatchManager/MSIL/MsilInstruction.cs @@ -34,6 +34,7 @@ namespace Torch.Managers.PatchManager.MSIL case OperandType.InlineField: Operand = new MsilOperandInline.MsilOperandReflected(this); break; + case OperandType.ShortInlineI: case OperandType.InlineI: Operand = new MsilOperandInline.MsilOperandInt32(this); break; @@ -63,16 +64,11 @@ namespace Torch.Managers.PatchManager.MSIL break; case OperandType.ShortInlineVar: case OperandType.InlineVar: - if (OpCode.Name.IndexOf("loc", StringComparison.OrdinalIgnoreCase) != -1) + if (OpCode.IsLocalStore() || OpCode.IsLocalLoad() || OpCode.IsLocalLoadByRef()) Operand = new MsilOperandInline.MsilOperandLocal(this); else Operand = new MsilOperandInline.MsilOperandArgument(this); break; - case OperandType.ShortInlineI: - Operand = OpCode == OpCodes.Ldc_I4_S - ? (MsilOperand)new MsilOperandInline.MsilOperandInt8(this) - : new MsilOperandInline.MsilOperandUInt8(this); - break; case OperandType.ShortInlineR: Operand = new MsilOperandInline.MsilOperandSingle(this); break; @@ -206,6 +202,8 @@ namespace Torch.Managers.PatchManager.MSIL Operand is MsilOperandInline inline) { MethodBase op = inline.Value; + if (op == null) + return num; if (op is MethodInfo mi && mi.ReturnType != typeof(void)) num++; num -= op.GetParameters().Length; diff --git a/Torch/Managers/PatchManager/MSIL/MsilInstructionExtensions.cs b/Torch/Managers/PatchManager/MSIL/MsilInstructionExtensions.cs index 74b8912..a69ada4 100644 --- a/Torch/Managers/PatchManager/MSIL/MsilInstructionExtensions.cs +++ b/Torch/Managers/PatchManager/MSIL/MsilInstructionExtensions.cs @@ -19,8 +19,7 @@ namespace Torch.Managers.PatchManager.MSIL /// public static bool IsLocalLoad(this MsilInstruction me) { - return me.OpCode == OpCodes.Ldloc || me.OpCode == OpCodes.Ldloc_S || me.OpCode == OpCodes.Ldloc_0 || - me.OpCode == OpCodes.Ldloc_1 || me.OpCode == OpCodes.Ldloc_2 || me.OpCode == OpCodes.Ldloc_3; + return me.OpCode.IsLocalLoad(); } /// @@ -28,7 +27,7 @@ namespace Torch.Managers.PatchManager.MSIL /// public static bool IsLocalLoadByRef(this MsilInstruction me) { - return me.OpCode == OpCodes.Ldloca || me.OpCode == OpCodes.Ldloca_S; + return me.OpCode.IsLocalLoadByRef(); } /// @@ -36,8 +35,33 @@ namespace Torch.Managers.PatchManager.MSIL /// public static bool IsLocalStore(this MsilInstruction me) { - return me.OpCode == OpCodes.Stloc || me.OpCode == OpCodes.Stloc_S || me.OpCode == OpCodes.Stloc_0 || - me.OpCode == OpCodes.Stloc_1 || me.OpCode == OpCodes.Stloc_2 || me.OpCode == OpCodes.Stloc_3; + return me.OpCode.IsLocalStore(); + } + + /// + /// Is this instruction a local load-by-value instruction. + /// + public static bool IsLocalLoad(this OpCode opcode) + { + return opcode == OpCodes.Ldloc || opcode == OpCodes.Ldloc_S || opcode == OpCodes.Ldloc_0 || + opcode == OpCodes.Ldloc_1 || opcode == OpCodes.Ldloc_2 || opcode == OpCodes.Ldloc_3; + } + + /// + /// Is this instruction a local load-by-reference instruction. + /// + public static bool IsLocalLoadByRef(this OpCode opcode) + { + return opcode == OpCodes.Ldloca || opcode == OpCodes.Ldloca_S; + } + + /// + /// Is this instruction a local store instruction. + /// + public static bool IsLocalStore(this OpCode opcode) + { + return opcode == OpCodes.Stloc || opcode == OpCodes.Stloc_S || opcode == OpCodes.Stloc_0 || + opcode == OpCodes.Stloc_1 || opcode == OpCodes.Stloc_2 || opcode == OpCodes.Stloc_3; } /// diff --git a/Torch/Managers/PatchManager/MSIL/MsilLocal.cs b/Torch/Managers/PatchManager/MSIL/MsilLocal.cs index 1db7eb1..0918118 100644 --- a/Torch/Managers/PatchManager/MSIL/MsilLocal.cs +++ b/Torch/Managers/PatchManager/MSIL/MsilLocal.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; diff --git a/Torch/Managers/PatchManager/MSIL/MsilOperandBrTarget.cs b/Torch/Managers/PatchManager/MSIL/MsilOperandBrTarget.cs index ea93ce4..8e1c934 100644 --- a/Torch/Managers/PatchManager/MSIL/MsilOperandBrTarget.cs +++ b/Torch/Managers/PatchManager/MSIL/MsilOperandBrTarget.cs @@ -21,15 +21,40 @@ namespace Torch.Managers.PatchManager.MSIL internal override void Read(MethodContext context, BinaryReader reader) { - int val = Instruction.OpCode.OperandType == OperandType.InlineBrTarget - ? reader.ReadInt32() - : reader.ReadSByte(); - Target = context.LabelAt((int)reader.BaseStream.Position + val); + + long offset; + + // ReSharper disable once SwitchStatementMissingSomeCases + switch (Instruction.OpCode.OperandType) + { + case OperandType.ShortInlineBrTarget: + offset = reader.ReadSByte(); + break; + case OperandType.InlineBrTarget: + offset = reader.ReadInt32(); + break; + default: + throw new InvalidBranchException( + $"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}"); + } + + Target = context.LabelAt((int)(reader.BaseStream.Position + offset)); } internal override void Emit(LoggingIlGenerator generator) { - generator.Emit(Instruction.OpCode, Target.LabelFor(generator)); + // ReSharper disable once SwitchStatementMissingSomeCases + switch (Instruction.OpCode.OperandType) + { + case OperandType.ShortInlineBrTarget: + case OperandType.InlineBrTarget: + generator.Emit(Instruction.OpCode, Target.LabelFor(generator)); + break; + default: + throw new InvalidBranchException( + $"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}"); + } + } internal override void CopyTo(MsilOperand operand) diff --git a/Torch/Managers/PatchManager/MSIL/MsilOperandInline.cs b/Torch/Managers/PatchManager/MSIL/MsilOperandInline.cs index 2442fd9..cf0dc34 100644 --- a/Torch/Managers/PatchManager/MSIL/MsilOperandInline.cs +++ b/Torch/Managers/PatchManager/MSIL/MsilOperandInline.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Reflection; using System.Reflection.Emit; using Torch.Managers.PatchManager.Transpile; +using Torch.Utils; namespace Torch.Managers.PatchManager.MSIL { @@ -44,47 +45,6 @@ namespace Torch.Managers.PatchManager.MSIL /// public static class MsilOperandInline { - /// - /// Inline unsigned byte - /// - public class MsilOperandUInt8 : MsilOperandInline - { - internal MsilOperandUInt8(MsilInstruction instruction) : base(instruction) - { - } - - internal override void Read(MethodContext context, BinaryReader reader) - { - Value = reader.ReadByte(); - } - - internal override void Emit(LoggingIlGenerator generator) - { - generator.Emit(Instruction.OpCode, Value); - } - } - - /// - /// Inline signed byte - /// - public class MsilOperandInt8 : MsilOperandInline - { - internal MsilOperandInt8(MsilInstruction instruction) : base(instruction) - { - } - - internal override void Read(MethodContext context, BinaryReader reader) - { - Value = - (sbyte)reader.ReadByte(); - } - - internal override void Emit(LoggingIlGenerator generator) - { - generator.Emit(Instruction.OpCode, Value); - } - } - /// /// Inline integer /// @@ -96,12 +56,36 @@ namespace Torch.Managers.PatchManager.MSIL internal override void Read(MethodContext context, BinaryReader reader) { - Value = reader.ReadInt32(); + // ReSharper disable once SwitchStatementMissingSomeCases + switch (Instruction.OpCode.OperandType) + { + case OperandType.ShortInlineI: + Value = reader.ReadByte(); + return; + case OperandType.InlineI: + Value = reader.ReadInt32(); + return; + default: + throw new InvalidBranchException( + $"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}"); + } } internal override void Emit(LoggingIlGenerator generator) { - generator.Emit(Instruction.OpCode, Value); + // ReSharper disable once SwitchStatementMissingSomeCases + switch (Instruction.OpCode.OperandType) + { + case OperandType.ShortInlineI: + generator.Emit(Instruction.OpCode, (byte)Value); + return; + case OperandType.InlineI: + generator.Emit(Instruction.OpCode, Value); + return; + default: + throw new InvalidBranchException( + $"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}"); + } } } @@ -116,12 +100,30 @@ namespace Torch.Managers.PatchManager.MSIL internal override void Read(MethodContext context, BinaryReader reader) { - Value = reader.ReadSingle(); + // ReSharper disable once SwitchStatementMissingSomeCases + switch (Instruction.OpCode.OperandType) + { + case OperandType.ShortInlineR: + Value = reader.ReadSingle(); + return; + default: + throw new InvalidBranchException( + $"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}"); + } } internal override void Emit(LoggingIlGenerator generator) { - generator.Emit(Instruction.OpCode, Value); + // ReSharper disable once SwitchStatementMissingSomeCases + switch (Instruction.OpCode.OperandType) + { + case OperandType.ShortInlineR: + generator.Emit(Instruction.OpCode, Value); + return; + default: + throw new InvalidBranchException( + $"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}"); + } } } @@ -136,12 +138,30 @@ namespace Torch.Managers.PatchManager.MSIL internal override void Read(MethodContext context, BinaryReader reader) { - Value = reader.ReadDouble(); + // ReSharper disable once SwitchStatementMissingSomeCases + switch (Instruction.OpCode.OperandType) + { + case OperandType.InlineR: + Value = reader.ReadDouble(); + return; + default: + throw new InvalidBranchException( + $"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}"); + } } internal override void Emit(LoggingIlGenerator generator) { - generator.Emit(Instruction.OpCode, Value); + // ReSharper disable once SwitchStatementMissingSomeCases + switch (Instruction.OpCode.OperandType) + { + case OperandType.InlineR: + generator.Emit(Instruction.OpCode, Value); + return; + default: + throw new InvalidBranchException( + $"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}"); + } } } @@ -156,12 +176,30 @@ namespace Torch.Managers.PatchManager.MSIL internal override void Read(MethodContext context, BinaryReader reader) { - Value = reader.ReadInt64(); + // ReSharper disable once SwitchStatementMissingSomeCases + switch (Instruction.OpCode.OperandType) + { + case OperandType.InlineI8: + Value = reader.ReadInt64(); + return; + default: + throw new InvalidBranchException( + $"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}"); + } } internal override void Emit(LoggingIlGenerator generator) { - generator.Emit(Instruction.OpCode, Value); + // ReSharper disable once SwitchStatementMissingSomeCases + switch (Instruction.OpCode.OperandType) + { + case OperandType.InlineI8: + generator.Emit(Instruction.OpCode, Value); + return; + default: + throw new InvalidBranchException( + $"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}"); + } } } @@ -176,13 +214,30 @@ namespace Torch.Managers.PatchManager.MSIL internal override void Read(MethodContext context, BinaryReader reader) { - Value = - context.TokenResolver.ResolveString(reader.ReadInt32()); + // ReSharper disable once SwitchStatementMissingSomeCases + switch (Instruction.OpCode.OperandType) + { + case OperandType.InlineString: + Value = context.TokenResolver.ResolveString(reader.ReadInt32()); + return; + default: + throw new InvalidBranchException( + $"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}"); + } } internal override void Emit(LoggingIlGenerator generator) { - generator.Emit(Instruction.OpCode, Value); + // ReSharper disable once SwitchStatementMissingSomeCases + switch (Instruction.OpCode.OperandType) + { + case OperandType.InlineString: + generator.Emit(Instruction.OpCode, Value); + return; + default: + throw new InvalidBranchException( + $"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}"); + } } } @@ -197,14 +252,28 @@ namespace Torch.Managers.PatchManager.MSIL internal override void Read(MethodContext context, BinaryReader reader) { - byte[] sig = context.TokenResolver - .ResolveSignature(reader.ReadInt32()); - throw new ArgumentException("Can't figure out how to convert this."); + // ReSharper disable once SwitchStatementMissingSomeCases + switch (Instruction.OpCode.OperandType) + { + case OperandType.InlineSig: + throw new NotImplementedException(); + default: + throw new InvalidBranchException( + $"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}"); + } } internal override void Emit(LoggingIlGenerator generator) { - generator.Emit(Instruction.OpCode, Value); + // ReSharper disable once SwitchStatementMissingSomeCases + switch (Instruction.OpCode.OperandType) + { + case OperandType.InlineSig: + throw new NotImplementedException(); + default: + throw new InvalidBranchException( + $"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}"); + } } } @@ -219,18 +288,45 @@ namespace Torch.Managers.PatchManager.MSIL internal override void Read(MethodContext context, BinaryReader reader) { - int paramID = - Instruction.OpCode.OperandType == OperandType.ShortInlineVar - ? reader.ReadByte() - : reader.ReadUInt16(); - if (paramID == 0 && !context.Method.IsStatic) + int id; + // ReSharper disable once SwitchStatementMissingSomeCases + switch (Instruction.OpCode.OperandType) + { + case OperandType.ShortInlineVar: + id = reader.ReadByte(); + break; + case OperandType.InlineVar: + id = reader.ReadUInt16(); + break; + default: + throw new InvalidBranchException( + $"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}"); + } + + if (id == 0 && !context.Method.IsStatic) throw new ArgumentException("Haven't figured out how to ldarg with the \"this\" argument"); - Value = new MsilArgument(context.Method.GetParameters()[paramID - (context.Method.IsStatic ? 0 : 1)]); + // ReSharper disable once ConvertIfStatementToConditionalTernaryExpression + if (context.Method == null) + Value = new MsilArgument(id); + else + Value = new MsilArgument(context.Method.GetParameters()[id - (context.Method.IsStatic ? 0 : 1)]); } internal override void Emit(LoggingIlGenerator generator) { - generator.Emit(Instruction.OpCode, Value.Position); + // ReSharper disable once SwitchStatementMissingSomeCases + switch (Instruction.OpCode.OperandType) + { + case OperandType.ShortInlineVar: + generator.Emit(Instruction.OpCode, (byte) Value.Position); + break; + case OperandType.InlineVar: + generator.Emit(Instruction.OpCode, (short)Value.Position); + break; + default: + throw new InvalidBranchException( + $"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}"); + } } } @@ -245,16 +341,42 @@ namespace Torch.Managers.PatchManager.MSIL internal override void Read(MethodContext context, BinaryReader reader) { - Value = - new MsilLocal(context.Method.GetMethodBody().LocalVariables[ - Instruction.OpCode.OperandType == OperandType.ShortInlineVar - ? reader.ReadByte() - : reader.ReadUInt16()]); + int id; + // ReSharper disable once SwitchStatementMissingSomeCases + switch (Instruction.OpCode.OperandType) + { + case OperandType.ShortInlineVar: + id = reader.ReadByte(); + break; + case OperandType.InlineVar: + id = reader.ReadUInt16(); + break; + default: + throw new InvalidBranchException( + $"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}"); + } + // ReSharper disable once ConvertIfStatementToConditionalTernaryExpression + if (context.MethodBody == null) + Value = new MsilLocal(id); + else + Value = new MsilLocal(context.MethodBody.LocalVariables[id]); } internal override void Emit(LoggingIlGenerator generator) { - generator.Emit(Instruction.OpCode, Value.Index); + // ReSharper disable once SwitchStatementMissingSomeCases + switch (Instruction.OpCode.OperandType) + { + case OperandType.ShortInlineVar: + generator.Emit(Instruction.OpCode, (byte)Value.Index); + break; + case OperandType.InlineVar: + generator.Emit(Instruction.OpCode, (short)Value.Index); + break; + default: + throw new InvalidBranchException( + $"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}"); + } } } @@ -286,16 +408,40 @@ namespace Torch.Managers.PatchManager.MSIL value = context.TokenResolver.ResolveField(reader.ReadInt32()); break; default: - throw new ArgumentException("Reflected operand only applies to inline reflected types"); + throw new InvalidBranchException( + $"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}"); } if (value is TY vty) Value = vty; + else if (value == null) + Value = null; else throw new Exception($"Expected type {typeof(TY).Name} from operand {Instruction.OpCode.OperandType}, got {value.GetType()?.Name ?? "null"}"); } internal override void Emit(LoggingIlGenerator generator) { + + switch (Instruction.OpCode.OperandType) + { + case OperandType.InlineTok: + Debug.Assert(Value is MethodBase || Value is Type || Value is FieldInfo, + $"Value {Value?.GetType()} doesn't match operand type"); + break; + case OperandType.InlineType: + Debug.Assert(Value is Type, $"Value {Value?.GetType()} doesn't match operand type"); + break; + case OperandType.InlineMethod: + Debug.Assert(Value is MethodBase, $"Value {Value?.GetType()} doesn't match operand type"); + break; + case OperandType.InlineField: + Debug.Assert(Value is FieldInfo, $"Value {Value?.GetType()} doesn't match operand type"); + break; + default: + throw new InvalidBranchException( + $"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}"); + } + if (Value is ConstructorInfo) generator.Emit(Instruction.OpCode, Value as ConstructorInfo); else if (Value is FieldInfo) diff --git a/Torch/Managers/PatchManager/MethodRewritePattern.cs b/Torch/Managers/PatchManager/MethodRewritePattern.cs index 3dab076..83ce8eb 100644 --- a/Torch/Managers/PatchManager/MethodRewritePattern.cs +++ b/Torch/Managers/PatchManager/MethodRewritePattern.cs @@ -156,12 +156,17 @@ namespace Torch.Managers.PatchManager /// public MethodRewriteSet Transpilers { get; } /// + /// Methods capable of accepting one and returing another, modified. + /// Runs after prefixes, suffixes, and normal transpilers are applied. + /// + public MethodRewriteSet PostTranspilers { get; } + /// /// Methods run after the original method has run. /// public MethodRewriteSet Suffixes { get; } /// - /// Should the resulting MSIL of this patch operation be printed. + /// Should the resulting MSIL of the transpile operation be printed. /// public bool PrintMsil { @@ -174,8 +179,8 @@ namespace Torch.Managers.PatchManager _printMsilBacking = value; } } - private bool _printMsilBacking; + private readonly MethodRewritePattern _parent; /// @@ -186,6 +191,7 @@ namespace Torch.Managers.PatchManager { Prefixes = new MethodRewriteSet(parentPattern?.Prefixes); Transpilers = new MethodRewriteSet(parentPattern?.Transpilers); + PostTranspilers = new MethodRewriteSet(parentPattern?.PostTranspilers); Suffixes = new MethodRewriteSet(parentPattern?.Suffixes); _parent = parentPattern; } diff --git a/Torch/Managers/PatchManager/PatchManager.cs b/Torch/Managers/PatchManager/PatchManager.cs index a04dd0d..0062a66 100644 --- a/Torch/Managers/PatchManager/PatchManager.cs +++ b/Torch/Managers/PatchManager/PatchManager.cs @@ -177,7 +177,9 @@ namespace Torch.Managers.PatchManager _finishedPatchCount = 0; _dirtyPatchCount = _rewritePatterns.Values.Sum(x => x.HasChanged() ? 1 : 0); #if true - ParallelTasks.Parallel.ForEach(_rewritePatterns.Values, DoCommit); + ParallelTasks.Parallel.ForEach(_rewritePatterns.Values.Where(x => !x.PrintMsil), DoCommit); + foreach (DecoratedMethod m in _rewritePatterns.Values.Where(x => x.PrintMsil)) + DoCommit(m); #else foreach (DecoratedMethod m in _rewritePatterns.Values) DoCommit(m); diff --git a/Torch/Managers/PatchManager/Transpile/LoggingILGenerator.cs b/Torch/Managers/PatchManager/Transpile/LoggingILGenerator.cs index 3f25a4e..2de3b21 100644 --- a/Torch/Managers/PatchManager/Transpile/LoggingILGenerator.cs +++ b/Torch/Managers/PatchManager/Transpile/LoggingILGenerator.cs @@ -24,20 +24,23 @@ namespace Torch.Managers.PatchManager.Transpile /// public ILGenerator Backing { get; } + private readonly LogLevel _level; + /// /// Creates a new logging IL generator backed by the given generator. /// /// Backing generator - public LoggingIlGenerator(ILGenerator backing) + public LoggingIlGenerator(ILGenerator backing, LogLevel level) { Backing = backing; + _level = level; } /// public LocalBuilder DeclareLocal(Type localType, bool isPinned = false) { LocalBuilder res = Backing.DeclareLocal(localType, isPinned); - _log?.Trace($"DclLoc\t{res.LocalIndex}\t=> {res.LocalType} {res.IsPinned}"); + _log?.Log(_level, $"DclLoc\t{res.LocalIndex}\t=> {res.LocalType} {res.IsPinned}"); return res; } @@ -45,70 +48,84 @@ namespace Torch.Managers.PatchManager.Transpile /// public void Emit(OpCode op) { - _log?.Trace($"Emit\t{op,_opcodePadding}"); + _log?.Log(_level, $"Emit\t{op,_opcodePadding}"); Backing.Emit(op); } /// public void Emit(OpCode op, LocalBuilder arg) { - _log?.Trace($"Emit\t{op,_opcodePadding} Local:{arg.LocalIndex}/{arg.LocalType}"); + _log?.Log(_level, $"Emit\t{op,_opcodePadding} Local:{arg.LocalIndex}/{arg.LocalType}"); + Backing.Emit(op, arg); + } + + /// + public void Emit(OpCode op, byte arg) + { + _log?.Log(_level, $"Emit\t{op,_opcodePadding} {arg}"); + Backing.Emit(op, arg); + } + + /// + public void Emit(OpCode op, short arg) + { + _log?.Log(_level, $"Emit\t{op,_opcodePadding} {arg}"); Backing.Emit(op, arg); } /// public void Emit(OpCode op, int arg) { - _log?.Trace($"Emit\t{op,_opcodePadding} {arg}"); + _log?.Log(_level, $"Emit\t{op,_opcodePadding} {arg}"); Backing.Emit(op, arg); } /// public void Emit(OpCode op, long arg) { - _log?.Trace($"Emit\t{op,_opcodePadding} {arg}"); + _log?.Log(_level, $"Emit\t{op,_opcodePadding} {arg}"); Backing.Emit(op, arg); } /// public void Emit(OpCode op, float arg) { - _log?.Trace($"Emit\t{op,_opcodePadding} {arg}"); + _log?.Log(_level, $"Emit\t{op,_opcodePadding} {arg}"); Backing.Emit(op, arg); } /// public void Emit(OpCode op, double arg) { - _log?.Trace($"Emit\t{op,_opcodePadding} {arg}"); + _log?.Log(_level, $"Emit\t{op,_opcodePadding} {arg}"); Backing.Emit(op, arg); } /// public void Emit(OpCode op, string arg) { - _log?.Trace($"Emit\t{op,_opcodePadding} {arg}"); + _log?.Log(_level, $"Emit\t{op,_opcodePadding} {arg}"); Backing.Emit(op, arg); } /// public void Emit(OpCode op, Type arg) { - _log?.Trace($"Emit\t{op,_opcodePadding} {arg}"); + _log?.Log(_level, $"Emit\t{op,_opcodePadding} {arg}"); Backing.Emit(op, arg); } /// public void Emit(OpCode op, FieldInfo arg) { - _log?.Trace($"Emit\t{op,_opcodePadding} {arg}"); + _log?.Log(_level, $"Emit\t{op,_opcodePadding} {arg}"); Backing.Emit(op, arg); } /// public void Emit(OpCode op, MethodInfo arg) { - _log?.Trace($"Emit\t{op,_opcodePadding} {arg}"); + _log?.Log(_level, $"Emit\t{op,_opcodePadding} {arg}"); Backing.Emit(op, arg); } @@ -121,28 +138,28 @@ namespace Torch.Managers.PatchManager.Transpile /// public void Emit(OpCode op, Label arg) { - _log?.Trace($"Emit\t{op,_opcodePadding}\tL:{_labelID.Invoke(arg)}"); + _log?.Log(_level, $"Emit\t{op,_opcodePadding}\tL:{_labelID.Invoke(arg)}"); Backing.Emit(op, arg); } /// public void Emit(OpCode op, Label[] arg) { - _log?.Trace($"Emit\t{op,_opcodePadding}\t{string.Join(", ", arg.Select(x => "L:" + _labelID.Invoke(x)))}"); + _log?.Log(_level, $"Emit\t{op,_opcodePadding}\t{string.Join(", ", arg.Select(x => "L:" + _labelID.Invoke(x)))}"); Backing.Emit(op, arg); } /// public void Emit(OpCode op, SignatureHelper arg) { - _log?.Trace($"Emit\t{op,_opcodePadding} {arg}"); + _log?.Log(_level, $"Emit\t{op,_opcodePadding} {arg}"); Backing.Emit(op, arg); } /// public void Emit(OpCode op, ConstructorInfo arg) { - _log?.Trace($"Emit\t{op,_opcodePadding} {arg}"); + _log?.Log(_level, $"Emit\t{op,_opcodePadding} {arg}"); Backing.Emit(op, arg); } @@ -151,42 +168,42 @@ namespace Torch.Managers.PatchManager.Transpile /// public Label BeginExceptionBlock() { - _log?.Trace($"BeginExceptionBlock"); + _log?.Log(_level, $"BeginExceptionBlock"); return Backing.BeginExceptionBlock(); } /// public void BeginCatchBlock(Type caught) { - _log?.Trace($"BeginCatchBlock {caught}"); + _log?.Log(_level, $"BeginCatchBlock {caught}"); Backing.BeginCatchBlock(caught); } /// public void BeginExceptFilterBlock() { - _log?.Trace($"BeginExceptFilterBlock"); + _log?.Log(_level, $"BeginExceptFilterBlock"); Backing.BeginExceptFilterBlock(); } /// public void BeginFaultBlock() { - _log?.Trace($"BeginFaultBlock"); + _log?.Log(_level, $"BeginFaultBlock"); Backing.BeginFaultBlock(); } /// public void BeginFinallyBlock() { - _log?.Trace($"BeginFinallyBlock"); + _log?.Log(_level, $"BeginFinallyBlock"); Backing.BeginFinallyBlock(); } /// public void EndExceptionBlock() { - _log?.Trace($"EndExceptionBlock"); + _log?.Log(_level, $"EndExceptionBlock"); Backing.EndExceptionBlock(); } #endregion @@ -194,7 +211,7 @@ namespace Torch.Managers.PatchManager.Transpile /// public void MarkLabel(Label label) { - _log?.Trace($"MkLbl\tL:{_labelID.Invoke(label)}"); + _log?.Log(_level, $"MkLbl\tL:{_labelID.Invoke(label)}"); Backing.MarkLabel(label); } @@ -211,7 +228,7 @@ namespace Torch.Managers.PatchManager.Transpile [Conditional("DEBUG")] public void EmitComment(string comment) { - _log?.Trace($"// {comment}"); + _log?.Log(_level, $"// {comment}"); } } #pragma warning restore 162 diff --git a/Torch/Managers/PatchManager/Transpile/MethodContext.cs b/Torch/Managers/PatchManager/Transpile/MethodContext.cs index 40bae88..aa7b203 100644 --- a/Torch/Managers/PatchManager/Transpile/MethodContext.cs +++ b/Torch/Managers/PatchManager/Transpile/MethodContext.cs @@ -42,6 +42,21 @@ namespace Torch.Managers.PatchManager.Transpile TokenResolver = new NormalTokenResolver(method); } + + +#pragma warning disable 649 + [ReflectedMethod(Name = "BakeByteArray")] + private static Func _ilGeneratorBakeByteArray; +#pragma warning restore 649 + + public MethodContext(DynamicMethod method) + { + Method = null; + MethodBody = null; + _msilBytes = _ilGeneratorBakeByteArray(method.GetILGenerator()); + TokenResolver = new DynamicMethodTokenResolver(method); + } + public void Read() { ReadInstructions(); @@ -64,7 +79,12 @@ namespace Torch.Managers.PatchManager.Transpile instructionValue = (short)((instructionValue << 8) | memory.ReadByte()); } if (!OpCodeLookup.TryGetValue(instructionValue, out OpCode opcode)) - throw new Exception($"Unknown opcode {instructionValue:X}"); + { + var msg = $"Unknown opcode {instructionValue:X}"; + _log.Error(msg); + Debug.Assert(false, msg); + continue; + } if (opcode.Size != memory.Position - opcodeOffset) throw new Exception($"Opcode said it was {opcode.Size} but we read {memory.Position - opcodeOffset}"); var instruction = new MsilInstruction(opcode) @@ -78,6 +98,8 @@ namespace Torch.Managers.PatchManager.Transpile private void ResolveCatchClauses() { + if (MethodBody == null) + return; foreach (ExceptionHandlingClause clause in MethodBody.ExceptionHandlingClauses) { var beginInstruction = FindInstruction(clause.TryOffset); @@ -111,7 +133,8 @@ namespace Torch.Managers.PatchManager.Transpile foreach (var label in Labels) { MsilInstruction target = FindInstruction(label.Key); - target.Labels?.Add(label.Value); + Debug.Assert(target != null, $"No label for offset {label.Key}"); + target?.Labels?.Add(label.Value); } } diff --git a/Torch/Managers/PatchManager/Transpile/MethodTranspiler.cs b/Torch/Managers/PatchManager/Transpile/MethodTranspiler.cs index fb972ee..b989e49 100644 --- a/Torch/Managers/PatchManager/Transpile/MethodTranspiler.cs +++ b/Torch/Managers/PatchManager/Transpile/MethodTranspiler.cs @@ -14,15 +14,19 @@ namespace Torch.Managers.PatchManager.Transpile { public static readonly Logger _log = LogManager.GetCurrentClassLogger(); - internal static void Transpile(MethodBase baseMethod, Func localCreator, - IEnumerable transpilers, LoggingIlGenerator output, Label? retLabel, - bool logMsil) + internal static IEnumerable Transpile(MethodBase baseMethod, Func localCreator, + IEnumerable transpilers, MsilLabel retLabel) { var context = new MethodContext(baseMethod); context.Read(); // IntegrityAnalysis(LogLevel.Trace, context.Instructions); + return Transpile(baseMethod, context.Instructions, localCreator, transpilers, retLabel); + } - var methodContent = (IEnumerable)context.Instructions; + internal static IEnumerable Transpile(MethodBase baseMethod, IEnumerable methodContent, + Func localCreator, + IEnumerable transpilers, MsilLabel retLabel) + { foreach (MethodInfo transpiler in transpilers) { var paramList = new List(); @@ -42,13 +46,7 @@ namespace Torch.Managers.PatchManager.Transpile } methodContent = (IEnumerable)transpiler.Invoke(null, paramList.ToArray()); } - methodContent = FixBranchAndReturn(methodContent, retLabel); - var list = methodContent.ToList(); - if (logMsil) - { - IntegrityAnalysis(LogLevel.Info, list); - } - EmitMethod(list, output); + return FixBranchAndReturn(methodContent, retLabel); } internal static void EmitMethod(IReadOnlyList instructions, LoggingIlGenerator target) @@ -89,7 +87,7 @@ namespace Torch.Managers.PatchManager.Transpile ilNext?.TryCatchOperation?.Type == MsilTryCatchOperationType.BeginCatchBlock || ilNext?.TryCatchOperation?.Type == MsilTryCatchOperationType.BeginFinallyBlock)) continue; - if ((il.OpCode == OpCodes.Leave || il.OpCode == OpCodes.Leave_S) && + if ((il.OpCode == OpCodes.Leave || il.OpCode == OpCodes.Leave_S || il.OpCode == OpCodes.Endfinally) && ilNext?.TryCatchOperation?.Type == MsilTryCatchOperationType.EndExceptionBlock) continue; @@ -105,7 +103,7 @@ namespace Torch.Managers.PatchManager.Transpile /// /// default logging level /// instructions - private static void IntegrityAnalysis(LogLevel level, IReadOnlyList instructions) + public static void IntegrityAnalysis(LogLevel level, IReadOnlyList instructions) { var targets = new Dictionary(); for (var i = 0; i < instructions.Count; i++) @@ -192,13 +190,13 @@ namespace Torch.Managers.PatchManager.Transpile } } - private static IEnumerable FixBranchAndReturn(IEnumerable insn, Label? retTarget) + private static IEnumerable FixBranchAndReturn(IEnumerable insn, MsilLabel retTarget) { foreach (MsilInstruction i in insn) { - if (retTarget.HasValue && i.OpCode == OpCodes.Ret) + if (retTarget != null && i.OpCode == OpCodes.Ret) { - var j = i.CopyWith(OpCodes.Br).InlineTarget(new MsilLabel(retTarget.Value)); + var j = i.CopyWith(OpCodes.Br).InlineTarget(retTarget); _log.Trace($"Replacing {i} with {j}"); yield return j; }