diff --git a/src/Libraries/Microsoft.Extensions.AI/Functions/AIFunctionFactory.cs b/src/Libraries/Microsoft.Extensions.AI/Functions/AIFunctionFactory.cs index c562db8ca3a..a3bd73c602a 100644 --- a/src/Libraries/Microsoft.Extensions.AI/Functions/AIFunctionFactory.cs +++ b/src/Libraries/Microsoft.Extensions.AI/Functions/AIFunctionFactory.cs @@ -44,8 +44,6 @@ class AIFunctionFactory /// The method to be represented via the created . /// Metadata to use to override defaults inferred from . /// The created for invoking . - [RequiresUnreferencedCode("Reflection is used to access types from the supplied MethodInfo.")] - [RequiresDynamicCode("Reflection is used to access types from the supplied MethodInfo.")] public static AIFunction Create(Delegate method, AIFunctionFactoryCreateOptions options) { _ = Throw.IfNull(method); @@ -69,8 +67,6 @@ public static AIFunction Create(Delegate method, string? name, string? descripti /// The name to use for the . /// The description to use for the . /// The created for invoking . - [RequiresUnreferencedCode("Reflection is used to access types from the supplied MethodInfo.")] - [RequiresDynamicCode("Reflection is used to access types from the supplied MethodInfo.")] public static AIFunction Create(Delegate method, JsonSerializerOptions options, string? name = null, string? description = null) { _ = Throw.IfNull(method); @@ -104,8 +100,6 @@ public static AIFunction Create(MethodInfo method, object? target = null) /// /// Metadata to use to override defaults inferred from . /// The created for invoking . - [RequiresUnreferencedCode("Reflection is used to access types from the supplied MethodInfo.")] - [RequiresDynamicCode("Reflection is used to access types from the supplied MethodInfo.")] public static AIFunction Create(MethodInfo method, object? target, AIFunctionFactoryCreateOptions options) { _ = Throw.IfNull(method); @@ -136,8 +130,6 @@ class ReflectionAIFunction : AIFunction /// This should be if and only if is a static method. /// /// Function creation options. - [RequiresUnreferencedCode("Reflection is used to access types from the supplied MethodInfo.")] - [RequiresDynamicCode("Reflection is used to access types from the supplied MethodInfo.")] public ReflectionAIFunction(MethodInfo method, object? target, AIFunctionFactoryCreateOptions options) { _ = Throw.IfNull(method); @@ -384,8 +376,6 @@ static bool IsAsyncMethod(MethodInfo method) /// /// Gets a delegate for handling the result value of a method, converting it into the to return from the invocation. /// - [RequiresUnreferencedCode("Reflection is used to access types from the supplied MethodInfo.")] - [RequiresDynamicCode("Reflection is used to access types from the supplied MethodInfo.")] private static Type GetReturnMarshaler(MethodInfo method, out Func> marshaler) { // Handle each known return type for the method @@ -416,9 +406,9 @@ private static Type GetReturnMarshaler(MethodInfo method, out Func - if (returnType.GetGenericTypeDefinition() == typeof(Task<>) && - returnType.GetProperty(nameof(Task.Result), BindingFlags.Public | BindingFlags.Instance)?.GetGetMethod() is MethodInfo taskResultGetter) + if (returnType.GetGenericTypeDefinition() == typeof(Task<>)) { + MethodInfo taskResultGetter = GetMethodFromGenericMethodDefinition(returnType, _taskGetResult); marshaler = async result => { await ((Task)ThrowIfNullResult(result)).ConfigureAwait(false); @@ -428,10 +418,10 @@ private static Type GetReturnMarshaler(MethodInfo method, out Func - if (returnType.GetGenericTypeDefinition() == typeof(ValueTask<>) && - returnType.GetMethod(nameof(ValueTask.AsTask), BindingFlags.Public | BindingFlags.Instance) is MethodInfo valueTaskAsTask && - valueTaskAsTask.ReturnType.GetProperty(nameof(ValueTask.Result), BindingFlags.Public | BindingFlags.Instance)?.GetGetMethod() is MethodInfo asTaskResultGetter) + if (returnType.GetGenericTypeDefinition() == typeof(ValueTask<>)) { + MethodInfo valueTaskAsTask = GetMethodFromGenericMethodDefinition(returnType, _valueTaskAsTask); + MethodInfo asTaskResultGetter = GetMethodFromGenericMethodDefinition(valueTaskAsTask.ReturnType, _taskGetResult); marshaler = async result => { var task = (Task)ReflectionInvoke(valueTaskAsTask, ThrowIfNullResult(result), null)!; @@ -471,6 +461,20 @@ private static Type GetReturnMarshaler(MethodInfo method, out Func).GetProperty(nameof(Task.Result), BindingFlags.Instance | BindingFlags.Public)!.GetMethod!; + private static readonly MethodInfo _valueTaskAsTask = typeof(ValueTask<>).GetMethod(nameof(ValueTask.AsTask), BindingFlags.Instance | BindingFlags.Public)!; + + [UnconditionalSuppressMessage("Trimming", "IL2070:'this' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to target method.", + Justification = "The MethodInfo we are looking for must have already been rooted by virtue of its generic definition being available.")] + private static MethodInfo GetMethodFromGenericMethodDefinition(Type specializedType, MethodInfo genericMethodDefinition) + { + Debug.Assert(specializedType.IsGenericType && specializedType.GetGenericTypeDefinition() == genericMethodDefinition.DeclaringType, "generic member definition doesn't match type."); +#pragma warning disable S3011 // Reflection should not be used to increase accessibility of classes, methods, or fields + const BindingFlags All = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance; +#pragma warning restore S3011 // Reflection should not be used to increase accessibility of classes, methods, or fields + return specializedType.GetMethods(All).First(m => m.MetadataToken == genericMethodDefinition.MetadataToken); + } + /// /// Remove characters from method name that are valid in metadata but shouldn't be used in a method name. /// This is primarily intended to remove characters emitted by for compiler-generated method name mangling.