Added FishTranspiler.NewLocalVariable, fixed a bug causing FishTranspiler.LocalVariable.Equals to always return false, minor refactoring
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -9,11 +9,21 @@ using System.Runtime.CompilerServices;
|
|||||||
namespace FisheryLib;
|
namespace FisheryLib;
|
||||||
public static class Convert
|
public static class Convert
|
||||||
{
|
{
|
||||||
|
[Obsolete("Renamed to Convert.Type for clarity")]
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static unsafe TTo To<TFrom, TTo>(TFrom from) where TFrom : IConvertible
|
public static unsafe TTo To<TFrom, TTo>(TFrom from) where TFrom : IConvertible
|
||||||
|
=> Type<TFrom, TTo>(from);
|
||||||
|
|
||||||
|
[Obsolete("Renamed to Convert.Type for clarity")]
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static unsafe TTo To<TFrom, TTo>(TFrom from, IFormatProvider provider) where TFrom : IConvertible
|
||||||
|
=> Type<TFrom, TTo>(from, provider);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static unsafe TTo Type<TFrom, TTo>(TFrom from) where TFrom : IConvertible
|
||||||
=> FunctionPointers.Convert<TFrom>.To<TTo>.Default(from, CultureInfo.InvariantCulture);
|
=> FunctionPointers.Convert<TFrom>.To<TTo>.Default(from, CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static unsafe TTo To<TFrom, TTo>(TFrom from, IFormatProvider provider) where TFrom : IConvertible
|
public static unsafe TTo Type<TFrom, TTo>(TFrom from, IFormatProvider provider) where TFrom : IConvertible
|
||||||
=> FunctionPointers.Convert<TFrom>.To<TTo>.Default(from, provider);
|
=> FunctionPointers.Convert<TFrom>.To<TTo>.Default(from, provider);
|
||||||
}
|
}
|
||||||
@@ -92,13 +92,10 @@ public static class FishTranspiler
|
|||||||
public CodeInstruction WithLabelsAndBlocks(CodeInstruction instruction)
|
public CodeInstruction WithLabelsAndBlocks(CodeInstruction instruction)
|
||||||
=> WithLabels(instruction.labels).WithBlocks(instruction.blocks);
|
=> WithLabels(instruction.labels).WithBlocks(instruction.blocks);
|
||||||
|
|
||||||
public int GetIndex() => Operand is LocalVariableInfo info ? info.LocalIndex
|
public int GetIndex()
|
||||||
: Operand is ushort ushortVar ? ushortVar
|
=> OpCode.TryGetIndex()
|
||||||
: Operand is byte byteVar ? byteVar
|
?? TryGetIndexFromOperand(Operand)
|
||||||
: Operand is int intVar ? intVar
|
?? ThrowHelper.ThrowArgumentException<int>($"{OpCode} has operand {Operand}. This is not a supported index.");
|
||||||
: OpCode.TryGetIndex() is { } index ? index
|
|
||||||
: Operand is IConvertible convertible ? convertible.ToInt32(CultureInfo.InvariantCulture)
|
|
||||||
: Operand as int? ?? ThrowHelper.ThrowArgumentException<int>($"{OpCode} has operand {Operand}. This is not a supported index.");
|
|
||||||
|
|
||||||
public bool LoadsLocalVariable(MethodBase method, Type localType)
|
public bool LoadsLocalVariable(MethodBase method, Type localType)
|
||||||
=> OpCode.LoadsLocalVariable()
|
=> OpCode.LoadsLocalVariable()
|
||||||
@@ -639,6 +636,21 @@ public static class FishTranspiler
|
|||||||
/// <param name="index">The index of the local variable</param>
|
/// <param name="index">The index of the local variable</param>
|
||||||
public static Container StoreLocalVariable(int index) => new(GetStoreLocalOpCode(index), GetOperandFromIndex(index));
|
public static Container StoreLocalVariable(int index) => new(GetStoreLocalOpCode(index), GetOperandFromIndex(index));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Declares a local variable of the specified type, optionally pinning the object referred
|
||||||
|
/// to by the variable. Then returns an instruction matching that of StoreLocalVariable.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="localType">A System.Type object that represents the type of the local variable</param>
|
||||||
|
/// <param name="generator">The ILGenerator used for the instruction stream</param>
|
||||||
|
/// <param name="pinned">True to pin the object in memory; otherwise, false</param>
|
||||||
|
public static Container NewLocalVariable(Type localType, ILGenerator generator, bool pinned = false)
|
||||||
|
{
|
||||||
|
Guard.IsNotNull(localType);
|
||||||
|
Guard.IsNotNull(generator);
|
||||||
|
|
||||||
|
return StoreLocalVariable(generator.DeclareLocal(localType, pinned));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Pushes a supplied value of type int32 onto the evaluation stack as an int32.
|
/// Pushes a supplied value of type int32 onto the evaluation stack as an int32.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -886,6 +898,22 @@ public static class FishTranspiler
|
|||||||
return new(fieldInfo.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, fieldInfo);
|
return new(fieldInfo.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, fieldInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete("Use FishTranspiler.PropertyGetter instead")]
|
||||||
|
public static Container CallPropertyGetter(Type type, string name, bool forceNoCallvirt = false)
|
||||||
|
=> PropertyGetter(type, name, forceNoCallvirt);
|
||||||
|
|
||||||
|
[Obsolete("Use FishTranspiler.PropertyGetter instead")]
|
||||||
|
public static Container CallPropertyGetter(PropertyInfo propertyInfo, bool forceNoCallvirt = false)
|
||||||
|
=> PropertyGetter(propertyInfo, forceNoCallvirt);
|
||||||
|
|
||||||
|
[Obsolete("Use FishTranspiler.PropertySetter instead")]
|
||||||
|
public static Container CallPropertySetter(Type type, string name, bool forceNoCallvirt = false)
|
||||||
|
=> PropertySetter(type, name, forceNoCallvirt);
|
||||||
|
|
||||||
|
[Obsolete("Use FishTranspiler.PropertySetter instead")]
|
||||||
|
public static Container CallPropertySetter(PropertyInfo propertyInfo, bool forceNoCallvirt = false)
|
||||||
|
=> PropertySetter(propertyInfo, forceNoCallvirt);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calls the property getter indicated by the passed arguments.
|
/// Calls the property getter indicated by the passed arguments.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -893,7 +921,7 @@ public static class FishTranspiler
|
|||||||
/// <param name="name">The name of the property</param>
|
/// <param name="name">The name of the property</param>
|
||||||
/// <param name="forceNoCallvirt">force a Call OpCode instead of using Callvirt where appropriate</param>
|
/// <param name="forceNoCallvirt">force a Call OpCode instead of using Callvirt where appropriate</param>
|
||||||
/// <exception cref="ArgumentException">No property found for the specified type and name</exception>
|
/// <exception cref="ArgumentException">No property found for the specified type and name</exception>
|
||||||
public static Container CallPropertyGetter(Type type, string name, bool forceNoCallvirt = false)
|
public static Container PropertyGetter(Type type, string name, bool forceNoCallvirt = false)
|
||||||
{
|
{
|
||||||
Guard.IsNotNull(type);
|
Guard.IsNotNull(type);
|
||||||
Guard.IsNotNullOrEmpty(name);
|
Guard.IsNotNullOrEmpty(name);
|
||||||
@@ -910,7 +938,7 @@ public static class FishTranspiler
|
|||||||
/// <param name="propertyInfo">The PropertyInfo</param>
|
/// <param name="propertyInfo">The PropertyInfo</param>
|
||||||
/// <param name="forceNoCallvirt">force a Call OpCode instead of using Callvirt where appropriate</param>
|
/// <param name="forceNoCallvirt">force a Call OpCode instead of using Callvirt where appropriate</param>
|
||||||
/// <exception cref="ArgumentException">No property getter found for the specified property info</exception>
|
/// <exception cref="ArgumentException">No property getter found for the specified property info</exception>
|
||||||
public static Container CallPropertyGetter(PropertyInfo propertyInfo, bool forceNoCallvirt = false)
|
public static Container PropertyGetter(PropertyInfo propertyInfo, bool forceNoCallvirt = false)
|
||||||
{
|
{
|
||||||
Guard.IsNotNull(propertyInfo);
|
Guard.IsNotNull(propertyInfo);
|
||||||
|
|
||||||
@@ -927,7 +955,7 @@ public static class FishTranspiler
|
|||||||
/// <param name="name">The name of the property</param>
|
/// <param name="name">The name of the property</param>
|
||||||
/// <param name="forceNoCallvirt">force a Call OpCode instead of using Callvirt where appropriate</param>
|
/// <param name="forceNoCallvirt">force a Call OpCode instead of using Callvirt where appropriate</param>
|
||||||
/// <exception cref="ArgumentException">No property found with specified type and name</exception>
|
/// <exception cref="ArgumentException">No property found with specified type and name</exception>
|
||||||
public static Container CallPropertySetter(Type type, string name, bool forceNoCallvirt = false)
|
public static Container PropertySetter(Type type, string name, bool forceNoCallvirt = false)
|
||||||
{
|
{
|
||||||
Guard.IsNotNull(type);
|
Guard.IsNotNull(type);
|
||||||
Guard.IsNotNullOrEmpty(name);
|
Guard.IsNotNullOrEmpty(name);
|
||||||
@@ -944,7 +972,7 @@ public static class FishTranspiler
|
|||||||
/// <param name="propertyInfo">The PropertyInfo</param>
|
/// <param name="propertyInfo">The PropertyInfo</param>
|
||||||
/// <param name="forceNoCallvirt">force a Call OpCode instead of using Callvirt where appropriate</param>
|
/// <param name="forceNoCallvirt">force a Call OpCode instead of using Callvirt where appropriate</param>
|
||||||
/// <exception cref="ArgumentException">No property setter found for the specified property info</exception>
|
/// <exception cref="ArgumentException">No property setter found for the specified property info</exception>
|
||||||
public static Container CallPropertySetter(PropertyInfo propertyInfo, bool forceNoCallvirt = false)
|
public static Container PropertySetter(PropertyInfo propertyInfo, bool forceNoCallvirt = false)
|
||||||
{
|
{
|
||||||
Guard.IsNotNull(propertyInfo);
|
Guard.IsNotNull(propertyInfo);
|
||||||
|
|
||||||
@@ -1748,12 +1776,9 @@ public static class FishTranspiler
|
|||||||
{
|
{
|
||||||
if (previousCode is not null && predicate(previousCode) && code.IsStloc() && code.opcode is var opcode)
|
if (previousCode is not null && predicate(previousCode) && code.IsStloc() && code.opcode is var opcode)
|
||||||
{
|
{
|
||||||
yield return opcode.TryGetIndex() as object
|
yield return opcode.TryGetIndex()
|
||||||
?? (code.operand is LocalBuilder builder ? builder
|
?? TryGetIndexFromOperand(code.operand)
|
||||||
: code.operand is byte index ? index
|
?? ThrowHelper.ThrowNotSupportedException<object>($"{code.opcode} returned {code.operand?.ToString() ?? "null"}.");
|
||||||
: code.operand is ushort unsigned ? unsigned
|
|
||||||
: ushort.TryParse(code.operand?.ToString(), out var parsedNumber) ? parsedNumber
|
|
||||||
: ThrowHelper.ThrowNotSupportedException<object>($"{code.opcode} returned {code.operand?.ToString() ?? "null"}."));
|
|
||||||
}
|
}
|
||||||
previousCode = code;
|
previousCode = code;
|
||||||
}
|
}
|
||||||
@@ -1765,7 +1790,7 @@ public static class FishTranspiler
|
|||||||
Guard.IsNotNull(predicate);
|
Guard.IsNotNull(predicate);
|
||||||
|
|
||||||
foreach (var operand in GetLocalOperandsOrIndices(codes, predicate))
|
foreach (var operand in GetLocalOperandsOrIndices(codes, predicate))
|
||||||
yield return operand is LocalBuilder builder ? builder.LocalIndex : (int)operand;
|
yield return (int)operand;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<int> GetLocalIndices(MethodBase method, Predicate<LocalVariableInfo> predicate)
|
public static IEnumerable<int> GetLocalIndices(MethodBase method, Predicate<LocalVariableInfo> predicate)
|
||||||
@@ -2332,8 +2357,21 @@ public static class FishTranspiler
|
|||||||
public static IList<LocalVariableInfo> GetLocalVariables(this MethodBase method)
|
public static IList<LocalVariableInfo> GetLocalVariables(this MethodBase method)
|
||||||
=> method.GetMethodBody().LocalVariables;
|
=> method.GetMethodBody().LocalVariables;
|
||||||
|
|
||||||
|
public static int? TryGetIndexFromOperand(object? obj)
|
||||||
|
=> obj is LocalVariableInfo info ? info.LocalIndex
|
||||||
|
: obj is byte @byte ? @byte
|
||||||
|
: obj is ushort @ushort ? @ushort
|
||||||
|
: obj is int @int ? @int
|
||||||
|
: obj is not string and IConvertible convertible ? convertible.ToInt32(CultureInfo.InvariantCulture)
|
||||||
|
: int.TryParse(obj?.ToString(), out var parsedNumber) ? parsedNumber
|
||||||
|
: null;
|
||||||
|
|
||||||
public static bool CompareOperands(object? lhs, object? rhs)
|
public static bool CompareOperands(object? lhs, object? rhs)
|
||||||
=> (lhs is LocalVariableInfo lhInfo ? lhInfo.LocalIndex : lhs) == (rhs is LocalVariableInfo rhInfo ? rhInfo.LocalIndex : rhs);
|
=> TryGetIndexFromOperand(lhs) is int lhIndex && TryGetIndexFromOperand(rhs) is int rhIndex
|
||||||
|
? lhIndex == rhIndex
|
||||||
|
: lhs is null
|
||||||
|
? rhs is null
|
||||||
|
: lhs.Equals(rhs);
|
||||||
|
|
||||||
public static void Emit(this ILGenerator generator, Container fishTranspiler)
|
public static void Emit(this ILGenerator generator, Container fishTranspiler)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,10 +10,12 @@ global using HarmonyLib;
|
|||||||
global using FisheryLib.Utility.Diagnostics;
|
global using FisheryLib.Utility.Diagnostics;
|
||||||
global using CodeInstructions = System.Collections.Generic.IEnumerable<HarmonyLib.CodeInstruction>;
|
global using CodeInstructions = System.Collections.Generic.IEnumerable<HarmonyLib.CodeInstruction>;
|
||||||
using System.Security;
|
using System.Security;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
[assembly: AllowPartiallyTrustedCallers]
|
[assembly: AllowPartiallyTrustedCallers]
|
||||||
[assembly: SecurityTransparent]
|
[assembly: SecurityTransparent]
|
||||||
[assembly: SecurityRules(SecurityRuleSet.Level2, SkipVerificationInFullTrust = true)]
|
[assembly: SecurityRules(SecurityRuleSet.Level2, SkipVerificationInFullTrust = true)]
|
||||||
|
[assembly: Debuggable(false, false)]
|
||||||
|
|
||||||
/*namespace FisheryLib;
|
/*namespace FisheryLib;
|
||||||
public class Fishery : Harmony
|
public class Fishery : Harmony
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
<Optimize>true</Optimize>
|
<Optimize>true</Optimize>
|
||||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
<!--PublicizeAsReferenceAssemblies>false</PublicizeAsReferenceAssemblies-->
|
|
||||||
<Authors>bradson</Authors>
|
<Authors>bradson</Authors>
|
||||||
<Copyright>Copyright (c) 2022 bradson</Copyright>
|
<Copyright>Copyright (c) 2022 bradson</Copyright>
|
||||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||||
|
|||||||
@@ -85,10 +85,10 @@ public static class Equals
|
|||||||
type.IsValueType
|
type.IsValueType
|
||||||
? nameof(Methods.EquatableValueType)
|
? nameof(Methods.EquatableValueType)
|
||||||
: nameof(Methods.EquatableReferenceType))
|
: nameof(Methods.EquatableReferenceType))
|
||||||
.MakeGenericMethod(type)/*.CreateDelegate(typeof(EqualsDelegate<>).MakeGenericType(type)).Method*/.GetFunctionPointer()
|
.MakeGenericMethod(type).GetFunctionPointer()
|
||||||
: type.IsNullable(out var genericArgument)
|
: type.IsNullable(out var genericArgument)
|
||||||
&& genericArgument.IsAssignableTo(typeof(IEquatable<>), genericArgument)
|
&& genericArgument.IsAssignableTo(typeof(IEquatable<>), genericArgument)
|
||||||
? GetMethod(nameof(Methods.Nullable)).MakeGenericMethod(genericArgument)/*.CreateDelegate(typeof(EqualsDelegate<>).MakeGenericType(type)).Method*/.GetFunctionPointer()
|
? GetMethod(nameof(Methods.Nullable)).MakeGenericMethod(genericArgument).GetFunctionPointer()
|
||||||
: GetMethod(
|
: GetMethod(
|
||||||
type.IsEnum
|
type.IsEnum
|
||||||
? Type.GetTypeCode(Enum.GetUnderlyingType(type))
|
? Type.GetTypeCode(Enum.GetUnderlyingType(type))
|
||||||
@@ -105,7 +105,7 @@ public static class Equals
|
|||||||
_ => ThrowHelper.ThrowNotSupportedException<string>()
|
_ => ThrowHelper.ThrowNotSupportedException<string>()
|
||||||
}
|
}
|
||||||
: nameof(Methods.Object)
|
: nameof(Methods.Object)
|
||||||
).MakeGenericMethod(type)/*.CreateDelegate(typeof(EqualsDelegate<>).MakeGenericType(type)).Method*/.GetFunctionPointer();
|
).MakeGenericMethod(type).GetFunctionPointer();
|
||||||
|
|
||||||
private static MethodInfo GetMethod(string name)
|
private static MethodInfo GetMethod(string name)
|
||||||
=> Array.Find(_equalsMethods, m => m.Name == name);
|
=> Array.Find(_equalsMethods, m => m.Name == name);
|
||||||
@@ -117,6 +117,4 @@ public static class Equals
|
|||||||
=> m.ReturnType == typeof(bool)
|
=> m.ReturnType == typeof(bool)
|
||||||
&& m.GetParameters().Length == 2)
|
&& m.GetParameters().Length == 2)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
//internal delegate bool EqualsDelegate<T>(T x, T y);
|
|
||||||
}
|
}
|
||||||
@@ -85,10 +85,10 @@ public static class EqualsByRef
|
|||||||
type.IsValueType
|
type.IsValueType
|
||||||
? nameof(Methods.EquatableValueType)
|
? nameof(Methods.EquatableValueType)
|
||||||
: nameof(Methods.EquatableReferenceType))
|
: nameof(Methods.EquatableReferenceType))
|
||||||
.MakeGenericMethod(type)/*.CreateDelegate(typeof(EqualsDelegate<>).MakeGenericType(type)).Method*/.GetFunctionPointer()
|
.MakeGenericMethod(type).GetFunctionPointer()
|
||||||
: type.IsNullable(out var genericArgument)
|
: type.IsNullable(out var genericArgument)
|
||||||
&& genericArgument.IsAssignableTo(typeof(IEquatable<>), genericArgument)
|
&& genericArgument.IsAssignableTo(typeof(IEquatable<>), genericArgument)
|
||||||
? GetMethod(nameof(Methods.Nullable)).MakeGenericMethod(genericArgument)/*.CreateDelegate(typeof(EqualsDelegate<>).MakeGenericType(type)).Method*/.GetFunctionPointer()
|
? GetMethod(nameof(Methods.Nullable)).MakeGenericMethod(genericArgument).GetFunctionPointer()
|
||||||
: GetMethod(
|
: GetMethod(
|
||||||
type.IsEnum
|
type.IsEnum
|
||||||
? Type.GetTypeCode(Enum.GetUnderlyingType(type))
|
? Type.GetTypeCode(Enum.GetUnderlyingType(type))
|
||||||
@@ -105,7 +105,7 @@ public static class EqualsByRef
|
|||||||
_ => ThrowHelper.ThrowNotSupportedException<string>()
|
_ => ThrowHelper.ThrowNotSupportedException<string>()
|
||||||
}
|
}
|
||||||
: nameof(Methods.Object)
|
: nameof(Methods.Object)
|
||||||
).MakeGenericMethod(type)/*.CreateDelegate(typeof(EqualsDelegate<>).MakeGenericType(type)).Method*/.GetFunctionPointer();
|
).MakeGenericMethod(type).GetFunctionPointer();
|
||||||
|
|
||||||
private static MethodInfo GetMethod(string name)
|
private static MethodInfo GetMethod(string name)
|
||||||
=> Array.Find(_equalsMethods, m => m.Name == name);
|
=> Array.Find(_equalsMethods, m => m.Name == name);
|
||||||
@@ -117,6 +117,4 @@ public static class EqualsByRef
|
|||||||
=> m.ReturnType == typeof(bool)
|
=> m.ReturnType == typeof(bool)
|
||||||
&& m.GetParameters().Length == 2)
|
&& m.GetParameters().Length == 2)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
//internal delegate bool EqualsDelegate<T>(ref T x, ref T y);
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user