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