Normal logging for patch manager
Fix: RE treating constructors as normal methods
This commit is contained in:
@@ -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);
|
||||
else if (prefix.ReturnType != typeof(void))
|
||||
throw new Exception($"Prefixes must return void or bool. {prefix.DeclaringType?.FullName}.{prefix.Name} returns {prefix.ReturnType}");
|
||||
{
|
||||
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}");
|
||||
}
|
||||
target.EmitComment("Prefixes End");
|
||||
|
||||
target.EmitComment("Original Begin");
|
||||
MethodTranspiler.Transpile(_method, Transpilers, target, labelAfterOriginalContent);
|
||||
target.MarkLabel(labelAfterOriginalContent);
|
||||
target.EmitComment("Original End");
|
||||
if (labelAfterOriginalContent.HasValue)
|
||||
{
|
||||
target.MarkLabel(labelAfterOriginalContent.Value);
|
||||
if (resultVariable != null)
|
||||
target.Emit(OpCodes.Stloc, resultVariable);
|
||||
target.MarkLabel(labelAfterOriginalReturn);
|
||||
}
|
||||
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 (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)
|
||||
|
@@ -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);
|
||||
|
@@ -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)
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
Reference in New Issue
Block a user