From 4f84cd8963c9018085cb5d4741f58e2dde33f830 Mon Sep 17 00:00:00 2001 From: Westin Miller Date: Sat, 9 Sep 2017 22:15:42 -0700 Subject: [PATCH] Normal logging for patch manager Fix: RE treating constructors as normal methods --- .../Managers/PatchManager/DecoratedMethod.cs | 46 ++++++++---- .../PatchManager/MSIL/MsilInstruction.cs | 2 +- .../PatchManager/MSIL/MsilOperandInline.cs | 14 ++-- .../PatchManager/MethodRewritePattern.cs | 2 +- .../Transpile/LoggingILGenerator.cs | 72 +++++++++---------- .../PatchManager/Transpile/MethodContext.cs | 10 ++- .../Transpile/MethodTranspiler.cs | 8 ++- 7 files changed, 96 insertions(+), 58 deletions(-) diff --git a/Torch/Managers/PatchManager/DecoratedMethod.cs b/Torch/Managers/PatchManager/DecoratedMethod.cs index 75d09ff..8c03138 100644 --- a/Torch/Managers/PatchManager/DecoratedMethod.cs +++ b/Torch/Managers/PatchManager/DecoratedMethod.cs @@ -112,8 +112,13 @@ namespace Torch.Managers.PatchManager var specialVariables = new Dictionary(); + Label? labelAfterOriginalContent = Suffixes.Count > 0 ? target.DefineLabel() : (Label?)null; + Label? labelAfterOriginalReturn = Prefixes.Any(x => x.ReturnType == typeof(bool)) ? target.DefineLabel() : (Label?)null; + + var returnType = _method is MethodInfo meth ? meth.ReturnType : typeof(void); - var resultVariable = returnType != typeof(void) && Prefixes.Concat(Suffixes).SelectMany(x => x.GetParameters()).Any(x => x.Name == RESULT_PARAMETER) + var resultVariable = returnType != typeof(void) && (labelAfterOriginalReturn.HasValue || // If we jump past main content we need local to store return val + Prefixes.Concat(Suffixes).SelectMany(x => x.GetParameters()).Any(x => x.Name == RESULT_PARAMETER)) ? target.DeclareLocal(returnType) : null; resultVariable?.SetToDefault(target); @@ -121,39 +126,54 @@ namespace Torch.Managers.PatchManager if (resultVariable != null) specialVariables.Add(RESULT_PARAMETER, resultVariable); - var labelAfterOriginalContent = target.DefineLabel(); - var labelAfterOriginalReturn = target.DefineLabel(); - + target.EmitComment("Prefixes Begin"); foreach (var prefix in Prefixes) { EmitMonkeyCall(target, prefix, specialVariables); if (prefix.ReturnType == typeof(bool)) - target.Emit(OpCodes.Brfalse, labelAfterOriginalReturn); + { + Debug.Assert(labelAfterOriginalReturn.HasValue); + target.Emit(OpCodes.Brfalse, labelAfterOriginalReturn.Value); + } else if (prefix.ReturnType != typeof(void)) - throw new Exception($"Prefixes must return void or bool. {prefix.DeclaringType?.FullName}.{prefix.Name} returns {prefix.ReturnType}"); + throw new Exception( + $"Prefixes must return void or bool. {prefix.DeclaringType?.FullName}.{prefix.Name} returns {prefix.ReturnType}"); } + target.EmitComment("Prefixes End"); + target.EmitComment("Original Begin"); MethodTranspiler.Transpile(_method, Transpilers, target, labelAfterOriginalContent); - target.MarkLabel(labelAfterOriginalContent); - if (resultVariable != null) - target.Emit(OpCodes.Stloc, resultVariable); - target.MarkLabel(labelAfterOriginalReturn); + target.EmitComment("Original End"); + if (labelAfterOriginalContent.HasValue) + { + target.MarkLabel(labelAfterOriginalContent.Value); + if (resultVariable != null) + target.Emit(OpCodes.Stloc, resultVariable); + } + if (labelAfterOriginalReturn.HasValue) + target.MarkLabel(labelAfterOriginalReturn.Value); + target.EmitComment("Suffixes Begin"); foreach (var suffix in Suffixes) { EmitMonkeyCall(target, 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); + if (labelAfterOriginalContent.HasValue || labelAfterOriginalReturn.HasValue) + { + if (resultVariable != null) + target.Emit(OpCodes.Ldloc, resultVariable); + target.Emit(OpCodes.Ret); + } } private void EmitMonkeyCall(LoggingIlGenerator target, MethodInfo patch, IReadOnlyDictionary specialVariables) { + target.EmitComment($"Call {patch.DeclaringType?.FullName}#{patch.Name}"); foreach (var param in patch.GetParameters()) { switch (param.Name) diff --git a/Torch/Managers/PatchManager/MSIL/MsilInstruction.cs b/Torch/Managers/PatchManager/MSIL/MsilInstruction.cs index a179a6e..9ac0359 100644 --- a/Torch/Managers/PatchManager/MSIL/MsilInstruction.cs +++ b/Torch/Managers/PatchManager/MSIL/MsilInstruction.cs @@ -40,7 +40,7 @@ namespace Torch.Managers.PatchManager.MSIL Operand = new MsilOperandInline.MsilOperandInt64(this); break; case OperandType.InlineMethod: - Operand = new MsilOperandInline.MsilOperandReflected(this); + Operand = new MsilOperandInline.MsilOperandReflected(this); break; case OperandType.InlineR: Operand = new MsilOperandInline.MsilOperandDouble(this); diff --git a/Torch/Managers/PatchManager/MSIL/MsilOperandInline.cs b/Torch/Managers/PatchManager/MSIL/MsilOperandInline.cs index f4c45d1..e327054 100644 --- a/Torch/Managers/PatchManager/MSIL/MsilOperandInline.cs +++ b/Torch/Managers/PatchManager/MSIL/MsilOperandInline.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.IO; using System.Reflection; using System.Reflection.Emit; @@ -257,23 +258,28 @@ namespace Torch.Managers.PatchManager.MSIL internal override void Read(MethodContext context, BinaryReader reader) { + object value = null; switch (Instruction.OpCode.OperandType) { case OperandType.InlineTok: - Value = context.TokenResolver.ResolveMember(reader.ReadInt32()) as TY; + value = context.TokenResolver.ResolveMember(reader.ReadInt32()); break; case OperandType.InlineType: - Value = context.TokenResolver.ResolveType(reader.ReadInt32()) as TY; + value = context.TokenResolver.ResolveType(reader.ReadInt32()); break; case OperandType.InlineMethod: - Value = context.TokenResolver.ResolveMethod(reader.ReadInt32()) as TY; + value = context.TokenResolver.ResolveMethod(reader.ReadInt32()); break; case OperandType.InlineField: - Value = context.TokenResolver.ResolveField(reader.ReadInt32()) as TY; + value = context.TokenResolver.ResolveField(reader.ReadInt32()); break; default: throw new ArgumentException("Reflected operand only applies to inline reflected types"); } + if (value is TY vty) + Value = vty; + 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) diff --git a/Torch/Managers/PatchManager/MethodRewritePattern.cs b/Torch/Managers/PatchManager/MethodRewritePattern.cs index b52226d..3696371 100644 --- a/Torch/Managers/PatchManager/MethodRewritePattern.cs +++ b/Torch/Managers/PatchManager/MethodRewritePattern.cs @@ -90,7 +90,7 @@ namespace Torch.Managers.PatchManager public void RemoveAll() { foreach (var k in _backingList) - _backingSet.Remove(k); + _backingSet?.Remove(k); _backingList.Clear(); _sortDirty = true; Interlocked.Exchange(ref _hasChanges, 1); diff --git a/Torch/Managers/PatchManager/Transpile/LoggingILGenerator.cs b/Torch/Managers/PatchManager/Transpile/LoggingILGenerator.cs index 0f49293..a170d0c 100644 --- a/Torch/Managers/PatchManager/Transpile/LoggingILGenerator.cs +++ b/Torch/Managers/PatchManager/Transpile/LoggingILGenerator.cs @@ -1,19 +1,22 @@ using System; +using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; using NLog; +using Torch.Utils; // ReSharper disable ConditionIsAlwaysTrueOrFalse #pragma warning disable 162 // unreachable code namespace Torch.Managers.PatchManager.Transpile { /// - /// An ILGenerator that can log emit calls when is enabled. + /// An ILGenerator that can log emit calls when the TRACE level is enabled. /// public class LoggingIlGenerator { - private const bool _logging = false; + private const int _opcodePadding = -10; + private static readonly Logger _log = LogManager.GetCurrentClassLogger(); /// @@ -34,8 +37,7 @@ namespace Torch.Managers.PatchManager.Transpile public LocalBuilder DeclareLocal(Type localType, bool isPinned = false) { LocalBuilder res = Backing.DeclareLocal(localType, isPinned); - if (_logging) - _log.Trace($"DeclareLocal {res.LocalType} {res.IsPinned} => {res.LocalIndex}"); + _log.Trace($"DclLoc\t{res.LocalIndex}\t=> {res.LocalType} {res.IsPinned}"); return res; } @@ -43,123 +45,111 @@ namespace Torch.Managers.PatchManager.Transpile /// public void Emit(OpCode op) { - if (_logging) - _log.Trace($"Emit {op}"); + _log.Trace($"Emit\t{op,_opcodePadding}"); Backing.Emit(op); } /// public void Emit(OpCode op, LocalBuilder arg) { - if (_logging) - _log.Trace($"Emit {op} L:{arg.LocalIndex} {arg.LocalType}"); + _log.Trace($"Emit\t{op,_opcodePadding} L:{arg.LocalIndex} {arg.LocalType}"); Backing.Emit(op, arg); } /// public void Emit(OpCode op, int arg) { - if (_logging) - _log.Trace($"Emit {op} {arg}"); + _log.Trace($"Emit\t{op,_opcodePadding} {arg}"); Backing.Emit(op, arg); } /// public void Emit(OpCode op, long arg) { - if (_logging) - _log.Trace($"Emit {op} {arg}"); + _log.Trace($"Emit\t{op,_opcodePadding} {arg}"); Backing.Emit(op, arg); } /// public void Emit(OpCode op, float arg) { - if (_logging) - _log.Trace($"Emit {op} {arg}"); + _log.Trace($"Emit\t{op,_opcodePadding} {arg}"); Backing.Emit(op, arg); } /// public void Emit(OpCode op, double arg) { - if (_logging) - _log.Trace($"Emit {op} {arg}"); + _log.Trace($"Emit\t{op,_opcodePadding} {arg}"); Backing.Emit(op, arg); } /// public void Emit(OpCode op, string arg) { - if (_logging) - _log.Trace($"Emit {op} {arg}"); + _log.Trace($"Emit\t{op,_opcodePadding} {arg}"); Backing.Emit(op, arg); } /// public void Emit(OpCode op, Type arg) { - if (_logging) - _log.Trace($"Emit {op} {arg}"); + _log.Trace($"Emit\t{op,_opcodePadding} {arg}"); Backing.Emit(op, arg); } /// public void Emit(OpCode op, FieldInfo arg) { - if (_logging) - _log.Trace($"Emit {op} {arg}"); + _log.Trace($"Emit\t{op,_opcodePadding} {arg}"); Backing.Emit(op, arg); } /// public void Emit(OpCode op, MethodInfo arg) { - if (_logging) - _log.Trace($"Emit {op} {arg}"); + _log.Trace($"Emit\t{op,_opcodePadding} {arg}"); Backing.Emit(op, arg); } - private static FieldInfo _labelID = - typeof(Label).GetField("m_label", BindingFlags.Instance | BindingFlags.NonPublic); + +#pragma warning disable 649 + [ReflectedGetter(Name="m_label")] + private static Func _labelID; +#pragma warning restore 649 /// public void Emit(OpCode op, Label arg) { - if (_logging) - _log.Trace($"Emit {op} L:{_labelID.GetValue(arg)}"); + _log.Trace($"Emit\t{op,_opcodePadding}\tL:{_labelID.Invoke(arg)}"); Backing.Emit(op, arg); } /// public void Emit(OpCode op, Label[] arg) { - if (_logging) - _log.Trace($"Emit {op} {string.Join(", ", arg.Select(x => "L:" + _labelID.GetValue(x)))}"); + _log.Trace($"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) { - if (_logging) - _log.Trace($"Emit {op} {arg}"); + _log.Trace($"Emit\t{op,_opcodePadding} {arg}"); Backing.Emit(op, arg); } /// public void Emit(OpCode op, ConstructorInfo arg) { - if (_logging) - _log.Trace($"Emit {op} {arg}"); + _log.Trace($"Emit\t{op,_opcodePadding} {arg}"); Backing.Emit(op, arg); } /// public void MarkLabel(Label label) { - if (_logging) - _log.Trace($"MarkLabel L:{_labelID.GetValue(label)}"); + _log.Trace($"MkLbl\tL:{_labelID.Invoke(label)}"); Backing.MarkLabel(label); } @@ -168,6 +158,16 @@ namespace Torch.Managers.PatchManager.Transpile { return Backing.DefineLabel(); } + + /// + /// Emits a comment to the log. + /// + /// Comment + [Conditional("DEBUG")] + public void EmitComment(string comment) + { + _log.Trace($"// {comment}"); + } } #pragma warning restore 162 } diff --git a/Torch/Managers/PatchManager/Transpile/MethodContext.cs b/Torch/Managers/PatchManager/Transpile/MethodContext.cs index afa230c..8c5be0d 100644 --- a/Torch/Managers/PatchManager/Transpile/MethodContext.cs +++ b/Torch/Managers/PatchManager/Transpile/MethodContext.cs @@ -52,11 +52,17 @@ namespace Torch.Managers.PatchManager.Transpile using (var reader = new BinaryReader(memory)) while (memory.Length > memory.Position) { + var count = 1; var instructionValue = (short)memory.ReadByte(); if (Prefixes.Contains(instructionValue)) - instructionValue = (short)((instructionValue << 8) | memory.ReadByte()); + { + instructionValue = (short) ((instructionValue << 8) | memory.ReadByte()); + count++; + } if (!OpCodeLookup.TryGetValue(instructionValue, out OpCode opcode)) throw new Exception($"Unknown opcode {instructionValue:X}"); + if (opcode.Size != count) + throw new Exception($"Opcode said it was {opcode.Size} but we read {count}"); var instruction = new MsilInstruction(opcode) { Offset = (int) memory.Position @@ -85,7 +91,7 @@ namespace Torch.Managers.PatchManager.Transpile public string ToHumanMsil() { - return string.Join("\n", _instructions.Select(x => x.Offset + "\t" + x)); + return string.Join("\n", _instructions.Select(x => $"IL_{x.Offset:X4}: {x}")); } private static readonly Dictionary OpCodeLookup; diff --git a/Torch/Managers/PatchManager/Transpile/MethodTranspiler.cs b/Torch/Managers/PatchManager/Transpile/MethodTranspiler.cs index 9709ed6..d9a37eb 100644 --- a/Torch/Managers/PatchManager/Transpile/MethodTranspiler.cs +++ b/Torch/Managers/PatchManager/Transpile/MethodTranspiler.cs @@ -2,16 +2,22 @@ using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; +using NLog; using Torch.Managers.PatchManager.MSIL; namespace Torch.Managers.PatchManager.Transpile { internal class MethodTranspiler { + private static readonly Logger _log = LogManager.GetCurrentClassLogger(); + internal static void Transpile(MethodBase baseMethod, IEnumerable transpilers, LoggingIlGenerator output, Label? retLabel) { var context = new MethodContext(baseMethod); context.Read(); + _log.Trace("Input Method:"); + _log.Trace(context.ToHumanMsil); + var methodContent = (IEnumerable) context.Instructions; foreach (var transpiler in transpilers) methodContent = (IEnumerable)transpiler.Invoke(null, new object[] { methodContent }); @@ -48,7 +54,7 @@ namespace Torch.Managers.PatchManager.Transpile { var opcode = (OpCode)field.GetValue(null); if (opcode.OperandType == OperandType.ShortInlineBrTarget && - opcode.Name.EndsWith("_S", StringComparison.OrdinalIgnoreCase)) + opcode.Name.EndsWith(".s", StringComparison.OrdinalIgnoreCase)) { var other = (OpCode?) typeof(OpCodes).GetField(field.Name.Substring(0, field.Name.Length - 2), BindingFlags.Static | BindingFlags.Public)?.GetValue(null);