Skip to content

Commit f88238f

Browse files
authored
[AOT] Add middleware reflection fallback (#45890)
1 parent 8686249 commit f88238f

File tree

1 file changed

+38
-6
lines changed

1 file changed

+38
-6
lines changed

src/Http/Http.Abstractions/src/Extensions/UseMiddlewareExtensions.cs

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Diagnostics;
45
using System.Diagnostics.CodeAnalysis;
56
using System.Linq.Expressions;
67
using System.Reflection;
8+
using System.Runtime.CompilerServices;
79
using Microsoft.AspNetCore.Http;
810
using Microsoft.AspNetCore.Http.Abstractions;
911
using Microsoft.Extensions.Internal;
@@ -107,7 +109,9 @@ public static IApplicationBuilder UseMiddleware(
107109
return (RequestDelegate)invokeMethod.CreateDelegate(typeof(RequestDelegate), instance);
108110
}
109111

110-
var factory = Compile<object>(invokeMethod, parameters);
112+
var factory = RuntimeFeature.IsDynamicCodeSupported
113+
? CompileExpression<object>(invokeMethod, parameters)
114+
: ReflectionFallback<object>(invokeMethod, parameters);
111115

112116
return context =>
113117
{
@@ -156,8 +160,36 @@ private static IApplicationBuilder UseMiddlewareInterface(
156160
});
157161
}
158162

159-
private static Func<T, HttpContext, IServiceProvider, Task> Compile<T>(MethodInfo methodInfo, ParameterInfo[] parameters)
163+
private static Func<T, HttpContext, IServiceProvider, Task> ReflectionFallback<T>(MethodInfo methodInfo, ParameterInfo[] parameters)
160164
{
165+
Debug.Assert(!RuntimeFeature.IsDynamicCodeSupported, "Use reflection fallback when dynamic code is not supported.");
166+
167+
for (var i = 1; i < parameters.Length; i++)
168+
{
169+
var parameterType = parameters[i].ParameterType;
170+
if (parameterType.IsByRef)
171+
{
172+
throw new NotSupportedException(Resources.FormatException_InvokeDoesNotSupportRefOrOutParams(InvokeMethodName));
173+
}
174+
}
175+
176+
return (middleware, context, serviceProvider) =>
177+
{
178+
var methodArguments = new object[parameters.Length];
179+
methodArguments[0] = context;
180+
for (var i = 1; i < parameters.Length; i++)
181+
{
182+
methodArguments[i] = GetService(serviceProvider, parameters[i].ParameterType, methodInfo.DeclaringType!);
183+
}
184+
185+
return (Task)methodInfo.Invoke(middleware, BindingFlags.DoNotWrapExceptions, binder: null, methodArguments, culture: null)!;
186+
};
187+
}
188+
189+
private static Func<T, HttpContext, IServiceProvider, Task> CompileExpression<T>(MethodInfo methodInfo, ParameterInfo[] parameters)
190+
{
191+
Debug.Assert(RuntimeFeature.IsDynamicCodeSupported, "Use compiled expression when dynamic code is supported.");
192+
161193
// If we call something like
162194
//
163195
// public class Middleware
@@ -192,7 +224,7 @@ private static Func<T, HttpContext, IServiceProvider, Task> Compile<T>(MethodInf
192224

193225
var methodArguments = new Expression[parameters.Length];
194226
methodArguments[0] = httpContextArg;
195-
for (int i = 1; i < parameters.Length; i++)
227+
for (var i = 1; i < parameters.Length; i++)
196228
{
197229
var parameterType = parameters[i].ParameterType;
198230
if (parameterType.IsByRef)
@@ -202,9 +234,9 @@ private static Func<T, HttpContext, IServiceProvider, Task> Compile<T>(MethodInf
202234

203235
var parameterTypeExpression = new Expression[]
204236
{
205-
providerArg,
206-
Expression.Constant(parameterType, typeof(Type)),
207-
Expression.Constant(methodInfo.DeclaringType, typeof(Type))
237+
providerArg,
238+
Expression.Constant(parameterType, typeof(Type)),
239+
Expression.Constant(methodInfo.DeclaringType, typeof(Type))
208240
};
209241

210242
var getServiceCall = Expression.Call(GetServiceInfo, parameterTypeExpression);

0 commit comments

Comments
 (0)