diff --git a/NLog.config b/NLog.config
index 3ac0285..270fe34 100644
--- a/NLog.config
+++ b/NLog.config
@@ -12,6 +12,6 @@
-
+
\ No newline at end of file
diff --git a/Torch/Managers/PatchManager/DecoratedMethod.cs b/Torch/Managers/PatchManager/DecoratedMethod.cs
index 5892de8..8ca6a47 100644
--- a/Torch/Managers/PatchManager/DecoratedMethod.cs
+++ b/Torch/Managers/PatchManager/DecoratedMethod.cs
@@ -7,6 +7,7 @@ using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using NLog;
+using Torch.Managers.PatchManager.MSIL;
using Torch.Managers.PatchManager.Transpile;
using Torch.Utils;
@@ -147,7 +148,7 @@ namespace Torch.Managers.PatchManager
target.EmitComment("Prefixes End");
target.EmitComment("Original Begin");
- MethodTranspiler.Transpile(_method, Transpilers, target, labelAfterOriginalContent);
+ MethodTranspiler.Transpile(_method, (type) => new MsilLocal(target.DeclareLocal(type)), Transpilers, target, labelAfterOriginalContent);
target.EmitComment("Original End");
target.MarkLabel(labelAfterOriginalContent);
diff --git a/Torch/Managers/PatchManager/MSIL/MsilArgument.cs b/Torch/Managers/PatchManager/MSIL/MsilArgument.cs
new file mode 100644
index 0000000..2e10b14
--- /dev/null
+++ b/Torch/Managers/PatchManager/MSIL/MsilArgument.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Torch.Managers.PatchManager.MSIL
+{
+ ///
+ /// Represents metadata about a method's parameter
+ ///
+ public class MsilArgument
+ {
+ ///
+ /// The positon of this argument. Note, if the method is static, index 0 is the instance.
+ ///
+ public int Position { get; }
+
+ ///
+ /// The type of this parameter, or null if unknown.
+ ///
+ public Type Type { get; }
+
+ ///
+ /// The name of this parameter, or null if unknown.
+ ///
+ public string Name { get; }
+
+ internal MsilArgument(ParameterInfo local)
+ {
+ Position = (((MethodBase)local.Member).IsStatic ? 0 : 1) + local.Position;
+ Type = local.ParameterType;
+ Name = local.Name;
+ }
+
+ ///
+ /// Creates an empty argument reference with the given position.
+ ///
+ /// The argument's position
+ public MsilArgument(int position)
+ {
+ Position = position;
+ Type = null;
+ Name = null;
+ }
+ }
+}
diff --git a/Torch/Managers/PatchManager/MSIL/MsilInstruction.cs b/Torch/Managers/PatchManager/MSIL/MsilInstruction.cs
index 4614098..54ad06e 100644
--- a/Torch/Managers/PatchManager/MSIL/MsilInstruction.cs
+++ b/Torch/Managers/PatchManager/MSIL/MsilInstruction.cs
@@ -67,7 +67,7 @@ namespace Torch.Managers.PatchManager.MSIL
if (OpCode.Name.IndexOf("loc", StringComparison.OrdinalIgnoreCase) != -1)
Operand = new MsilOperandInline.MsilOperandLocal(this);
else
- Operand = new MsilOperandInline.MsilOperandParameter(this);
+ Operand = new MsilOperandInline.MsilOperandArgument(this);
break;
case OperandType.ShortInlineI:
Operand = OpCode == OpCodes.Ldc_I4_S
@@ -193,66 +193,6 @@ namespace Torch.Managers.PatchManager.MSIL
private static Func _stackChange;
#pragma warning restore 169
-
- ///
- /// Gets an instruction that represents the inverse of this load or store instruction.
- ///
- ///
- ///
- /// new MsilInstruction(OpCodes.Ldloc_0).StoreLoadInverse().OpCode == OpCodes.Stloc_0
- ///
- ///
- /// Inverse
- public MsilInstruction StoreLoadInverse()
- {
- if (OpCode == OpCodes.Ldloc)
- return new MsilInstruction(OpCodes.Stloc).InlineValue(
- ((MsilOperandInline)Operand).Value);
- if (OpCode == OpCodes.Ldloc_S)
- return new MsilInstruction(OpCodes.Stloc_S).InlineValue(
- ((MsilOperandInline)Operand).Value);
- if (OpCode == OpCodes.Ldloc_0)
- return new MsilInstruction(OpCodes.Stloc_0);
- if (OpCode == OpCodes.Ldloc_1)
- return new MsilInstruction(OpCodes.Stloc_1);
- if (OpCode == OpCodes.Ldloc_2)
- return new MsilInstruction(OpCodes.Stloc_2);
- if (OpCode == OpCodes.Ldloc_3)
- return new MsilInstruction(OpCodes.Stloc_3);
-
- if (OpCode == OpCodes.Stloc)
- return new MsilInstruction(OpCodes.Ldloc).InlineValue(
- ((MsilOperandInline)Operand).Value);
- if (OpCode == OpCodes.Stloc_S)
- return new MsilInstruction(OpCodes.Ldloc_S).InlineValue(
- ((MsilOperandInline)Operand).Value);
- if (OpCode == OpCodes.Stloc_0)
- return new MsilInstruction(OpCodes.Ldloc_0);
- if (OpCode == OpCodes.Stloc_1)
- return new MsilInstruction(OpCodes.Ldloc_1);
- if (OpCode == OpCodes.Stloc_2)
- return new MsilInstruction(OpCodes.Ldloc_2);
- if (OpCode == OpCodes.Stloc_3)
- return new MsilInstruction(OpCodes.Ldloc_3);
-
- if (OpCode == OpCodes.Ldarg)
- return new MsilInstruction(OpCodes.Starg).InlineValue(
- ((MsilOperandInline)Operand).Value);
- if (OpCode == OpCodes.Ldarg_S)
- return new MsilInstruction(OpCodes.Starg_S).InlineValue(
- ((MsilOperandInline)Operand).Value);
- // TODO Ldarg_0 etc
-
- if (OpCode == OpCodes.Starg)
- return new MsilInstruction(OpCodes.Ldarg).InlineValue(
- ((MsilOperandInline)Operand).Value);
- if (OpCode == OpCodes.Starg_S)
- return new MsilInstruction(OpCodes.Ldarg_S).InlineValue(
- ((MsilOperandInline)Operand).Value);
-
- throw new ArgumentException($"Can't invert the instruction {this}");
- }
-
///
/// Estimates the stack delta for this instruction.
///
diff --git a/Torch/Managers/PatchManager/MSIL/MsilInstructionExtensions.cs b/Torch/Managers/PatchManager/MSIL/MsilInstructionExtensions.cs
new file mode 100644
index 0000000..4629f7f
--- /dev/null
+++ b/Torch/Managers/PatchManager/MSIL/MsilInstructionExtensions.cs
@@ -0,0 +1,202 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Torch.Managers.PatchManager.MSIL
+{
+ ///
+ /// Various methods to make composing MSIL easier
+ ///
+ public static class MsilInstructionExtensions
+ {
+ #region Local Utils
+ ///
+ /// Is this instruction a local load-by-value instruction.
+ ///
+ public static bool IsLocalLoad(this MsilInstruction me)
+ {
+ return me.OpCode == OpCodes.Ldloc || me.OpCode == OpCodes.Ldloc_S || me.OpCode == OpCodes.Ldloc_0 ||
+ me.OpCode == OpCodes.Ldloc_1 || me.OpCode == OpCodes.Ldloc_2 || me.OpCode == OpCodes.Ldloc_3;
+ }
+
+ ///
+ /// Is this instruction a local load-by-reference instruction.
+ ///
+ public static bool IsLocalLoadByRef(this MsilInstruction me)
+ {
+ return me.OpCode == OpCodes.Ldloca || me.OpCode == OpCodes.Ldloca_S;
+ }
+
+ ///
+ /// Is this instruction a local store instruction.
+ ///
+ public static bool IsLocalStore(this MsilInstruction me)
+ {
+ return me.OpCode == OpCodes.Stloc || me.OpCode == OpCodes.Stloc_S || me.OpCode == OpCodes.Stloc_0 ||
+ me.OpCode == OpCodes.Stloc_1 || me.OpCode == OpCodes.Stloc_2 || me.OpCode == OpCodes.Stloc_3;
+ }
+
+ ///
+ /// For a local referencing opcode, get the local it is referencing.
+ ///
+ public static MsilLocal GetReferencedLocal(this MsilInstruction me)
+ {
+ if (me.Operand is MsilOperandInline.MsilOperandLocal mol)
+ return mol.Value;
+ if (me.OpCode == OpCodes.Stloc_0 || me.OpCode == OpCodes.Ldloc_0)
+ return new MsilLocal(0);
+ if (me.OpCode == OpCodes.Stloc_1 || me.OpCode == OpCodes.Ldloc_1)
+ return new MsilLocal(1);
+ if (me.OpCode == OpCodes.Stloc_2 || me.OpCode == OpCodes.Ldloc_2)
+ return new MsilLocal(2);
+ if (me.OpCode == OpCodes.Stloc_3 || me.OpCode == OpCodes.Ldloc_3)
+ return new MsilLocal(3);
+ throw new ArgumentException($"Can't get referenced local in instruction {me}");
+ }
+ ///
+ /// Gets an instruction representing a load-by-value from the given local.
+ ///
+ /// Local to load
+ /// Loading instruction
+ public static MsilInstruction AsValueLoad(this MsilLocal local)
+ {
+ switch (local.Index)
+ {
+ case 0:
+ return new MsilInstruction(OpCodes.Ldloc_0);
+ case 1:
+ return new MsilInstruction(OpCodes.Ldloc_1);
+ case 2:
+ return new MsilInstruction(OpCodes.Ldloc_2);
+ case 3:
+ return new MsilInstruction(OpCodes.Ldloc_3);
+ default:
+ return new MsilInstruction(local.Index < 0xFF ? OpCodes.Ldloc_S : OpCodes.Ldloc).InlineValue(local);
+ }
+ }
+
+ ///
+ /// Gets an instruction representing a store-by-value to the given local.
+ ///
+ /// Local to write to
+ /// Loading instruction
+ public static MsilInstruction AsValueStore(this MsilLocal local)
+ {
+ switch (local.Index)
+ {
+ case 0:
+ return new MsilInstruction(OpCodes.Stloc_0);
+ case 1:
+ return new MsilInstruction(OpCodes.Stloc_1);
+ case 2:
+ return new MsilInstruction(OpCodes.Stloc_2);
+ case 3:
+ return new MsilInstruction(OpCodes.Stloc_3);
+ default:
+ return new MsilInstruction(local.Index < 0xFF ? OpCodes.Stloc_S : OpCodes.Stloc).InlineValue(local);
+ }
+ }
+
+ ///
+ /// Gets an instruction representing a load-by-reference from the given local.
+ ///
+ /// Local to load
+ /// Loading instruction
+ public static MsilInstruction AsReferenceLoad(this MsilLocal local)
+ {
+ return new MsilInstruction(local.Index < 0xFF ? OpCodes.Ldloca_S : OpCodes.Ldloca).InlineValue(local);
+ }
+ #endregion
+
+ #region Argument Utils
+ ///
+ /// Is this instruction an argument load-by-value instruction.
+ ///
+ public static bool IsArgumentLoad(this MsilInstruction me)
+ {
+ return me.OpCode == OpCodes.Ldarg || me.OpCode == OpCodes.Ldarg_S || me.OpCode == OpCodes.Ldarg_0 ||
+ me.OpCode == OpCodes.Ldarg_1 || me.OpCode == OpCodes.Ldarg_2 || me.OpCode == OpCodes.Ldarg_3;
+ }
+
+ ///
+ /// Is this instruction an argument load-by-reference instruction.
+ ///
+ public static bool IsArgumentLoadByRef(this MsilInstruction me)
+ {
+ return me.OpCode == OpCodes.Ldarga || me.OpCode == OpCodes.Ldarga_S;
+ }
+
+ ///
+ /// Is this instruction an argument store instruction.
+ ///
+ public static bool IsArgumentStore(this MsilInstruction me)
+ {
+ return me.OpCode == OpCodes.Starg || me.OpCode == OpCodes.Starg_S;
+ }
+
+ ///
+ /// For an argument referencing opcode, get the index of the local it is referencing.
+ ///
+ public static MsilArgument GetReferencedArgument(this MsilInstruction me)
+ {
+ if (me.Operand is MsilOperandInline.MsilOperandArgument mol)
+ return mol.Value;
+ if (me.OpCode == OpCodes.Ldarg_0)
+ return new MsilArgument(0);
+ if (me.OpCode == OpCodes.Ldarg_1)
+ return new MsilArgument(1);
+ if (me.OpCode == OpCodes.Ldarg_2)
+ return new MsilArgument(2);
+ if (me.OpCode == OpCodes.Ldarg_3)
+ return new MsilArgument(3);
+ throw new ArgumentException($"Can't get referenced argument in instruction {me}");
+ }
+
+ ///
+ /// Gets an instruction representing a load-by-value from the given argument.
+ ///
+ /// argument to load
+ /// Load instruction
+ public static MsilInstruction AsValueLoad(this MsilArgument argument)
+ {
+ switch (argument.Position)
+ {
+ case 0:
+ return new MsilInstruction(OpCodes.Ldarg_0);
+ case 1:
+ return new MsilInstruction(OpCodes.Ldarg_1);
+ case 2:
+ return new MsilInstruction(OpCodes.Ldarg_2);
+ case 3:
+ return new MsilInstruction(OpCodes.Ldarg_3);
+ default:
+ return new MsilInstruction(argument.Position < 0xFF ? OpCodes.Ldarg_S : OpCodes.Ldarg).InlineValue(argument);
+ }
+ }
+
+ ///
+ /// Gets an instruction representing a store-by-value to the given argument.
+ ///
+ /// argument to write to
+ /// Store instruction
+ public static MsilInstruction AsValueStore(this MsilArgument argument)
+ {
+ return new MsilInstruction(argument.Position < 0xFF ? OpCodes.Starg_S : OpCodes.Starg).InlineValue(argument);
+ }
+
+ ///
+ /// Gets an instruction representing a load-by-reference from the given argument.
+ ///
+ /// argument to load
+ /// Reference load instruction
+ public static MsilInstruction AsReferenceLoad(this MsilArgument argument)
+ {
+ return new MsilInstruction(argument.Position < 0xFF ? OpCodes.Ldarga_S : OpCodes.Ldarga).InlineValue(argument);
+ }
+ #endregion
+ }
+}
diff --git a/Torch/Managers/PatchManager/MSIL/MsilLocal.cs b/Torch/Managers/PatchManager/MSIL/MsilLocal.cs
new file mode 100644
index 0000000..8268aee
--- /dev/null
+++ b/Torch/Managers/PatchManager/MSIL/MsilLocal.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Torch.Managers.PatchManager.MSIL
+{
+ ///
+ /// Represents metadata about a method's local
+ ///
+ public class MsilLocal
+ {
+ ///
+ /// The index of this local.
+ ///
+ public int Index { get; }
+
+ ///
+ /// The type of this local, or null if unknown.
+ ///
+ public Type Type { get; }
+
+ ///
+ /// The name of this local, or null if unknown.
+ ///
+ public string Name { get; }
+
+ internal MsilLocal(LocalBuilder local)
+ {
+ Index = local.LocalIndex;
+ Type = local.LocalType;
+ Name = null;
+ }
+
+ internal MsilLocal(LocalVariableInfo local)
+ {
+ Index = local.LocalIndex;
+ Type = local.LocalType;
+ Name = null;
+ }
+
+ ///
+ /// Creates an empty local reference with the given index.
+ ///
+ /// The local's index
+ public MsilLocal(int index)
+ {
+ Index = index;
+ Type = null;
+ Name = null;
+ }
+ }
+}
diff --git a/Torch/Managers/PatchManager/MSIL/MsilOperandInline.cs b/Torch/Managers/PatchManager/MSIL/MsilOperandInline.cs
index b247a54..2442fd9 100644
--- a/Torch/Managers/PatchManager/MSIL/MsilOperandInline.cs
+++ b/Torch/Managers/PatchManager/MSIL/MsilOperandInline.cs
@@ -209,11 +209,11 @@ namespace Torch.Managers.PatchManager.MSIL
}
///
- /// Inline parameter reference
+ /// Inline argument reference
///
- public class MsilOperandParameter : MsilOperandInline
+ public class MsilOperandArgument : MsilOperandInline
{
- internal MsilOperandParameter(MsilInstruction instruction) : base(instruction)
+ internal MsilOperandArgument(MsilInstruction instruction) : base(instruction)
{
}
@@ -225,20 +225,19 @@ namespace Torch.Managers.PatchManager.MSIL
: 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)];
+ Value = new MsilArgument(context.Method.GetParameters()[paramID - (context.Method.IsStatic ? 0 : 1)]);
}
internal override void Emit(LoggingIlGenerator generator)
{
- var methodInfo = Value.Member as MethodBase;
- generator.Emit(Instruction.OpCode, Value.Position + (methodInfo != null && methodInfo.IsStatic ? 0 : 1));
+ generator.Emit(Instruction.OpCode, Value.Position);
}
}
///
/// Inline local variable reference
///
- public class MsilOperandLocal : MsilOperandInline
+ public class MsilOperandLocal : MsilOperandInline
{
internal MsilOperandLocal(MsilInstruction instruction) : base(instruction)
{
@@ -247,15 +246,15 @@ namespace Torch.Managers.PatchManager.MSIL
internal override void Read(MethodContext context, BinaryReader reader)
{
Value =
- context.Method.GetMethodBody().LocalVariables[
+ new MsilLocal(context.Method.GetMethodBody().LocalVariables[
Instruction.OpCode.OperandType == OperandType.ShortInlineVar
? reader.ReadByte()
- : reader.ReadUInt16()];
+ : reader.ReadUInt16()]);
}
internal override void Emit(LoggingIlGenerator generator)
{
- generator.Emit(Instruction.OpCode, Value.LocalIndex);
+ generator.Emit(Instruction.OpCode, Value.Index);
}
}
diff --git a/Torch/Managers/PatchManager/Transpile/MethodTranspiler.cs b/Torch/Managers/PatchManager/Transpile/MethodTranspiler.cs
index 33faedb..e2db491 100644
--- a/Torch/Managers/PatchManager/Transpile/MethodTranspiler.cs
+++ b/Torch/Managers/PatchManager/Transpile/MethodTranspiler.cs
@@ -11,17 +11,32 @@ namespace Torch.Managers.PatchManager.Transpile
{
public static readonly Logger _log = LogManager.GetCurrentClassLogger();
- internal static void Transpile(MethodBase baseMethod, IEnumerable transpilers, LoggingIlGenerator output, Label? retLabel)
+ internal static void Transpile(MethodBase baseMethod, Func localCreator, IEnumerable transpilers, LoggingIlGenerator output, Label? retLabel)
{
var context = new MethodContext(baseMethod);
context.Read();
context.CheckIntegrity();
-// _log.Trace("Input Method:");
-// _log.Trace(context.ToHumanMsil);
+ // _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 });
+ foreach (MethodInfo transpiler in transpilers)
+ {
+ var paramList = new List