Fixed issues with operand replacing, and branch instructions.
This commit is contained in:
@@ -4,6 +4,7 @@ using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Text;
|
||||
using Torch.Managers.PatchManager.Transpile;
|
||||
using Torch.Utils;
|
||||
using Label = System.Windows.Controls.Label;
|
||||
|
||||
namespace Torch.Managers.PatchManager.MSIL
|
||||
@@ -13,8 +14,6 @@ namespace Torch.Managers.PatchManager.MSIL
|
||||
/// </summary>
|
||||
public class MsilInstruction
|
||||
{
|
||||
private MsilOperand _operandBacking;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instruction with the given opcode.
|
||||
/// </summary>
|
||||
@@ -97,16 +96,7 @@ namespace Torch.Managers.PatchManager.MSIL
|
||||
/// <summary>
|
||||
/// The operand for this instruction, or null.
|
||||
/// </summary>
|
||||
public MsilOperand Operand
|
||||
{
|
||||
get => _operandBacking;
|
||||
set
|
||||
{
|
||||
if (_operandBacking != null && value.GetType() != _operandBacking.GetType())
|
||||
throw new ArgumentException($"Operand for {OpCode.Name} must be {_operandBacking.GetType().Name}");
|
||||
_operandBacking = value;
|
||||
}
|
||||
}
|
||||
public MsilOperand Operand { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Labels pointing to this instruction.
|
||||
@@ -128,11 +118,12 @@ namespace Torch.Managers.PatchManager.MSIL
|
||||
/// <summary>
|
||||
/// Makes a copy of the instruction with a new opcode.
|
||||
/// </summary>
|
||||
/// <param name="code">The new opcode</param>
|
||||
/// <param name="newOpcode">The new opcode</param>
|
||||
/// <returns>The copy</returns>
|
||||
public MsilInstruction CopyWith(OpCode code)
|
||||
public MsilInstruction CopyWith(OpCode newOpcode)
|
||||
{
|
||||
var result = new MsilInstruction(code) { Operand = this.Operand };
|
||||
var result = new MsilInstruction(newOpcode);
|
||||
Operand?.CopyTo(result.Operand);
|
||||
foreach (MsilLabel x in Labels)
|
||||
result.Labels.Add(x);
|
||||
return result;
|
||||
@@ -172,5 +163,28 @@ namespace Torch.Managers.PatchManager.MSIL
|
||||
sb.Append(OpCode.Name).Append("\t").Append(Operand);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
#pragma warning disable 169
|
||||
[ReflectedMethod(Name = "StackChange")]
|
||||
private static Func<OpCode, int> _stackChange;
|
||||
#pragma warning restore 169
|
||||
|
||||
public int StackChange()
|
||||
{
|
||||
int num = _stackChange.Invoke(OpCode);
|
||||
if ((OpCode == OpCodes.Call || OpCode == OpCodes.Callvirt || OpCode == OpCodes.Newobj) &&
|
||||
Operand is MsilOperandInline<MethodBase> inline)
|
||||
{
|
||||
MethodBase op = inline.Value;
|
||||
if (op is MethodInfo mi && mi.ReturnType != typeof(void))
|
||||
num++;
|
||||
num -= op.GetParameters().Length;
|
||||
if (!op.IsStatic && OpCode != OpCodes.Newobj)
|
||||
num--;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
}
|
||||
}
|
@@ -18,6 +18,8 @@ namespace Torch.Managers.PatchManager.MSIL
|
||||
/// </summary>
|
||||
public MsilInstruction Instruction { get; }
|
||||
|
||||
internal abstract void CopyTo(MsilOperand operand);
|
||||
|
||||
internal abstract void Read(MethodContext context, BinaryReader reader);
|
||||
|
||||
internal abstract void Emit(LoggingIlGenerator generator);
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using System.IO;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection.Emit;
|
||||
using Torch.Managers.PatchManager.Transpile;
|
||||
|
||||
@@ -22,8 +23,8 @@ namespace Torch.Managers.PatchManager.MSIL
|
||||
{
|
||||
int val = Instruction.OpCode.OperandType == OperandType.InlineBrTarget
|
||||
? reader.ReadInt32()
|
||||
: reader.ReadByte();
|
||||
Target = context.LabelAt((int) reader.BaseStream.Position + val);
|
||||
: reader.ReadSByte();
|
||||
Target = context.LabelAt((int)reader.BaseStream.Position + val);
|
||||
}
|
||||
|
||||
internal override void Emit(LoggingIlGenerator generator)
|
||||
@@ -31,6 +32,14 @@ namespace Torch.Managers.PatchManager.MSIL
|
||||
generator.Emit(Instruction.OpCode, Target.LabelFor(generator));
|
||||
}
|
||||
|
||||
internal override void CopyTo(MsilOperand operand)
|
||||
{
|
||||
var lt = operand as MsilOperandBrTarget;
|
||||
if (lt == null)
|
||||
throw new ArgumentException($"Target {operand?.GetType().Name} must be of same type {GetType().Name}", nameof(operand));
|
||||
lt.Target = Target;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
|
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using Torch.Managers.PatchManager.Transpile;
|
||||
@@ -22,6 +23,15 @@ namespace Torch.Managers.PatchManager.MSIL
|
||||
/// </summary>
|
||||
public T Value { get; set; }
|
||||
|
||||
internal override void CopyTo(MsilOperand operand)
|
||||
{
|
||||
var lt = operand as MsilOperandInline<T>;
|
||||
if (lt == null)
|
||||
throw new ArgumentException($"Target {operand?.GetType().Name} must be of same type {GetType().Name}", nameof(operand));
|
||||
lt.Value = Value;
|
||||
;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
@@ -66,7 +76,7 @@ namespace Torch.Managers.PatchManager.MSIL
|
||||
internal override void Read(MethodContext context, BinaryReader reader)
|
||||
{
|
||||
Value =
|
||||
(sbyte) reader.ReadByte();
|
||||
(sbyte)reader.ReadByte();
|
||||
}
|
||||
|
||||
internal override void Emit(LoggingIlGenerator generator)
|
||||
@@ -209,16 +219,19 @@ namespace Torch.Managers.PatchManager.MSIL
|
||||
|
||||
internal override void Read(MethodContext context, BinaryReader reader)
|
||||
{
|
||||
Value =
|
||||
context.Method.GetParameters()[
|
||||
int paramID =
|
||||
Instruction.OpCode.OperandType == OperandType.ShortInlineVar
|
||||
? reader.ReadByte()
|
||||
: reader.ReadUInt16()];
|
||||
: reader.ReadUInt16();
|
||||
if (paramID == 0 && !context.Method.IsStatic)
|
||||
throw new ArgumentException("Haven't figured out how to ldarg with the \"this\" argument");
|
||||
Value = context.Method.GetParameters()[paramID - (context.Method.IsStatic ? 0 : 1)];
|
||||
}
|
||||
|
||||
internal override void Emit(LoggingIlGenerator generator)
|
||||
{
|
||||
generator.Emit(Instruction.OpCode, Value.Position);
|
||||
var methodInfo = Value.Member as MethodBase;
|
||||
generator.Emit(Instruction.OpCode, Value.Position + (methodInfo != null && methodInfo.IsStatic ? 0 : 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using System.IO;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection.Emit;
|
||||
using Torch.Managers.PatchManager.Transpile;
|
||||
@@ -19,6 +20,20 @@ namespace Torch.Managers.PatchManager.MSIL
|
||||
/// </summary>
|
||||
public MsilLabel[] Labels { get; set; }
|
||||
|
||||
|
||||
internal override void CopyTo(MsilOperand operand)
|
||||
{
|
||||
var lt = operand as MsilOperandSwitch;
|
||||
if (lt == null)
|
||||
throw new ArgumentException($"Target {operand?.GetType().Name} must be of same type {GetType().Name}", nameof(operand));
|
||||
if (Labels == null)
|
||||
lt.Labels = null;
|
||||
else
|
||||
{
|
||||
lt.Labels = new MsilLabel[Labels.Length];
|
||||
Array.Copy(Labels, lt.Labels, Labels.Length);
|
||||
}
|
||||
}
|
||||
internal override void Read(MethodContext context, BinaryReader reader)
|
||||
{
|
||||
int length = reader.ReadInt32();
|
||||
|
@@ -52,7 +52,7 @@ namespace Torch.Managers.PatchManager.Transpile
|
||||
/// <inheritdoc cref="ILGenerator.Emit(OpCode, LocalBuilder)"/>
|
||||
public void Emit(OpCode op, LocalBuilder arg)
|
||||
{
|
||||
_log?.Trace($"Emit\t{op,_opcodePadding} L:{arg.LocalIndex} {arg.LocalType}");
|
||||
_log?.Trace($"Emit\t{op,_opcodePadding} Local:{arg.LocalIndex}/{arg.LocalType}");
|
||||
Backing.Emit(op, arg);
|
||||
}
|
||||
|
||||
|
@@ -7,6 +7,7 @@ using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using NLog;
|
||||
using Torch.Managers.PatchManager.MSIL;
|
||||
using Torch.Utils;
|
||||
|
||||
namespace Torch.Managers.PatchManager.Transpile
|
||||
{
|
||||
@@ -52,20 +53,19 @@ namespace Torch.Managers.PatchManager.Transpile
|
||||
using (var reader = new BinaryReader(memory))
|
||||
while (memory.Length > memory.Position)
|
||||
{
|
||||
var count = 1;
|
||||
var opcodeOffset = (int) memory.Position;
|
||||
var instructionValue = (short)memory.ReadByte();
|
||||
if (Prefixes.Contains(instructionValue))
|
||||
{
|
||||
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}");
|
||||
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)
|
||||
{
|
||||
Offset = (int)memory.Position
|
||||
Offset = opcodeOffset
|
||||
};
|
||||
_instructions.Add(instruction);
|
||||
instruction.Operand?.Read(this, reader);
|
||||
@@ -76,22 +76,74 @@ namespace Torch.Managers.PatchManager.Transpile
|
||||
{
|
||||
foreach (var label in Labels)
|
||||
{
|
||||
int min = 0, max = _instructions.Count - 1;
|
||||
while (min <= max)
|
||||
int min = 0, max = _instructions.Count;
|
||||
while (min != max)
|
||||
{
|
||||
var mid = min + ((max - min) / 2);
|
||||
if (label.Key < _instructions[mid].Offset)
|
||||
max = mid - 1;
|
||||
else
|
||||
int mid = (min + max) / 2;
|
||||
if (_instructions[mid].Offset < label.Key)
|
||||
min = mid + 1;
|
||||
else
|
||||
max = mid;
|
||||
}
|
||||
#if DEBUG
|
||||
if (min >= _instructions.Count || min < 0)
|
||||
{
|
||||
_log.Trace(
|
||||
$"Want offset {label.Key} for {label.Value}, instruction offsets at\n {string.Join("\n", _instructions.Select(x => $"IL_{x.Offset:X4} {x}"))}");
|
||||
}
|
||||
MsilInstruction prevInsn = min > 0 ? _instructions[min - 1] : null;
|
||||
if ((prevInsn == null || prevInsn.Offset >= label.Key) ||
|
||||
_instructions[min].Offset < label.Key)
|
||||
_log.Error($"Label {label.Value} wanted {label.Key} but instruction is at {_instructions[min].Offset}. Previous instruction is at {prevInsn?.Offset ?? -1}");
|
||||
#endif
|
||||
_instructions[min]?.Labels?.Add(label.Value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
public void CheckIntegrity()
|
||||
{
|
||||
var entryStackCount = new Dictionary<MsilLabel, Dictionary<MsilInstruction, int>>();
|
||||
var currentStackSize = 0;
|
||||
foreach (MsilInstruction insn in _instructions)
|
||||
{
|
||||
// I don't want to deal with this, so I won't
|
||||
if (insn.OpCode == OpCodes.Br || insn.OpCode == OpCodes.Br_S || insn.OpCode == OpCodes.Jmp ||
|
||||
insn.OpCode == OpCodes.Leave || insn.OpCode == OpCodes.Leave_S)
|
||||
break;
|
||||
foreach (MsilLabel label in insn.Labels)
|
||||
if (entryStackCount.TryGetValue(label, out Dictionary<MsilInstruction, int> dict))
|
||||
dict.Add(insn, currentStackSize);
|
||||
else
|
||||
(entryStackCount[label] = new Dictionary<MsilInstruction, int>()).Add(insn, currentStackSize);
|
||||
|
||||
currentStackSize += insn.StackChange();
|
||||
|
||||
if (insn.Operand is MsilOperandBrTarget br)
|
||||
if (entryStackCount.TryGetValue(br.Target, out Dictionary<MsilInstruction, int> dict))
|
||||
dict.Add(insn, currentStackSize);
|
||||
else
|
||||
(entryStackCount[br.Target] = new Dictionary<MsilInstruction, int>()).Add(insn, currentStackSize);
|
||||
}
|
||||
foreach (KeyValuePair<MsilLabel, Dictionary<MsilInstruction, int>> label in entryStackCount)
|
||||
{
|
||||
if (label.Value.Values.Aggregate(new HashSet<int>(), (a, b) =>
|
||||
{
|
||||
a.Add(b);
|
||||
return a;
|
||||
}).Count > 1)
|
||||
{
|
||||
_log.Warn($"Label {label.Key} has multiple entry stack counts");
|
||||
foreach (KeyValuePair<MsilInstruction, int> kv in label.Value)
|
||||
_log.Warn($"{kv.Key.Offset:X4} {kv.Key} => {kv.Value}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string ToHumanMsil()
|
||||
{
|
||||
return string.Join("\n", _instructions.Select(x => $"IL_{x.Offset:X4}: {x}"));
|
||||
return string.Join("\n", _instructions.Select(x => $"IL_{x.Offset:X4}: {x.StackChange()} {x}"));
|
||||
}
|
||||
|
||||
private static readonly Dictionary<short, OpCode> OpCodeLookup;
|
||||
|
@@ -9,16 +9,17 @@ namespace Torch.Managers.PatchManager.Transpile
|
||||
{
|
||||
internal class MethodTranspiler
|
||||
{
|
||||
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
|
||||
public 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();
|
||||
context.CheckIntegrity();
|
||||
_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)
|
||||
methodContent = (IEnumerable<MsilInstruction>)transpiler.Invoke(null, new object[] { methodContent });
|
||||
methodContent = FixBranchAndReturn(methodContent, retLabel);
|
||||
@@ -35,14 +36,16 @@ namespace Torch.Managers.PatchManager.Transpile
|
||||
MsilInstruction j = new MsilInstruction(OpCodes.Br).InlineTarget(new MsilLabel(retTarget.Value));
|
||||
foreach (MsilLabel l in i.Labels)
|
||||
j.Labels.Add(l);
|
||||
_log.Trace($"Replacing {i} with {j}");
|
||||
yield return j;
|
||||
continue;
|
||||
}
|
||||
if (_opcodeReplaceRule.TryGetValue(i.OpCode, out OpCode replaceOpcode))
|
||||
else if (_opcodeReplaceRule.TryGetValue(i.OpCode, out OpCode replaceOpcode))
|
||||
{
|
||||
yield return i.CopyWith(replaceOpcode);
|
||||
continue;
|
||||
var result = i.CopyWith(replaceOpcode);
|
||||
_log.Trace($"Replacing {i} with {result}");
|
||||
yield return result;
|
||||
}
|
||||
else
|
||||
yield return i;
|
||||
}
|
||||
}
|
||||
@@ -57,7 +60,7 @@ namespace Torch.Managers.PatchManager.Transpile
|
||||
if (opcode.OperandType == OperandType.ShortInlineBrTarget &&
|
||||
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);
|
||||
if (other.HasValue && other.Value.OperandType == OperandType.InlineBrTarget)
|
||||
_opcodeReplaceRule.Add(opcode, other.Value);
|
||||
|
Reference in New Issue
Block a user