mirror of https://github.com/dotnet/runtime
Merge dc6d56d067
into 02596ba8d9
This commit is contained in:
commit
c4f2871486
|
@ -2736,20 +2736,30 @@ namespace System
|
|||
im.InterfaceMethods = new MethodInfo[ifaceVirtualMethodCount];
|
||||
im.TargetMethods = new MethodInfo[ifaceVirtualMethodCount];
|
||||
|
||||
int actualCount = 0;
|
||||
for (int i = 0; i < ifaceVirtualMethodCount; i++)
|
||||
{
|
||||
RuntimeMethodHandleInternal ifaceRtMethodHandle = RuntimeTypeHandle.GetMethodAt(ifaceRtType, i);
|
||||
|
||||
// GetMethodAt may return null handle for methods that do not exist or are not supposed
|
||||
// to be seen in reflection. One example is async variant methods.
|
||||
// We do not record mapping for interface methods that do not exist.
|
||||
if (ifaceRtMethodHandle.IsNullHandle())
|
||||
continue;
|
||||
|
||||
// GetMethodBase will convert this to the instantiating/unboxing stub if necessary
|
||||
MethodBase ifaceMethodBase = GetMethodBase(ifaceRtType, ifaceRtMethodHandle)!;
|
||||
Debug.Assert(ifaceMethodBase is RuntimeMethodInfo);
|
||||
im.InterfaceMethods[i] = (MethodInfo)ifaceMethodBase;
|
||||
im.InterfaceMethods[actualCount] = (MethodInfo)ifaceMethodBase;
|
||||
|
||||
// If the impl is null, then virtual stub dispatch is active.
|
||||
RuntimeMethodHandleInternal classRtMethodHandle = TypeHandle.GetInterfaceMethodImplementation(ifaceRtTypeHandle, ifaceRtMethodHandle);
|
||||
|
||||
if (classRtMethodHandle.IsNullHandle())
|
||||
{
|
||||
actualCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we resolved to an interface method, use the interface type as reflected type. Otherwise use `this`.
|
||||
RuntimeType reflectedType = RuntimeMethodHandle.GetDeclaringType(classRtMethodHandle);
|
||||
|
@ -2764,10 +2774,16 @@ namespace System
|
|||
// the TargetMethod provided to us by runtime internals may be a generic method instance,
|
||||
// potentially with invalid arguments. TargetMethods in the InterfaceMap should never be
|
||||
// instances, only definitions.
|
||||
im.TargetMethods[i] = (targetMethod is { IsGenericMethod: true, IsGenericMethodDefinition: false })
|
||||
im.TargetMethods[actualCount++] = (targetMethod is { IsGenericMethod: true, IsGenericMethodDefinition: false })
|
||||
? targetMethod.GetGenericMethodDefinition() : targetMethod!;
|
||||
}
|
||||
|
||||
if (actualCount != ifaceVirtualMethodCount)
|
||||
{
|
||||
Array.Resize(ref im.InterfaceMethods, actualCount);
|
||||
Array.Resize(ref im.TargetMethods, actualCount);
|
||||
}
|
||||
|
||||
return im;
|
||||
}
|
||||
#endregion
|
||||
|
|
|
@ -709,7 +709,7 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableRiscV64Zbb, W("EnableRiscV64
|
|||
#endif
|
||||
|
||||
// Runtime-async
|
||||
RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_RuntimeAsync, W("RuntimeAsync"), 0, "Enables runtime async method support")
|
||||
RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_RuntimeAsync, W("RuntimeAsync"), 1, "Enables runtime async method support")
|
||||
|
||||
///
|
||||
/// Uncategorized
|
||||
|
|
|
@ -18,7 +18,7 @@ bool MethodDesc::TryGenerateAsyncThunk(DynamicResolver** resolver, COR_ILMETHOD_
|
|||
_ASSERTE(methodILDecoder != NULL);
|
||||
_ASSERTE(*resolver == NULL && *methodILDecoder == NULL);
|
||||
_ASSERTE(IsIL());
|
||||
_ASSERTE(GetRVA() == 0);
|
||||
_ASSERTE(!HasILHeader());
|
||||
|
||||
if (!IsAsyncThunkMethod())
|
||||
{
|
||||
|
|
|
@ -913,7 +913,7 @@ PTR_COR_ILMETHOD ILCodeVersion::GetIL() const
|
|||
{
|
||||
PTR_Module pModule = GetModule();
|
||||
PTR_MethodDesc pMethodDesc = dac_cast<PTR_MethodDesc>(pModule->LookupMethodDef(GetMethodDef()));
|
||||
if (pMethodDesc != NULL)
|
||||
if (pMethodDesc != NULL && pMethodDesc->MayHaveILHeader())
|
||||
{
|
||||
pIL = dac_cast<PTR_COR_ILMETHOD>(pMethodDesc->GetILHeader());
|
||||
}
|
||||
|
|
|
@ -326,7 +326,6 @@ protected:
|
|||
void GetExceptionName(_Out_writes_(maxLength) WCHAR* targetParam, int maxLength);
|
||||
void GetPackageMoniker(_Out_writes_(maxLength) WCHAR* targetParam, int maxLength);
|
||||
void GetPRAID(_Out_writes_(maxLength) WCHAR* targetParam, int maxLength);
|
||||
void GetIlRva(_Out_writes_(maxLength) WCHAR* targetParam, int maxLength);
|
||||
|
||||
public:
|
||||
BaseBucketParamsManager(GenericModeBlock* pGenericModeBlock, TypeOfReportedError typeOfError, PCODE initialFaultingPc, Thread* pFaultingThread, OBJECTREF* pThrownException);
|
||||
|
@ -810,31 +809,6 @@ void BaseBucketParamsManager::GetPRAID(_Out_writes_(maxLength) WCHAR* targetPara
|
|||
_ASSERTE(!"PRAID support NYI for CoreCLR");
|
||||
}
|
||||
|
||||
void BaseBucketParamsManager::GetIlRva(_Out_writes_(maxLength) WCHAR* targetParam, int maxLength)
|
||||
{
|
||||
CONTRACTL
|
||||
{
|
||||
NOTHROW;
|
||||
GC_NOTRIGGER;
|
||||
MODE_ANY;
|
||||
}
|
||||
CONTRACTL_END;
|
||||
|
||||
DWORD ilOffset = GetILOffset();
|
||||
|
||||
if (ilOffset == MAXDWORD)
|
||||
ilOffset = 0;
|
||||
|
||||
if (m_pFaultingMD)
|
||||
ilOffset += m_pFaultingMD->GetRVA();
|
||||
|
||||
_snwprintf_s(targetParam,
|
||||
maxLength,
|
||||
_TRUNCATE,
|
||||
W("%x"),
|
||||
ilOffset);
|
||||
}
|
||||
|
||||
// helper functions
|
||||
|
||||
DWORD BaseBucketParamsManager::GetILOffset()
|
||||
|
|
|
@ -264,8 +264,6 @@ public:
|
|||
|
||||
virtual COR_ILMETHOD* MethodDescGetILHeader(MethodDesc *pFD) = 0;
|
||||
|
||||
virtual ULONG MethodDescGetRVA(MethodDesc *pFD) = 0;
|
||||
|
||||
virtual void MarkDebuggerAttached(void) = 0;
|
||||
|
||||
virtual void MarkDebuggerUnattached(void) = 0;
|
||||
|
|
|
@ -721,19 +721,6 @@ COR_ILMETHOD* EEDbgInterfaceImpl::MethodDescGetILHeader(MethodDesc *pFD)
|
|||
RETURN NULL;
|
||||
}
|
||||
|
||||
ULONG EEDbgInterfaceImpl::MethodDescGetRVA(MethodDesc *pFD)
|
||||
{
|
||||
CONTRACTL
|
||||
{
|
||||
NOTHROW;
|
||||
GC_NOTRIGGER;
|
||||
PRECONDITION(CheckPointer(pFD));
|
||||
}
|
||||
CONTRACTL_END;
|
||||
|
||||
return pFD->GetRVA();
|
||||
}
|
||||
|
||||
MethodDesc *EEDbgInterfaceImpl::FindLoadedMethodRefOrDef(Module* pModule,
|
||||
mdToken memberRef)
|
||||
{
|
||||
|
|
|
@ -156,8 +156,6 @@ public:
|
|||
|
||||
COR_ILMETHOD* MethodDescGetILHeader(MethodDesc *pFD);
|
||||
|
||||
ULONG MethodDescGetRVA(MethodDesc *pFD);
|
||||
|
||||
MethodDesc *FindLoadedMethodRefOrDef(Module* pModule,
|
||||
mdToken memberRef);
|
||||
|
||||
|
|
|
@ -7680,20 +7680,16 @@ CEEInfo::getMethodInfo(
|
|||
JIT_TO_EE_TRANSITION();
|
||||
|
||||
MethodDesc* ftn = GetMethod(ftnHnd);
|
||||
if (ftn->IsDynamicMethod())
|
||||
{
|
||||
getMethodInfoWorker(ftn, NULL, methInfo, context);
|
||||
result = true;
|
||||
}
|
||||
else if (!ftn->IsWrapperStub() && ftn->HasILHeader())
|
||||
if (ftn->HasILHeader())
|
||||
{
|
||||
// Get the IL header and set it.
|
||||
COR_ILMETHOD_DECODER header(ftn->GetILHeader(), ftn->GetMDImport(), NULL);
|
||||
getMethodInfoWorker(ftn, &header, methInfo, context);
|
||||
result = true;
|
||||
}
|
||||
else if (ftn->IsIL() && ftn->GetRVA() == 0) // IL methods with no RVA indicate there is no implementation defined in metadata.
|
||||
else if (ftn->IsIL() || ftn->IsDynamicMethod())
|
||||
{
|
||||
// IL methods with no IL header indicate there is no implementation defined in metadata.
|
||||
getMethodInfoWorker(ftn, NULL, methInfo, context);
|
||||
result = true;
|
||||
}
|
||||
|
@ -7875,7 +7871,7 @@ CorInfoInline CEEInfo::canInline (CORINFO_METHOD_HANDLE hCaller,
|
|||
goto exit;
|
||||
}
|
||||
}
|
||||
else if (pCallee->IsIL() && pCallee->GetRVA() == 0)
|
||||
else if (pCallee->IsIL())
|
||||
{
|
||||
CORINFO_METHOD_INFO methodInfo;
|
||||
getMethodInfoWorker(pCallee, NULL, &methodInfo);
|
||||
|
@ -12788,7 +12784,7 @@ void CEECodeGenInfo::getEHinfo(
|
|||
COR_ILMETHOD_DECODER header(pMD->GetILHeader(), pMD->GetMDImport(), NULL);
|
||||
getEHinfoHelper(ftn, EHnumber, clause, &header);
|
||||
}
|
||||
else if (pMD->IsIL() && pMD->GetRVA() == 0)
|
||||
else if (pMD->IsIL())
|
||||
{
|
||||
TransientMethodDetails* details;
|
||||
if (!FindTransientMethodDetails(pMD, &details))
|
||||
|
|
|
@ -1123,8 +1123,10 @@ BOOL MethodDesc::HasRetBuffArg()
|
|||
}
|
||||
|
||||
//*******************************************************************************
|
||||
// This returns the offset of the IL.
|
||||
// The offset is relative to the base of the IL image.
|
||||
// This typically returns the offset of the IL.
|
||||
// Another case when a method may have an RVA is earlybound IJW PInvokes,
|
||||
// in which case the RVA is referring to native code.
|
||||
// The offset is relative to the base of the image.
|
||||
ULONG MethodDesc::GetRVA()
|
||||
{
|
||||
CONTRACTL
|
||||
|
@ -1133,27 +1135,11 @@ ULONG MethodDesc::GetRVA()
|
|||
GC_NOTRIGGER;
|
||||
FORBID_FAULT;
|
||||
SUPPORTS_DAC;
|
||||
PRECONDITION((IsIL() && MayHaveILHeader()) ||
|
||||
(IsNDirect() && ((NDirectMethodDesc*)this)->IsEarlyBound()));
|
||||
}
|
||||
CONTRACTL_END
|
||||
|
||||
if (IsRuntimeSupplied())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Methods without metadata don't have an RVA. Examples are IL stubs and LCG methods.
|
||||
if (IsNoMetadata())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Between two Async variants of the same method only one represents the actual IL.
|
||||
// It is the variant that is not a thunk.
|
||||
if (IsAsyncThunkMethod())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (GetMemberDef() & 0x00FFFFFF)
|
||||
{
|
||||
Module *pModule = GetModule();
|
||||
|
@ -1198,8 +1184,7 @@ COR_ILMETHOD* MethodDesc::GetILHeader()
|
|||
{
|
||||
THROWS;
|
||||
GC_NOTRIGGER;
|
||||
PRECONDITION(IsIL());
|
||||
PRECONDITION(!IsUnboxingStub());
|
||||
PRECONDITION(MayHaveILHeader());
|
||||
}
|
||||
CONTRACTL_END
|
||||
|
||||
|
|
|
@ -901,7 +901,8 @@ public:
|
|||
MODE_ANY;
|
||||
}
|
||||
CONTRACTL_END;
|
||||
return IsIL() && !IsUnboxingStub() && GetRVA();
|
||||
|
||||
return MayHaveILHeader() && GetRVA();
|
||||
}
|
||||
|
||||
COR_ILMETHOD* GetILHeader();
|
||||
|
@ -1499,6 +1500,14 @@ public:
|
|||
|
||||
BOOL MayHaveNativeCode();
|
||||
|
||||
BOOL MayHaveILHeader()
|
||||
{
|
||||
LIMITED_METHOD_DAC_CONTRACT;
|
||||
|
||||
// methods with transient IL bodies do not have IL headers
|
||||
return IsIL() && !IsUnboxingStub() && !IsAsyncThunkMethod();
|
||||
}
|
||||
|
||||
ULONG GetRVA();
|
||||
|
||||
public:
|
||||
|
|
|
@ -706,15 +706,20 @@ namespace
|
|||
_ASSERTE(pConfig != NULL);
|
||||
_ASSERTE(pDecoderMemory != NULL);
|
||||
|
||||
if (!pMD->MayHaveILHeader())
|
||||
return NULL;
|
||||
|
||||
COR_ILMETHOD_DECODER* pHeader = NULL;
|
||||
|
||||
COR_ILMETHOD* ilHeader = pConfig->GetILHeader();
|
||||
|
||||
// For a Runtime Async method the methoddef maps to a Task-returning thunk with runtime-provided implementation,
|
||||
// while the default IL belongs to the Async implementation variant.
|
||||
// By default the config captures the default methoddesc, which would be a thunk, thus no IL header.
|
||||
// So, if config provides no header and we see an implementation method desc, then just ask the method desc itself.
|
||||
if (ilHeader == NULL && pMD->IsAsyncVariantMethod() && !pMD->IsAsyncThunkMethod())
|
||||
if (ilHeader == NULL && pMD->IsAsyncVariantMethod())
|
||||
{
|
||||
_ASSERTE(!pMD->IsAsyncThunkMethod());
|
||||
ilHeader = pMD->GetILHeader();
|
||||
}
|
||||
|
||||
|
|
|
@ -342,6 +342,11 @@ extern "C" void QCALLTYPE RuntimeMethodHandle_InvokeMethod(
|
|||
COMPlusThrow(kNotSupportedException, W("NotSupported_Type"));
|
||||
}
|
||||
|
||||
if (pMeth->IsAsyncMethod())
|
||||
{
|
||||
COMPlusThrow(kNotSupportedException, W("NotSupported_Async"));
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
if (g_pConfig->ShouldInvokeHalt(pMeth))
|
||||
{
|
||||
|
|
|
@ -441,6 +441,15 @@ extern "C" MethodDesc* QCALLTYPE RuntimeTypeHandle_GetMethodAt(MethodTable* pMT,
|
|||
}
|
||||
}
|
||||
|
||||
if (pRetMethod != NULL && pRetMethod->IsAsyncVariantMethod())
|
||||
{
|
||||
// do not return methoddescs for async variants.
|
||||
// NOTE: The only scenario where this is relevant is when caller iterates through all slots.
|
||||
// If GetMethodAt is used to find a method associated with another one,
|
||||
// then we would be starting with "real" method and will get a "real" method here.
|
||||
pRetMethod = NULL;
|
||||
}
|
||||
|
||||
END_QCALL;
|
||||
|
||||
return pRetMethod;
|
||||
|
|
|
@ -1019,7 +1019,7 @@ bool MethodDesc::TryGenerateUnsafeAccessor(DynamicResolver** resolver, COR_ILMET
|
|||
_ASSERTE(methodILDecoder != NULL);
|
||||
_ASSERTE(*resolver == NULL && *methodILDecoder == NULL);
|
||||
_ASSERTE(IsIL());
|
||||
_ASSERTE(GetRVA() == 0);
|
||||
_ASSERTE(!HasILHeader());
|
||||
|
||||
// The UnsafeAccessorAttribute is applied to methods with an
|
||||
// RVA of 0 (for example, C#'s extern keyword).
|
||||
|
|
|
@ -2987,6 +2987,9 @@
|
|||
<data name="NotSupported_ByRefToVoidReturn" xml:space="preserve">
|
||||
<value>ByRef to void return values are not supported in reflection invocation.</value>
|
||||
</data>
|
||||
<data name="NotSupported_Async" xml:space="preserve">
|
||||
<value>Async infrastructure methods are not supported in reflection invocation.</value>
|
||||
</data>
|
||||
<data name="NotSupported_CallToVarArg" xml:space="preserve">
|
||||
<value>Vararg calling convention not supported.</value>
|
||||
</data>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<!-- runtime async testing in main repo NYI -->
|
||||
<DisableProjectBuild>true</DisableProjectBuild>
|
||||
<!-- <DisableProjectBuild>true</DisableProjectBuild> -->
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.targets, $(MSBuildThisFileDirectory)..))" />
|
||||
|
|
|
@ -2,14 +2,17 @@
|
|||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
public class Async2Reflection
|
||||
{
|
||||
[Fact]
|
||||
public static void TestEntryPoint()
|
||||
public static void MethodInfo_Invoke_TaskReturning()
|
||||
{
|
||||
var mi = typeof(Async2Reflection).GetMethod("Foo", BindingFlags.Static | BindingFlags.NonPublic)!;
|
||||
Task<int> r = (Task<int>)mi.Invoke(null, null)!;
|
||||
|
@ -19,15 +22,217 @@ public class Async2Reflection
|
|||
Assert.Equal(100, (int)(r.Result + d.Bar().Result));
|
||||
}
|
||||
|
||||
#pragma warning disable SYSLIB5007 // 'System.Runtime.CompilerServices.AsyncHelpers' is for evaluation purposes only
|
||||
[Fact]
|
||||
public static void MethodInfo_Invoke_AsyncHelper()
|
||||
{
|
||||
var mi = typeof(System.Runtime.CompilerServices.AsyncHelpers).GetMethod("Await", BindingFlags.Static | BindingFlags.Public, new Type[] { typeof(Task) })!;
|
||||
Assert.NotNull(mi);
|
||||
Assert.Throws<TargetInvocationException>(() => mi.Invoke(null, new object[] { FooTask() }));
|
||||
|
||||
// Sadly the following does not throw and results in UB
|
||||
// We cannot completely prevent putting a token of an Async method into IL stream.
|
||||
// CONSIDER: perhaps JIT could throw?
|
||||
//
|
||||
// dynamic d = FooTask();
|
||||
// System.Runtime.CompilerServices.AsyncHelpers.Await(d);
|
||||
}
|
||||
#pragma warning restore SYSLIB5007
|
||||
|
||||
private static async Task<int> Foo()
|
||||
{
|
||||
await Task.Yield();
|
||||
return 90;
|
||||
}
|
||||
|
||||
private static async Task FooTask()
|
||||
{
|
||||
await Task.Yield();
|
||||
}
|
||||
|
||||
private async Task<int> Bar()
|
||||
{
|
||||
await Task.Yield();
|
||||
return 10;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void AwaitTaskReturningExpressionLambda()
|
||||
{
|
||||
var expr1 = (Expression<Func<Task<int>>>)(() => Task.FromResult(42));
|
||||
var del = expr1.Compile();
|
||||
Assert.Equal(42, del().Result);
|
||||
|
||||
AwaitF(42, del).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
static async Task AwaitF<T>(T expected, Func<Task<T>> f)
|
||||
{
|
||||
var res = await f.Invoke();
|
||||
Assert.Equal(expected, res);
|
||||
}
|
||||
|
||||
public interface IExample<T>
|
||||
{
|
||||
Task TaskReturning();
|
||||
T TReturning();
|
||||
}
|
||||
|
||||
public class ExampleClass : IExample<Task>
|
||||
{
|
||||
public Task TaskReturning()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public Task TReturning()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public struct ExampleStruct : IExample<Task>
|
||||
{
|
||||
public Task TaskReturning()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public Task TReturning()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void GetInterfaceMap()
|
||||
{
|
||||
Type interfaceType = typeof(IExample<Task>);
|
||||
Type classType = typeof(ExampleClass);
|
||||
|
||||
InterfaceMapping map = classType.GetInterfaceMap(interfaceType);
|
||||
|
||||
Assert.Equal(2, map.InterfaceMethods.Length);
|
||||
Assert.Equal("System.Threading.Tasks.Task TaskReturning() --> System.Threading.Tasks.Task TaskReturning()",
|
||||
$"{map.InterfaceMethods[0]?.ToString()} --> {map.TargetMethods[0]?.ToString()}");
|
||||
|
||||
Assert.Equal("System.Threading.Tasks.Task TReturning() --> System.Threading.Tasks.Task TReturning()",
|
||||
$"{map.InterfaceMethods[1]?.ToString()} --> {map.TargetMethods[1]?.ToString()}");
|
||||
|
||||
Type structType = typeof(ExampleStruct);
|
||||
|
||||
map = structType.GetInterfaceMap(interfaceType);
|
||||
Assert.Equal(2, map.InterfaceMethods.Length);
|
||||
Assert.Equal("System.Threading.Tasks.Task TaskReturning() --> System.Threading.Tasks.Task TaskReturning()",
|
||||
$"{map.InterfaceMethods[0]?.ToString()} --> {map.TargetMethods[0]?.ToString()}");
|
||||
|
||||
Assert.Equal("System.Threading.Tasks.Task TReturning() --> System.Threading.Tasks.Task TReturning()",
|
||||
$"{map.InterfaceMethods[1]?.ToString()} --> {map.TargetMethods[1]?.ToString()}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void TypeBuilder_DefineMethod()
|
||||
{
|
||||
// we will be compiling a dynamic vesion of this method
|
||||
//
|
||||
// public async static Task StaticMethod(Task arg)
|
||||
// {
|
||||
// await arg;
|
||||
// }
|
||||
|
||||
// Define a dynamic assembly and module
|
||||
AssemblyName assemblyName = new AssemblyName("DynamicAssembly");
|
||||
AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
|
||||
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicModule");
|
||||
|
||||
// Define a type
|
||||
TypeBuilder typeBuilder = moduleBuilder.DefineType("DynamicType", TypeAttributes.Public);
|
||||
|
||||
// Define a method
|
||||
MethodBuilder methodBuilder = typeBuilder.DefineMethod(
|
||||
"DynamicMethod",
|
||||
MethodAttributes.Public | MethodAttributes.Static,
|
||||
typeof(Task),
|
||||
new Type[] { typeof(Task) });
|
||||
|
||||
// Set `MethodImpl.Async` flag
|
||||
methodBuilder.SetImplementationFlags(MethodImplAttributes.Async);
|
||||
|
||||
// {
|
||||
// Await(arg_0);
|
||||
// ret;
|
||||
// }
|
||||
ILGenerator ilGenerator = methodBuilder.GetILGenerator();
|
||||
ilGenerator.Emit(OpCodes.Ldarg_0);
|
||||
#pragma warning disable SYSLIB5007 // 'System.Runtime.CompilerServices.AsyncHelpers' is for evaluation purposes only
|
||||
var mi = typeof(System.Runtime.CompilerServices.AsyncHelpers).GetMethod("Await", BindingFlags.Static | BindingFlags.Public, new Type[] { typeof(Task) })!;
|
||||
#pragma warning restore SYSLIB5007
|
||||
ilGenerator.EmitCall(OpCodes.Call, mi, new Type[] { typeof(Task) });
|
||||
ilGenerator.Emit(OpCodes.Ret);
|
||||
|
||||
// Create the type and invoke the method
|
||||
Type dynamicType = typeBuilder.CreateType();
|
||||
MethodInfo dynamicMethod = dynamicType.GetMethod("DynamicMethod");
|
||||
var del = dynamicMethod.CreateDelegate<Func<Task, Task>>();
|
||||
|
||||
// the following should not crash
|
||||
del(Task.CompletedTask);
|
||||
del(FooTask());
|
||||
}
|
||||
|
||||
public class PrivateAsync1<T>
|
||||
{
|
||||
public static int s;
|
||||
private static async Task<T> a_task1(int i)
|
||||
{
|
||||
s++;
|
||||
if (i == 0)
|
||||
{
|
||||
await Task.Yield();
|
||||
return default;
|
||||
}
|
||||
|
||||
return await Accessors2.accessor<T>(null, i - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public class PrivateAsync2
|
||||
{
|
||||
public static int s;
|
||||
private static async Task<T> a_task2<T>(int i)
|
||||
{
|
||||
s++;
|
||||
if (i == 0)
|
||||
{
|
||||
await Task.Yield();
|
||||
return default;
|
||||
}
|
||||
|
||||
return await Accessors1<T>.accessor(null, i - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public class Accessors1<T>
|
||||
{
|
||||
[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "a_task1")]
|
||||
public extern static Task<T> accessor(PrivateAsync1<T> o, int i);
|
||||
}
|
||||
|
||||
public class Accessors2
|
||||
{
|
||||
[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "a_task2")]
|
||||
public extern static Task<T> accessor<T>(PrivateAsync2 o, int i);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void UnsafeAccessors()
|
||||
{
|
||||
Accessors2.accessor<int>(null, 7).GetAwaiter().GetResult();
|
||||
Assert.Equal(4, PrivateAsync1<int>.s);
|
||||
Assert.Equal(4, PrivateAsync2.s);
|
||||
|
||||
Accessors1<int>.accessor(null, 7).GetAwaiter().GetResult();
|
||||
Assert.Equal(8, PrivateAsync1<int>.s);
|
||||
Assert.Equal(8, PrivateAsync2.s);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<RequiresProcessIsolation>true</RequiresProcessIsolation>
|
||||
<CLRTestPriority>0</CLRTestPriority>
|
||||
<Optimize>True</Optimize>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
|
|
Loading…
Reference in New Issue