Normal logging for patch manager

Fix: RE treating constructors as normal methods
This commit is contained in:
Westin Miller
2017-09-09 22:15:42 -07:00
parent 837b56462f
commit 4f84cd8963
7 changed files with 96 additions and 58 deletions

View File

@@ -112,8 +112,13 @@ namespace Torch.Managers.PatchManager
var specialVariables = new Dictionary<string, LocalBuilder>(); var specialVariables = new Dictionary<string, LocalBuilder>();
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 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) ? target.DeclareLocal(returnType)
: null; : null;
resultVariable?.SetToDefault(target); resultVariable?.SetToDefault(target);
@@ -121,39 +126,54 @@ namespace Torch.Managers.PatchManager
if (resultVariable != null) if (resultVariable != null)
specialVariables.Add(RESULT_PARAMETER, resultVariable); specialVariables.Add(RESULT_PARAMETER, resultVariable);
var labelAfterOriginalContent = target.DefineLabel(); target.EmitComment("Prefixes Begin");
var labelAfterOriginalReturn = target.DefineLabel();
foreach (var prefix in Prefixes) foreach (var prefix in Prefixes)
{ {
EmitMonkeyCall(target, prefix, specialVariables); EmitMonkeyCall(target, prefix, specialVariables);
if (prefix.ReturnType == typeof(bool)) 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)) 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); MethodTranspiler.Transpile(_method, Transpilers, target, labelAfterOriginalContent);
target.MarkLabel(labelAfterOriginalContent); target.EmitComment("Original End");
if (resultVariable != null) if (labelAfterOriginalContent.HasValue)
target.Emit(OpCodes.Stloc, resultVariable); {
target.MarkLabel(labelAfterOriginalReturn); 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) foreach (var suffix in Suffixes)
{ {
EmitMonkeyCall(target, suffix, specialVariables); EmitMonkeyCall(target, suffix, specialVariables);
if (suffix.ReturnType != typeof(void)) if (suffix.ReturnType != typeof(void))
throw new Exception($"Suffixes must return void. {suffix.DeclaringType?.FullName}.{suffix.Name} returns {suffix.ReturnType}"); throw new Exception($"Suffixes must return void. {suffix.DeclaringType?.FullName}.{suffix.Name} returns {suffix.ReturnType}");
} }
target.EmitComment("Suffixes End");
if (resultVariable != null) if (labelAfterOriginalContent.HasValue || labelAfterOriginalReturn.HasValue)
target.Emit(OpCodes.Ldloc, resultVariable); {
target.Emit(OpCodes.Ret); if (resultVariable != null)
target.Emit(OpCodes.Ldloc, resultVariable);
target.Emit(OpCodes.Ret);
}
} }
private void EmitMonkeyCall(LoggingIlGenerator target, MethodInfo patch, private void EmitMonkeyCall(LoggingIlGenerator target, MethodInfo patch,
IReadOnlyDictionary<string, LocalBuilder> specialVariables) IReadOnlyDictionary<string, LocalBuilder> specialVariables)
{ {
target.EmitComment($"Call {patch.DeclaringType?.FullName}#{patch.Name}");
foreach (var param in patch.GetParameters()) foreach (var param in patch.GetParameters())
{ {
switch (param.Name) switch (param.Name)

View File

@@ -40,7 +40,7 @@ namespace Torch.Managers.PatchManager.MSIL
Operand = new MsilOperandInline.MsilOperandInt64(this); Operand = new MsilOperandInline.MsilOperandInt64(this);
break; break;
case OperandType.InlineMethod: case OperandType.InlineMethod:
Operand = new MsilOperandInline.MsilOperandReflected<MethodInfo>(this); Operand = new MsilOperandInline.MsilOperandReflected<MethodBase>(this);
break; break;
case OperandType.InlineR: case OperandType.InlineR:
Operand = new MsilOperandInline.MsilOperandDouble(this); Operand = new MsilOperandInline.MsilOperandDouble(this);

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using System.Reflection.Emit; using System.Reflection.Emit;
@@ -257,23 +258,28 @@ namespace Torch.Managers.PatchManager.MSIL
internal override void Read(MethodContext context, BinaryReader reader) internal override void Read(MethodContext context, BinaryReader reader)
{ {
object value = null;
switch (Instruction.OpCode.OperandType) switch (Instruction.OpCode.OperandType)
{ {
case OperandType.InlineTok: case OperandType.InlineTok:
Value = context.TokenResolver.ResolveMember(reader.ReadInt32()) as TY; value = context.TokenResolver.ResolveMember(reader.ReadInt32());
break; break;
case OperandType.InlineType: case OperandType.InlineType:
Value = context.TokenResolver.ResolveType(reader.ReadInt32()) as TY; value = context.TokenResolver.ResolveType(reader.ReadInt32());
break; break;
case OperandType.InlineMethod: case OperandType.InlineMethod:
Value = context.TokenResolver.ResolveMethod(reader.ReadInt32()) as TY; value = context.TokenResolver.ResolveMethod(reader.ReadInt32());
break; break;
case OperandType.InlineField: case OperandType.InlineField:
Value = context.TokenResolver.ResolveField(reader.ReadInt32()) as TY; value = context.TokenResolver.ResolveField(reader.ReadInt32());
break; break;
default: default:
throw new ArgumentException("Reflected operand only applies to inline reflected types"); 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) internal override void Emit(LoggingIlGenerator generator)

View File

@@ -90,7 +90,7 @@ namespace Torch.Managers.PatchManager
public void RemoveAll() public void RemoveAll()
{ {
foreach (var k in _backingList) foreach (var k in _backingList)
_backingSet.Remove(k); _backingSet?.Remove(k);
_backingList.Clear(); _backingList.Clear();
_sortDirty = true; _sortDirty = true;
Interlocked.Exchange(ref _hasChanges, 1); Interlocked.Exchange(ref _hasChanges, 1);

View File

@@ -1,19 +1,22 @@
using System; using System;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Reflection.Emit; using System.Reflection.Emit;
using NLog; using NLog;
using Torch.Utils;
// ReSharper disable ConditionIsAlwaysTrueOrFalse // ReSharper disable ConditionIsAlwaysTrueOrFalse
#pragma warning disable 162 // unreachable code #pragma warning disable 162 // unreachable code
namespace Torch.Managers.PatchManager.Transpile namespace Torch.Managers.PatchManager.Transpile
{ {
/// <summary> /// <summary>
/// An ILGenerator that can log emit calls when <see cref="LoggingIlGenerator._logging"/> is enabled. /// An ILGenerator that can log emit calls when the TRACE level is enabled.
/// </summary> /// </summary>
public class LoggingIlGenerator public class LoggingIlGenerator
{ {
private const bool _logging = false; private const int _opcodePadding = -10;
private static readonly Logger _log = LogManager.GetCurrentClassLogger(); private static readonly Logger _log = LogManager.GetCurrentClassLogger();
/// <summary> /// <summary>
@@ -34,8 +37,7 @@ namespace Torch.Managers.PatchManager.Transpile
public LocalBuilder DeclareLocal(Type localType, bool isPinned = false) public LocalBuilder DeclareLocal(Type localType, bool isPinned = false)
{ {
LocalBuilder res = Backing.DeclareLocal(localType, isPinned); LocalBuilder res = Backing.DeclareLocal(localType, isPinned);
if (_logging) _log.Trace($"DclLoc\t{res.LocalIndex}\t=> {res.LocalType} {res.IsPinned}");
_log.Trace($"DeclareLocal {res.LocalType} {res.IsPinned} => {res.LocalIndex}");
return res; return res;
} }
@@ -43,123 +45,111 @@ namespace Torch.Managers.PatchManager.Transpile
/// <inheritdoc cref="ILGenerator.Emit(OpCode)"/> /// <inheritdoc cref="ILGenerator.Emit(OpCode)"/>
public void Emit(OpCode op) public void Emit(OpCode op)
{ {
if (_logging) _log.Trace($"Emit\t{op,_opcodePadding}");
_log.Trace($"Emit {op}");
Backing.Emit(op); Backing.Emit(op);
} }
/// <inheritdoc cref="ILGenerator.Emit(OpCode, LocalBuilder)"/> /// <inheritdoc cref="ILGenerator.Emit(OpCode, LocalBuilder)"/>
public void Emit(OpCode op, LocalBuilder arg) public void Emit(OpCode op, LocalBuilder arg)
{ {
if (_logging) _log.Trace($"Emit\t{op,_opcodePadding} L:{arg.LocalIndex} {arg.LocalType}");
_log.Trace($"Emit {op} L:{arg.LocalIndex} {arg.LocalType}");
Backing.Emit(op, arg); Backing.Emit(op, arg);
} }
/// <inheritdoc cref="ILGenerator.Emit(OpCode, int)"/> /// <inheritdoc cref="ILGenerator.Emit(OpCode, int)"/>
public void Emit(OpCode op, int arg) public void Emit(OpCode op, int arg)
{ {
if (_logging) _log.Trace($"Emit\t{op,_opcodePadding} {arg}");
_log.Trace($"Emit {op} {arg}");
Backing.Emit(op, arg); Backing.Emit(op, arg);
} }
/// <inheritdoc cref="ILGenerator.Emit(OpCode, long)"/> /// <inheritdoc cref="ILGenerator.Emit(OpCode, long)"/>
public void Emit(OpCode op, long arg) public void Emit(OpCode op, long arg)
{ {
if (_logging) _log.Trace($"Emit\t{op,_opcodePadding} {arg}");
_log.Trace($"Emit {op} {arg}");
Backing.Emit(op, arg); Backing.Emit(op, arg);
} }
/// <inheritdoc cref="ILGenerator.Emit(OpCode, float)"/> /// <inheritdoc cref="ILGenerator.Emit(OpCode, float)"/>
public void Emit(OpCode op, float arg) public void Emit(OpCode op, float arg)
{ {
if (_logging) _log.Trace($"Emit\t{op,_opcodePadding} {arg}");
_log.Trace($"Emit {op} {arg}");
Backing.Emit(op, arg); Backing.Emit(op, arg);
} }
/// <inheritdoc cref="ILGenerator.Emit(OpCode, double)"/> /// <inheritdoc cref="ILGenerator.Emit(OpCode, double)"/>
public void Emit(OpCode op, double arg) public void Emit(OpCode op, double arg)
{ {
if (_logging) _log.Trace($"Emit\t{op,_opcodePadding} {arg}");
_log.Trace($"Emit {op} {arg}");
Backing.Emit(op, arg); Backing.Emit(op, arg);
} }
/// <inheritdoc cref="ILGenerator.Emit(OpCode, string)"/> /// <inheritdoc cref="ILGenerator.Emit(OpCode, string)"/>
public void Emit(OpCode op, string arg) public void Emit(OpCode op, string arg)
{ {
if (_logging) _log.Trace($"Emit\t{op,_opcodePadding} {arg}");
_log.Trace($"Emit {op} {arg}");
Backing.Emit(op, arg); Backing.Emit(op, arg);
} }
/// <inheritdoc cref="ILGenerator.Emit(OpCode, Type)"/> /// <inheritdoc cref="ILGenerator.Emit(OpCode, Type)"/>
public void Emit(OpCode op, Type arg) public void Emit(OpCode op, Type arg)
{ {
if (_logging) _log.Trace($"Emit\t{op,_opcodePadding} {arg}");
_log.Trace($"Emit {op} {arg}");
Backing.Emit(op, arg); Backing.Emit(op, arg);
} }
/// <inheritdoc cref="ILGenerator.Emit(OpCode, FieldInfo)"/> /// <inheritdoc cref="ILGenerator.Emit(OpCode, FieldInfo)"/>
public void Emit(OpCode op, FieldInfo arg) public void Emit(OpCode op, FieldInfo arg)
{ {
if (_logging) _log.Trace($"Emit\t{op,_opcodePadding} {arg}");
_log.Trace($"Emit {op} {arg}");
Backing.Emit(op, arg); Backing.Emit(op, arg);
} }
/// <inheritdoc cref="ILGenerator.Emit(OpCode, MethodInfo)"/> /// <inheritdoc cref="ILGenerator.Emit(OpCode, MethodInfo)"/>
public void Emit(OpCode op, MethodInfo arg) public void Emit(OpCode op, MethodInfo arg)
{ {
if (_logging) _log.Trace($"Emit\t{op,_opcodePadding} {arg}");
_log.Trace($"Emit {op} {arg}");
Backing.Emit(op, 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<Label, int> _labelID;
#pragma warning restore 649
/// <inheritdoc cref="ILGenerator.Emit(OpCode, Label)"/> /// <inheritdoc cref="ILGenerator.Emit(OpCode, Label)"/>
public void Emit(OpCode op, Label arg) public void Emit(OpCode op, Label arg)
{ {
if (_logging) _log.Trace($"Emit\t{op,_opcodePadding}\tL:{_labelID.Invoke(arg)}");
_log.Trace($"Emit {op} L:{_labelID.GetValue(arg)}");
Backing.Emit(op, arg); Backing.Emit(op, arg);
} }
/// <inheritdoc cref="ILGenerator.Emit(OpCode, Label[])"/> /// <inheritdoc cref="ILGenerator.Emit(OpCode, Label[])"/>
public void Emit(OpCode op, Label[] arg) public void Emit(OpCode op, Label[] arg)
{ {
if (_logging) _log.Trace($"Emit\t{op,_opcodePadding}\t{string.Join(", ", arg.Select(x => "L:" + _labelID.Invoke(x)))}");
_log.Trace($"Emit {op} {string.Join(", ", arg.Select(x => "L:" + _labelID.GetValue(x)))}");
Backing.Emit(op, arg); Backing.Emit(op, arg);
} }
/// <inheritdoc cref="ILGenerator.Emit(OpCode, SignatureHelper)"/> /// <inheritdoc cref="ILGenerator.Emit(OpCode, SignatureHelper)"/>
public void Emit(OpCode op, SignatureHelper arg) public void Emit(OpCode op, SignatureHelper arg)
{ {
if (_logging) _log.Trace($"Emit\t{op,_opcodePadding} {arg}");
_log.Trace($"Emit {op} {arg}");
Backing.Emit(op, arg); Backing.Emit(op, arg);
} }
/// <inheritdoc cref="ILGenerator.Emit(OpCode, ConstructorInfo)"/> /// <inheritdoc cref="ILGenerator.Emit(OpCode, ConstructorInfo)"/>
public void Emit(OpCode op, ConstructorInfo arg) public void Emit(OpCode op, ConstructorInfo arg)
{ {
if (_logging) _log.Trace($"Emit\t{op,_opcodePadding} {arg}");
_log.Trace($"Emit {op} {arg}");
Backing.Emit(op, arg); Backing.Emit(op, arg);
} }
/// <inheritdoc cref="ILGenerator.MarkLabel(Label)"/> /// <inheritdoc cref="ILGenerator.MarkLabel(Label)"/>
public void MarkLabel(Label label) public void MarkLabel(Label label)
{ {
if (_logging) _log.Trace($"MkLbl\tL:{_labelID.Invoke(label)}");
_log.Trace($"MarkLabel L:{_labelID.GetValue(label)}");
Backing.MarkLabel(label); Backing.MarkLabel(label);
} }
@@ -168,6 +158,16 @@ namespace Torch.Managers.PatchManager.Transpile
{ {
return Backing.DefineLabel(); return Backing.DefineLabel();
} }
/// <summary>
/// Emits a comment to the log.
/// </summary>
/// <param name="comment">Comment</param>
[Conditional("DEBUG")]
public void EmitComment(string comment)
{
_log.Trace($"// {comment}");
}
} }
#pragma warning restore 162 #pragma warning restore 162
} }

View File

@@ -52,11 +52,17 @@ namespace Torch.Managers.PatchManager.Transpile
using (var reader = new BinaryReader(memory)) using (var reader = new BinaryReader(memory))
while (memory.Length > memory.Position) while (memory.Length > memory.Position)
{ {
var count = 1;
var instructionValue = (short)memory.ReadByte(); var instructionValue = (short)memory.ReadByte();
if (Prefixes.Contains(instructionValue)) if (Prefixes.Contains(instructionValue))
instructionValue = (short)((instructionValue << 8) | memory.ReadByte()); {
instructionValue = (short) ((instructionValue << 8) | memory.ReadByte());
count++;
}
if (!OpCodeLookup.TryGetValue(instructionValue, out OpCode opcode)) if (!OpCodeLookup.TryGetValue(instructionValue, out OpCode opcode))
throw new Exception($"Unknown opcode {instructionValue:X}"); 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) var instruction = new MsilInstruction(opcode)
{ {
Offset = (int) memory.Position Offset = (int) memory.Position
@@ -85,7 +91,7 @@ namespace Torch.Managers.PatchManager.Transpile
public string ToHumanMsil() 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<short, OpCode> OpCodeLookup; private static readonly Dictionary<short, OpCode> OpCodeLookup;

View File

@@ -2,16 +2,22 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
using System.Reflection.Emit; using System.Reflection.Emit;
using NLog;
using Torch.Managers.PatchManager.MSIL; using Torch.Managers.PatchManager.MSIL;
namespace Torch.Managers.PatchManager.Transpile namespace Torch.Managers.PatchManager.Transpile
{ {
internal class MethodTranspiler internal class MethodTranspiler
{ {
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
internal static void Transpile(MethodBase baseMethod, IEnumerable<MethodInfo> transpilers, LoggingIlGenerator output, Label? retLabel) internal static void Transpile(MethodBase baseMethod, IEnumerable<MethodInfo> transpilers, LoggingIlGenerator output, Label? retLabel)
{ {
var context = new MethodContext(baseMethod); var context = new MethodContext(baseMethod);
context.Read(); context.Read();
_log.Trace("Input Method:");
_log.Trace(context.ToHumanMsil);
var methodContent = (IEnumerable<MsilInstruction>) context.Instructions; var methodContent = (IEnumerable<MsilInstruction>) context.Instructions;
foreach (var transpiler in transpilers) foreach (var transpiler in transpilers)
methodContent = (IEnumerable<MsilInstruction>)transpiler.Invoke(null, new object[] { methodContent }); methodContent = (IEnumerable<MsilInstruction>)transpiler.Invoke(null, new object[] { methodContent });
@@ -48,7 +54,7 @@ namespace Torch.Managers.PatchManager.Transpile
{ {
var opcode = (OpCode)field.GetValue(null); var opcode = (OpCode)field.GetValue(null);
if (opcode.OperandType == OperandType.ShortInlineBrTarget && 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), var other = (OpCode?) typeof(OpCodes).GetField(field.Name.Substring(0, field.Name.Length - 2),
BindingFlags.Static | BindingFlags.Public)?.GetValue(null); BindingFlags.Static | BindingFlags.Public)?.GetValue(null);