Files
bbradson cdf209eb02 Added FisheryLib.Collections.FishTable and Mono.Cecil extension methods
A bit of refactoring has been done too
2023-06-03 20:10:58 +02:00

340 lines
9.7 KiB
C#

// Copyright (c) 2022 bradson
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
/*using System.Linq;
namespace FisheryLib;
public class InfixMethod : HarmonyMethod
{
public InfixMethod(MethodInfo method, InfixTarget target)
{
Guard.IsNotNull(method, nameof(method));
ImportMethod(method, target);
}
private void ImportMethod(MethodInfo theMethod, InfixTarget target)
{
method = MakeInfixMethod(theMethod, target).Method;
if (method is not null
&& HarmonyMethodExtensions.GetFromMethod(method) is { } fromMethod)
{
Merge(fromMethod).CopyTo(this);
}
}
private static InfixDelegate MakeInfixMethod(MethodInfo theMethod, InfixTarget target)
=> (codes, original)
=> target.Mode switch
{
InfixTargetMode.After => codes.InsertAfter(target.Predicate, GetInfixInstructions(original, theMethod)),
InfixTargetMode.Replace => codes.Replace(target.Predicate, code => FishTranspiler.Call(theMethod)), // <-- this one is different
InfixTargetMode.Before => codes.InsertBefore(target.Predicate, GetInfixInstructions(original, theMethod)),
_ => throw new($"Invalid InfixTargetMode for {theMethod.FullDescription()}")
};
private static IEnumerable<CodeInstruction> GetInfixInstructions(MethodBase original, MethodInfo infixMethod)
{
var originalParameters = original.GetParameters();
foreach (var argument in infixMethod.GetParameters())
{
var argumentName = argument.Name;
var argumentType = argument.ParameterType;
if (argumentName == "__instance")
{
yield return FishTranspiler.This;
}
else if (argumentName == "__args")
{
foreach (var code in GetArgsArray(originalParameters))
yield return code;
}
else if (argumentName == "__originalMethod")
{
foreach (var code in GetOriginalMethod(original))
yield return code;
}
else if (argumentName is "__result")
{
throw new NotSupportedException("Infixes do not support __result.");
}
else if (argumentName is "__state")
{
throw new NotSupportedException("Infixes do not support __state.");
}
//argument
else if (original.GetParameters().Any(parm => parm.Name == argumentName))
{
yield return FishTranspiler.Argument(original, argumentName);
}
//___field
else if (argumentName.StartsWith("___"))
{
yield return FishTranspiler.Field(argumentType, argumentName.Remove(0, 3));
}
//vars
else if (argumentName.StartsWith("__"))
{
yield return GetVarInstruction(original, argumentName, argumentType);
}
}
yield return FishTranspiler.Call(infixMethod);
}
public static IEnumerable<CodeInstruction> GetOriginalMethod(MethodBase original)
{
Guard.IsNotNull(original, nameof(original));
if (original is MethodInfo or ConstructorInfo)
{
yield return FishTranspiler.Token(original);
if (original.ReflectedType is { IsGenericType: true } type)
{
yield return FishTranspiler.Token(type);
yield return FishTranspiler.Call<Func<RuntimeMethodHandle, RuntimeTypeHandle, MethodBase>>(MethodBase.GetMethodFromHandle);
}
else
{
yield return FishTranspiler.Call<Func<RuntimeMethodHandle, MethodBase>>(MethodBase.GetMethodFromHandle);
}
}
else
{
yield return FishTranspiler.Null;
}
}
public static IEnumerable<CodeInstruction> GetArgsArray(ParameterInfo[] originalParameters)
{
Guard.IsNotNull(originalParameters, nameof(originalParameters));
yield return FishTranspiler.Constant(originalParameters.Length);
yield return FishTranspiler.New<object[]>();
for (var i = 0; i < originalParameters.Length; i++)
{
var parameter = originalParameters[i];
yield return FishTranspiler.Duplicate;
yield return FishTranspiler.Constant(i);
yield return FishTranspiler.Argument(parameter);
var parameterType = parameter.ParameterType;
if (parameterType.IsByRef)
{
parameterType = parameterType.GetElementType();
yield return FishTranspiler.LoadByRef(parameterType);
}
if (parameterType.IsValueType)
yield return FishTranspiler.Box(parameterType);
yield return FishTranspiler.StoreElement<object>();
}
}
public static CodeInstruction GetVarInstruction(MethodBase original, string argumentName, Type argumentType)
{
Guard.IsNotNull(original, nameof(original));
Guard.IsNotNullOrEmpty(argumentName, nameof(argumentName));
Guard.IsNotNull(argumentType, nameof(argumentType));
//__var
return argumentName == "__var"
? FishTranspiler.FirstLocalVariable(original, argumentType)
//__var#
: argumentName.Contains("__var") && int.TryParse(argumentName.Trim('_', 'v', 'a', 'r'), out var index)
? FishTranspiler.LocalVariable(index)
//__*#
: int.TryParse(new(argumentName.Where(char.IsDigit).ToArray()), out index)
? FishTranspiler.MatchingLocalVariables(original, loc => loc.LocalType == argumentType).ElementAt(index)
//__*
: FishTranspiler.FirstLocalVariable(original, argumentType);
}
//public static HarmonyMethod Merge(List<HarmonyMethod> attributes)
//{
// var harmonyMethod = new HarmonyMethod();
// if (attributes == null)
// return harmonyMethod;
// var resultTrv = Traverse.Create(harmonyMethod);
// attributes.ForEach((HarmonyMethod attribute) =>
// {
// var trv = Traverse.Create(attribute);
// HarmonyFields().ForEach(delegate (string f)
// {
// var value = trv.Field(f).GetValue();
// if (value != null && (f != "priority" || (int)value != -1))
// {
// HarmonyMethodExtensions.SetValue(resultTrv, f, value);
// }
// });
// });
// return harmonyMethod;
//}
/// <summary>
/// hide overridden HarmonyMethod ctors as they're insufficient for infixes
/// </summary>
#pragma warning disable IDE0051, IDE0060
private InfixMethod(MethodInfo method) { }
private InfixMethod(MethodInfo method, int priority, string[] before, string[] after, bool? debug) { }
private InfixMethod(Type methodType, string methodName, Type[] argumentTypes) { }
#pragma warning restore IDE0051, IDE0060
}
public delegate IEnumerable<CodeInstruction> InfixDelegate(IEnumerable<CodeInstruction> codes, MethodBase original);
public struct InfixTarget
{
public Predicate<CodeInstruction> Predicate { get; }
public InfixTargetMode Mode { get; }
public InfixTarget(MethodInfo calledMethod, InfixTargetMode targetMode)
{
Guard.IsNotNull(calledMethod, nameof(calledMethod));
Predicate = code => code.Calls(calledMethod);
Mode = targetMode;
}
public InfixTarget(FieldInfo loadedField, InfixTargetMode targetMode)
{
Guard.IsNotNull(loadedField, nameof(loadedField));
Predicate = code => code.LoadsField(loadedField);
Mode = targetMode;
}
public InfixTarget(long constantValue, InfixTargetMode targetMode)
{
Predicate = code => code.LoadsConstant(constantValue);
Mode = targetMode;
}
public InfixTarget(double constantValue, InfixTargetMode targetMode)
{
Predicate = code => code.LoadsConstant(constantValue);
Mode = targetMode;
}
public InfixTarget(Enum constantValue, InfixTargetMode targetMode)
{
Predicate = code => code.LoadsConstant(constantValue);
Mode = targetMode;
}
public InfixTarget(Predicate<CodeInstruction> predicate, InfixTargetMode targetMode)
{
Guard.IsNotNull(predicate, nameof(predicate));
Predicate = predicate;
Mode = targetMode;
}
}
public enum InfixTargetMode
{
After,
Replace,
Before
}
public abstract class InfixAttribute : Attribute
{
public InfixTarget InfixTarget { get; }
public InfixAttribute(InfixTarget target) => InfixTarget = target;
}
public class InfixBeforeAttribute : InfixAttribute
{
public InfixBeforeAttribute(MethodInfo methodCall)
: base(new(NotNull(methodCall), InfixTargetMode.Before))
{ }
public InfixBeforeAttribute(FieldInfo fieldToLoad)
: base(new(NotNull(fieldToLoad), InfixTargetMode.Before))
{ }
public InfixBeforeAttribute(long constantValue)
: base(new(constantValue, InfixTargetMode.Before))
{ }
public InfixBeforeAttribute(double constantValue)
: base(new(constantValue, InfixTargetMode.Before))
{ }
public InfixBeforeAttribute(Enum constantValue)
: base(new(constantValue, InfixTargetMode.Before))
{ }
public InfixBeforeAttribute(Predicate<CodeInstruction> predicate)
: base(new(NotNull(predicate), InfixTargetMode.Before))
{ }
}
public class InfixAfterAttribute : InfixAttribute
{
public InfixAfterAttribute(MethodInfo methodCall)
: base(new(NotNull(methodCall), InfixTargetMode.After))
{ }
public InfixAfterAttribute(FieldInfo fieldToLoad)
: base(new(NotNull(fieldToLoad), InfixTargetMode.After))
{ }
public InfixAfterAttribute(long constantValue)
: base(new(constantValue, InfixTargetMode.After))
{ }
public InfixAfterAttribute(double constantValue)
: base(new(constantValue, InfixTargetMode.After))
{ }
public InfixAfterAttribute(Enum constantValue)
: base(new(constantValue, InfixTargetMode.After))
{ }
public InfixAfterAttribute(Predicate<CodeInstruction> predicate)
: base(new(NotNull(predicate), InfixTargetMode.After))
{ }
}
public class InfixReplaceAttribute : InfixAttribute
{
public InfixReplaceAttribute(MethodInfo methodCall)
: base(new(NotNull(methodCall), InfixTargetMode.Replace))
{ }
public InfixReplaceAttribute(FieldInfo fieldToLoad)
: base(new(NotNull(fieldToLoad), InfixTargetMode.Replace))
{ }
public InfixReplaceAttribute(long constantValue)
: base(new(constantValue, InfixTargetMode.Replace))
{ }
public InfixReplaceAttribute(double constantValue)
: base(new(constantValue, InfixTargetMode.Replace))
{ }
public InfixReplaceAttribute(Enum constantValue)
: base(new(constantValue, InfixTargetMode.Replace))
{ }
public InfixReplaceAttribute(Predicate<CodeInstruction> predicate)
: base(new(NotNull(predicate), InfixTargetMode.Replace))
{ }
}*/