// 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.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; using System.Reflection.Emit; using System.Runtime.CompilerServices; using Mono.Cecil; using MonoMod.Utils; using Verse; using static Mono.Cecil.Mixin; namespace FisheryLib; public static class Reflection { public static CodeInstructions GetCodeInstructions(Delegate method) => PatchProcessor.GetCurrentInstructions(method.Method); public static CodeInstructions GetCodeInstructions(Expression method) => PatchProcessor.GetCurrentInstructions(SymbolExtensions.GetMethodInfo(method)); public static CodeInstructions MakeReplacementCall(Delegate method) => MakeReplacementCall(method.Method); public static CodeInstructions MakeReplacementCall(MethodInfo method) { if (!method.IsStatic) yield return FishTranspiler.This; foreach (var argument in method.GetParameters()) yield return FishTranspiler.Argument(argument); yield return FishTranspiler.Call(method); yield return FishTranspiler.Return; } public static Type Type(string assembly, string type) => System.Type.GetType($"{type}, {assembly}"); public static MethodInfo MethodInfo(T method) where T : Delegate => method.Method; public static MethodInfo MethodInfo(string assembly, string type, string name, Type[]? parameters = null, Type[]? generics = null) => AccessTools.Method(Type(assembly, type), name, parameters, generics); public static IEnumerable MethodsOfName(Type type, string name) { var methods = MethodsOfNameSilentFail(type, name).ToArray(); if (!methods.Any()) ThrowForMethodsOfName(type, name); return methods; } private static void ThrowForMethodsOfName(Type type, string name) => ThrowHelper.ThrowArgumentException($"No methods found for {type.FullDescription()}:{name}"); public static IEnumerable MethodsOfNameSilentFail(Type type, string name) => type?.GetMethods(AccessTools.allDeclared) .Where(m => m.Name == name) ?? Enumerable.Empty(); public static FieldInfo FieldInfo(string assembly, string type, string name) => AccessTools.Field(Type(assembly, type), name); public static bool IsAssignableTo(this Type type, [NotNullWhen(true)] Type? targetType, params Type[] generics) { Guard.IsNotNull(type); return targetType != null && (generics is { Length: >= 1 } ? targetType.MakeGenericType(generics) : targetType) .IsAssignableFrom(type); } public static bool AreAssignableFrom(this IEnumerable types, IEnumerable targetTypes) => targetTypes.AreAssignableTo(types); public static bool AreAssignableTo(this IEnumerable types, IEnumerable targetTypes) { Guard.IsNotNull(types); Guard.IsNotNull(targetTypes); using var typesEnumerator = types.GetEnumerator(); using var targetTypesEnumerator = targetTypes.GetEnumerator(); while (typesEnumerator.MoveNext()) { if (!targetTypesEnumerator.MoveNext() || !targetTypesEnumerator.Current.IsAssignableFrom(typesEnumerator.Current)) { return false; } } return !targetTypesEnumerator.MoveNext(); } public static bool IsNullable(this Type type) { Guard.IsNotNull(type); return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); } public static bool IsNullable(this Type nullableType, [NotNullWhen(true)] out Type? valueType) { if (nullableType.IsNullable()) { valueType = nullableType.GetGenericArguments()[0]; return true; } else { valueType = null; return false; } } public static bool HasGenericInterfaceDefinition(this Type type, Type interfaceType) { Guard.IsNotNull(type); return Array.Exists(type.GetInterfaces(), @interface => @interface.IsGenericType && @interface.GetGenericTypeDefinition() == interfaceType); } public static unsafe IntPtr GetFunctionPointer(this MethodBase method) => method.MethodHandle.GetFunctionPointer(); public static unsafe delegate* GetFunctionPointer(this MethodBase method) => (delegate*)method.MethodHandle.GetFunctionPointer(); public static unsafe delegate* GetFunctionPointer(this MethodBase method) => (delegate*)method.MethodHandle.GetFunctionPointer(); public static unsafe delegate* GetFunctionPointer(this MethodBase method) => (delegate*)method.MethodHandle.GetFunctionPointer(); public static unsafe delegate* GetFunctionPointer(this MethodBase method) => (delegate*)method.MethodHandle.GetFunctionPointer(); public static unsafe delegate* GetFunctionPointer(this MethodBase method) => (delegate*)method.MethodHandle.GetFunctionPointer(); public static unsafe delegate* GetFunctionPointer(this MethodBase method) => (delegate*)method.MethodHandle.GetFunctionPointer(); public static unsafe delegate* GetFunctionPointer(this MethodBase method) => (delegate*)method.MethodHandle.GetFunctionPointer(); public static unsafe delegate* GetFunctionPointer(this MethodBase method) => (delegate*)method.MethodHandle.GetFunctionPointer(); /// /// Finds a constructor with parameters that are either assignable to or assignable from the types in the provided parameters array /// /// The type of the object to construct /// An array of parameter types that are either assignable to or from the parameters of a constructor for the target type /// Search for static constructors instead of instance constructors /// Throw an exception on failure to find a matching constructor /// A ConstructorInfo that best matches the provided arguments. Null on failure if throwOnFailure is set to false public static ConstructorInfo? MatchingConstructor(Type type, Type[]? parameters = null, bool searchForStatic = false, bool throwOnFailure = true) { Guard.IsNotNull(type); parameters ??= Array.Empty(); var flags = searchForStatic ? (AccessTools.all & ~BindingFlags.Instance) : (AccessTools.all & ~BindingFlags.Static); return TryGetConstructor(type, flags, paramTypes => paramTypes.AreAssignableFrom(parameters)) ?? TryGetConstructor(type, flags, paramTypes => paramTypes.AreAssignableTo(parameters)) ?? (throwOnFailure ? ThrowHelper.ThrowInvalidOperationException( $"No constructor found for type: {type}, parameters: {parameters.ToStringSafeEnumerable()}, static: {searchForStatic}, found constructors: {type.GetConstructors(flags).ToStringSafeEnumerable()}") : null); } private static ConstructorInfo TryGetConstructor(Type type, BindingFlags flags, Predicate> predicate) => type.GetConstructors(flags).FirstOrDefault(c => predicate(c.GetParameters().Select(p => p.ParameterType))); private static Delegate MakeStructDefaultConstructor(Type type) { var dm = new DynamicMethod("StructDefaultConstructor", type, Array.Empty(), type, true); var il = dm.GetILGenerator(); var variable = il.DeclareLocal(type); il.Emit(FishTranspiler.LocalVariable(variable).Address()); il.Emit(FishTranspiler.New(type)); il.Emit(FishTranspiler.LocalVariable(variable)); il.Emit(FishTranspiler.Return); return dm.CreateDelegate(typeof(Func<>).MakeGenericType(type)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T New() => Create.@new(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T_result New(T_argument argument) => Create.@new(argument); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T_result New(T1 arg1, T2 arg2) => Create.@new(arg1, arg2); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T_result New(T1 arg1, T2 arg2, T3 arg3) => Create.@new(arg1, arg2, arg3); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T_result New(T1 arg1, T2 arg2, T3 arg3, T4 arg4) => Create.@new(arg1, arg2, arg3, arg4); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T_result New(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) => Create.@new(arg1, arg2, arg3, arg4, arg5); private static class Create { internal static Func @new = typeof(T).IsValueType ? (Func)MakeStructDefaultConstructor(typeof(T)) : MatchingConstructor(typeof(T), Array.Empty())!.CreateDelegate>(); } private static class Create { internal static Func @new = MatchingConstructor(typeof(T_result), new[] { typeof(T_argument) })!.CreateDelegate>(); } private static class Create { internal static Func @new = MatchingConstructor(typeof(T_result), new[] { typeof(T1), typeof(T2) })!.CreateDelegate>(); } private static class Create { internal static Func @new = MatchingConstructor(typeof(T_result), new[] { typeof(T1), typeof(T2), typeof(T3) })!.CreateDelegate>(); } private static class Create { internal static Func @new = MatchingConstructor(typeof(T_result), new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) })!.CreateDelegate>(); } private static class Create { internal static Func @new = MatchingConstructor(typeof(T_result), new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5) })!.CreateDelegate>(); } }