Skip to content

Commit f944483

Browse files
committed
Fix check for invocation of async infrastructure methods via reflection
MethodInfo.Invoke has multiple implementation strategies. The check for async infrastructure methods needs to be placed in the code that is shared by all implementation strategies. Before this change, this check would not kick under managed debugger or when the test is run with Switch.System.Reflection.ForceEmitInvoke=true appcontext switch. Also, this check should fail with NotSupportedException, without wrapping it in TargetInvocationException() to match existing behaviors.
1 parent 2551e93 commit f944483

File tree

9 files changed

+39
-15
lines changed

9 files changed

+39
-15
lines changed

src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.CoreCLR.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ private ParameterInfo[] FetchNonReturnParameters() =>
7878

7979
private ParameterInfo FetchReturnParameter() =>
8080
m_returnParameter ??= RuntimeParameterInfo.GetReturnParameter(this, this, Signature);
81+
82+
private bool IsDisallowedAsyncHelper =>
83+
RuntimeMethodHandle.IsAsyncMethod(new RuntimeMethodHandleInternal(m_handle));
8184
#endregion
8285

8386
#region Internal Members

src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,6 +1337,9 @@ internal static IRuntimeMethodInfo StripMethodInstantiation(IRuntimeMethodInfo m
13371337
[MethodImpl(MethodImplOptions.InternalCall)]
13381338
internal static extern bool IsConstructor(RuntimeMethodHandleInternal method);
13391339

1340+
[MethodImpl(MethodImplOptions.InternalCall)]
1341+
internal static extern bool IsAsyncMethod(RuntimeMethodHandleInternal method);
1342+
13401343
[MethodImpl(MethodImplOptions.InternalCall)]
13411344
private static extern LoaderAllocator GetLoaderAllocatorInternal(RuntimeMethodHandleInternal method);
13421345

src/coreclr/vm/ecalllist.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ FCFuncStart(gRuntimeMethodHandle)
148148
FCFuncElement("GetMethodFromCanonical", RuntimeMethodHandle::GetMethodFromCanonical)
149149
FCFuncElement("IsDynamicMethod", RuntimeMethodHandle::IsDynamicMethod)
150150
FCFuncElement("IsConstructor", RuntimeMethodHandle::IsConstructor)
151+
FCFuncElement("IsAsyncMethod", RuntimeMethodHandle::IsAsyncMethod)
151152
FCFuncElement("GetResolver", RuntimeMethodHandle::GetResolver)
152153
FCFuncElement("GetLoaderAllocatorInternal", RuntimeMethodHandle::GetLoaderAllocatorInternal)
153154
FCFuncEnd()

src/coreclr/vm/reflectioninvocation.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -342,11 +342,6 @@ extern "C" void QCALLTYPE RuntimeMethodHandle_InvokeMethod(
342342
COMPlusThrow(kNotSupportedException, W("NotSupported_Type"));
343343
}
344344

345-
if (pMeth->IsAsyncMethod())
346-
{
347-
COMPlusThrow(kNotSupportedException, W("NotSupported_Async"));
348-
}
349-
350345
#ifdef _DEBUG
351346
if (g_pConfig->ShouldInvokeHalt(pMeth))
352347
{

src/coreclr/vm/runtimehandles.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2120,8 +2120,19 @@ FCIMPL1(FC_BOOL_RET, RuntimeMethodHandle::IsConstructor, MethodDesc *pMethod)
21202120
}
21212121
CONTRACTL_END;
21222122

2123-
BOOL ret = (BOOL)pMethod->IsClassConstructorOrCtor();
2124-
FC_RETURN_BOOL(ret);
2123+
FC_RETURN_BOOL(pMethod->IsClassConstructorOrCtor());
2124+
}
2125+
FCIMPLEND
2126+
2127+
FCIMPL1(FC_BOOL_RET, RuntimeMethodHandle::IsAsyncMethod, MethodDesc *pMethod)
2128+
{
2129+
CONTRACTL {
2130+
FCALL_CHECK;
2131+
PRECONDITION(CheckPointer(pMethod));
2132+
}
2133+
CONTRACTL_END;
2134+
2135+
FC_RETURN_BOOL(pMethod->IsAsyncMethod());
21252136
}
21262137
FCIMPLEND
21272138

src/coreclr/vm/runtimehandles.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,8 @@ class RuntimeMethodHandle
215215

216216
static FCDECL1(FC_BOOL_RET, IsConstructor, MethodDesc *pMethod);
217217

218+
static FCDECL1(FC_BOOL_RET, IsAsyncMethod, MethodDesc *pMethod);
219+
218220
static FCDECL1(Object*, GetLoaderAllocatorInternal, MethodDesc *pMethod);
219221
};
220222

src/libraries/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ internal InvocationFlags ComputeAndUpdateInvocationFlags()
1616
InvocationFlags invocationFlags = InvocationFlags.Unknown;
1717

1818
Type? declaringType = DeclaringType;
19+
Type returnType = ReturnType;
1920

2021
if (ContainsGenericParameters // Method has unbound generics
21-
|| IsDisallowedByRefType(ReturnType) // Return type is an invalid by-ref (i.e., by-ref-like or void*)
22+
|| IsDisallowedByRefType(returnType) // Return type is an invalid by-ref (i.e., by-ref-like or void*)
2223
|| (CallingConvention & CallingConventions.VarArgs) == CallingConventions.VarArgs // Managed varargs
2324
)
2425
{
@@ -36,9 +37,13 @@ internal InvocationFlags ComputeAndUpdateInvocationFlags()
3637
{
3738
invocationFlags |= InvocationFlags.ContainsStackPointers;
3839
}
40+
else if (IsDisallowedAsyncHelper)
41+
{
42+
invocationFlags = InvocationFlags.NoInvoke;
43+
}
3944
}
4045

41-
if (ReturnType.IsByRefLike) // Check for byref-like types for return
46+
if (returnType.IsByRefLike) // Check for byref-like types for return
4247
{
4348
invocationFlags |= InvocationFlags.ContainsStackPointers;
4449
}
@@ -60,6 +65,8 @@ static bool IsDisallowedByRefType(Type type)
6065
[DoesNotReturn]
6166
internal void ThrowNoInvokeException()
6267
{
68+
Type? declaringType = DeclaringType;
69+
6370
// method is on a class that contains stack pointers
6471
if ((InvocationFlags & InvocationFlags.ContainsStackPointers) != 0)
6572
{
@@ -71,7 +78,7 @@ internal void ThrowNoInvokeException()
7178
throw new NotSupportedException();
7279
}
7380
// method is generic or on a generic class
74-
else if (DeclaringType!.ContainsGenericParameters || ContainsGenericParameters)
81+
else if ((declaringType != null && declaringType.ContainsGenericParameters) || ContainsGenericParameters)
7582
{
7683
throw new InvalidOperationException(SR.Arg_UnboundGenParam);
7784
}
@@ -88,6 +95,10 @@ internal void ThrowNoInvokeException()
8895
if (elementType == typeof(void))
8996
throw new NotSupportedException(SR.NotSupported_ByRefToVoidReturn);
9097
}
98+
else if (IsDisallowedAsyncHelper)
99+
{
100+
throw new NotSupportedException(SR.NotSupported_Async);
101+
}
91102

92103
throw new TargetException();
93104
}

src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.Mono.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -930,5 +930,7 @@ public override int MetadataToken
930930

931931
[MethodImplAttribute(MethodImplOptions.InternalCall)]
932932
internal static extern int get_metadata_token(RuntimeConstructorInfo method);
933+
934+
private IsDisallowedAsyncHelper => false;
933935
}
934936
}

src/tests/async/reflection/reflection-simple.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,7 @@ public static void MethodInfo_Invoke_AsyncHelper()
2727
{
2828
var mi = typeof(System.Runtime.CompilerServices.AsyncHelpers).GetMethod("Await", BindingFlags.Static | BindingFlags.Public, new Type[] { typeof(Task) })!;
2929
Assert.NotNull(mi);
30-
31-
if (TestLibrary.Utilities.IsNativeAot)
32-
Assert.Throws<NotSupportedException>(() => mi.Invoke(null, new object[] { FooTask() }));
33-
else
34-
Assert.Throws<TargetInvocationException>(() => mi.Invoke(null, new object[] { FooTask() }));
30+
Assert.Throws<NotSupportedException>(() => mi.Invoke(null, new object[] { FooTask() }));
3531

3632
// Sadly the following does not throw and results in UB
3733
// We cannot completely prevent putting a token of an Async method into IL stream.

0 commit comments

Comments
 (0)