Post transpilers (happen after prefix and suffix)

More debugging options
More robust inline writing and reading
This commit is contained in:
Westin Miller
2017-11-04 10:09:22 -07:00
parent 0bc0b0dc77
commit 600e73ad43
13 changed files with 498 additions and 236 deletions

View File

@@ -29,20 +29,21 @@ namespace Torch.Managers.PatchManager
internal bool HasChanged() internal bool HasChanged()
{ {
return Prefixes.HasChanges() || Suffixes.HasChanges() || Transpilers.HasChanges(); return Prefixes.HasChanges() || Suffixes.HasChanges() || Transpilers.HasChanges() || PostTranspilers.HasChanges();
} }
internal void Commit() internal void Commit()
{ {
try try
{ {
if (!Prefixes.HasChanges(true) && !Suffixes.HasChanges(true) && !Transpilers.HasChanges(true)) // non-greedy so they are all reset
if (!Prefixes.HasChanges(true) & !Suffixes.HasChanges(true) & !Transpilers.HasChanges(true) & !PostTranspilers.HasChanges(true))
return; return;
Revert(); Revert();
if (Prefixes.Count == 0 && Suffixes.Count == 0 && Transpilers.Count == 0) if (Prefixes.Count == 0 && Suffixes.Count == 0 && Transpilers.Count == 0 && PostTranspilers.Count == 0)
return; return;
_log.Debug( _log.Log(PrintMsil ? LogLevel.Info : LogLevel.Debug,
$"Begin patching {_method.DeclaringType?.FullName}#{_method.Name}({string.Join(", ", _method.GetParameters().Select(x => x.ParameterType.Name))})"); $"Begin patching {_method.DeclaringType?.FullName}#{_method.Name}({string.Join(", ", _method.GetParameters().Select(x => x.ParameterType.Name))})");
var patch = ComposePatchedMethod(); var patch = ComposePatchedMethod();
@@ -50,7 +51,7 @@ namespace Torch.Managers.PatchManager
var newAddress = AssemblyMemory.GetMethodBodyStart(patch); var newAddress = AssemblyMemory.GetMethodBodyStart(patch);
_revertData = AssemblyMemory.WriteJump(_revertAddress, newAddress); _revertData = AssemblyMemory.WriteJump(_revertAddress, newAddress);
_pinnedPatch = GCHandle.Alloc(patch); _pinnedPatch = GCHandle.Alloc(patch);
_log.Debug( _log.Log(PrintMsil ? LogLevel.Info : LogLevel.Debug,
$"Done patching {_method.DeclaringType?.FullName}#{_method.Name}({string.Join(", ", _method.GetParameters().Select(x => x.ParameterType.Name))})"); $"Done patching {_method.DeclaringType?.FullName}#{_method.Name}({string.Join(", ", _method.GetParameters().Select(x => x.ParameterType.Name))})");
} }
catch (Exception exception) catch (Exception exception)
@@ -110,100 +111,119 @@ namespace Torch.Managers.PatchManager
public DynamicMethod ComposePatchedMethod() public DynamicMethod ComposePatchedMethod()
{ {
DynamicMethod method = AllocatePatchMethod(); DynamicMethod method = AllocatePatchMethod();
var generator = new LoggingIlGenerator(method.GetILGenerator()); var generator = new LoggingIlGenerator(method.GetILGenerator(), PrintMsil ? LogLevel.Info : LogLevel.Trace);
EmitPatched(generator); List<MsilInstruction> il = EmitPatched((type, pinned) => new MsilLocal(generator.DeclareLocal(type, pinned))).ToList();
if (PrintMsil)
{
lock (_log)
{
MethodTranspiler.IntegrityAnalysis(LogLevel.Info, il);
}
}
MethodTranspiler.EmitMethod(il, generator);
// Force it to compile try
RuntimeMethodHandle handle = _getMethodHandle.Invoke(method); {
object runtimeMethodInfo = _getMethodInfo.Invoke(handle); // Force it to compile
_compileDynamicMethod.Invoke(runtimeMethodInfo); RuntimeMethodHandle handle = _getMethodHandle.Invoke(method);
object runtimeMethodInfo = _getMethodInfo.Invoke(handle);
_compileDynamicMethod.Invoke(runtimeMethodInfo);
}
catch
{
lock (_log)
{
var ctx = new MethodContext(method);
ctx.Read();
MethodTranspiler.IntegrityAnalysis(LogLevel.Warn, ctx.Instructions);
}
throw;
}
return method; return method;
} }
#endregion #endregion
#region Emit #region Emit
private void EmitPatched(LoggingIlGenerator target) private IEnumerable<MsilInstruction> EmitPatched(Func<Type, bool, MsilLocal> declareLocal)
{ {
var originalLocalVariables = _method.GetMethodBody().LocalVariables var methodBody = _method.GetMethodBody();
.Select(x => Debug.Assert(methodBody != null, "Method body is null");
{ foreach (var localVar in methodBody.LocalVariables)
Debug.Assert(x.LocalType != null); {
return target.DeclareLocal(x.LocalType, x.IsPinned); Debug.Assert(localVar.LocalType != null);
}).ToArray(); declareLocal(localVar.LocalType, localVar.IsPinned);
}
var instructions = new List<MsilInstruction>();
var specialVariables = new Dictionary<string, MsilLocal>();
var specialVariables = new Dictionary<string, LocalBuilder>(); var labelAfterOriginalContent = new MsilLabel();
var labelSkipMethodContent = new MsilLabel();
Label labelAfterOriginalContent = target.DefineLabel();
Label labelSkipMethodContent = target.DefineLabel();
Type returnType = _method is MethodInfo meth ? meth.ReturnType : typeof(void); Type returnType = _method is MethodInfo meth ? meth.ReturnType : typeof(void);
LocalBuilder resultVariable = null; MsilLocal resultVariable = null;
if (returnType != typeof(void)) if (returnType != typeof(void))
{ {
if (Prefixes.Concat(Suffixes).SelectMany(x => x.GetParameters()).Any(x => x.Name == RESULT_PARAMETER)) if (Prefixes.Concat(Suffixes).SelectMany(x => x.GetParameters()).Any(x => x.Name == RESULT_PARAMETER)
resultVariable = target.DeclareLocal(returnType); || Prefixes.Any(x => x.ReturnType == typeof(bool)))
else if (Prefixes.Any(x => x.ReturnType == typeof(bool))) resultVariable = declareLocal(returnType, false);
resultVariable = target.DeclareLocal(returnType);
} }
resultVariable?.SetToDefault(target); if (resultVariable != null)
LocalBuilder prefixSkippedVariable = null; instructions.AddRange(resultVariable.SetToDefault());
MsilLocal prefixSkippedVariable = null;
if (Prefixes.Count > 0 && Suffixes.Any(x => x.GetParameters() if (Prefixes.Count > 0 && Suffixes.Any(x => x.GetParameters()
.Any(y => y.Name.Equals(PREFIX_SKIPPED_PARAMETER)))) .Any(y => y.Name.Equals(PREFIX_SKIPPED_PARAMETER))))
{ {
prefixSkippedVariable = target.DeclareLocal(typeof(bool)); prefixSkippedVariable = declareLocal(typeof(bool), false);
specialVariables.Add(PREFIX_SKIPPED_PARAMETER, prefixSkippedVariable); specialVariables.Add(PREFIX_SKIPPED_PARAMETER, prefixSkippedVariable);
} }
if (resultVariable != null) if (resultVariable != null)
specialVariables.Add(RESULT_PARAMETER, resultVariable); specialVariables.Add(RESULT_PARAMETER, resultVariable);
target.EmitComment("Prefixes Begin");
foreach (MethodInfo prefix in Prefixes) foreach (MethodInfo prefix in Prefixes)
{ {
EmitMonkeyCall(target, prefix, specialVariables); instructions.AddRange(EmitMonkeyCall(prefix, specialVariables));
if (prefix.ReturnType == typeof(bool)) if (prefix.ReturnType == typeof(bool))
target.Emit(OpCodes.Brfalse, labelSkipMethodContent); instructions.Add(new MsilInstruction(OpCodes.Brfalse).InlineTarget(labelSkipMethodContent));
else if (prefix.ReturnType != typeof(void)) else if (prefix.ReturnType != typeof(void))
throw new Exception( throw new Exception(
$"Prefixes must return void or bool. {prefix.DeclaringType?.FullName}.{prefix.Name} returns {prefix.ReturnType}"); $"Prefixes must return void or bool. {prefix.DeclaringType?.FullName}.{prefix.Name} returns {prefix.ReturnType}");
} }
target.EmitComment("Prefixes End"); instructions.AddRange(MethodTranspiler.Transpile(_method, (x) => declareLocal(x, false), Transpilers, labelAfterOriginalContent));
target.EmitComment("Original Begin"); instructions.Add(new MsilInstruction(OpCodes.Nop).LabelWith(labelAfterOriginalContent));
MethodTranspiler.Transpile(_method, (type) => new MsilLocal(target.DeclareLocal(type)), Transpilers, target, labelAfterOriginalContent, PrintMsil);
target.EmitComment("Original End");
target.MarkLabel(labelAfterOriginalContent);
if (resultVariable != null) if (resultVariable != null)
target.Emit(OpCodes.Stloc, resultVariable); instructions.Add(new MsilInstruction(OpCodes.Stloc).InlineValue(resultVariable));
Label notSkip = target.DefineLabel(); var notSkip = new MsilLabel();
target.Emit(OpCodes.Br, notSkip); instructions.Add(new MsilInstruction(OpCodes.Br).InlineTarget(notSkip));
target.MarkLabel(labelSkipMethodContent); instructions.Add(new MsilInstruction(OpCodes.Nop).LabelWith(labelSkipMethodContent));
if (prefixSkippedVariable != null) if (prefixSkippedVariable != null)
{ {
target.Emit(OpCodes.Ldc_I4_1); instructions.Add(new MsilInstruction(OpCodes.Ldc_I4_1));
target.Emit(OpCodes.Stloc, prefixSkippedVariable); instructions.Add(new MsilInstruction(OpCodes.Stloc).InlineValue(prefixSkippedVariable));
} }
target.MarkLabel(notSkip); instructions.Add(new MsilInstruction(OpCodes.Nop).LabelWith(notSkip));
target.EmitComment("Suffixes Begin");
foreach (MethodInfo suffix in Suffixes) foreach (MethodInfo suffix in Suffixes)
{ {
EmitMonkeyCall(target, suffix, specialVariables); instructions.AddRange(EmitMonkeyCall(suffix, specialVariables));
if (suffix.ReturnType != typeof(void)) if (suffix.ReturnType != typeof(void))
throw new Exception($"Suffixes must return void. {suffix.DeclaringType?.FullName}.{suffix.Name} returns {suffix.ReturnType}"); throw new Exception($"Suffixes must return void. {suffix.DeclaringType?.FullName}.{suffix.Name} returns {suffix.ReturnType}");
} }
target.EmitComment("Suffixes End");
if (resultVariable != null) if (resultVariable != null)
target.Emit(OpCodes.Ldloc, resultVariable); instructions.Add(new MsilInstruction(OpCodes.Ldloc).InlineValue(resultVariable));
target.Emit(OpCodes.Ret); instructions.Add(new MsilInstruction(OpCodes.Ret));
var result = MethodTranspiler.Transpile(_method, instructions, (x) => declareLocal(x, false), PostTranspilers, null).ToList();
if (result.Last().OpCode != OpCodes.Ret)
result.Add(new MsilInstruction(OpCodes.Ret));
return result;
} }
private void EmitMonkeyCall(LoggingIlGenerator target, MethodInfo patch, private IEnumerable<MsilInstruction> EmitMonkeyCall(MethodInfo patch,
IReadOnlyDictionary<string, LocalBuilder> specialVariables) IReadOnlyDictionary<string, MsilLocal> specialVariables)
{ {
target.EmitComment($"Call {patch.DeclaringType?.FullName}#{patch.Name}");
foreach (var param in patch.GetParameters()) foreach (var param in patch.GetParameters())
{ {
switch (param.Name) switch (param.Name)
@@ -211,25 +231,26 @@ namespace Torch.Managers.PatchManager
case INSTANCE_PARAMETER: case INSTANCE_PARAMETER:
if (_method.IsStatic) if (_method.IsStatic)
throw new Exception("Can't use an instance parameter for a static method"); throw new Exception("Can't use an instance parameter for a static method");
target.Emit(OpCodes.Ldarg_0); yield return new MsilInstruction(OpCodes.Ldarg_0);
break; break;
case PREFIX_SKIPPED_PARAMETER: case PREFIX_SKIPPED_PARAMETER:
if (param.ParameterType != typeof(bool)) if (param.ParameterType != typeof(bool))
throw new Exception($"Prefix skipped parameter {param.ParameterType} must be of type bool"); throw new Exception($"Prefix skipped parameter {param.ParameterType} must be of type bool");
if (param.ParameterType.IsByRef || param.IsOut) if (param.ParameterType.IsByRef || param.IsOut)
throw new Exception($"Prefix skipped parameter {param.ParameterType} can't be a reference type"); throw new Exception($"Prefix skipped parameter {param.ParameterType} can't be a reference type");
if (specialVariables.TryGetValue(PREFIX_SKIPPED_PARAMETER, out LocalBuilder prefixSkip)) if (specialVariables.TryGetValue(PREFIX_SKIPPED_PARAMETER, out MsilLocal prefixSkip))
target.Emit(OpCodes.Ldloc, prefixSkip); yield return new MsilInstruction(OpCodes.Ldloc).InlineValue(prefixSkip);
else else
target.Emit(OpCodes.Ldc_I4_0); yield return new MsilInstruction(OpCodes.Ldc_I4_0);
break; break;
case RESULT_PARAMETER: case RESULT_PARAMETER:
Type retType = param.ParameterType.IsByRef Type retType = param.ParameterType.IsByRef
? param.ParameterType.GetElementType() ? param.ParameterType.GetElementType()
: param.ParameterType; : param.ParameterType;
if (retType == null || !retType.IsAssignableFrom(specialVariables[RESULT_PARAMETER].LocalType)) if (retType == null || !retType.IsAssignableFrom(specialVariables[RESULT_PARAMETER].Type))
throw new Exception($"Return type {specialVariables[RESULT_PARAMETER].LocalType} can't be assigned to result parameter type {retType}"); throw new Exception($"Return type {specialVariables[RESULT_PARAMETER].Type} can't be assigned to result parameter type {retType}");
target.Emit(param.ParameterType.IsByRef ? OpCodes.Ldloca : OpCodes.Ldloc, specialVariables[RESULT_PARAMETER]); yield return new MsilInstruction(param.ParameterType.IsByRef ? OpCodes.Ldloca : OpCodes.Ldloc)
.InlineValue(specialVariables[RESULT_PARAMETER]);
break; break;
default: default:
ParameterInfo declParam = _method.GetParameters().FirstOrDefault(x => x.Name == param.Name); ParameterInfo declParam = _method.GetParameters().FirstOrDefault(x => x.Name == param.Name);
@@ -240,18 +261,18 @@ namespace Torch.Managers.PatchManager
bool patchByRef = param.IsOut || param.ParameterType.IsByRef; bool patchByRef = param.IsOut || param.ParameterType.IsByRef;
bool declByRef = declParam.IsOut || declParam.ParameterType.IsByRef; bool declByRef = declParam.IsOut || declParam.ParameterType.IsByRef;
if (patchByRef == declByRef) if (patchByRef == declByRef)
target.Emit(OpCodes.Ldarg, paramIdx); yield return new MsilInstruction(OpCodes.Ldarg).InlineValue(new MsilArgument(paramIdx));
else if (patchByRef) else if (patchByRef)
target.Emit(OpCodes.Ldarga, paramIdx); yield return new MsilInstruction(OpCodes.Ldarga).InlineValue(new MsilArgument(paramIdx));
else else
{ {
target.Emit(OpCodes.Ldarg, paramIdx); yield return new MsilInstruction(OpCodes.Ldarg).InlineValue(new MsilArgument(paramIdx));
target.EmitDereference(declParam.ParameterType); yield return EmitExtensions.EmitDereference(declParam.ParameterType);
} }
break; break;
} }
} }
target.Emit(OpCodes.Call, patch); yield return new MsilInstruction(OpCodes.Call).InlineValue(patch);
} }
#endregion #endregion
} }

View File

@@ -1,6 +1,8 @@
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Reflection.Emit; using System.Reflection.Emit;
using Torch.Managers.PatchManager.MSIL;
using Torch.Managers.PatchManager.Transpile; using Torch.Managers.PatchManager.Transpile;
namespace Torch.Managers.PatchManager namespace Torch.Managers.PatchManager
@@ -11,65 +13,64 @@ namespace Torch.Managers.PatchManager
/// Sets the given local to its default value in the given IL generator. /// Sets the given local to its default value in the given IL generator.
/// </summary> /// </summary>
/// <param name="local">Local to set to default</param> /// <param name="local">Local to set to default</param>
/// <param name="target">The IL generator</param> /// <returns>Instructions</returns>
public static void SetToDefault(this LocalBuilder local, LoggingIlGenerator target) public static IEnumerable<MsilInstruction> SetToDefault(this MsilLocal local)
{ {
Debug.Assert(local.LocalType != null); Debug.Assert(local.Type != null);
if (local.LocalType.IsEnum || local.LocalType.IsPrimitive) if (local.Type.IsEnum || local.Type.IsPrimitive)
{ {
if (local.LocalType == typeof(float)) if (local.Type == typeof(float))
target.Emit(OpCodes.Ldc_R4, 0f); yield return new MsilInstruction(OpCodes.Ldc_R4).InlineValue(0f);
else if (local.LocalType == typeof(double)) else if (local.Type == typeof(double))
target.Emit(OpCodes.Ldc_R8, 0d); yield return new MsilInstruction(OpCodes.Ldc_R8).InlineValue(0d);
else if (local.LocalType == typeof(long) || local.LocalType == typeof(ulong)) else if (local.Type == typeof(long) || local.Type == typeof(ulong))
target.Emit(OpCodes.Ldc_I8, 0L); yield return new MsilInstruction(OpCodes.Ldc_I8).InlineValue(0L);
else else
target.Emit(OpCodes.Ldc_I4, 0); yield return new MsilInstruction(OpCodes.Ldc_I4).InlineValue(0);
target.Emit(OpCodes.Stloc, local); yield return new MsilInstruction(OpCodes.Stloc).InlineValue(local);
} }
else if (local.LocalType.IsValueType) // struct else if (local.Type.IsValueType) // struct
{ {
target.Emit(OpCodes.Ldloca, local); yield return new MsilInstruction(OpCodes.Ldloca).InlineValue(local);
target.Emit(OpCodes.Initobj, local.LocalType); yield return new MsilInstruction(OpCodes.Initobj).InlineValue(local.Type);
} }
else // class else // class
{ {
target.Emit(OpCodes.Ldnull); yield return new MsilInstruction(OpCodes.Ldnull);
target.Emit(OpCodes.Stloc, local); yield return new MsilInstruction(OpCodes.Stloc).InlineValue(local);
} }
} }
/// <summary> /// <summary>
/// Emits a dereference for the given type. /// Emits a dereference for the given type.
/// </summary> /// </summary>
/// <param name="target">IL Generator to emit on</param>
/// <param name="type">Type to dereference</param> /// <param name="type">Type to dereference</param>
public static void EmitDereference(this LoggingIlGenerator target, Type type) /// <returns>Derference instruction</returns>
public static MsilInstruction EmitDereference(Type type)
{ {
if (type.IsByRef) if (type.IsByRef)
type = type.GetElementType(); type = type.GetElementType();
Debug.Assert(type != null); Debug.Assert(type != null);
if (type == typeof(float)) if (type == typeof(float))
target.Emit(OpCodes.Ldind_R4); return new MsilInstruction(OpCodes.Ldind_R4);
else if (type == typeof(double)) if (type == typeof(double))
target.Emit(OpCodes.Ldind_R8); return new MsilInstruction(OpCodes.Ldind_R8);
else if (type == typeof(byte)) if (type == typeof(byte))
target.Emit(OpCodes.Ldind_U1); return new MsilInstruction(OpCodes.Ldind_U1);
else if (type == typeof(ushort) || type == typeof(char)) if (type == typeof(ushort) || type == typeof(char))
target.Emit(OpCodes.Ldind_U2); return new MsilInstruction(OpCodes.Ldind_U2);
else if (type == typeof(uint)) if (type == typeof(uint))
target.Emit(OpCodes.Ldind_U4); return new MsilInstruction(OpCodes.Ldind_U4);
else if (type == typeof(sbyte)) if (type == typeof(sbyte))
target.Emit(OpCodes.Ldind_I1); return new MsilInstruction(OpCodes.Ldind_I1);
else if (type == typeof(short)) if (type == typeof(short))
target.Emit(OpCodes.Ldind_I2); return new MsilInstruction(OpCodes.Ldind_I2);
else if (type == typeof(int) || type.IsEnum) if (type == typeof(int) || type.IsEnum)
target.Emit(OpCodes.Ldind_I4); return new MsilInstruction(OpCodes.Ldind_I4);
else if (type == typeof(long) || type == typeof(ulong)) if (type == typeof(long) || type == typeof(ulong))
target.Emit(OpCodes.Ldind_I8); return new MsilInstruction(OpCodes.Ldind_I8);
else return new MsilInstruction(OpCodes.Ldind_Ref);
target.Emit(OpCodes.Ldind_Ref);
} }
} }
} }

View File

@@ -62,7 +62,7 @@ namespace Torch.Managers.PatchManager.MSIL
{ {
internal static readonly NullTokenResolver Instance = new NullTokenResolver(); internal static readonly NullTokenResolver Instance = new NullTokenResolver();
private NullTokenResolver() internal NullTokenResolver()
{ {
} }

View File

@@ -34,6 +34,7 @@ namespace Torch.Managers.PatchManager.MSIL
case OperandType.InlineField: case OperandType.InlineField:
Operand = new MsilOperandInline.MsilOperandReflected<FieldInfo>(this); Operand = new MsilOperandInline.MsilOperandReflected<FieldInfo>(this);
break; break;
case OperandType.ShortInlineI:
case OperandType.InlineI: case OperandType.InlineI:
Operand = new MsilOperandInline.MsilOperandInt32(this); Operand = new MsilOperandInline.MsilOperandInt32(this);
break; break;
@@ -63,16 +64,11 @@ namespace Torch.Managers.PatchManager.MSIL
break; break;
case OperandType.ShortInlineVar: case OperandType.ShortInlineVar:
case OperandType.InlineVar: case OperandType.InlineVar:
if (OpCode.Name.IndexOf("loc", StringComparison.OrdinalIgnoreCase) != -1) if (OpCode.IsLocalStore() || OpCode.IsLocalLoad() || OpCode.IsLocalLoadByRef())
Operand = new MsilOperandInline.MsilOperandLocal(this); Operand = new MsilOperandInline.MsilOperandLocal(this);
else else
Operand = new MsilOperandInline.MsilOperandArgument(this); Operand = new MsilOperandInline.MsilOperandArgument(this);
break; break;
case OperandType.ShortInlineI:
Operand = OpCode == OpCodes.Ldc_I4_S
? (MsilOperand)new MsilOperandInline.MsilOperandInt8(this)
: new MsilOperandInline.MsilOperandUInt8(this);
break;
case OperandType.ShortInlineR: case OperandType.ShortInlineR:
Operand = new MsilOperandInline.MsilOperandSingle(this); Operand = new MsilOperandInline.MsilOperandSingle(this);
break; break;
@@ -206,6 +202,8 @@ namespace Torch.Managers.PatchManager.MSIL
Operand is MsilOperandInline<MethodBase> inline) Operand is MsilOperandInline<MethodBase> inline)
{ {
MethodBase op = inline.Value; MethodBase op = inline.Value;
if (op == null)
return num;
if (op is MethodInfo mi && mi.ReturnType != typeof(void)) if (op is MethodInfo mi && mi.ReturnType != typeof(void))
num++; num++;
num -= op.GetParameters().Length; num -= op.GetParameters().Length;

View File

@@ -19,8 +19,7 @@ namespace Torch.Managers.PatchManager.MSIL
/// </summary> /// </summary>
public static bool IsLocalLoad(this MsilInstruction me) public static bool IsLocalLoad(this MsilInstruction me)
{ {
return me.OpCode == OpCodes.Ldloc || me.OpCode == OpCodes.Ldloc_S || me.OpCode == OpCodes.Ldloc_0 || return me.OpCode.IsLocalLoad();
me.OpCode == OpCodes.Ldloc_1 || me.OpCode == OpCodes.Ldloc_2 || me.OpCode == OpCodes.Ldloc_3;
} }
/// <summary> /// <summary>
@@ -28,7 +27,7 @@ namespace Torch.Managers.PatchManager.MSIL
/// </summary> /// </summary>
public static bool IsLocalLoadByRef(this MsilInstruction me) public static bool IsLocalLoadByRef(this MsilInstruction me)
{ {
return me.OpCode == OpCodes.Ldloca || me.OpCode == OpCodes.Ldloca_S; return me.OpCode.IsLocalLoadByRef();
} }
/// <summary> /// <summary>
@@ -36,8 +35,33 @@ namespace Torch.Managers.PatchManager.MSIL
/// </summary> /// </summary>
public static bool IsLocalStore(this MsilInstruction me) public static bool IsLocalStore(this MsilInstruction me)
{ {
return me.OpCode == OpCodes.Stloc || me.OpCode == OpCodes.Stloc_S || me.OpCode == OpCodes.Stloc_0 || return me.OpCode.IsLocalStore();
me.OpCode == OpCodes.Stloc_1 || me.OpCode == OpCodes.Stloc_2 || me.OpCode == OpCodes.Stloc_3; }
/// <summary>
/// Is this instruction a local load-by-value instruction.
/// </summary>
public static bool IsLocalLoad(this OpCode opcode)
{
return opcode == OpCodes.Ldloc || opcode == OpCodes.Ldloc_S || opcode == OpCodes.Ldloc_0 ||
opcode == OpCodes.Ldloc_1 || opcode == OpCodes.Ldloc_2 || opcode == OpCodes.Ldloc_3;
}
/// <summary>
/// Is this instruction a local load-by-reference instruction.
/// </summary>
public static bool IsLocalLoadByRef(this OpCode opcode)
{
return opcode == OpCodes.Ldloca || opcode == OpCodes.Ldloca_S;
}
/// <summary>
/// Is this instruction a local store instruction.
/// </summary>
public static bool IsLocalStore(this OpCode opcode)
{
return opcode == OpCodes.Stloc || opcode == OpCodes.Stloc_S || opcode == OpCodes.Stloc_0 ||
opcode == OpCodes.Stloc_1 || opcode == OpCodes.Stloc_2 || opcode == OpCodes.Stloc_3;
} }
/// <summary> /// <summary>

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Reflection.Emit; using System.Reflection.Emit;

View File

@@ -21,15 +21,40 @@ namespace Torch.Managers.PatchManager.MSIL
internal override void Read(MethodContext context, BinaryReader reader) internal override void Read(MethodContext context, BinaryReader reader)
{ {
int val = Instruction.OpCode.OperandType == OperandType.InlineBrTarget
? reader.ReadInt32() long offset;
: reader.ReadSByte();
Target = context.LabelAt((int)reader.BaseStream.Position + val); // ReSharper disable once SwitchStatementMissingSomeCases
switch (Instruction.OpCode.OperandType)
{
case OperandType.ShortInlineBrTarget:
offset = reader.ReadSByte();
break;
case OperandType.InlineBrTarget:
offset = reader.ReadInt32();
break;
default:
throw new InvalidBranchException(
$"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}");
}
Target = context.LabelAt((int)(reader.BaseStream.Position + offset));
} }
internal override void Emit(LoggingIlGenerator generator) internal override void Emit(LoggingIlGenerator generator)
{ {
generator.Emit(Instruction.OpCode, Target.LabelFor(generator)); // ReSharper disable once SwitchStatementMissingSomeCases
switch (Instruction.OpCode.OperandType)
{
case OperandType.ShortInlineBrTarget:
case OperandType.InlineBrTarget:
generator.Emit(Instruction.OpCode, Target.LabelFor(generator));
break;
default:
throw new InvalidBranchException(
$"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}");
}
} }
internal override void CopyTo(MsilOperand operand) internal override void CopyTo(MsilOperand operand)

View File

@@ -5,6 +5,7 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using System.Reflection.Emit; using System.Reflection.Emit;
using Torch.Managers.PatchManager.Transpile; using Torch.Managers.PatchManager.Transpile;
using Torch.Utils;
namespace Torch.Managers.PatchManager.MSIL namespace Torch.Managers.PatchManager.MSIL
{ {
@@ -44,47 +45,6 @@ namespace Torch.Managers.PatchManager.MSIL
/// </summary> /// </summary>
public static class MsilOperandInline public static class MsilOperandInline
{ {
/// <summary>
/// Inline unsigned byte
/// </summary>
public class MsilOperandUInt8 : MsilOperandInline<byte>
{
internal MsilOperandUInt8(MsilInstruction instruction) : base(instruction)
{
}
internal override void Read(MethodContext context, BinaryReader reader)
{
Value = reader.ReadByte();
}
internal override void Emit(LoggingIlGenerator generator)
{
generator.Emit(Instruction.OpCode, Value);
}
}
/// <summary>
/// Inline signed byte
/// </summary>
public class MsilOperandInt8 : MsilOperandInline<sbyte>
{
internal MsilOperandInt8(MsilInstruction instruction) : base(instruction)
{
}
internal override void Read(MethodContext context, BinaryReader reader)
{
Value =
(sbyte)reader.ReadByte();
}
internal override void Emit(LoggingIlGenerator generator)
{
generator.Emit(Instruction.OpCode, Value);
}
}
/// <summary> /// <summary>
/// Inline integer /// Inline integer
/// </summary> /// </summary>
@@ -96,12 +56,36 @@ namespace Torch.Managers.PatchManager.MSIL
internal override void Read(MethodContext context, BinaryReader reader) internal override void Read(MethodContext context, BinaryReader reader)
{ {
Value = reader.ReadInt32(); // ReSharper disable once SwitchStatementMissingSomeCases
switch (Instruction.OpCode.OperandType)
{
case OperandType.ShortInlineI:
Value = reader.ReadByte();
return;
case OperandType.InlineI:
Value = reader.ReadInt32();
return;
default:
throw new InvalidBranchException(
$"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}");
}
} }
internal override void Emit(LoggingIlGenerator generator) internal override void Emit(LoggingIlGenerator generator)
{ {
generator.Emit(Instruction.OpCode, Value); // ReSharper disable once SwitchStatementMissingSomeCases
switch (Instruction.OpCode.OperandType)
{
case OperandType.ShortInlineI:
generator.Emit(Instruction.OpCode, (byte)Value);
return;
case OperandType.InlineI:
generator.Emit(Instruction.OpCode, Value);
return;
default:
throw new InvalidBranchException(
$"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}");
}
} }
} }
@@ -116,12 +100,30 @@ namespace Torch.Managers.PatchManager.MSIL
internal override void Read(MethodContext context, BinaryReader reader) internal override void Read(MethodContext context, BinaryReader reader)
{ {
Value = reader.ReadSingle(); // ReSharper disable once SwitchStatementMissingSomeCases
switch (Instruction.OpCode.OperandType)
{
case OperandType.ShortInlineR:
Value = reader.ReadSingle();
return;
default:
throw new InvalidBranchException(
$"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}");
}
} }
internal override void Emit(LoggingIlGenerator generator) internal override void Emit(LoggingIlGenerator generator)
{ {
generator.Emit(Instruction.OpCode, Value); // ReSharper disable once SwitchStatementMissingSomeCases
switch (Instruction.OpCode.OperandType)
{
case OperandType.ShortInlineR:
generator.Emit(Instruction.OpCode, Value);
return;
default:
throw new InvalidBranchException(
$"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}");
}
} }
} }
@@ -136,12 +138,30 @@ namespace Torch.Managers.PatchManager.MSIL
internal override void Read(MethodContext context, BinaryReader reader) internal override void Read(MethodContext context, BinaryReader reader)
{ {
Value = reader.ReadDouble(); // ReSharper disable once SwitchStatementMissingSomeCases
switch (Instruction.OpCode.OperandType)
{
case OperandType.InlineR:
Value = reader.ReadDouble();
return;
default:
throw new InvalidBranchException(
$"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}");
}
} }
internal override void Emit(LoggingIlGenerator generator) internal override void Emit(LoggingIlGenerator generator)
{ {
generator.Emit(Instruction.OpCode, Value); // ReSharper disable once SwitchStatementMissingSomeCases
switch (Instruction.OpCode.OperandType)
{
case OperandType.InlineR:
generator.Emit(Instruction.OpCode, Value);
return;
default:
throw new InvalidBranchException(
$"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}");
}
} }
} }
@@ -156,12 +176,30 @@ namespace Torch.Managers.PatchManager.MSIL
internal override void Read(MethodContext context, BinaryReader reader) internal override void Read(MethodContext context, BinaryReader reader)
{ {
Value = reader.ReadInt64(); // ReSharper disable once SwitchStatementMissingSomeCases
switch (Instruction.OpCode.OperandType)
{
case OperandType.InlineI8:
Value = reader.ReadInt64();
return;
default:
throw new InvalidBranchException(
$"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}");
}
} }
internal override void Emit(LoggingIlGenerator generator) internal override void Emit(LoggingIlGenerator generator)
{ {
generator.Emit(Instruction.OpCode, Value); // ReSharper disable once SwitchStatementMissingSomeCases
switch (Instruction.OpCode.OperandType)
{
case OperandType.InlineI8:
generator.Emit(Instruction.OpCode, Value);
return;
default:
throw new InvalidBranchException(
$"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}");
}
} }
} }
@@ -176,13 +214,30 @@ namespace Torch.Managers.PatchManager.MSIL
internal override void Read(MethodContext context, BinaryReader reader) internal override void Read(MethodContext context, BinaryReader reader)
{ {
Value = // ReSharper disable once SwitchStatementMissingSomeCases
context.TokenResolver.ResolveString(reader.ReadInt32()); switch (Instruction.OpCode.OperandType)
{
case OperandType.InlineString:
Value = context.TokenResolver.ResolveString(reader.ReadInt32());
return;
default:
throw new InvalidBranchException(
$"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}");
}
} }
internal override void Emit(LoggingIlGenerator generator) internal override void Emit(LoggingIlGenerator generator)
{ {
generator.Emit(Instruction.OpCode, Value); // ReSharper disable once SwitchStatementMissingSomeCases
switch (Instruction.OpCode.OperandType)
{
case OperandType.InlineString:
generator.Emit(Instruction.OpCode, Value);
return;
default:
throw new InvalidBranchException(
$"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}");
}
} }
} }
@@ -197,14 +252,28 @@ namespace Torch.Managers.PatchManager.MSIL
internal override void Read(MethodContext context, BinaryReader reader) internal override void Read(MethodContext context, BinaryReader reader)
{ {
byte[] sig = context.TokenResolver // ReSharper disable once SwitchStatementMissingSomeCases
.ResolveSignature(reader.ReadInt32()); switch (Instruction.OpCode.OperandType)
throw new ArgumentException("Can't figure out how to convert this."); {
case OperandType.InlineSig:
throw new NotImplementedException();
default:
throw new InvalidBranchException(
$"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}");
}
} }
internal override void Emit(LoggingIlGenerator generator) internal override void Emit(LoggingIlGenerator generator)
{ {
generator.Emit(Instruction.OpCode, Value); // ReSharper disable once SwitchStatementMissingSomeCases
switch (Instruction.OpCode.OperandType)
{
case OperandType.InlineSig:
throw new NotImplementedException();
default:
throw new InvalidBranchException(
$"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}");
}
} }
} }
@@ -219,18 +288,45 @@ namespace Torch.Managers.PatchManager.MSIL
internal override void Read(MethodContext context, BinaryReader reader) internal override void Read(MethodContext context, BinaryReader reader)
{ {
int paramID = int id;
Instruction.OpCode.OperandType == OperandType.ShortInlineVar // ReSharper disable once SwitchStatementMissingSomeCases
? reader.ReadByte() switch (Instruction.OpCode.OperandType)
: reader.ReadUInt16(); {
if (paramID == 0 && !context.Method.IsStatic) case OperandType.ShortInlineVar:
id = reader.ReadByte();
break;
case OperandType.InlineVar:
id = reader.ReadUInt16();
break;
default:
throw new InvalidBranchException(
$"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}");
}
if (id == 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 = new MsilArgument(context.Method.GetParameters()[paramID - (context.Method.IsStatic ? 0 : 1)]); // ReSharper disable once ConvertIfStatementToConditionalTernaryExpression
if (context.Method == null)
Value = new MsilArgument(id);
else
Value = new MsilArgument(context.Method.GetParameters()[id - (context.Method.IsStatic ? 0 : 1)]);
} }
internal override void Emit(LoggingIlGenerator generator) internal override void Emit(LoggingIlGenerator generator)
{ {
generator.Emit(Instruction.OpCode, Value.Position); // ReSharper disable once SwitchStatementMissingSomeCases
switch (Instruction.OpCode.OperandType)
{
case OperandType.ShortInlineVar:
generator.Emit(Instruction.OpCode, (byte) Value.Position);
break;
case OperandType.InlineVar:
generator.Emit(Instruction.OpCode, (short)Value.Position);
break;
default:
throw new InvalidBranchException(
$"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}");
}
} }
} }
@@ -245,16 +341,42 @@ namespace Torch.Managers.PatchManager.MSIL
internal override void Read(MethodContext context, BinaryReader reader) internal override void Read(MethodContext context, BinaryReader reader)
{ {
Value = int id;
new MsilLocal(context.Method.GetMethodBody().LocalVariables[ // ReSharper disable once SwitchStatementMissingSomeCases
Instruction.OpCode.OperandType == OperandType.ShortInlineVar switch (Instruction.OpCode.OperandType)
? reader.ReadByte() {
: reader.ReadUInt16()]); case OperandType.ShortInlineVar:
id = reader.ReadByte();
break;
case OperandType.InlineVar:
id = reader.ReadUInt16();
break;
default:
throw new InvalidBranchException(
$"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}");
}
// ReSharper disable once ConvertIfStatementToConditionalTernaryExpression
if (context.MethodBody == null)
Value = new MsilLocal(id);
else
Value = new MsilLocal(context.MethodBody.LocalVariables[id]);
} }
internal override void Emit(LoggingIlGenerator generator) internal override void Emit(LoggingIlGenerator generator)
{ {
generator.Emit(Instruction.OpCode, Value.Index); // ReSharper disable once SwitchStatementMissingSomeCases
switch (Instruction.OpCode.OperandType)
{
case OperandType.ShortInlineVar:
generator.Emit(Instruction.OpCode, (byte)Value.Index);
break;
case OperandType.InlineVar:
generator.Emit(Instruction.OpCode, (short)Value.Index);
break;
default:
throw new InvalidBranchException(
$"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}");
}
} }
} }
@@ -286,16 +408,40 @@ namespace Torch.Managers.PatchManager.MSIL
value = context.TokenResolver.ResolveField(reader.ReadInt32()); value = context.TokenResolver.ResolveField(reader.ReadInt32());
break; break;
default: default:
throw new ArgumentException("Reflected operand only applies to inline reflected types"); throw new InvalidBranchException(
$"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}");
} }
if (value is TY vty) if (value is TY vty)
Value = vty; Value = vty;
else if (value == null)
Value = null;
else else
throw new Exception($"Expected type {typeof(TY).Name} from operand {Instruction.OpCode.OperandType}, got {value.GetType()?.Name ?? "null"}"); throw new Exception($"Expected type {typeof(TY).Name} from operand {Instruction.OpCode.OperandType}, got {value.GetType()?.Name ?? "null"}");
} }
internal override void Emit(LoggingIlGenerator generator) internal override void Emit(LoggingIlGenerator generator)
{ {
switch (Instruction.OpCode.OperandType)
{
case OperandType.InlineTok:
Debug.Assert(Value is MethodBase || Value is Type || Value is FieldInfo,
$"Value {Value?.GetType()} doesn't match operand type");
break;
case OperandType.InlineType:
Debug.Assert(Value is Type, $"Value {Value?.GetType()} doesn't match operand type");
break;
case OperandType.InlineMethod:
Debug.Assert(Value is MethodBase, $"Value {Value?.GetType()} doesn't match operand type");
break;
case OperandType.InlineField:
Debug.Assert(Value is FieldInfo, $"Value {Value?.GetType()} doesn't match operand type");
break;
default:
throw new InvalidBranchException(
$"OpCode {Instruction.OpCode}, operand type {Instruction.OpCode.OperandType} doesn't match {GetType().Name}");
}
if (Value is ConstructorInfo) if (Value is ConstructorInfo)
generator.Emit(Instruction.OpCode, Value as ConstructorInfo); generator.Emit(Instruction.OpCode, Value as ConstructorInfo);
else if (Value is FieldInfo) else if (Value is FieldInfo)

View File

@@ -156,12 +156,17 @@ namespace Torch.Managers.PatchManager
/// </summary> /// </summary>
public MethodRewriteSet Transpilers { get; } public MethodRewriteSet Transpilers { get; }
/// <summary> /// <summary>
/// Methods capable of accepting one <see cref="IEnumerable{MsilInstruction}"/> and returing another, modified.
/// Runs after prefixes, suffixes, and normal transpilers are applied.
/// </summary>
public MethodRewriteSet PostTranspilers { get; }
/// <summary>
/// Methods run after the original method has run. /// Methods run after the original method has run.
/// </summary> /// </summary>
public MethodRewriteSet Suffixes { get; } public MethodRewriteSet Suffixes { get; }
/// <summary> /// <summary>
/// Should the resulting MSIL of this patch operation be printed. /// Should the resulting MSIL of the transpile operation be printed.
/// </summary> /// </summary>
public bool PrintMsil public bool PrintMsil
{ {
@@ -174,8 +179,8 @@ namespace Torch.Managers.PatchManager
_printMsilBacking = value; _printMsilBacking = value;
} }
} }
private bool _printMsilBacking; private bool _printMsilBacking;
private readonly MethodRewritePattern _parent; private readonly MethodRewritePattern _parent;
/// <summary> /// <summary>
@@ -186,6 +191,7 @@ namespace Torch.Managers.PatchManager
{ {
Prefixes = new MethodRewriteSet(parentPattern?.Prefixes); Prefixes = new MethodRewriteSet(parentPattern?.Prefixes);
Transpilers = new MethodRewriteSet(parentPattern?.Transpilers); Transpilers = new MethodRewriteSet(parentPattern?.Transpilers);
PostTranspilers = new MethodRewriteSet(parentPattern?.PostTranspilers);
Suffixes = new MethodRewriteSet(parentPattern?.Suffixes); Suffixes = new MethodRewriteSet(parentPattern?.Suffixes);
_parent = parentPattern; _parent = parentPattern;
} }

View File

@@ -177,7 +177,9 @@ namespace Torch.Managers.PatchManager
_finishedPatchCount = 0; _finishedPatchCount = 0;
_dirtyPatchCount = _rewritePatterns.Values.Sum(x => x.HasChanged() ? 1 : 0); _dirtyPatchCount = _rewritePatterns.Values.Sum(x => x.HasChanged() ? 1 : 0);
#if true #if true
ParallelTasks.Parallel.ForEach(_rewritePatterns.Values, DoCommit); ParallelTasks.Parallel.ForEach(_rewritePatterns.Values.Where(x => !x.PrintMsil), DoCommit);
foreach (DecoratedMethod m in _rewritePatterns.Values.Where(x => x.PrintMsil))
DoCommit(m);
#else #else
foreach (DecoratedMethod m in _rewritePatterns.Values) foreach (DecoratedMethod m in _rewritePatterns.Values)
DoCommit(m); DoCommit(m);

View File

@@ -24,20 +24,23 @@ namespace Torch.Managers.PatchManager.Transpile
/// </summary> /// </summary>
public ILGenerator Backing { get; } public ILGenerator Backing { get; }
private readonly LogLevel _level;
/// <summary> /// <summary>
/// Creates a new logging IL generator backed by the given generator. /// Creates a new logging IL generator backed by the given generator.
/// </summary> /// </summary>
/// <param name="backing">Backing generator</param> /// <param name="backing">Backing generator</param>
public LoggingIlGenerator(ILGenerator backing) public LoggingIlGenerator(ILGenerator backing, LogLevel level)
{ {
Backing = backing; Backing = backing;
_level = level;
} }
/// <inheritdoc cref="ILGenerator.DeclareLocal(Type, bool)"/> /// <inheritdoc cref="ILGenerator.DeclareLocal(Type, bool)"/>
public LocalBuilder DeclareLocal(Type localType, bool isPinned = false) public LocalBuilder DeclareLocal(Type localType, bool isPinned = false)
{ {
LocalBuilder res = Backing.DeclareLocal(localType, isPinned); LocalBuilder res = Backing.DeclareLocal(localType, isPinned);
_log?.Trace($"DclLoc\t{res.LocalIndex}\t=> {res.LocalType} {res.IsPinned}"); _log?.Log(_level, $"DclLoc\t{res.LocalIndex}\t=> {res.LocalType} {res.IsPinned}");
return res; return res;
} }
@@ -45,70 +48,84 @@ namespace Torch.Managers.PatchManager.Transpile
/// <inheritdoc cref="ILGenerator.Emit(OpCode)"/> /// <inheritdoc cref="ILGenerator.Emit(OpCode)"/>
public void Emit(OpCode op) public void Emit(OpCode op)
{ {
_log?.Trace($"Emit\t{op,_opcodePadding}"); _log?.Log(_level, $"Emit\t{op,_opcodePadding}");
Backing.Emit(op); Backing.Emit(op);
} }
/// <inheritdoc cref="ILGenerator.Emit(OpCode, LocalBuilder)"/> /// <inheritdoc cref="ILGenerator.Emit(OpCode, LocalBuilder)"/>
public void Emit(OpCode op, LocalBuilder arg) public void Emit(OpCode op, LocalBuilder arg)
{ {
_log?.Trace($"Emit\t{op,_opcodePadding} Local:{arg.LocalIndex}/{arg.LocalType}"); _log?.Log(_level, $"Emit\t{op,_opcodePadding} Local:{arg.LocalIndex}/{arg.LocalType}");
Backing.Emit(op, arg);
}
/// <inheritdoc cref="ILGenerator.Emit(OpCode, byte)"/>
public void Emit(OpCode op, byte arg)
{
_log?.Log(_level, $"Emit\t{op,_opcodePadding} {arg}");
Backing.Emit(op, arg);
}
/// <inheritdoc cref="ILGenerator.Emit(OpCode, int)"/>
public void Emit(OpCode op, short arg)
{
_log?.Log(_level, $"Emit\t{op,_opcodePadding} {arg}");
Backing.Emit(op, arg); Backing.Emit(op, arg);
} }
/// <inheritdoc cref="ILGenerator.Emit(OpCode, int)"/> /// <inheritdoc cref="ILGenerator.Emit(OpCode, int)"/>
public void Emit(OpCode op, int arg) public void Emit(OpCode op, int arg)
{ {
_log?.Trace($"Emit\t{op,_opcodePadding} {arg}"); _log?.Log(_level, $"Emit\t{op,_opcodePadding} {arg}");
Backing.Emit(op, arg); Backing.Emit(op, arg);
} }
/// <inheritdoc cref="ILGenerator.Emit(OpCode, long)"/> /// <inheritdoc cref="ILGenerator.Emit(OpCode, long)"/>
public void Emit(OpCode op, long arg) public void Emit(OpCode op, long arg)
{ {
_log?.Trace($"Emit\t{op,_opcodePadding} {arg}"); _log?.Log(_level, $"Emit\t{op,_opcodePadding} {arg}");
Backing.Emit(op, arg); Backing.Emit(op, arg);
} }
/// <inheritdoc cref="ILGenerator.Emit(OpCode, float)"/> /// <inheritdoc cref="ILGenerator.Emit(OpCode, float)"/>
public void Emit(OpCode op, float arg) public void Emit(OpCode op, float arg)
{ {
_log?.Trace($"Emit\t{op,_opcodePadding} {arg}"); _log?.Log(_level, $"Emit\t{op,_opcodePadding} {arg}");
Backing.Emit(op, arg); Backing.Emit(op, arg);
} }
/// <inheritdoc cref="ILGenerator.Emit(OpCode, double)"/> /// <inheritdoc cref="ILGenerator.Emit(OpCode, double)"/>
public void Emit(OpCode op, double arg) public void Emit(OpCode op, double arg)
{ {
_log?.Trace($"Emit\t{op,_opcodePadding} {arg}"); _log?.Log(_level, $"Emit\t{op,_opcodePadding} {arg}");
Backing.Emit(op, arg); Backing.Emit(op, arg);
} }
/// <inheritdoc cref="ILGenerator.Emit(OpCode, string)"/> /// <inheritdoc cref="ILGenerator.Emit(OpCode, string)"/>
public void Emit(OpCode op, string arg) public void Emit(OpCode op, string arg)
{ {
_log?.Trace($"Emit\t{op,_opcodePadding} {arg}"); _log?.Log(_level, $"Emit\t{op,_opcodePadding} {arg}");
Backing.Emit(op, arg); Backing.Emit(op, arg);
} }
/// <inheritdoc cref="ILGenerator.Emit(OpCode, Type)"/> /// <inheritdoc cref="ILGenerator.Emit(OpCode, Type)"/>
public void Emit(OpCode op, Type arg) public void Emit(OpCode op, Type arg)
{ {
_log?.Trace($"Emit\t{op,_opcodePadding} {arg}"); _log?.Log(_level, $"Emit\t{op,_opcodePadding} {arg}");
Backing.Emit(op, arg); Backing.Emit(op, arg);
} }
/// <inheritdoc cref="ILGenerator.Emit(OpCode, FieldInfo)"/> /// <inheritdoc cref="ILGenerator.Emit(OpCode, FieldInfo)"/>
public void Emit(OpCode op, FieldInfo arg) public void Emit(OpCode op, FieldInfo arg)
{ {
_log?.Trace($"Emit\t{op,_opcodePadding} {arg}"); _log?.Log(_level, $"Emit\t{op,_opcodePadding} {arg}");
Backing.Emit(op, arg); Backing.Emit(op, arg);
} }
/// <inheritdoc cref="ILGenerator.Emit(OpCode, MethodInfo)"/> /// <inheritdoc cref="ILGenerator.Emit(OpCode, MethodInfo)"/>
public void Emit(OpCode op, MethodInfo arg) public void Emit(OpCode op, MethodInfo arg)
{ {
_log?.Trace($"Emit\t{op,_opcodePadding} {arg}"); _log?.Log(_level, $"Emit\t{op,_opcodePadding} {arg}");
Backing.Emit(op, arg); Backing.Emit(op, arg);
} }
@@ -121,28 +138,28 @@ namespace Torch.Managers.PatchManager.Transpile
/// <inheritdoc cref="ILGenerator.Emit(OpCode, Label)"/> /// <inheritdoc cref="ILGenerator.Emit(OpCode, Label)"/>
public void Emit(OpCode op, Label arg) public void Emit(OpCode op, Label arg)
{ {
_log?.Trace($"Emit\t{op,_opcodePadding}\tL:{_labelID.Invoke(arg)}"); _log?.Log(_level, $"Emit\t{op,_opcodePadding}\tL:{_labelID.Invoke(arg)}");
Backing.Emit(op, arg); Backing.Emit(op, arg);
} }
/// <inheritdoc cref="ILGenerator.Emit(OpCode, Label[])"/> /// <inheritdoc cref="ILGenerator.Emit(OpCode, Label[])"/>
public void Emit(OpCode op, Label[] arg) public void Emit(OpCode op, Label[] arg)
{ {
_log?.Trace($"Emit\t{op,_opcodePadding}\t{string.Join(", ", arg.Select(x => "L:" + _labelID.Invoke(x)))}"); _log?.Log(_level, $"Emit\t{op,_opcodePadding}\t{string.Join(", ", arg.Select(x => "L:" + _labelID.Invoke(x)))}");
Backing.Emit(op, arg); Backing.Emit(op, arg);
} }
/// <inheritdoc cref="ILGenerator.Emit(OpCode, SignatureHelper)"/> /// <inheritdoc cref="ILGenerator.Emit(OpCode, SignatureHelper)"/>
public void Emit(OpCode op, SignatureHelper arg) public void Emit(OpCode op, SignatureHelper arg)
{ {
_log?.Trace($"Emit\t{op,_opcodePadding} {arg}"); _log?.Log(_level, $"Emit\t{op,_opcodePadding} {arg}");
Backing.Emit(op, arg); Backing.Emit(op, arg);
} }
/// <inheritdoc cref="ILGenerator.Emit(OpCode, ConstructorInfo)"/> /// <inheritdoc cref="ILGenerator.Emit(OpCode, ConstructorInfo)"/>
public void Emit(OpCode op, ConstructorInfo arg) public void Emit(OpCode op, ConstructorInfo arg)
{ {
_log?.Trace($"Emit\t{op,_opcodePadding} {arg}"); _log?.Log(_level, $"Emit\t{op,_opcodePadding} {arg}");
Backing.Emit(op, arg); Backing.Emit(op, arg);
} }
@@ -151,42 +168,42 @@ namespace Torch.Managers.PatchManager.Transpile
/// <inheritdoc cref="ILGenerator.BeginExceptionBlock"/> /// <inheritdoc cref="ILGenerator.BeginExceptionBlock"/>
public Label BeginExceptionBlock() public Label BeginExceptionBlock()
{ {
_log?.Trace($"BeginExceptionBlock"); _log?.Log(_level, $"BeginExceptionBlock");
return Backing.BeginExceptionBlock(); return Backing.BeginExceptionBlock();
} }
/// <inheritdoc cref="ILGenerator.BeginCatchBlock"/> /// <inheritdoc cref="ILGenerator.BeginCatchBlock"/>
public void BeginCatchBlock(Type caught) public void BeginCatchBlock(Type caught)
{ {
_log?.Trace($"BeginCatchBlock {caught}"); _log?.Log(_level, $"BeginCatchBlock {caught}");
Backing.BeginCatchBlock(caught); Backing.BeginCatchBlock(caught);
} }
/// <inheritdoc cref="ILGenerator.BeginExceptFilterBlock"/> /// <inheritdoc cref="ILGenerator.BeginExceptFilterBlock"/>
public void BeginExceptFilterBlock() public void BeginExceptFilterBlock()
{ {
_log?.Trace($"BeginExceptFilterBlock"); _log?.Log(_level, $"BeginExceptFilterBlock");
Backing.BeginExceptFilterBlock(); Backing.BeginExceptFilterBlock();
} }
/// <inheritdoc cref="ILGenerator.BeginFaultBlock"/> /// <inheritdoc cref="ILGenerator.BeginFaultBlock"/>
public void BeginFaultBlock() public void BeginFaultBlock()
{ {
_log?.Trace($"BeginFaultBlock"); _log?.Log(_level, $"BeginFaultBlock");
Backing.BeginFaultBlock(); Backing.BeginFaultBlock();
} }
/// <inheritdoc cref="ILGenerator.BeginFinallyBlock"/> /// <inheritdoc cref="ILGenerator.BeginFinallyBlock"/>
public void BeginFinallyBlock() public void BeginFinallyBlock()
{ {
_log?.Trace($"BeginFinallyBlock"); _log?.Log(_level, $"BeginFinallyBlock");
Backing.BeginFinallyBlock(); Backing.BeginFinallyBlock();
} }
/// <inheritdoc cref="ILGenerator.EndExceptionBlock"/> /// <inheritdoc cref="ILGenerator.EndExceptionBlock"/>
public void EndExceptionBlock() public void EndExceptionBlock()
{ {
_log?.Trace($"EndExceptionBlock"); _log?.Log(_level, $"EndExceptionBlock");
Backing.EndExceptionBlock(); Backing.EndExceptionBlock();
} }
#endregion #endregion
@@ -194,7 +211,7 @@ namespace Torch.Managers.PatchManager.Transpile
/// <inheritdoc cref="ILGenerator.MarkLabel(Label)"/> /// <inheritdoc cref="ILGenerator.MarkLabel(Label)"/>
public void MarkLabel(Label label) public void MarkLabel(Label label)
{ {
_log?.Trace($"MkLbl\tL:{_labelID.Invoke(label)}"); _log?.Log(_level, $"MkLbl\tL:{_labelID.Invoke(label)}");
Backing.MarkLabel(label); Backing.MarkLabel(label);
} }
@@ -211,7 +228,7 @@ namespace Torch.Managers.PatchManager.Transpile
[Conditional("DEBUG")] [Conditional("DEBUG")]
public void EmitComment(string comment) public void EmitComment(string comment)
{ {
_log?.Trace($"// {comment}"); _log?.Log(_level, $"// {comment}");
} }
} }
#pragma warning restore 162 #pragma warning restore 162

View File

@@ -42,6 +42,21 @@ namespace Torch.Managers.PatchManager.Transpile
TokenResolver = new NormalTokenResolver(method); TokenResolver = new NormalTokenResolver(method);
} }
#pragma warning disable 649
[ReflectedMethod(Name = "BakeByteArray")]
private static Func<ILGenerator, byte[]> _ilGeneratorBakeByteArray;
#pragma warning restore 649
public MethodContext(DynamicMethod method)
{
Method = null;
MethodBody = null;
_msilBytes = _ilGeneratorBakeByteArray(method.GetILGenerator());
TokenResolver = new DynamicMethodTokenResolver(method);
}
public void Read() public void Read()
{ {
ReadInstructions(); ReadInstructions();
@@ -64,7 +79,12 @@ namespace Torch.Managers.PatchManager.Transpile
instructionValue = (short)((instructionValue << 8) | memory.ReadByte()); instructionValue = (short)((instructionValue << 8) | memory.ReadByte());
} }
if (!OpCodeLookup.TryGetValue(instructionValue, out OpCode opcode)) if (!OpCodeLookup.TryGetValue(instructionValue, out OpCode opcode))
throw new Exception($"Unknown opcode {instructionValue:X}"); {
var msg = $"Unknown opcode {instructionValue:X}";
_log.Error(msg);
Debug.Assert(false, msg);
continue;
}
if (opcode.Size != memory.Position - opcodeOffset) if (opcode.Size != memory.Position - opcodeOffset)
throw new Exception($"Opcode said it was {opcode.Size} but we read {memory.Position - opcodeOffset}"); throw new Exception($"Opcode said it was {opcode.Size} but we read {memory.Position - opcodeOffset}");
var instruction = new MsilInstruction(opcode) var instruction = new MsilInstruction(opcode)
@@ -78,6 +98,8 @@ namespace Torch.Managers.PatchManager.Transpile
private void ResolveCatchClauses() private void ResolveCatchClauses()
{ {
if (MethodBody == null)
return;
foreach (ExceptionHandlingClause clause in MethodBody.ExceptionHandlingClauses) foreach (ExceptionHandlingClause clause in MethodBody.ExceptionHandlingClauses)
{ {
var beginInstruction = FindInstruction(clause.TryOffset); var beginInstruction = FindInstruction(clause.TryOffset);
@@ -111,7 +133,8 @@ namespace Torch.Managers.PatchManager.Transpile
foreach (var label in Labels) foreach (var label in Labels)
{ {
MsilInstruction target = FindInstruction(label.Key); MsilInstruction target = FindInstruction(label.Key);
target.Labels?.Add(label.Value); Debug.Assert(target != null, $"No label for offset {label.Key}");
target?.Labels?.Add(label.Value);
} }
} }

View File

@@ -14,15 +14,19 @@ 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, Func<Type, MsilLocal> localCreator, internal static IEnumerable<MsilInstruction> Transpile(MethodBase baseMethod, Func<Type, MsilLocal> localCreator,
IEnumerable<MethodInfo> transpilers, LoggingIlGenerator output, Label? retLabel, IEnumerable<MethodInfo> transpilers, MsilLabel retLabel)
bool logMsil)
{ {
var context = new MethodContext(baseMethod); var context = new MethodContext(baseMethod);
context.Read(); context.Read();
// IntegrityAnalysis(LogLevel.Trace, context.Instructions); // IntegrityAnalysis(LogLevel.Trace, context.Instructions);
return Transpile(baseMethod, context.Instructions, localCreator, transpilers, retLabel);
}
var methodContent = (IEnumerable<MsilInstruction>)context.Instructions; internal static IEnumerable<MsilInstruction> Transpile(MethodBase baseMethod, IEnumerable<MsilInstruction> methodContent,
Func<Type, MsilLocal> localCreator,
IEnumerable<MethodInfo> transpilers, MsilLabel retLabel)
{
foreach (MethodInfo transpiler in transpilers) foreach (MethodInfo transpiler in transpilers)
{ {
var paramList = new List<object>(); var paramList = new List<object>();
@@ -42,13 +46,7 @@ namespace Torch.Managers.PatchManager.Transpile
} }
methodContent = (IEnumerable<MsilInstruction>)transpiler.Invoke(null, paramList.ToArray()); methodContent = (IEnumerable<MsilInstruction>)transpiler.Invoke(null, paramList.ToArray());
} }
methodContent = FixBranchAndReturn(methodContent, retLabel); return FixBranchAndReturn(methodContent, retLabel);
var list = methodContent.ToList();
if (logMsil)
{
IntegrityAnalysis(LogLevel.Info, list);
}
EmitMethod(list, output);
} }
internal static void EmitMethod(IReadOnlyList<MsilInstruction> instructions, LoggingIlGenerator target) internal static void EmitMethod(IReadOnlyList<MsilInstruction> instructions, LoggingIlGenerator target)
@@ -89,7 +87,7 @@ namespace Torch.Managers.PatchManager.Transpile
ilNext?.TryCatchOperation?.Type == MsilTryCatchOperationType.BeginCatchBlock || ilNext?.TryCatchOperation?.Type == MsilTryCatchOperationType.BeginCatchBlock ||
ilNext?.TryCatchOperation?.Type == MsilTryCatchOperationType.BeginFinallyBlock)) ilNext?.TryCatchOperation?.Type == MsilTryCatchOperationType.BeginFinallyBlock))
continue; continue;
if ((il.OpCode == OpCodes.Leave || il.OpCode == OpCodes.Leave_S) && if ((il.OpCode == OpCodes.Leave || il.OpCode == OpCodes.Leave_S || il.OpCode == OpCodes.Endfinally) &&
ilNext?.TryCatchOperation?.Type == MsilTryCatchOperationType.EndExceptionBlock) ilNext?.TryCatchOperation?.Type == MsilTryCatchOperationType.EndExceptionBlock)
continue; continue;
@@ -105,7 +103,7 @@ namespace Torch.Managers.PatchManager.Transpile
/// </summary> /// </summary>
/// <param name="level">default logging level</param> /// <param name="level">default logging level</param>
/// <param name="instructions">instructions</param> /// <param name="instructions">instructions</param>
private static void IntegrityAnalysis(LogLevel level, IReadOnlyList<MsilInstruction> instructions) public static void IntegrityAnalysis(LogLevel level, IReadOnlyList<MsilInstruction> instructions)
{ {
var targets = new Dictionary<MsilLabel, int>(); var targets = new Dictionary<MsilLabel, int>();
for (var i = 0; i < instructions.Count; i++) for (var i = 0; i < instructions.Count; i++)
@@ -192,13 +190,13 @@ namespace Torch.Managers.PatchManager.Transpile
} }
} }
private static IEnumerable<MsilInstruction> FixBranchAndReturn(IEnumerable<MsilInstruction> insn, Label? retTarget) private static IEnumerable<MsilInstruction> FixBranchAndReturn(IEnumerable<MsilInstruction> insn, MsilLabel retTarget)
{ {
foreach (MsilInstruction i in insn) foreach (MsilInstruction i in insn)
{ {
if (retTarget.HasValue && i.OpCode == OpCodes.Ret) if (retTarget != null && i.OpCode == OpCodes.Ret)
{ {
var j = i.CopyWith(OpCodes.Br).InlineTarget(new MsilLabel(retTarget.Value)); var j = i.CopyWith(OpCodes.Br).InlineTarget(retTarget);
_log.Trace($"Replacing {i} with {j}"); _log.Trace($"Replacing {i} with {j}");
yield return j; yield return j;
} }