Files
Torch/Torch/Managers/PatchManager/MSIL/MsilInstruction.cs

190 lines
6.9 KiB
C#

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using Torch.Managers.PatchManager.Transpile;
using Torch.Utils;
using Label = System.Windows.Controls.Label;
namespace Torch.Managers.PatchManager.MSIL
{
/// <summary>
/// Represents a single MSIL instruction, and its operand
/// </summary>
public class MsilInstruction
{
/// <summary>
/// Creates a new instruction with the given opcode.
/// </summary>
/// <param name="opcode">Opcode</param>
public MsilInstruction(OpCode opcode)
{
OpCode = opcode;
switch (opcode.OperandType)
{
case OperandType.InlineNone:
Operand = null;
break;
case OperandType.ShortInlineBrTarget:
case OperandType.InlineBrTarget:
Operand = new MsilOperandBrTarget(this);
break;
case OperandType.InlineField:
Operand = new MsilOperandInline.MsilOperandReflected<FieldInfo>(this);
break;
case OperandType.InlineI:
Operand = new MsilOperandInline.MsilOperandInt32(this);
break;
case OperandType.InlineI8:
Operand = new MsilOperandInline.MsilOperandInt64(this);
break;
case OperandType.InlineMethod:
Operand = new MsilOperandInline.MsilOperandReflected<MethodBase>(this);
break;
case OperandType.InlineR:
Operand = new MsilOperandInline.MsilOperandDouble(this);
break;
case OperandType.InlineSig:
Operand = new MsilOperandInline.MsilOperandSignature(this);
break;
case OperandType.InlineString:
Operand = new MsilOperandInline.MsilOperandString(this);
break;
case OperandType.InlineSwitch:
Operand = new MsilOperandSwitch(this);
break;
case OperandType.InlineTok:
Operand = new MsilOperandInline.MsilOperandReflected<MemberInfo>(this);
break;
case OperandType.InlineType:
Operand = new MsilOperandInline.MsilOperandReflected<Type>(this);
break;
case OperandType.ShortInlineVar:
case OperandType.InlineVar:
if (OpCode.Name.IndexOf("loc", StringComparison.OrdinalIgnoreCase) != -1)
Operand = new MsilOperandInline.MsilOperandLocal(this);
else
Operand = new MsilOperandInline.MsilOperandParameter(this);
break;
case OperandType.ShortInlineI:
Operand = OpCode == OpCodes.Ldc_I4_S
? (MsilOperand)new MsilOperandInline.MsilOperandInt8(this)
: new MsilOperandInline.MsilOperandUInt8(this);
break;
case OperandType.ShortInlineR:
Operand = new MsilOperandInline.MsilOperandSingle(this);
break;
#pragma warning disable 618
case OperandType.InlinePhi:
#pragma warning restore 618
default:
throw new ArgumentOutOfRangeException();
}
}
/// <summary>
/// Opcode of this instruction
/// </summary>
public OpCode OpCode { get; }
/// <summary>
/// Raw memory offset of this instruction; optional.
/// </summary>
public int Offset { get; internal set; }
/// <summary>
/// The operand for this instruction, or null.
/// </summary>
public MsilOperand Operand { get; }
/// <summary>
/// Labels pointing to this instruction.
/// </summary>
public HashSet<MsilLabel> Labels { get; } = new HashSet<MsilLabel>();
/// <summary>
/// Sets the inline value for this instruction.
/// </summary>
/// <typeparam name="T">The type of the inline constraint</typeparam>
/// <param name="o">Value</param>
/// <returns>This instruction</returns>
public MsilInstruction InlineValue<T>(T o)
{
((MsilOperandInline<T>)Operand).Value = o;
return this;
}
/// <summary>
/// Makes a copy of the instruction with a new opcode.
/// </summary>
/// <param name="newOpcode">The new opcode</param>
/// <returns>The copy</returns>
public MsilInstruction CopyWith(OpCode newOpcode)
{
var result = new MsilInstruction(newOpcode);
Operand?.CopyTo(result.Operand);
foreach (MsilLabel x in Labels)
result.Labels.Add(x);
return result;
}
/// <summary>
/// Sets the inline branch target for this instruction.
/// </summary>
/// <param name="label">Target to jump to</param>
/// <returns>This instruction</returns>
public MsilInstruction InlineTarget(MsilLabel label)
{
((MsilOperandBrTarget)Operand).Target = label;
return this;
}
/// <summary>
/// Emits this instruction to the given generator
/// </summary>
/// <param name="target">Emit target</param>
public void Emit(LoggingIlGenerator target)
{
foreach (MsilLabel label in Labels)
target.MarkLabel(label.LabelFor(target));
if (Operand != null)
Operand.Emit(target);
else
target.Emit(OpCode);
}
/// <inheritdoc />
public override string ToString()
{
var sb = new StringBuilder();
foreach (MsilLabel label in Labels)
sb.Append(label).Append(": ");
sb.Append(OpCode.Name).Append("\t").Append(Operand);
return sb.ToString();
}
#pragma warning disable 169
[ReflectedMethod(Name = "StackChange")]
private static Func<OpCode, int> _stackChange;
#pragma warning restore 169
public int StackChange()
{
int num = _stackChange.Invoke(OpCode);
if ((OpCode == OpCodes.Call || OpCode == OpCodes.Callvirt || OpCode == OpCodes.Newobj) &&
Operand is MsilOperandInline<MethodBase> inline)
{
MethodBase op = inline.Value;
if (op is MethodInfo mi && mi.ReturnType != typeof(void))
num++;
num -= op.GetParameters().Length;
if (!op.IsStatic && OpCode != OpCodes.Newobj)
num--;
}
return num;
}
}
}