Added FishTranspiler.NewLocalVariable, fixed a bug causing FishTranspiler.LocalVariable.Equals to always return false, minor refactoring

This commit is contained in:
bbradson
2022-12-14 22:27:52 +01:00
parent 9cdc8ccb4f
commit 8fa92f15a6
8 changed files with 76 additions and 31 deletions
Binary file not shown.
Binary file not shown.
+11 -1
View File
@@ -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);
} }
+57 -19
View File
@@ -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)
{ {
+2
View File
@@ -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
-1
View File
@@ -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>
+3 -5
View File
@@ -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);
} }