ReflectedMethodInfo allows non-public type names.

MsilInstructionExtensions to make life easier
This commit is contained in:
Westin Miller
2017-09-11 22:32:37 -07:00
parent 373c476d2d
commit a61b646295
10 changed files with 353 additions and 78 deletions

View File

@@ -12,6 +12,6 @@
<rules> <rules>
<logger name="*" minlevel="Info" writeTo="main, console" /> <logger name="*" minlevel="Info" writeTo="main, console" />
<logger name="Chat" minlevel="Info" writeTo="chat" /> <logger name="Chat" minlevel="Info" writeTo="chat" />
<logger name="Torch.Managers.PatchManager.*" minlevel="Trace" writeTo="patch"/> <!--<logger name="Torch.Managers.PatchManager.*" minlevel="Trace" writeTo="patch"/>-->
</rules> </rules>
</nlog> </nlog>

View File

@@ -7,6 +7,7 @@ using System.Reflection.Emit;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using NLog; using NLog;
using Torch.Managers.PatchManager.MSIL;
using Torch.Managers.PatchManager.Transpile; using Torch.Managers.PatchManager.Transpile;
using Torch.Utils; using Torch.Utils;
@@ -147,7 +148,7 @@ namespace Torch.Managers.PatchManager
target.EmitComment("Prefixes End"); target.EmitComment("Prefixes End");
target.EmitComment("Original Begin"); 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.EmitComment("Original End");
target.MarkLabel(labelAfterOriginalContent); target.MarkLabel(labelAfterOriginalContent);

View File

@@ -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
{
/// <summary>
/// Represents metadata about a method's parameter
/// </summary>
public class MsilArgument
{
/// <summary>
/// The positon of this argument. Note, if the method is static, index 0 is the instance.
/// </summary>
public int Position { get; }
/// <summary>
/// The type of this parameter, or null if unknown.
/// </summary>
public Type Type { get; }
/// <summary>
/// The name of this parameter, or null if unknown.
/// </summary>
public string Name { get; }
internal MsilArgument(ParameterInfo local)
{
Position = (((MethodBase)local.Member).IsStatic ? 0 : 1) + local.Position;
Type = local.ParameterType;
Name = local.Name;
}
/// <summary>
/// Creates an empty argument reference with the given position.
/// </summary>
/// <param name="position">The argument's position</param>
public MsilArgument(int position)
{
Position = position;
Type = null;
Name = null;
}
}
}

View File

@@ -67,7 +67,7 @@ namespace Torch.Managers.PatchManager.MSIL
if (OpCode.Name.IndexOf("loc", StringComparison.OrdinalIgnoreCase) != -1) if (OpCode.Name.IndexOf("loc", StringComparison.OrdinalIgnoreCase) != -1)
Operand = new MsilOperandInline.MsilOperandLocal(this); Operand = new MsilOperandInline.MsilOperandLocal(this);
else else
Operand = new MsilOperandInline.MsilOperandParameter(this); Operand = new MsilOperandInline.MsilOperandArgument(this);
break; break;
case OperandType.ShortInlineI: case OperandType.ShortInlineI:
Operand = OpCode == OpCodes.Ldc_I4_S Operand = OpCode == OpCodes.Ldc_I4_S
@@ -193,66 +193,6 @@ namespace Torch.Managers.PatchManager.MSIL
private static Func<OpCode, int> _stackChange; private static Func<OpCode, int> _stackChange;
#pragma warning restore 169 #pragma warning restore 169
/// <summary>
/// Gets an instruction that represents the inverse of this load or store instruction.
/// </summary>
/// <remarks>
/// <example>
/// new MsilInstruction(OpCodes.Ldloc_0).StoreLoadInverse().OpCode == OpCodes.Stloc_0
/// </example>
/// </remarks>
/// <returns>Inverse</returns>
public MsilInstruction StoreLoadInverse()
{
if (OpCode == OpCodes.Ldloc)
return new MsilInstruction(OpCodes.Stloc).InlineValue(
((MsilOperandInline<LocalVariableInfo>)Operand).Value);
if (OpCode == OpCodes.Ldloc_S)
return new MsilInstruction(OpCodes.Stloc_S).InlineValue(
((MsilOperandInline<LocalVariableInfo>)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<LocalVariableInfo>)Operand).Value);
if (OpCode == OpCodes.Stloc_S)
return new MsilInstruction(OpCodes.Ldloc_S).InlineValue(
((MsilOperandInline<LocalVariableInfo>)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<ParameterInfo>)Operand).Value);
if (OpCode == OpCodes.Ldarg_S)
return new MsilInstruction(OpCodes.Starg_S).InlineValue(
((MsilOperandInline<ParameterInfo>)Operand).Value);
// TODO Ldarg_0 etc
if (OpCode == OpCodes.Starg)
return new MsilInstruction(OpCodes.Ldarg).InlineValue(
((MsilOperandInline<ParameterInfo>)Operand).Value);
if (OpCode == OpCodes.Starg_S)
return new MsilInstruction(OpCodes.Ldarg_S).InlineValue(
((MsilOperandInline<ParameterInfo>)Operand).Value);
throw new ArgumentException($"Can't invert the instruction {this}");
}
/// <summary> /// <summary>
/// Estimates the stack delta for this instruction. /// Estimates the stack delta for this instruction.
/// </summary> /// </summary>

View File

@@ -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
{
/// <summary>
/// Various methods to make composing MSIL easier
/// </summary>
public static class MsilInstructionExtensions
{
#region Local Utils
/// <summary>
/// Is this instruction a local load-by-value instruction.
/// </summary>
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;
}
/// <summary>
/// Is this instruction a local load-by-reference instruction.
/// </summary>
public static bool IsLocalLoadByRef(this MsilInstruction me)
{
return me.OpCode == OpCodes.Ldloca || me.OpCode == OpCodes.Ldloca_S;
}
/// <summary>
/// Is this instruction a local store instruction.
/// </summary>
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;
}
/// <summary>
/// For a local referencing opcode, get the local it is referencing.
/// </summary>
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}");
}
/// <summary>
/// Gets an instruction representing a load-by-value from the given local.
/// </summary>
/// <param name="local">Local to load</param>
/// <returns>Loading instruction</returns>
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);
}
}
/// <summary>
/// Gets an instruction representing a store-by-value to the given local.
/// </summary>
/// <param name="local">Local to write to</param>
/// <returns>Loading instruction</returns>
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);
}
}
/// <summary>
/// Gets an instruction representing a load-by-reference from the given local.
/// </summary>
/// <param name="local">Local to load</param>
/// <returns>Loading instruction</returns>
public static MsilInstruction AsReferenceLoad(this MsilLocal local)
{
return new MsilInstruction(local.Index < 0xFF ? OpCodes.Ldloca_S : OpCodes.Ldloca).InlineValue(local);
}
#endregion
#region Argument Utils
/// <summary>
/// Is this instruction an argument load-by-value instruction.
/// </summary>
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;
}
/// <summary>
/// Is this instruction an argument load-by-reference instruction.
/// </summary>
public static bool IsArgumentLoadByRef(this MsilInstruction me)
{
return me.OpCode == OpCodes.Ldarga || me.OpCode == OpCodes.Ldarga_S;
}
/// <summary>
/// Is this instruction an argument store instruction.
/// </summary>
public static bool IsArgumentStore(this MsilInstruction me)
{
return me.OpCode == OpCodes.Starg || me.OpCode == OpCodes.Starg_S;
}
/// <summary>
/// For an argument referencing opcode, get the index of the local it is referencing.
/// </summary>
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}");
}
/// <summary>
/// Gets an instruction representing a load-by-value from the given argument.
/// </summary>
/// <param name="argument">argument to load</param>
/// <returns>Load instruction</returns>
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);
}
}
/// <summary>
/// Gets an instruction representing a store-by-value to the given argument.
/// </summary>
/// <param name="argument">argument to write to</param>
/// <returns>Store instruction</returns>
public static MsilInstruction AsValueStore(this MsilArgument argument)
{
return new MsilInstruction(argument.Position < 0xFF ? OpCodes.Starg_S : OpCodes.Starg).InlineValue(argument);
}
/// <summary>
/// Gets an instruction representing a load-by-reference from the given argument.
/// </summary>
/// <param name="argument">argument to load</param>
/// <returns>Reference load instruction</returns>
public static MsilInstruction AsReferenceLoad(this MsilArgument argument)
{
return new MsilInstruction(argument.Position < 0xFF ? OpCodes.Ldarga_S : OpCodes.Ldarga).InlineValue(argument);
}
#endregion
}
}

View File

@@ -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
{
/// <summary>
/// Represents metadata about a method's local
/// </summary>
public class MsilLocal
{
/// <summary>
/// The index of this local.
/// </summary>
public int Index { get; }
/// <summary>
/// The type of this local, or null if unknown.
/// </summary>
public Type Type { get; }
/// <summary>
/// The name of this local, or null if unknown.
/// </summary>
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;
}
/// <summary>
/// Creates an empty local reference with the given index.
/// </summary>
/// <param name="index">The local's index</param>
public MsilLocal(int index)
{
Index = index;
Type = null;
Name = null;
}
}
}

View File

@@ -209,11 +209,11 @@ namespace Torch.Managers.PatchManager.MSIL
} }
/// <summary> /// <summary>
/// Inline parameter reference /// Inline argument reference
/// </summary> /// </summary>
public class MsilOperandParameter : MsilOperandInline<ParameterInfo> public class MsilOperandArgument : MsilOperandInline<MsilArgument>
{ {
internal MsilOperandParameter(MsilInstruction instruction) : base(instruction) internal MsilOperandArgument(MsilInstruction instruction) : base(instruction)
{ {
} }
@@ -225,20 +225,19 @@ namespace Torch.Managers.PatchManager.MSIL
: reader.ReadUInt16(); : reader.ReadUInt16();
if (paramID == 0 && !context.Method.IsStatic) if (paramID == 0 && !context.Method.IsStatic)
throw new ArgumentException("Haven't figured out how to ldarg with the \"this\" argument"); 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) internal override void Emit(LoggingIlGenerator generator)
{ {
var methodInfo = Value.Member as MethodBase; generator.Emit(Instruction.OpCode, Value.Position);
generator.Emit(Instruction.OpCode, Value.Position + (methodInfo != null && methodInfo.IsStatic ? 0 : 1));
} }
} }
/// <summary> /// <summary>
/// Inline local variable reference /// Inline local variable reference
/// </summary> /// </summary>
public class MsilOperandLocal : MsilOperandInline<LocalVariableInfo> public class MsilOperandLocal : MsilOperandInline<MsilLocal>
{ {
internal MsilOperandLocal(MsilInstruction instruction) : base(instruction) internal MsilOperandLocal(MsilInstruction instruction) : base(instruction)
{ {
@@ -247,15 +246,15 @@ namespace Torch.Managers.PatchManager.MSIL
internal override void Read(MethodContext context, BinaryReader reader) internal override void Read(MethodContext context, BinaryReader reader)
{ {
Value = Value =
context.Method.GetMethodBody().LocalVariables[ new MsilLocal(context.Method.GetMethodBody().LocalVariables[
Instruction.OpCode.OperandType == OperandType.ShortInlineVar Instruction.OpCode.OperandType == OperandType.ShortInlineVar
? reader.ReadByte() ? reader.ReadByte()
: reader.ReadUInt16()]; : reader.ReadUInt16()]);
} }
internal override void Emit(LoggingIlGenerator generator) internal override void Emit(LoggingIlGenerator generator)
{ {
generator.Emit(Instruction.OpCode, Value.LocalIndex); generator.Emit(Instruction.OpCode, Value.Index);
} }
} }

View File

@@ -11,17 +11,32 @@ namespace Torch.Managers.PatchManager.Transpile
{ {
public 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) internal static void Transpile(MethodBase baseMethod, Func<Type, MsilLocal> localCreator, IEnumerable<MethodInfo> transpilers, LoggingIlGenerator output, Label? retLabel)
{ {
var context = new MethodContext(baseMethod); var context = new MethodContext(baseMethod);
context.Read(); context.Read();
context.CheckIntegrity(); context.CheckIntegrity();
// _log.Trace("Input Method:"); // _log.Trace("Input Method:");
// _log.Trace(context.ToHumanMsil); // _log.Trace(context.ToHumanMsil);
var methodContent = (IEnumerable<MsilInstruction>)context.Instructions; var methodContent = (IEnumerable<MsilInstruction>)context.Instructions;
foreach (var transpiler in transpilers) foreach (MethodInfo transpiler in transpilers)
methodContent = (IEnumerable<MsilInstruction>)transpiler.Invoke(null, new object[] { methodContent }); {
var paramList = new List<object>();
foreach (var parameter in transpiler.GetParameters())
{
if (parameter.Name.Equals("__methodBody"))
paramList.Add(baseMethod.GetMethodBody());
else if (parameter.Name.Equals("__localCreator"))
paramList.Add(localCreator);
else if (parameter.ParameterType == typeof(IEnumerable<MsilInstruction>))
paramList.Add(methodContent);
else
throw new ArgumentException(
$"Bad transpiler parameter type {parameter.ParameterType.FullName} {parameter.Name}");
}
methodContent = (IEnumerable<MsilInstruction>)transpiler.Invoke(null, paramList.ToArray());
}
methodContent = FixBranchAndReturn(methodContent, retLabel); methodContent = FixBranchAndReturn(methodContent, retLabel);
foreach (var k in methodContent) foreach (var k in methodContent)
k.Emit(output); k.Emit(output);

View File

@@ -165,7 +165,10 @@
<Compile Include="Managers\PatchManager\EmitExtensions.cs" /> <Compile Include="Managers\PatchManager\EmitExtensions.cs" />
<Compile Include="Managers\PatchManager\MSIL\ITokenResolver.cs" /> <Compile Include="Managers\PatchManager\MSIL\ITokenResolver.cs" />
<Compile Include="Managers\PatchManager\MSIL\MsilInstruction.cs" /> <Compile Include="Managers\PatchManager\MSIL\MsilInstruction.cs" />
<Compile Include="Managers\PatchManager\MSIL\MsilInstructionExtensions.cs" />
<Compile Include="Managers\PatchManager\MSIL\MsilLabel.cs" /> <Compile Include="Managers\PatchManager\MSIL\MsilLabel.cs" />
<Compile Include="Managers\PatchManager\MSIL\MsilArgument.cs" />
<Compile Include="Managers\PatchManager\MSIL\MsilLocal.cs" />
<Compile Include="Managers\PatchManager\MSIL\MsilOperand.cs" /> <Compile Include="Managers\PatchManager\MSIL\MsilOperand.cs" />
<Compile Include="Managers\PatchManager\MSIL\MsilOperandBrTarget.cs" /> <Compile Include="Managers\PatchManager\MSIL\MsilOperandBrTarget.cs" />
<Compile Include="Managers\PatchManager\MSIL\MsilOperandInline.cs" /> <Compile Include="Managers\PatchManager\MSIL\MsilOperandInline.cs" />

View File

@@ -74,6 +74,16 @@ namespace Torch.Utils
/// Expected parameters of this method, or null if any parameters are accepted. /// Expected parameters of this method, or null if any parameters are accepted.
/// </summary> /// </summary>
public Type[] Parameters { get; set; } = null; public Type[] Parameters { get; set; } = null;
/// <summary>
/// Assembly qualified names of <see cref="Parameters"/>
/// </summary>
public string[] ParameterNames
{
get => Parameters.Select(x => x.AssemblyQualifiedName).ToArray();
set => Parameters = value?.Select(x => x == null ? null : Type.GetType(x)).ToArray();
}
/// <summary> /// <summary>
/// Expected return type of this method, or null if any return type is accepted. /// Expected return type of this method, or null if any return type is accepted.
/// </summary> /// </summary>