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>();
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<string, LocalBuilder> specialVariables)
{
target.EmitComment($"Call {patch.DeclaringType?.FullName}#{patch.Name}");
foreach (var param in patch.GetParameters())
{
switch (param.Name)

View File

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

View File

@@ -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)

View File

@@ -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);

View File

@@ -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
{
/// <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>
public class LoggingIlGenerator
{
private const bool _logging = false;
private const int _opcodePadding = -10;
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
/// <summary>
@@ -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
/// <inheritdoc cref="ILGenerator.Emit(OpCode)"/>
public void Emit(OpCode op)
{
if (_logging)
_log.Trace($"Emit {op}");
_log.Trace($"Emit\t{op,_opcodePadding}");
Backing.Emit(op);
}
/// <inheritdoc cref="ILGenerator.Emit(OpCode, LocalBuilder)"/>
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);
}
/// <inheritdoc cref="ILGenerator.Emit(OpCode, int)"/>
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);
}
/// <inheritdoc cref="ILGenerator.Emit(OpCode, long)"/>
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);
}
/// <inheritdoc cref="ILGenerator.Emit(OpCode, float)"/>
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);
}
/// <inheritdoc cref="ILGenerator.Emit(OpCode, double)"/>
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);
}
/// <inheritdoc cref="ILGenerator.Emit(OpCode, string)"/>
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);
}
/// <inheritdoc cref="ILGenerator.Emit(OpCode, Type)"/>
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);
}
/// <inheritdoc cref="ILGenerator.Emit(OpCode, FieldInfo)"/>
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);
}
/// <inheritdoc cref="ILGenerator.Emit(OpCode, MethodInfo)"/>
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<Label, int> _labelID;
#pragma warning restore 649
/// <inheritdoc cref="ILGenerator.Emit(OpCode, Label)"/>
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);
}
/// <inheritdoc cref="ILGenerator.Emit(OpCode, Label[])"/>
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);
}
/// <inheritdoc cref="ILGenerator.Emit(OpCode, SignatureHelper)"/>
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);
}
/// <inheritdoc cref="ILGenerator.Emit(OpCode, ConstructorInfo)"/>
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);
}
/// <inheritdoc cref="ILGenerator.MarkLabel(Label)"/>
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();
}
/// <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
}

View File

@@ -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<short, OpCode> OpCodeLookup;

View File

@@ -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<MethodInfo> transpilers, LoggingIlGenerator output, Label? retLabel)
{
var context = new MethodContext(baseMethod);
context.Read();
_log.Trace("Input Method:");
_log.Trace(context.ToHumanMsil);
var methodContent = (IEnumerable<MsilInstruction>) context.Instructions;
foreach (var transpiler in transpilers)
methodContent = (IEnumerable<MsilInstruction>)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);