Skip to content
Browse files

[MERGE #6187 @nhat-nguyen] Fix lowering code for ES2018 Async Iteration

Merge pull request #6187 from nhat-nguyen:async

Also took this opportunity to do a bit refactor on how we initialize the `generator` pointer inside `ResumeYieldData`. I think it's cleaner if we do everything in the constructor instead of only setting it in `CallAsyncGenerator`.

Related PR: #5834
  • Loading branch information...
nhat-nguyen committed Jul 1, 2019
2 parents bb7131b + e0a0b16 commit b83edb02fc00573ab1fb76618be51d0ef672337f
@@ -1879,6 +1879,24 @@ IRBuilder::BuildReg2(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0, Js::Re

case Js::OpCode::Await:
case Js::OpCode::AsyncYield:
case Js::OpCode::AsyncYieldStar:
// All of these opcodes rely on the ResumeYieldData passed as an argument to the jit'd frame,
// load it first before we do the actual operation.
// Also need to create the instruction differently because, unlike other Reg2 instructions,
// these opcodes actually use their destination operand as a source and have no destination.

IR::Instr* loadResumeYieldData = IR::Instr::New(Js::OpCode::GeneratorLoadResumeYieldData, dstOpnd /* dst */, m_func);
this->AddInstr(loadResumeYieldData, offset);

instr = IR::Instr::New(newOpcode, nullptr /* dst */, dstOpnd /* src1 */, src1Opnd /* src2 */, m_func);
this->AddInstr(instr, offset);


case Js::OpCode::Yield:
instr = IR::Instr::New(newOpcode, dstOpnd, src1Opnd, m_func);
this->AddInstr(instr, offset);
@@ -2957,11 +2957,9 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa
case Js::OpCode::NewAsyncFromSyncIterator:
IR::Opnd *src1Opnd = instr->UnlinkSrc1();

m_lowererMD.LoadHelperArgument(instr, src1Opnd);
m_lowererMD.ChangeToHelperCall(instr, IR::HelperNewAsyncFromSyncIterator);


@@ -325,8 +325,9 @@ namespace Js

AsyncGeneratorNextProcessor* resumeNextReturnProcessor = VarTo<AsyncGeneratorNextProcessor>(function);

ResumeYieldData yieldData(args[1], RecyclerNew(scriptContext->GetRecycler(), JavascriptExceptionObject, args[1], scriptContext, nullptr));
Var data = args[1];
JavascriptExceptionObject* exceptionObj = RecyclerNew(scriptContext->GetRecycler(), JavascriptExceptionObject, args[1], scriptContext, nullptr);
resumeNextReturnProcessor->GetGenerator()->CallAsyncGenerator(data, exceptionObj);
return scriptContext->GetLibrary()->GetUndefined();

@@ -340,22 +341,23 @@ namespace Js

AsyncGeneratorNextProcessor* resumeNextReturnProcessor = VarTo<AsyncGeneratorNextProcessor>(function);

ResumeYieldData yieldData(args.Values[1], nullptr);
Var data = args.Values[1];
JavascriptExceptionObject* exceptionObj = nullptr;
resumeNextReturnProcessor->GetGenerator()->CallAsyncGenerator(data, exceptionObj);
return scriptContext->GetLibrary()->GetUndefined();

void JavascriptGenerator::CallAsyncGenerator(ResumeYieldData* yieldData)
void JavascriptGenerator::CallAsyncGenerator(Var data, JavascriptExceptionObject* exceptionObj)
AssertOrFailFastMsg(isAsync, "Should not call CallAsyncGenerator on a non-async generator");
ScriptContext* scriptContext = this->GetScriptContext();
Var result = nullptr;
JavascriptExceptionObject *exception = nullptr;
yieldData->generator = this;


Var thunkArgs[] = { this, yieldData };
ResumeYieldData yieldData(data, exceptionObj, this);
Var thunkArgs[] = { this, &yieldData };
Arguments arguments(_countof(thunkArgs), thunkArgs);
@@ -557,8 +559,7 @@ namespace Js
// 17. Push genContext onto the execution context stack; genContext is now the running execution context.
// 18. Resume the suspended evaluation of genContext using completion as the result of the operation that suspended it. Let result be the completion record returned by the resumed computation.
ResumeYieldData data(next->data, next->exceptionObj);
CallAsyncGenerator(next->data, next->exceptionObj);
// 19. Assert: result is never an abrupt completion.
// 20. Assert: When we return here, genContext has already been removed from the execution context stack and callerContext is the currently running execution context.
// 21. Return undefined.
@@ -11,22 +11,22 @@ namespace Js
// `data` stores the value that was passed in as parameter to .next()
struct ResumeYieldData
Var data;
JavascriptExceptionObject* exceptionObj;
JavascriptGenerator* generator = nullptr;
Var const data;
JavascriptExceptionObject* const exceptionObj;
JavascriptGenerator* const generator;

ResumeYieldData(Var data, JavascriptExceptionObject* exceptionObj) : data(data), exceptionObj(exceptionObj) { }
ResumeYieldData(Var data, JavascriptExceptionObject* exceptionObj, JavascriptGenerator* generator = nullptr) :
data(data), exceptionObj(exceptionObj), generator(generator) {}

struct AsyncGeneratorRequest
Field(Var) data;
Field(JavascriptExceptionObject*) exceptionObj;
Field(JavascriptPromise*) promise;
Field(Var) const data;
Field(JavascriptExceptionObject*) const exceptionObj;
Field(JavascriptPromise*) const promise;

AsyncGeneratorRequest(Var data, JavascriptExceptionObject* exceptionObj, JavascriptPromise* promise)
: data(data), exceptionObj(exceptionObj), promise(promise) { }

: data(data), exceptionObj(exceptionObj), promise(promise) {}

typedef JsUtil::List<AsyncGeneratorRequest*, Recycler> AsyncGeneratorQueue;
@@ -114,7 +114,7 @@ namespace Js
void AsyncGeneratorResumeNext();
void AsyncGeneratorReject(Var reason);
void AsyncGeneratorResolve(Var value, bool done);
void CallAsyncGenerator(ResumeYieldData* yieldData);
void CallAsyncGenerator(Var data, JavascriptExceptionObject* exceptionObj);
void InitialiseAsyncGenerator(ScriptContext* scriptContext);

void SetScriptFunction(ScriptFunction* sf)

0 comments on commit b83edb0

Please sign in to comment.
You can’t perform that action at this time.