Added FisheryLib.Collections.FishTable and Mono.Cecil extension methods
A bit of refactoring has been done too
This commit is contained in:
@@ -3,16 +3,25 @@
|
||||
// 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.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security;
|
||||
using FisheryLib.Collections;
|
||||
|
||||
namespace FisheryLib.FunctionPointers;
|
||||
public static class Convert<TFrom> where TFrom : IConvertible
|
||||
|
||||
public static class Convert<TFrom>
|
||||
{
|
||||
public static class To<TTo>
|
||||
{
|
||||
public static readonly unsafe delegate*<TFrom, IFormatProvider, TTo> Default = Convert.GetDefaultConverter<TFrom, TTo>();
|
||||
public static readonly unsafe delegate*<TFrom, IFormatProvider, TTo> Default
|
||||
= Convert.GetDefaultConverter<TFrom, TTo>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used when TFrom isn't a known sealed type, i.e. with just the IConvertible interface
|
||||
/// Used when TFrom isn't a known sealed type, e.g. with just the IConvertible interface
|
||||
/// </summary>
|
||||
internal static class Fallback<TTo>
|
||||
{
|
||||
@@ -23,64 +32,202 @@ public static class Convert<TFrom> where TFrom : IConvertible
|
||||
|
||||
internal static class Convert
|
||||
{
|
||||
private static MethodInfo GetConverterMethod<TFrom>(Type to) where TFrom : IConvertible
|
||||
private static class FromObjectTo<TTo>
|
||||
{
|
||||
var info = typeof(TFrom).GetMethod(GetConvertMethodName(to));
|
||||
if (info.GetParameters().Length > 1)
|
||||
private static FishTable<RuntimeTypeHandle, FuncWrapper> _delegates = new();
|
||||
|
||||
static FromObjectTo()
|
||||
=> _delegates.EntryAdded += static pair =>
|
||||
{
|
||||
var from = Type.GetTypeFromHandle(pair.Key);
|
||||
ref var entryRef = ref _delegates.GetReference(pair.Key);
|
||||
var to = typeof(TTo);
|
||||
|
||||
entryRef.Func = CreateObjectConverter(from, to);
|
||||
};
|
||||
|
||||
private static Func<object, IFormatProvider, TTo> CreateObjectConverter(Type from, Type to)
|
||||
{
|
||||
info = typeof(Convert)
|
||||
.GetMethod(nameof(ConvertToType), AccessTools.allDeclared)
|
||||
.MakeGenericMethod(new[] { typeof(TFrom), to });
|
||||
try
|
||||
{
|
||||
var dm = new DynamicMethod($"ObjectConverter_{from}_{to}", to,
|
||||
new[] { typeof(object), typeof(IFormatProvider) }, typeof(FromObjectTo<TTo>), true);
|
||||
var il = dm.GetILGenerator();
|
||||
|
||||
il.Emit(FishTranspiler.This);
|
||||
|
||||
if (from.IsValueType)
|
||||
{
|
||||
il.Emit(FishTranspiler.Call(typeof(FromObjectTo<TTo>), nameof(UnboxSafely),
|
||||
generics: new[] { from }));
|
||||
}
|
||||
|
||||
il.Emit(FishTranspiler.Argument(1));
|
||||
|
||||
il.Emit(FishTranspiler.Call(GetConverterMethod(from, to)));
|
||||
|
||||
il.Emit(FishTranspiler.Return);
|
||||
|
||||
return (Func<object, IFormatProvider, TTo>)dm.CreateDelegate(typeof(Func<object, IFormatProvider, TTo>));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new($"Failed to compile object converter from {from.FullDescription()} to {
|
||||
to.FullDescription()}\n{ex}");
|
||||
}
|
||||
}
|
||||
|
||||
return info;
|
||||
public static TTo Method(object obj, IFormatProvider provider)
|
||||
=> _delegates.GetOrAdd(obj.GetType().TypeHandle).Func(obj, provider);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[SecuritySafeCritical]
|
||||
private static ref T UnboxSafely<T>(object obj) where T : struct
|
||||
=> ref Unsafe.Unbox<T>(obj is T ? obj : default);
|
||||
|
||||
private record struct FuncWrapper
|
||||
{
|
||||
public Func<object, IFormatProvider, TTo> Func;
|
||||
}
|
||||
}
|
||||
|
||||
internal static Func<TFrom, IFormatProvider, TTo> GetDefaultConverterFunc<TFrom, TTo>() where TFrom : IConvertible
|
||||
=> (Func<TFrom, IFormatProvider, TTo>)GetConverterMethod<TFrom>(typeof(TTo))
|
||||
.CreateDelegate(typeof(Func<TFrom, IFormatProvider, TTo>));
|
||||
private static MethodInfo GetConverterMethod<TFrom>(Type to) => GetConverterMethod(typeof(TFrom), to);
|
||||
|
||||
private static MethodInfo GetConverterMethod(Type from, Type to)
|
||||
{
|
||||
try
|
||||
{
|
||||
var info = _convertMethodsByType.TryGetValue(to, out var value)
|
||||
&& TryFindInterfaceMethod(from, value) is { } methodByName
|
||||
? methodByName
|
||||
: TryFindMethodThroughReflection(to, from);
|
||||
|
||||
internal static unsafe delegate*<TFrom, IFormatProvider, TTo> GetDefaultConverter<TFrom, TTo>() where TFrom : IConvertible
|
||||
=> !typeof(TFrom).IsValueType && (!typeof(TFrom).IsSealed || typeof(TFrom).IsInterface || typeof(TFrom).IsAbstract)
|
||||
? &Convert<TFrom>.Fallback<TTo>.FallbackMethod
|
||||
: (delegate*<TFrom, IFormatProvider, TTo>)GetConverterMethod<TFrom>(typeof(TTo))
|
||||
.MethodHandle.GetFunctionPointer();
|
||||
if (info is null)
|
||||
ThrowFailedToFindConverterInvalidOperationException(from, to);
|
||||
|
||||
if (info.IsStatic)
|
||||
{
|
||||
if (info.GetParameters() is [_])
|
||||
info = GenerateWrapperMethod(from, to, info);
|
||||
}
|
||||
else if (info.GetParameters().Length > 1)
|
||||
{
|
||||
info = typeof(Convert)
|
||||
.GetMethod(nameof(ConvertToType), AccessTools.allDeclared)!
|
||||
.MakeGenericMethod(from, to);
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"Exception thrown while trying to get converter method:\n{e}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static MethodInfo? TryFindInterfaceMethod(Type from, MethodInfo interfaceMethod)
|
||||
{
|
||||
if (!typeof(IConvertible).IsAssignableFrom(from))
|
||||
return null;
|
||||
|
||||
var interfaceMap = from.GetInterfaceMap(typeof(IConvertible));
|
||||
|
||||
var index = Array.IndexOf(interfaceMap.InterfaceMethods, interfaceMethod);
|
||||
|
||||
return index < 0 ? null : interfaceMap.TargetMethods[index];
|
||||
}
|
||||
|
||||
[DoesNotReturn]
|
||||
private static MethodInfo ThrowFailedToFindConverterInvalidOperationException(Type from, Type to)
|
||||
=> ThrowHelper.ThrowInvalidOperationException<MethodInfo>($"Failed to find converter from type '{
|
||||
from.FullDescription()}' to type '{to.FullDescription()}'");
|
||||
|
||||
private static MethodInfo? TryFindMethodThroughReflection(Type to, Type from)
|
||||
{
|
||||
var parameters = new[] { from };
|
||||
|
||||
var fromMethods = from.GetMethods(BindingFlags.Public | BindingFlags.Static);
|
||||
if (TryFindMethod(fromMethods, "op_Implicit", parameters, to) is { } info)
|
||||
return info;
|
||||
|
||||
var toMethods = to.GetMethods(BindingFlags.Public | BindingFlags.Static);
|
||||
return TryFindMethod(toMethods, "op_Implicit", parameters, from)
|
||||
?? TryFindMethod(fromMethods, "op_Explicit", parameters, to)
|
||||
?? TryFindMethod(toMethods, "op_Explicit", parameters, from)
|
||||
?? (!from.IsValueType
|
||||
? typeof(FromObjectTo<>).MakeGenericType(to).GetMethod(nameof(FromObjectTo<object>.Method))
|
||||
: ThrowFailedToFindConverterInvalidOperationException(from, to));
|
||||
}
|
||||
|
||||
private static MethodInfo GenerateWrapperMethod(Type from, Type to, MethodBase method)
|
||||
{
|
||||
var dm = new DynamicMethod($"Wrapper_op_{from.Name}_{to.Name}", to,
|
||||
new[] { from.IsValueType ? from.MakeByRefType() : from, typeof(IFormatProvider) }, from, true);
|
||||
var il = dm.GetILGenerator();
|
||||
|
||||
il.Emit(FishTranspiler.Argument(0));
|
||||
|
||||
if (from.IsValueType)
|
||||
il.Emit(FishTranspiler.LoadIndirectly(from));
|
||||
|
||||
il.Emit(FishTranspiler.Call(method));
|
||||
il.Emit(FishTranspiler.Return);
|
||||
|
||||
return dm.CreateDelegate(from.IsValueType
|
||||
? typeof(RefConverterFunc<,>).MakeGenericType(from, to)
|
||||
: typeof(Func<,,>).MakeGenericType(from, typeof(IFormatProvider), to)).Method;
|
||||
}
|
||||
|
||||
private static MethodInfo? TryFindMethod(MethodInfo[] array, string name, Type[] parameters, Type returnType)
|
||||
{
|
||||
for (var i = array.Length; i-- > 0;)
|
||||
{
|
||||
var method = array[i];
|
||||
if (method.Name != name || method.ReturnType != returnType)
|
||||
continue;
|
||||
|
||||
var testedParameters = method.GetParameters();
|
||||
if (testedParameters.Length != parameters.Length)
|
||||
continue;
|
||||
|
||||
var parametersMatch = true;
|
||||
for (var j = testedParameters.Length; j-- > 0;)
|
||||
{
|
||||
if (testedParameters[j].ParameterType == parameters[j])
|
||||
continue;
|
||||
|
||||
parametersMatch = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (parametersMatch)
|
||||
return method;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static Func<TFrom, IFormatProvider, TTo> GetDefaultConverterFunc<TFrom, TTo>()
|
||||
=> (Func<TFrom, IFormatProvider, TTo>)GetConverterMethod<TFrom>(typeof(TTo))
|
||||
.CreateDelegate(typeof(Func<TFrom, IFormatProvider, TTo>));
|
||||
|
||||
internal static unsafe delegate*<TFrom, IFormatProvider, TTo> GetDefaultConverter<TFrom, TTo>()
|
||||
=> !typeof(TFrom).IsValueType
|
||||
&& (!typeof(TFrom).IsSealed || typeof(TFrom).IsInterface || typeof(TFrom).IsAbstract)
|
||||
? &Convert<TFrom>.Fallback<TTo>.FallbackMethod
|
||||
: (delegate*<TFrom, IFormatProvider, TTo>)GetConverterMethod<TFrom>(typeof(TTo))
|
||||
.MethodHandle.GetFunctionPointer();
|
||||
|
||||
private static TTo ConvertToType<TFrom, TTo>(TFrom from, IFormatProvider provider) where TFrom : IConvertible
|
||||
=> (TTo)from.ToType(typeof(TTo), provider);
|
||||
|
||||
private static string GetConvertMethodName(Type type)
|
||||
=> type == typeof(bool)
|
||||
? nameof(IConvertible.ToBoolean)
|
||||
: type == typeof(char)
|
||||
? nameof(IConvertible.ToChar)
|
||||
: type == typeof(sbyte)
|
||||
? nameof(IConvertible.ToSByte)
|
||||
: type == typeof(byte)
|
||||
? nameof(IConvertible.ToByte)
|
||||
: type == typeof(short)
|
||||
? nameof(IConvertible.ToInt16)
|
||||
: type == typeof(ushort)
|
||||
? nameof(IConvertible.ToUInt16)
|
||||
: type == typeof(int)
|
||||
? nameof(IConvertible.ToInt32)
|
||||
: type == typeof(uint)
|
||||
? nameof(IConvertible.ToUInt32)
|
||||
: type == typeof(long)
|
||||
? nameof(IConvertible.ToInt64)
|
||||
: type == typeof(ulong)
|
||||
? nameof(IConvertible.ToUInt64)
|
||||
: type == typeof(float)
|
||||
? nameof(IConvertible.ToSingle)
|
||||
: type == typeof(double)
|
||||
? nameof(IConvertible.ToDouble)
|
||||
: type == typeof(decimal)
|
||||
? nameof(IConvertible.ToDecimal)
|
||||
: type == typeof(DateTime)
|
||||
? nameof(IConvertible.ToDateTime)
|
||||
: type == typeof(string)
|
||||
? nameof(IConvertible.ToString)
|
||||
//: type == typeof(object)
|
||||
//? value
|
||||
: nameof(IConvertible.ToType);
|
||||
}
|
||||
private static readonly FishTable<Type, MethodInfo> _convertMethodsByType
|
||||
= typeof(IConvertible).GetMethods()
|
||||
.Where(static method
|
||||
=> method.GetParameters() is [{ ParameterType: var type }] && type == typeof(IFormatProvider))
|
||||
.ToFishTable(static info => info.ReturnType);
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "TypeParameterCanBeVariant")]
|
||||
file delegate TReturn RefConverterFunc<TInstance, TReturn>(TInstance instance, IFormatProvider provider);
|
||||
@@ -3,118 +3,95 @@
|
||||
// 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;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace FisheryLib.FunctionPointers;
|
||||
|
||||
public static class Equals<T>
|
||||
{
|
||||
public static readonly unsafe delegate*<T, T, bool> Default = (delegate*<T, T, bool>)FunctionPointers.Equals.MakeDelegate(typeof(T));
|
||||
public static readonly unsafe delegate*<T, T, bool> Default
|
||||
= (delegate*<T, T, bool>)FunctionPointers.Equals.EqualsMethods.GetFunctionPointer(typeof(T));
|
||||
}
|
||||
|
||||
public static class Equals
|
||||
{
|
||||
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
|
||||
public static class Methods
|
||||
{
|
||||
public static bool Byte(byte x, byte y)
|
||||
=> x == y;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Byte(byte x, byte y) => x == y;
|
||||
|
||||
public static bool Int(int x, int y)
|
||||
=> x == y;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Int(int x, int y) => x == y;
|
||||
|
||||
public static bool Uint(uint x, uint y)
|
||||
=> x == y;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Uint(uint x, uint y) => x == y;
|
||||
|
||||
public static bool Ushort(ushort x, ushort y)
|
||||
=> x == y;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Ushort(ushort x, ushort y) => x == y;
|
||||
|
||||
public static bool IntPtr(IntPtr x, IntPtr y)
|
||||
=> x == y;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IntPtr(IntPtr x, IntPtr y) => x == y;
|
||||
|
||||
public static bool String(string x, string y)
|
||||
=> string.Equals(x, y);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool String(string x, string y) => string.Equals(x, y);
|
||||
|
||||
public static unsafe bool VoidPtr(void* x, void* y)
|
||||
=> x == y;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe bool VoidPtr(void* x, void* y) => x == y;
|
||||
|
||||
public static bool RuntimeFieldHandle(RuntimeFieldHandle x, RuntimeFieldHandle y)
|
||||
=> x.Value == y.Value;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[SecuritySafeCritical]
|
||||
public static bool RuntimeFieldHandle(RuntimeFieldHandle x, RuntimeFieldHandle y) => x.Value == y.Value;
|
||||
|
||||
public static bool RuntimeTypeHandle(RuntimeTypeHandle x, RuntimeTypeHandle y)
|
||||
=> x.Value == y.Value;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[SecuritySafeCritical]
|
||||
public static bool RuntimeTypeHandle(RuntimeTypeHandle x, RuntimeTypeHandle y) => x.Value == y.Value;
|
||||
|
||||
public static bool RuntimeMethodHandle(RuntimeMethodHandle x, RuntimeMethodHandle y)
|
||||
=> x.Value == y.Value;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[SecuritySafeCritical]
|
||||
public static bool RuntimeMethodHandle(RuntimeMethodHandle x, RuntimeMethodHandle y) => x.Value == y.Value;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Label(Label x, Label y) => x.label == y.label;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Enum<T>(T x, T y) where T : struct
|
||||
=> JitHelpers.UnsafeEnumCast(x) == JitHelpers.UnsafeEnumCast(y);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool LongEnum<T>(T x, T y) where T : struct
|
||||
=> JitHelpers.UnsafeEnumCastLong(x) == JitHelpers.UnsafeEnumCastLong(y);
|
||||
|
||||
public static bool EquatableValueType<T>(T x, T y) where T : struct, IEquatable<T>
|
||||
=> x.Equals(y);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool EquatableValueType<T>(T x, T y) where T : struct, IEquatable<T> => x.Equals(y);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Nullable<T>(T? x, T? y) where T : struct, IEquatable<T>
|
||||
=> x.HasValue
|
||||
? y.HasValue && x.Value.Equals(y.Value)
|
||||
: !y.HasValue;
|
||||
? y.HasValue && x.Value.Equals(y.Value)
|
||||
: !y.HasValue;
|
||||
|
||||
public static bool EquatableReferenceType<T>(T x, T y) where T : IEquatable<T>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool EquatableReferenceType<T>(T? x, T? y) where T : class, IEquatable<T>
|
||||
=> x != null
|
||||
? y != null && x.Equals(y)
|
||||
: y == null;
|
||||
? y != null && x.Equals(y)
|
||||
: y == null;
|
||||
|
||||
public static bool Reference<T>(T x, T y) where T : class
|
||||
=> x == y;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Reference<T>(T x, T y) where T : class => x == y;
|
||||
|
||||
public static bool Object<T>(T x, T y)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Object<T>(T? x, T? y) where T : class
|
||||
=> x != null
|
||||
? y != null && x.Equals(y)
|
||||
: y == null;
|
||||
? y != null && x.Equals(y)
|
||||
: y == null;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool ValueType<T>(T x, T y) where T : struct => x.Equals(y);
|
||||
}
|
||||
|
||||
internal static unsafe IntPtr MakeDelegate(Type type)
|
||||
=> _equalsMethods.Where(m => !m.IsGenericMethod)
|
||||
.FirstOrDefault(m
|
||||
=> m.GetParameters().First().ParameterType == type)
|
||||
is { } specializedMethod
|
||||
? specializedMethod.GetFunctionPointer()
|
||||
: type.IsAssignableTo(typeof(IEquatable<>), type)
|
||||
? GetMethod(
|
||||
type.IsValueType
|
||||
? nameof(Methods.EquatableValueType)
|
||||
: nameof(Methods.EquatableReferenceType))
|
||||
.MakeGenericMethod(type).GetFunctionPointer()
|
||||
: type.IsNullable(out var genericArgument)
|
||||
&& genericArgument.IsAssignableTo(typeof(IEquatable<>), genericArgument)
|
||||
? GetMethod(nameof(Methods.Nullable)).MakeGenericMethod(genericArgument).GetFunctionPointer()
|
||||
: GetMethod(
|
||||
type.IsEnum
|
||||
? Type.GetTypeCode(Enum.GetUnderlyingType(type))
|
||||
switch
|
||||
{
|
||||
TypeCode.Int16
|
||||
or TypeCode.SByte
|
||||
or TypeCode.Byte
|
||||
or TypeCode.UInt16
|
||||
or TypeCode.Int32
|
||||
or TypeCode.UInt32 => nameof(Methods.Enum),
|
||||
TypeCode.Int64
|
||||
or TypeCode.UInt64 => nameof(Methods.LongEnum),
|
||||
_ => ThrowHelper.ThrowNotSupportedException<string>()
|
||||
}
|
||||
: nameof(Methods.Object)
|
||||
).MakeGenericMethod(type).GetFunctionPointer();
|
||||
|
||||
private static MethodInfo GetMethod(string name)
|
||||
=> Array.Find(_equalsMethods, m => m.Name == name);
|
||||
|
||||
private static MethodInfo[] _equalsMethods
|
||||
= typeof(Methods)
|
||||
.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly)
|
||||
.Where(m
|
||||
=> m.ReturnType == typeof(bool)
|
||||
&& m.GetParameters().Length == 2)
|
||||
.ToArray();
|
||||
public static readonly MethodFactory.Equals EqualsMethods = new(typeof(Methods), "FisheryEqualsMethod_", false);
|
||||
}
|
||||
@@ -3,118 +3,96 @@
|
||||
// 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;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace FisheryLib.FunctionPointers;
|
||||
|
||||
public static class EqualsByRef<T>
|
||||
{
|
||||
public static readonly unsafe delegate*<ref T, ref T, bool> Default = (delegate*<ref T, ref T, bool>)EqualsByRef.MakeDelegate(typeof(T));
|
||||
public static readonly unsafe delegate*<ref T, ref T, bool> Default
|
||||
= (delegate*<ref T, ref T, bool>)EqualsByRef.EqualsMethods.GetFunctionPointer(typeof(T));
|
||||
}
|
||||
|
||||
public static class EqualsByRef
|
||||
{
|
||||
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
|
||||
public static class Methods
|
||||
{
|
||||
public static bool Byte(ref byte x, ref byte y)
|
||||
=> x == y;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Byte(ref byte x, ref byte y) => x == y;
|
||||
|
||||
public static bool Int(ref int x, ref int y)
|
||||
=> x == y;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Int(ref int x, ref int y) => x == y;
|
||||
|
||||
public static bool Uint(ref uint x, ref uint y)
|
||||
=> x == y;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Uint(ref uint x, ref uint y) => x == y;
|
||||
|
||||
public static bool Ushort(ref ushort x, ref ushort y)
|
||||
=> x == y;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Ushort(ref ushort x, ref ushort y) => x == y;
|
||||
|
||||
public static bool IntPtr(ref IntPtr x, ref IntPtr y)
|
||||
=> x == y;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IntPtr(ref IntPtr x, ref IntPtr y) => x == y;
|
||||
|
||||
public static bool String(ref string x, ref string y)
|
||||
=> string.Equals(x, y);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool String(ref string x, ref string y) => string.Equals(x, y);
|
||||
|
||||
public static unsafe bool VoidPtr(ref void* x, ref void* y)
|
||||
=> x == y;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe bool VoidPtr(ref void* x, ref void* y) => x == y;
|
||||
|
||||
public static bool RuntimeFieldHandle(ref RuntimeFieldHandle x, ref RuntimeFieldHandle y)
|
||||
=> x.Value == y.Value;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[SecuritySafeCritical]
|
||||
public static bool RuntimeFieldHandle(ref RuntimeFieldHandle x, ref RuntimeFieldHandle y) => x.Value == y.Value;
|
||||
|
||||
public static bool RuntimeTypeHandle(ref RuntimeTypeHandle x, ref RuntimeTypeHandle y)
|
||||
=> x.Value == y.Value;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[SecuritySafeCritical]
|
||||
public static bool RuntimeTypeHandle(ref RuntimeTypeHandle x, ref RuntimeTypeHandle y) => x.Value == y.Value;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[SecuritySafeCritical]
|
||||
public static bool RuntimeMethodHandle(ref RuntimeMethodHandle x, ref RuntimeMethodHandle y)
|
||||
=> x.Value == y.Value;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Label(ref Label x, ref Label y) => x.label == y.label;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Enum<T>(ref T x, ref T y) where T : struct
|
||||
=> JitHelpers.UnsafeEnumCast(x) == JitHelpers.UnsafeEnumCast(y);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool LongEnum<T>(ref T x, ref T y) where T : struct
|
||||
=> JitHelpers.UnsafeEnumCastLong(x) == JitHelpers.UnsafeEnumCastLong(y);
|
||||
|
||||
public static bool EquatableValueType<T>(ref T x, ref T y) where T : struct, IEquatable<T>
|
||||
=> x.Equals(y);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool EquatableValueType<T>(ref T x, ref T y) where T : struct, IEquatable<T> => x.Equals(y);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Nullable<T>(ref T? x, ref T? y) where T : struct, IEquatable<T>
|
||||
=> x.HasValue
|
||||
? y.HasValue && x.Value.Equals(y.Value)
|
||||
: !y.HasValue;
|
||||
? y.HasValue && x.Value.Equals(y.Value)
|
||||
: !y.HasValue;
|
||||
|
||||
public static bool EquatableReferenceType<T>(ref T x, ref T y) where T : IEquatable<T>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool EquatableReferenceType<T>(ref T? x, ref T? y) where T : class, IEquatable<T>
|
||||
=> x != null
|
||||
? y != null && x.Equals(y)
|
||||
: y == null;
|
||||
? y != null && x.Equals(y)
|
||||
: y == null;
|
||||
|
||||
public static bool Reference<T>(ref T x, ref T y) where T : class
|
||||
=> x == y;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Reference<T>(ref T x, ref T y) where T : class => x == y;
|
||||
|
||||
public static bool Object<T>(ref T x, ref T y)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Object<T>(ref T? x, ref T? y) where T : class
|
||||
=> x != null
|
||||
? y != null && x.Equals(y)
|
||||
: y == null;
|
||||
? y != null && x.Equals(y)
|
||||
: y == null;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool ValueType<T>(ref T x, ref T y) where T : struct => x.Equals(y);
|
||||
}
|
||||
|
||||
internal static unsafe IntPtr MakeDelegate(Type type)
|
||||
=> _equalsMethods.Where(m => !m.IsGenericMethod)
|
||||
.FirstOrDefault(m
|
||||
=> m.GetParameters().First().ParameterType.GetElementType() == type)
|
||||
is { } specializedMethod
|
||||
? specializedMethod.GetFunctionPointer()
|
||||
: type.IsAssignableTo(typeof(IEquatable<>), type)
|
||||
? GetMethod(
|
||||
type.IsValueType
|
||||
? nameof(Methods.EquatableValueType)
|
||||
: nameof(Methods.EquatableReferenceType))
|
||||
.MakeGenericMethod(type).GetFunctionPointer()
|
||||
: type.IsNullable(out var genericArgument)
|
||||
&& genericArgument.IsAssignableTo(typeof(IEquatable<>), genericArgument)
|
||||
? GetMethod(nameof(Methods.Nullable)).MakeGenericMethod(genericArgument).GetFunctionPointer()
|
||||
: GetMethod(
|
||||
type.IsEnum
|
||||
? Type.GetTypeCode(Enum.GetUnderlyingType(type))
|
||||
switch
|
||||
{
|
||||
TypeCode.Int16
|
||||
or TypeCode.SByte
|
||||
or TypeCode.Byte
|
||||
or TypeCode.UInt16
|
||||
or TypeCode.Int32
|
||||
or TypeCode.UInt32 => nameof(Methods.Enum),
|
||||
TypeCode.Int64
|
||||
or TypeCode.UInt64 => nameof(Methods.LongEnum),
|
||||
_ => ThrowHelper.ThrowNotSupportedException<string>()
|
||||
}
|
||||
: nameof(Methods.Object)
|
||||
).MakeGenericMethod(type).GetFunctionPointer();
|
||||
|
||||
private static MethodInfo GetMethod(string name)
|
||||
=> Array.Find(_equalsMethods, m => m.Name == name);
|
||||
|
||||
private static MethodInfo[] _equalsMethods
|
||||
= typeof(Methods)
|
||||
.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly)
|
||||
.Where(m
|
||||
=> m.ReturnType == typeof(bool)
|
||||
&& m.GetParameters().Length == 2)
|
||||
.ToArray();
|
||||
public static readonly MethodFactory.Equals EqualsMethods = new(typeof(Methods), "FisheryEqualsByRefMethod_", true);
|
||||
}
|
||||
@@ -3,104 +3,50 @@
|
||||
// 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;
|
||||
using System.Runtime.CompilerServices;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace FisheryLib.FunctionPointers;
|
||||
|
||||
public static class GetHashCode<T>
|
||||
{
|
||||
public static readonly unsafe delegate*<T, int> Default = (delegate*<T, int>)FunctionPointers.GetHashCode.MakeDelegate(typeof(T));
|
||||
public static readonly unsafe delegate*<T, int> Default
|
||||
= (delegate*<T, int>)FunctionPointers.GetHashCode.HashCodeMethods.GetFunctionPointer(typeof(T));
|
||||
}
|
||||
|
||||
public static class GetHashCode
|
||||
{
|
||||
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
|
||||
public static class Methods
|
||||
{
|
||||
public static int Byte(byte obj)
|
||||
=> obj;
|
||||
public static int Byte(byte obj) => obj;
|
||||
|
||||
public static int Int(int obj)
|
||||
=> obj;
|
||||
public static int Int(int obj) => obj;
|
||||
|
||||
public static int Uint(uint obj)
|
||||
=> (int)obj;
|
||||
public static int Uint(uint obj) => (int)obj;
|
||||
|
||||
public static int Ushort(ushort obj)
|
||||
=> obj;
|
||||
public static int Ushort(ushort obj) => obj;
|
||||
|
||||
public static unsafe int VoidPtr(void* obj)
|
||||
=> (int)obj;
|
||||
public static unsafe int VoidPtr(void* obj) => (int)obj;
|
||||
|
||||
public static int Enum<T>(T obj) where T : struct
|
||||
=> JitHelpers.UnsafeEnumCast(obj).GetHashCode();
|
||||
public static int Enum<T>(T obj) where T : struct => JitHelpers.UnsafeEnumCast(obj).GetHashCode();
|
||||
|
||||
public static int SbyteEnum<T>(T obj) where T : struct
|
||||
=> ((sbyte)JitHelpers.UnsafeEnumCast(obj)).GetHashCode();
|
||||
public static int SbyteEnum<T>(T obj) where T : struct => ((sbyte)JitHelpers.UnsafeEnumCast(obj)).GetHashCode();
|
||||
|
||||
public static int ShortEnum<T>(T obj) where T : struct
|
||||
=> ((short)JitHelpers.UnsafeEnumCast(obj)).GetHashCode();
|
||||
public static int ShortEnum<T>(T obj) where T : struct => ((short)JitHelpers.UnsafeEnumCast(obj)).GetHashCode();
|
||||
|
||||
public static int LongEnum<T>(T obj) where T : struct
|
||||
=> JitHelpers.UnsafeEnumCastLong(obj).GetHashCode();
|
||||
public static int LongEnum<T>(T obj) where T : struct => JitHelpers.UnsafeEnumCastLong(obj).GetHashCode();
|
||||
|
||||
public static int ValueType<T>(T obj) where T : struct
|
||||
=> obj.GetHashCode();
|
||||
public static int ValueType<T>(T obj) where T : struct => obj.GetHashCode();
|
||||
|
||||
public static int Nullable<T>(T? obj) where T : struct
|
||||
=> obj.GetHashCode();
|
||||
public static unsafe int Nullable<T>(T? obj) where T : struct
|
||||
=> !obj.HasValue ? 0 : GetHashCode<T>.Default(obj.GetValueOrDefault());
|
||||
|
||||
public static int Reference<T>(T obj) where T : class
|
||||
=> RuntimeHelpers.GetHashCode(obj);
|
||||
public static int Reference<T>(T obj) where T : class => RuntimeHelpers.GetHashCode(obj);
|
||||
|
||||
public static int Object<T>(T obj)
|
||||
=> obj?.GetHashCode() ?? 0;
|
||||
public static int Object<T>(T? obj) where T : class => obj?.GetHashCode() ?? 0;
|
||||
}
|
||||
|
||||
internal static unsafe IntPtr MakeDelegate(Type type)
|
||||
=> _getHashCodeMethods.Where(m => !m.IsGenericMethod)
|
||||
.FirstOrDefault(m
|
||||
=> m.GetParameters().First().ParameterType == type)
|
||||
is { } specializedMethod
|
||||
? specializedMethod.GetFunctionPointer()
|
||||
: type.IsNullable(out var genericArgument)
|
||||
&& genericArgument.IsAssignableTo(typeof(IEquatable<>), genericArgument)
|
||||
? GetMethod(nameof(Methods.Nullable)).MakeGenericMethod(genericArgument).GetFunctionPointer()
|
||||
: GetMethod(
|
||||
type.IsEnum
|
||||
? Type.GetTypeCode(Enum.GetUnderlyingType(type))
|
||||
switch
|
||||
{
|
||||
TypeCode.Int16 => nameof(Methods.ShortEnum),
|
||||
TypeCode.SByte => nameof(Methods.SbyteEnum),
|
||||
TypeCode.Byte
|
||||
or TypeCode.UInt16
|
||||
or TypeCode.Int32
|
||||
or TypeCode.UInt32 => nameof(Methods.Enum),
|
||||
TypeCode.Int64
|
||||
or TypeCode.UInt64 => nameof(Methods.LongEnum),
|
||||
_ => ThrowHelper.ThrowNotSupportedException<string>()
|
||||
}
|
||||
: type.IsValueType
|
||||
? nameof(Methods.ValueType)
|
||||
: nameof(Methods.Object)
|
||||
).MakeGenericMethod(type).GetFunctionPointer();
|
||||
|
||||
private static MethodInfo GetMethod(string name)
|
||||
=> Array.Find(_getHashCodeMethods, m => m.Name == name);
|
||||
|
||||
private static MethodInfo[] _getHashCodeMethods
|
||||
= typeof(Methods)
|
||||
.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly)
|
||||
.Where(m
|
||||
=> m.ReturnType == typeof(int)
|
||||
&& m.GetParameters().Length == 1)
|
||||
.ToArray();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe delegate*<V, int> Cast<T,V>(delegate*<T, int> @delegate)
|
||||
=> (delegate*<V, int>)@delegate;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static unsafe delegate*<T, int> CastVoid<T>(delegate*<void*, int> @delegate)
|
||||
=> (delegate*<T, int>)@delegate;
|
||||
public static readonly MethodFactory.HashCode HashCodeMethods
|
||||
= new(typeof(Methods), "FisheryHashCodeMethod_", false);
|
||||
}
|
||||
@@ -3,36 +3,33 @@
|
||||
// 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;
|
||||
using System.Runtime.CompilerServices;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace FisheryLib.FunctionPointers;
|
||||
|
||||
public static class GetHashCodeByRef<T>
|
||||
{
|
||||
public static readonly unsafe delegate*<ref T, int> Default = (delegate*<ref T, int>)GetHashCodeByRef.MakeDelegate(typeof(T));
|
||||
public static readonly unsafe delegate*<ref T, int> Default
|
||||
= (delegate*<ref T, int>)GetHashCodeByRef.HashCodeMethods.GetFunctionPointer(typeof(T));
|
||||
}
|
||||
|
||||
public static class GetHashCodeByRef
|
||||
{
|
||||
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
|
||||
public static class Methods
|
||||
{
|
||||
public static int Byte(ref byte obj)
|
||||
=> obj;
|
||||
public static int Byte(ref byte obj) => obj;
|
||||
|
||||
public static int Int(ref int obj)
|
||||
=> obj;
|
||||
public static int Int(ref int obj) => obj;
|
||||
|
||||
public static int Uint(ref uint obj)
|
||||
=> (int)obj;
|
||||
public static int Uint(ref uint obj) => (int)obj;
|
||||
|
||||
public static int Ushort(ref ushort obj)
|
||||
=> obj;
|
||||
public static int Ushort(ref ushort obj) => obj;
|
||||
|
||||
public static unsafe int VoidPtr(ref void* obj)
|
||||
=> (int)obj;
|
||||
public static unsafe int VoidPtr(ref void* obj) => (int)obj;
|
||||
|
||||
public static int Enum<T>(ref T obj) where T : struct
|
||||
=> JitHelpers.UnsafeEnumCast(obj).GetHashCode();
|
||||
public static int Enum<T>(ref T obj) where T : struct => JitHelpers.UnsafeEnumCast(obj).GetHashCode();
|
||||
|
||||
public static int SbyteEnum<T>(ref T obj) where T : struct
|
||||
=> ((sbyte)JitHelpers.UnsafeEnumCast(obj)).GetHashCode();
|
||||
@@ -40,59 +37,17 @@ public static class GetHashCodeByRef
|
||||
public static int ShortEnum<T>(ref T obj) where T : struct
|
||||
=> ((short)JitHelpers.UnsafeEnumCast(obj)).GetHashCode();
|
||||
|
||||
public static int LongEnum<T>(ref T obj) where T : struct
|
||||
=> JitHelpers.UnsafeEnumCastLong(obj).GetHashCode();
|
||||
public static int LongEnum<T>(ref T obj) where T : struct => JitHelpers.UnsafeEnumCastLong(obj).GetHashCode();
|
||||
|
||||
public static int ValueType<T>(ref T obj) where T : struct
|
||||
=> obj.GetHashCode();
|
||||
public static int ValueType<T>(ref T obj) where T : struct => obj.GetHashCode();
|
||||
|
||||
public static int Nullable<T>(ref T? obj) where T : struct
|
||||
=> obj.GetHashCode();
|
||||
public static int Nullable<T>(ref T? obj) where T : struct => obj.GetHashCode();
|
||||
|
||||
public static int Reference<T>(ref T obj) where T : class
|
||||
=> RuntimeHelpers.GetHashCode(obj);
|
||||
public static int Reference<T>(ref T obj) where T : class => RuntimeHelpers.GetHashCode(obj);
|
||||
|
||||
public static int Object<T>(ref T obj)
|
||||
=> obj?.GetHashCode() ?? 0;
|
||||
public static int Object<T>(ref T? obj) where T : class => obj?.GetHashCode() ?? 0;
|
||||
}
|
||||
|
||||
internal static unsafe IntPtr MakeDelegate(Type type)
|
||||
=> _getHashCodeMethods.Where(m => !m.IsGenericMethod)
|
||||
.FirstOrDefault(m
|
||||
=> m.GetParameters().First().ParameterType.GetElementType() == type)
|
||||
is { } specializedMethod
|
||||
? specializedMethod.GetFunctionPointer()
|
||||
: type.IsNullable(out var genericArgument)
|
||||
&& genericArgument.IsAssignableTo(typeof(IEquatable<>), genericArgument)
|
||||
? GetMethod(nameof(Methods.Nullable)).MakeGenericMethod(genericArgument).GetFunctionPointer()
|
||||
: GetMethod(
|
||||
type.IsEnum
|
||||
? Type.GetTypeCode(Enum.GetUnderlyingType(type))
|
||||
switch
|
||||
{
|
||||
TypeCode.Int16 => nameof(Methods.ShortEnum),
|
||||
TypeCode.SByte => nameof(Methods.SbyteEnum),
|
||||
TypeCode.Byte
|
||||
or TypeCode.UInt16
|
||||
or TypeCode.Int32
|
||||
or TypeCode.UInt32 => nameof(Methods.Enum),
|
||||
TypeCode.Int64
|
||||
or TypeCode.UInt64 => nameof(Methods.LongEnum),
|
||||
_ => ThrowHelper.ThrowNotSupportedException<string>()
|
||||
}
|
||||
: type.IsValueType
|
||||
? nameof(Methods.ValueType)
|
||||
: nameof(Methods.Object)
|
||||
).MakeGenericMethod(type).GetFunctionPointer();
|
||||
|
||||
private static MethodInfo GetMethod(string name)
|
||||
=> Array.Find(_getHashCodeMethods, m => m.Name == name);
|
||||
|
||||
private static MethodInfo[] _getHashCodeMethods
|
||||
= typeof(Methods)
|
||||
.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly)
|
||||
.Where(m
|
||||
=> m.ReturnType == typeof(int)
|
||||
&& m.GetParameters().Length == 1)
|
||||
.ToArray();
|
||||
public static readonly MethodFactory.HashCode HashCodeMethods
|
||||
= new(typeof(Methods), "FisheryHashCodeByRefMethod_", true);
|
||||
}
|
||||
@@ -0,0 +1,393 @@
|
||||
// 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.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection.Emit;
|
||||
using System.Security;
|
||||
|
||||
namespace FisheryLib.FunctionPointers;
|
||||
|
||||
public abstract class MethodFactory
|
||||
{
|
||||
protected readonly MethodInfo[] _methods;
|
||||
|
||||
protected readonly ConcurrentDictionary<Type, MethodInfo> _methodsByType = new();
|
||||
|
||||
protected readonly string _namePrefix;
|
||||
|
||||
protected readonly bool _byRef;
|
||||
|
||||
public MethodInfo GetGeneric(string name, params Type[] typeArguments)
|
||||
=> GetNamed(name).MakeGenericMethod(typeArguments);
|
||||
|
||||
public MethodInfo GetNamed(string name)
|
||||
{
|
||||
foreach (var method in _methods)
|
||||
{
|
||||
if (method.Name == name)
|
||||
return method;
|
||||
}
|
||||
|
||||
return ThrowHelper.ThrowArgumentException<MethodInfo>($"No method found with name: {name}");
|
||||
}
|
||||
|
||||
protected MethodInfo? GetSpecializedMethod(Type type)
|
||||
{
|
||||
if (_byRef)
|
||||
type = type.MakeByRefType();
|
||||
|
||||
for (var i = 0; i < _methods.Length; i++)
|
||||
{
|
||||
var method = _methods[i];
|
||||
if (!method.IsGenericMethod && method.GetParameters()[0].ParameterType == type)
|
||||
return method;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public MethodInfo GetForType(Type type)
|
||||
=> _methodsByType.TryGetValue(type, out var value)
|
||||
? value
|
||||
: _methodsByType[type] = GetOrMakeMethodForType(type);
|
||||
|
||||
protected abstract MethodInfo GetOrMakeMethodForType(Type type);
|
||||
|
||||
[SecuritySafeCritical]
|
||||
public IntPtr GetFunctionPointer(Type type) => GetForType(type).GetFunctionPointer();
|
||||
|
||||
protected static MethodInfo[] GetMethodInfoArray(Type type, Func<MethodInfo, bool> predicate)
|
||||
=> type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly)
|
||||
.Where(predicate).ToArray();
|
||||
|
||||
protected MethodFactory(MethodInfo[] methods, string namePrefix, bool byRef)
|
||||
{
|
||||
_byRef = byRef;
|
||||
_methods = methods;
|
||||
_namePrefix = namePrefix;
|
||||
}
|
||||
|
||||
public new class Equals : MethodFactory
|
||||
{
|
||||
protected override MethodInfo GetOrMakeMethodForType(Type type)
|
||||
=> GetSpecializedMethod(type)
|
||||
?? (type.IsAssignableTo(typeof(IEquatable<>), type)
|
||||
? GetGeneric(type.IsValueType
|
||||
? nameof(FunctionPointers.Equals.Methods.EquatableValueType)
|
||||
: nameof(FunctionPointers.Equals.Methods.EquatableReferenceType), type)
|
||||
: type.IsNullable(out var genericArgument)
|
||||
&& genericArgument.IsAssignableTo(typeof(IEquatable<>), genericArgument)
|
||||
? GetGeneric(nameof(FunctionPointers.Equals.Methods.Nullable), genericArgument)
|
||||
: type.IsEnum
|
||||
? GetGeneric(Type.GetTypeCode(Enum.GetUnderlyingType(type))
|
||||
switch
|
||||
{
|
||||
TypeCode.Int16
|
||||
or TypeCode.SByte
|
||||
or TypeCode.Byte
|
||||
or TypeCode.UInt16
|
||||
or TypeCode.Int32
|
||||
or TypeCode.UInt32 => nameof(FunctionPointers.Equals.Methods.Enum),
|
||||
TypeCode.Int64
|
||||
or TypeCode.UInt64 => nameof(FunctionPointers.Equals.Methods.LongEnum),
|
||||
_ => ThrowHelper.ThrowNotSupportedException<string>()
|
||||
}, type)
|
||||
: type.IsValueType
|
||||
? TryGetMethodByName(type) ?? CompileEqualsMethod(type)
|
||||
: GetGeneric(nameof(FunctionPointers.Equals.Methods.Object), type));
|
||||
|
||||
protected MethodInfo? TryGetMethodByName(Type type)
|
||||
{
|
||||
var methods = type.GetMethods(AccessTools.allDeclared);
|
||||
if (_byRef)
|
||||
type = type.MakeByRefType();
|
||||
|
||||
MethodInfo? methodThatNeedsWrapper = null;
|
||||
|
||||
for (var i = 0; i < methods.Length; i++)
|
||||
{
|
||||
var method = methods[i];
|
||||
if (method.Name is not ("Equals" or "op_Equality")
|
||||
|| method.ReturnType != typeof(bool))
|
||||
continue;
|
||||
|
||||
var parameters = method.GetParameters();
|
||||
var isGood = (parameters.Length == 2 && method.IsStatic) || parameters.Length == 1;
|
||||
for (var j = 0; j < parameters.Length; j++)
|
||||
{
|
||||
if (parameters[j].ParameterType != type)
|
||||
isGood = false;
|
||||
}
|
||||
|
||||
if (!isGood)
|
||||
continue;
|
||||
|
||||
if (_byRef || method.IsStatic)
|
||||
return method;
|
||||
else
|
||||
methodThatNeedsWrapper = method;
|
||||
}
|
||||
|
||||
return methodThatNeedsWrapper != null ? CompileWrapperMethod(type, methodThatNeedsWrapper) : null;
|
||||
}
|
||||
|
||||
protected MethodInfo CompileWrapperMethod(Type type, MethodInfo method)
|
||||
{
|
||||
#if DEBUG
|
||||
Log.Message($"Method {method.FullDescription()} determined to be in need of wrapper");
|
||||
#endif
|
||||
|
||||
return GetGeneric(nameof(FunctionPointers.Equals.Methods.ValueType), type);
|
||||
|
||||
// var dm = new DynamicMethod($"{_namePrefix}{type.Name}", typeof(bool),
|
||||
// new[] { type, type }, type, true);
|
||||
// var il = dm.GetILGenerator();
|
||||
//
|
||||
// il.Emit(FishTranspiler.ArgumentAddress(0));
|
||||
// il.Emit(FishTranspiler.Argument(1));
|
||||
// il.Emit(FishTranspiler.Call(method)); // throws NullRef somehow
|
||||
// il.Emit(FishTranspiler.Return);
|
||||
//
|
||||
// return dm.CreateDelegate(typeof(Func<,,>).MakeGenericType(type, type, typeof(bool))).Method;
|
||||
}
|
||||
|
||||
protected MethodInfo CompileEqualsMethod(Type type)
|
||||
{
|
||||
try
|
||||
{
|
||||
var dm = new DynamicMethod($"{_namePrefix}{type.Name}", typeof(bool),
|
||||
_byRef ? new[] { type.MakeByRefType(), type.MakeByRefType() } : new[] { type, type }, type, true);
|
||||
var il = dm.GetILGenerator();
|
||||
|
||||
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
|
||||
var needsJump = fields.Length > 1;
|
||||
|
||||
var falseLabel = needsJump ? il.DefineLabel() : default;
|
||||
var trueLabel = needsJump ? il.DefineLabel() : default;
|
||||
|
||||
for (var i = 0; i < fields.Length; i++)
|
||||
{
|
||||
var field = fields[i];
|
||||
|
||||
for (var j = 0; j < 2; j++)
|
||||
{
|
||||
il.Emit(FishTranspiler.Argument(j));
|
||||
il.Emit(FishTranspiler.Field(field));
|
||||
}
|
||||
|
||||
il.Emit(FishTranspiler.Call(GetForType(field.FieldType)));
|
||||
|
||||
if (needsJump)
|
||||
{
|
||||
il.Emit(i == fields.Length - 1
|
||||
? FishTranspiler.GoTo_Short(trueLabel)
|
||||
: FishTranspiler.IfFalse_Short(falseLabel));
|
||||
}
|
||||
}
|
||||
|
||||
if (needsJump)
|
||||
{
|
||||
il.MarkLabel(falseLabel);
|
||||
il.Emit(FishTranspiler.Constant(0));
|
||||
|
||||
il.MarkLabel(trueLabel);
|
||||
}
|
||||
|
||||
il.Emit(FishTranspiler.Return);
|
||||
|
||||
return dm.CreateDelegate(
|
||||
(_byRef ? typeof(ByRefFunc<,,>) : typeof(Func<,,>)).MakeGenericType(type, type, typeof(bool)))
|
||||
.Method;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return LogErrorAndReturnFallback(type, e);
|
||||
}
|
||||
}
|
||||
|
||||
private MethodInfo LogErrorAndReturnFallback(Type type, Exception e)
|
||||
{
|
||||
Log.Error($"Failed compiling specialized Equals method for {
|
||||
type.FullDescription()}. Returning fallback instead.\n{e}{new StackTrace()}");
|
||||
|
||||
return GetGeneric(nameof(FunctionPointers.Equals.Methods.ValueType), type);
|
||||
}
|
||||
|
||||
private static MethodInfo[] GetMethodInfoArraySafely(Type type)
|
||||
{
|
||||
try
|
||||
{
|
||||
return GetMethodInfoArray(type, static m
|
||||
=> m.ReturnType == typeof(bool)
|
||||
&& m.GetParameters().Length == 2);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"Exception while preparing method info array for type {type}:\n{e}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public Equals(Type type, string dynamicMethodNamePrefix, bool byRef)
|
||||
: base(GetMethodInfoArraySafely(type), dynamicMethodNamePrefix, byRef)
|
||||
{
|
||||
}
|
||||
|
||||
private delegate TResult ByRefFunc<T1, T2, out TResult>(ref T1 arg1, ref T2 arg2);
|
||||
}
|
||||
|
||||
public class HashCode : MethodFactory
|
||||
{
|
||||
protected override MethodInfo GetOrMakeMethodForType(Type type)
|
||||
=> GetSpecializedMethod(type)
|
||||
?? (type.IsNullable(out var genericArgument)
|
||||
&& genericArgument.IsAssignableTo(typeof(IEquatable<>), genericArgument)
|
||||
? GetGeneric(nameof(FunctionPointers.GetHashCode.Methods.Nullable), genericArgument)
|
||||
: type.IsEnum
|
||||
? GetGeneric(Type.GetTypeCode(Enum.GetUnderlyingType(type))
|
||||
switch
|
||||
{
|
||||
TypeCode.Int16 => nameof(FunctionPointers.GetHashCode.Methods.ShortEnum),
|
||||
TypeCode.SByte => nameof(FunctionPointers.GetHashCode.Methods.SbyteEnum),
|
||||
TypeCode.Byte
|
||||
or TypeCode.UInt16
|
||||
or TypeCode.Int32
|
||||
or TypeCode.UInt32 => nameof(FunctionPointers.GetHashCode.Methods.Enum),
|
||||
TypeCode.Int64
|
||||
or TypeCode.UInt64 => nameof(FunctionPointers.GetHashCode.Methods.LongEnum),
|
||||
_ => ThrowHelper.ThrowNotSupportedException<string>()
|
||||
}, type)
|
||||
: type.IsValueType
|
||||
? TryGetMethodByName(type) ?? CompileHashCodeMethod(type)
|
||||
: GetGeneric(nameof(FunctionPointers.GetHashCode.Methods.Object), type));
|
||||
|
||||
protected MethodInfo? TryGetMethodByName(Type type)
|
||||
{
|
||||
var methods = type.GetMethods(AccessTools.allDeclared);
|
||||
if (_byRef)
|
||||
type = type.MakeByRefType();
|
||||
|
||||
MethodInfo? methodThatNeedsWrapper = null;
|
||||
|
||||
for (var i = 0; i < methods.Length; i++)
|
||||
{
|
||||
var method = methods[i];
|
||||
if (method.Name != "GetHashCode"
|
||||
|| method.ReturnType != typeof(int))
|
||||
continue;
|
||||
|
||||
var parameters = method.GetParameters();
|
||||
var isGood = (parameters.Length == 1 && method.IsStatic) || parameters.Length == 0;
|
||||
for (var j = 0; j < parameters.Length; j++)
|
||||
{
|
||||
if (parameters[j].ParameterType != type)
|
||||
isGood = false;
|
||||
}
|
||||
|
||||
if (!isGood)
|
||||
continue;
|
||||
|
||||
if (_byRef || method.IsStatic)
|
||||
return method;
|
||||
else
|
||||
methodThatNeedsWrapper = method;
|
||||
}
|
||||
|
||||
return methodThatNeedsWrapper != null ? CompileWrapperMethod(type, methodThatNeedsWrapper) : null;
|
||||
}
|
||||
|
||||
protected MethodInfo CompileWrapperMethod(Type type, MethodInfo method)
|
||||
{
|
||||
#if DEBUG
|
||||
Log.Message($"Method {method.FullDescription()} determined to be in need of wrapper");
|
||||
#endif
|
||||
|
||||
return GetGeneric(nameof(FunctionPointers.GetHashCode.Methods.ValueType), type);
|
||||
|
||||
// var dm = new DynamicMethod($"{_namePrefix}{type.Name}", typeof(int),
|
||||
// new[] { type }, type, true);
|
||||
// var il = dm.GetILGenerator();
|
||||
//
|
||||
// il.Emit(FishTranspiler.ArgumentAddress(0));
|
||||
// il.Emit(FishTranspiler.Call(method));
|
||||
// il.Emit(FishTranspiler.Return);
|
||||
//
|
||||
// return dm.CreateDelegate(typeof(HashCodeGetter<>).MakeGenericType(type)).Method;
|
||||
}
|
||||
|
||||
private MethodInfo CompileHashCodeMethod(Type type)
|
||||
{
|
||||
try
|
||||
{
|
||||
var dm = new DynamicMethod($"{_namePrefix}{type.Name}", typeof(int),
|
||||
_byRef ? new[] { type.MakeByRefType() } : new[] { type }, type, true);
|
||||
var il = dm.GetILGenerator();
|
||||
|
||||
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
|
||||
for (var i = 0; i < fields.Length; i++)
|
||||
{
|
||||
var field = fields[i];
|
||||
|
||||
il.Emit(FishTranspiler.Argument(0));
|
||||
il.Emit(FishTranspiler.Field(field));
|
||||
|
||||
if (field.FieldType != typeof(int))
|
||||
il.Emit(FishTranspiler.Call(GetForType(field.FieldType)));
|
||||
|
||||
if (i < 1)
|
||||
continue;
|
||||
|
||||
il.Emit(FishTranspiler.Call<Func<int, int, int>>(i == 1
|
||||
? global::FisheryLib.HashCode.Combine
|
||||
: global::FisheryLib.HashCode.CombineWith));
|
||||
}
|
||||
|
||||
il.Emit(FishTranspiler.Return);
|
||||
|
||||
return dm.CreateDelegate((_byRef ? typeof(ByRefHashCodeGetter<>) : typeof(HashCodeGetter<>))
|
||||
.MakeGenericType(type)).Method;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return LogErrorAndReturnFallback(type, e);
|
||||
}
|
||||
}
|
||||
|
||||
private MethodInfo LogErrorAndReturnFallback(Type type, Exception e)
|
||||
{
|
||||
Log.Error($"Failed compiling specialized GetHashCode method for {
|
||||
type.FullDescription()}. Returning fallback instead.\n{e}{new StackTrace()}");
|
||||
|
||||
return GetGeneric(nameof(FunctionPointers.GetHashCode.Methods.ValueType), type);
|
||||
}
|
||||
|
||||
private static MethodInfo[] GetMethodInfoArraySafely(Type type)
|
||||
{
|
||||
try
|
||||
{
|
||||
return GetMethodInfoArray(type, static m
|
||||
=> m.ReturnType == typeof(int)
|
||||
&& m.GetParameters().Length == 1);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"Exception while preparing method info array for type {type}:\n{e}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public HashCode(Type type, string dynamicMethodNamePrefix, bool byRef)
|
||||
: base(GetMethodInfoArraySafely(type), dynamicMethodNamePrefix, byRef)
|
||||
{
|
||||
}
|
||||
|
||||
private delegate int ByRefHashCodeGetter<T>(ref T arg);
|
||||
private delegate int HashCodeGetter<in T>(T arg);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user