Files
Nitrox/NitroxPatcher/PatternMatching/InstructionPattern.cs
2025-07-06 00:23:46 +02:00

81 lines
2.8 KiB
C#

using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using HarmonyLib;
using NitroxModel.Helper;
namespace NitroxPatcher.PatternMatching;
public readonly struct InstructionPattern
{
public bool Equals(InstructionPattern other) => OpCode.Equals(other.OpCode) && Operand.Equals(other.Operand) && Label == other.Label;
public override bool Equals(object obj) => obj is InstructionPattern other && Equals(other);
public override int GetHashCode()
{
unchecked
{
int hashCode = OpCode.GetHashCode();
hashCode = (hashCode * 397) ^ Operand.GetHashCode();
hashCode = (hashCode * 397) ^ (Label != null ? Label.GetHashCode() : 0);
return hashCode;
}
}
public OpCodePattern OpCode { get; init; }
public OperandPattern Operand { get; init; }
public string Label { get; init; }
public static implicit operator InstructionPattern(OpCode opCode) => new() { OpCode = opCode };
public static implicit operator InstructionPattern(OperandPattern operand) => new() { Operand = operand };
public static implicit operator InstructionPattern(MethodInfo method) => Call(method, true);
public static InstructionPattern Call(string className, string methodName) => new() { OpCode = OpCodes.Call, Operand = new(className, methodName) };
public static InstructionPattern Call(MethodInfo method) => Call(method, false);
private static InstructionPattern Call(MethodInfo method, bool matchAnyCallOpcode)
{
Type methodDeclaringType = method.DeclaringType;
Validate.NotNull(methodDeclaringType);
return new()
{
OpCode = new OpCodePattern
{
OpCode = OpCodes.Call,
WeakMatch = matchAnyCallOpcode
},
Operand = new(methodDeclaringType.FullName, method.Name, method.GetParameters().Select(p => p.ParameterType).ToArray())
};
}
public static bool operator ==(InstructionPattern pattern, CodeInstruction instruction)
{
if (instruction == null)
{
return false;
}
return pattern.OpCode == instruction.opcode && pattern.Operand == instruction.operand;
}
public static bool operator ==(CodeInstruction instruction, InstructionPattern pattern)
{
return pattern == instruction;
}
public static bool operator !=(CodeInstruction instruction, InstructionPattern pattern)
{
return !(instruction == pattern);
}
public static bool operator !=(InstructionPattern pattern, CodeInstruction instruction)
{
return !(pattern == instruction);
}
public override string ToString() => $"{OpCode.OpCode}{(Operand != default ? $" {Operand}" : "")}{(Label != null ? $" '{Label}'" : "")}";
}