Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 123 additions & 2 deletions src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,137 @@ public static MethodIL EmitTaskReturningThunk(MethodDesc taskReturningMethod, Me
return emitter.Link(taskReturningMethod);
}

// The emitted code matches method EmitAsyncMethodThunk in CoreCLR VM.
public static MethodIL EmitAsyncMethodThunk(MethodDesc asyncMethod, MethodDesc taskReturningMethod)
{
TypeSystemContext context = asyncMethod.Context;

var emitter = new ILEmitter();
var codestream = emitter.NewCodeStream();

// TODO: match EmitAsyncMethodThunk in CoreCLR VM
if (taskReturningMethod.OwningType.HasInstantiation)
{
var instantiatedType = (InstantiatedType)TypeSystemHelpers.InstantiateAsOpen(taskReturningMethod.OwningType);
taskReturningMethod = context.GetMethodForInstantiatedType(taskReturningMethod, instantiatedType);
}

if (taskReturningMethod.HasInstantiation)
{
var inst = new TypeDesc[taskReturningMethod.Instantiation.Length];
for (int i = 0; i < inst.Length; i++)
{
inst[i] = context.GetSignatureVariable(i, true);
}
taskReturningMethod = taskReturningMethod.MakeInstantiatedMethod(new Instantiation(inst));
}

codestream.EmitCallThrowHelper(emitter, context.GetHelperEntryPoint("ThrowHelpers"u8, "ThrowNotSupportedException"u8));
MethodSignature sig = asyncMethod.Signature;

int localArg = 0;
if (!sig.IsStatic)
{
codestream.EmitLdArg(localArg++);
}

for (int iArg = 0; iArg < sig.Length; iArg++)
{
codestream.EmitLdArg(localArg++);
}

codestream.Emit(ILOpcode.call, emitter.NewToken(taskReturningMethod));

TypeDesc taskReturningMethodReturnType = taskReturningMethod.Signature.ReturnType;

bool isValueTask = taskReturningMethodReturnType.IsValueType;

if (isValueTask)
{
TypeDesc valueTaskType = taskReturningMethodReturnType;
MethodDesc isCompletedMethod;
MethodDesc completionResultMethod;
MethodDesc asTaskOrNotifierMethod;

if (!taskReturningMethodReturnType.HasInstantiation)
{
// ValueTask (non-generic)
isCompletedMethod = valueTaskType.GetKnownMethod("get_IsCompleted"u8, null);
completionResultMethod = valueTaskType.GetKnownMethod("ThrowIfCompletedUnsuccessfully"u8, null);
asTaskOrNotifierMethod = valueTaskType.GetKnownMethod("AsTaskOrNotifier"u8, null);
}
else
{
// ValueTask<T> (generic)
isCompletedMethod = valueTaskType.GetKnownMethod("get_IsCompleted"u8, null);
completionResultMethod = valueTaskType.GetKnownMethod("get_Result"u8, null);
asTaskOrNotifierMethod = valueTaskType.GetKnownMethod("AsTaskOrNotifier"u8, null);
}

ILLocalVariable valueTaskLocal = emitter.NewLocal(valueTaskType);
ILCodeLabel valueTaskCompletedLabel = emitter.NewCodeLabel();

// Store value task returned by call to actual user func
codestream.EmitStLoc(valueTaskLocal);
codestream.EmitLdLoca(valueTaskLocal);
codestream.Emit(ILOpcode.call, emitter.NewToken(isCompletedMethod));
codestream.Emit(ILOpcode.brtrue, valueTaskCompletedLabel);

codestream.EmitLdLoca(valueTaskLocal);
codestream.Emit(ILOpcode.call, emitter.NewToken(asTaskOrNotifierMethod));
codestream.Emit(ILOpcode.call, emitter.NewToken(
context.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
.GetKnownMethod("TransparentAwait"u8, null)));

codestream.EmitLabel(valueTaskCompletedLabel);
codestream.EmitLdLoca(valueTaskLocal);
codestream.Emit(ILOpcode.call, emitter.NewToken(completionResultMethod));
codestream.Emit(ILOpcode.ret);
}
else
{
// Task path
TypeDesc taskType = taskReturningMethodReturnType;
MethodDesc completedTaskResultMethod;

if (!taskReturningMethodReturnType.HasInstantiation)
{
// Task (non-generic)
completedTaskResultMethod = context.SystemModule
.GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
.GetKnownMethod("CompletedTask"u8, null);
}
else
{
// Task<T> (generic)
TypeDesc logicalReturnType = taskReturningMethodReturnType.Instantiation[0];

MethodDesc completedTaskResultMethodOpen = context.SystemModule
.GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
.GetKnownMethod("CompletedTaskResult"u8, null);
completedTaskResultMethod = completedTaskResultMethodOpen.MakeInstantiatedMethod(new Instantiation(logicalReturnType));
}

ILLocalVariable taskLocal = emitter.NewLocal(taskType);
ILCodeLabel getResultLabel = emitter.NewCodeLabel();

// Store task returned by actual user func or by ValueTask.AsTask
codestream.EmitStLoc(taskLocal);

codestream.EmitLdLoc(taskLocal);
codestream.Emit(ILOpcode.call, emitter.NewToken(
context.SystemModule.GetKnownType("System.Threading.Tasks"u8, "Task"u8)
.GetKnownMethod("get_IsCompleted"u8, null)));
codestream.Emit(ILOpcode.brtrue, getResultLabel);

codestream.EmitLdLoc(taskLocal);
codestream.Emit(ILOpcode.call, emitter.NewToken(
context.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8)
.GetKnownMethod("TransparentAwait"u8, null)));

codestream.EmitLabel(getResultLabel);
codestream.EmitLdLoc(taskLocal);
codestream.Emit(ILOpcode.call, emitter.NewToken(completedTaskResultMethod));
codestream.Emit(ILOpcode.ret);
}

return emitter.Link(asyncMethod);
}
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/vm/asyncthunks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,8 @@ int MethodDesc::GetTokenForGenericTypeMethodCallWithAsyncReturnType(ILCodeStream
return pCode->GetToken(md, typeSigToken);
}

// provided a Task-returning method, emits an async wrapper.
// Provided a Task-returning method, emits an async wrapper.
// The emitted code matches method EmitAsyncMethodThunk in the Managed Type System.
void MethodDesc::EmitAsyncMethodThunk(MethodDesc* pAsyncOtherVariant, MetaSig& msig, ILStubLinker* pSL)
{
_ASSERTE(!pAsyncOtherVariant->IsAsyncThunkMethod());
Expand Down
Loading