From 3f262b7f14e96b43bbe62dc947e0bfeed86f1ef7 Mon Sep 17 00:00:00 2001 From: rhuanjl Date: Thu, 11 Jun 2020 11:41:41 +0100 Subject: [PATCH 1/6] Generator method error fixes --- .../Library/JavascriptAsyncGenerator.cpp | 3 ++- lib/Runtime/Library/JavascriptError.cpp | 8 +++++++ lib/Runtime/Library/JavascriptError.h | 1 + lib/Runtime/Library/JavascriptGenerator.cpp | 6 ++--- test/es7/async-generator-apis.js | 23 +++++++++++++++---- test/es7/async-generator-functionality.js | 23 +++++++++++++++++++ 6 files changed, 56 insertions(+), 8 deletions(-) diff --git a/lib/Runtime/Library/JavascriptAsyncGenerator.cpp b/lib/Runtime/Library/JavascriptAsyncGenerator.cpp index 6638716800c..7a32a218207 100644 --- a/lib/Runtime/Library/JavascriptAsyncGenerator.cpp +++ b/lib/Runtime/Library/JavascriptAsyncGenerator.cpp @@ -176,8 +176,9 @@ Var JavascriptAsyncGenerator::EnqueueRequest( JavascriptError::SetErrorMessage( error, JSERR_NeedObjectOfType, + scriptContext, apiNameForErrorMessage, - scriptContext); + _u("AsyncGenerator")); promise->Reject(error, scriptContext); } diff --git a/lib/Runtime/Library/JavascriptError.cpp b/lib/Runtime/Library/JavascriptError.cpp index edd0e361492..d24f0145a7d 100644 --- a/lib/Runtime/Library/JavascriptError.cpp +++ b/lib/Runtime/Library/JavascriptError.cpp @@ -347,6 +347,14 @@ namespace Js pError->SetNotEnumerable(PropertyIds::number); } + void JavascriptError::SetErrorMessage(JavascriptError *pError, HRESULT hr, ScriptContext* scriptContext, ...) + { + va_list argList; + va_start(argList, scriptContext); + JavascriptError::SetErrorMessage(pError, hr, scriptContext, argList); + va_end(argList); + } + void JavascriptError::SetErrorMessage(JavascriptError *pError, HRESULT hr, ScriptContext* scriptContext, va_list argList) { Assert(FAILED(hr)); diff --git a/lib/Runtime/Library/JavascriptError.h b/lib/Runtime/Library/JavascriptError.h index fcfa0498817..0292c923cd5 100644 --- a/lib/Runtime/Library/JavascriptError.h +++ b/lib/Runtime/Library/JavascriptError.h @@ -107,6 +107,7 @@ namespace Js static void SetErrorMessageProperties(JavascriptError *pError, HRESULT errCode, PCWSTR message, ScriptContext* scriptContext); static void SetErrorMessage(JavascriptError *pError, HRESULT errCode, PCWSTR varName, ScriptContext* scriptContext); static void SetErrorMessage(JavascriptError *pError, HRESULT hr, ScriptContext* scriptContext, va_list argList); + static void SetErrorMessage(JavascriptError *pError, HRESULT hr, ScriptContext* scriptContext, ...); static void SetErrorType(JavascriptError *pError, ErrorTypeEnum errorType); static bool ThrowCantAssign(PropertyOperationFlags flags, ScriptContext* scriptContext, PropertyId propertyId); diff --git a/lib/Runtime/Library/JavascriptGenerator.cpp b/lib/Runtime/Library/JavascriptGenerator.cpp index d7fd07c0333..e1907c22cd0 100644 --- a/lib/Runtime/Library/JavascriptGenerator.cpp +++ b/lib/Runtime/Library/JavascriptGenerator.cpp @@ -231,7 +231,7 @@ Var JavascriptGenerator::EntryNext(RecyclableObject* function, CallInfo callInfo AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Generator.prototype.next")); - if (!VarIs(args[0])) + if (!VarIs(args[0]) || JavascriptOperators::GetTypeId(args[0]) != TypeIds_Generator) { JavascriptError::ThrowTypeErrorVar( scriptContext, @@ -262,7 +262,7 @@ Var JavascriptGenerator::EntryReturn(RecyclableObject* function, CallInfo callIn AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Generator.prototype.return")); - if (!VarIs(args[0])) + if (!VarIs(args[0]) || JavascriptOperators::GetTypeId(args[0]) != TypeIds_Generator) { JavascriptError::ThrowTypeErrorVar( scriptContext, @@ -295,7 +295,7 @@ Var JavascriptGenerator::EntryThrow(RecyclableObject* function, CallInfo callInf AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Generator.prototype.throw")); - if (!VarIs(args[0])) + if (!VarIs(args[0]) || JavascriptOperators::GetTypeId(args[0]) != TypeIds_Generator) { JavascriptError::ThrowTypeErrorVar( scriptContext, diff --git a/test/es7/async-generator-apis.js b/test/es7/async-generator-apis.js index 3aab14b9d33..d8a53a13f96 100644 --- a/test/es7/async-generator-apis.js +++ b/test/es7/async-generator-apis.js @@ -205,10 +205,6 @@ const tests = [ agf = new asyncGeneratorFunction('a', 'b', 'c', 'await a; await b; await c;'); assert.areEqual("async function* anonymous(a,b,c\n) {await a; await b; await c;\n}", agf.toString(), "toString of asyncGeneratorFunction constructed function is named 'anonymous' with specified parameters"); - - // Cannot easily verify behavior of async generator functions in conjunction with UnitTestFramework.js - // due to callback nature of their execution. Instead, verification of behavior is - // found in async-generator-functionality.js test. } }, { @@ -230,7 +226,26 @@ const tests = [ assert.areEqual(asyncGeneratorFunctionPrototype, Object.getPrototypeOf(cla.scagf), "Async generator class static method should have the same prototype as async generator function"); assert.areEqual(asyncGeneratorFunctionPrototype, Object.getPrototypeOf(obj.oagf), "Async generator object method should have the same prototype as async generator function"); } + }, + { + name: "Attempt to use Generator methods with AsyncGenerator", + body() { + function* gf () {} + async function* agf() {} + + const ag = agf(); + const g = gf(); + + assert.throws(()=>{g.next.call(ag)}, TypeError, "Generator.prototype.next should throw TypeError when called on an Async Generator"); + assert.throws(()=>{g.throw.call(ag)}, TypeError, "Generator.prototype.throw should throw TypeError when called on an Async Generator"); + assert.throws(()=>{g.return.call(ag)}, TypeError, "Generator.prototype.return should throw TypeError when called on an Async Generator"); + } } ]; + +// Cannot easily verify behavior of async generator functions in conjunction with UnitTestFramework.js +// due to callback nature of their execution. Instead, verification of behavior is +// found in async-generator-functionality.js test. + testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" }); diff --git a/test/es7/async-generator-functionality.js b/test/es7/async-generator-functionality.js index 0c09202341f..c2cd76f3dca 100644 --- a/test/es7/async-generator-functionality.js +++ b/test/es7/async-generator-functionality.js @@ -28,6 +28,14 @@ function equal(expected, actual) { return expected === actual; } +function ErrorPromise(test, promise, errorType, msg) { + const resultPromise = + promise.then((noError) => { throw new Error (`${test} failed - ${msg}`); }, + (error) => { if (!(error instanceof errorType)) { + throw new Error (`${test} failed - ${msg} - ${error} thrown instead of ${errorType.name}`)}}); + promises.push(resultPromise); +} + const tests = [ { name : "Basic functionality", @@ -224,6 +232,21 @@ const tests = [ AddPromise(this.name, "return a rejected promise, should reject", gen.next(), "reason", true); AddPromise(this.name, "next after rejected promise - iterator should be closed", gen.next(), {done : true}); } + }, + { + name : "Attempt to use AsyncGenerator methods with bad input", + body() { + function* gf () {} + async function* agf() {} + const ag = agf(); + const g = gf(); + const inputs = [g, {}, true, 5, []]; + for (const input of inputs) { + ErrorPromise(this.name, ag.next.call(input), TypeError, `AsyncGenerator.prototype.next should reject with TypeError when called on ${typeof input} ${input}`); + ErrorPromise(this.name, ag.throw.call(input), TypeError, `AsyncGenerator.prototype.throw should reject with TypeError when called on ${typeof input} ${input}`); + ErrorPromise(this.name, ag.return.call(input), TypeError, `AsyncGenerator.prototype.return should reject with TypeError when called on ${typeof input} ${input}`); + } + } } ]; From a0006425a499e1e9009909447fd4794b8b422601 Mon Sep 17 00:00:00 2001 From: rhuanjl Date: Mon, 28 Oct 2019 22:35:24 +0000 Subject: [PATCH 2/6] Cache Await Object --- lib/Backend/Lower.cpp | 2 -- lib/Runtime/ByteCode/ByteCodeEmitter.cpp | 22 ++++++++++++++------ lib/Runtime/ByteCode/FuncInfo.cpp | 1 + lib/Runtime/ByteCode/FuncInfo.h | 8 +++++++ lib/Runtime/ByteCode/OpCodes.h | 2 +- lib/Runtime/Language/InterpreterHandler.inl | 2 +- lib/Runtime/Language/JavascriptOperators.cpp | 3 +-- lib/Runtime/Language/JavascriptOperators.h | 2 +- 8 files changed, 29 insertions(+), 13 deletions(-) diff --git a/lib/Backend/Lower.cpp b/lib/Backend/Lower.cpp index a2901465799..7a0b5b00b56 100644 --- a/lib/Backend/Lower.cpp +++ b/lib/Backend/Lower.cpp @@ -2957,9 +2957,7 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa case Js::OpCode::NewAwaitObject: { - IR::Opnd *src1Opnd = instr->UnlinkSrc1(); LoadScriptContext(instr); - m_lowererMD.LoadHelperArgument(instr, src1Opnd); m_lowererMD.ChangeToHelperCall(instr, IR::HelperNewAwaitObject); break; } diff --git a/lib/Runtime/ByteCode/ByteCodeEmitter.cpp b/lib/Runtime/ByteCode/ByteCodeEmitter.cpp index 08ce45701cb..499051dc77c 100644 --- a/lib/Runtime/ByteCode/ByteCodeEmitter.cpp +++ b/lib/Runtime/ByteCode/ByteCodeEmitter.cpp @@ -1794,6 +1794,10 @@ void ByteCodeGenerator::FinalizeRegisters(FuncInfo* funcInfo, Js::FunctionBody* // NOTE: The FB expects the yield reg to be the final non-temp. if (byteCodeFunction->IsCoroutine()) { + if (funcInfo->root->IsAsync()) + { + funcInfo->AssignAwaitRegister(); + } funcInfo->AssignYieldRegister(); } @@ -2903,6 +2907,11 @@ void ByteCodeGenerator::EmitOneFunction(ParseNodeFnc *pnodeFnc) LoadAllConstants(funcInfo); HomeArguments(funcInfo); + if (funcInfo->root->IsAsync()) + { + Writer()->Reg1(Js::OpCode::NewAwaitObject, funcInfo->awaitRegister); + } + if (!funcInfo->IsBodyAndParamScopeMerged()) { byteCodeFunction->SetParamAndBodyScopeNotMerged(); @@ -10547,17 +10556,18 @@ void EmitAwait( ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo) { - // OPTIMIZE: We should only have to allocate this object once before any awaits. - // Awaiting can merely set the value property of that object. - - auto* writer = byteCodeGenerator->Writer(); - writer->Reg2(Js::OpCode::NewAwaitObject, funcInfo->yieldRegister, inputReg); + auto writer = byteCodeGenerator->Writer(); + writer->PatchableProperty( + Js::OpCode::StFld, + inputReg, + funcInfo->awaitRegister, + funcInfo->FindOrAddInlineCacheId(funcInfo->awaitRegister, Js::PropertyIds::value, false, true)); Js::ByteCodeLabel resumeNormal = writer->DefineLabel(); EmitYieldAndResume( resultReg, - funcInfo->yieldRegister, + funcInfo->awaitRegister, resumeNormal, Js::Constants::NoByteCodeLabel, byteCodeGenerator, diff --git a/lib/Runtime/ByteCode/FuncInfo.cpp b/lib/Runtime/ByteCode/FuncInfo.cpp index 618958e85c0..ca277e059cb 100644 --- a/lib/Runtime/ByteCode/FuncInfo.cpp +++ b/lib/Runtime/ByteCode/FuncInfo.cpp @@ -49,6 +49,7 @@ FuncInfo::FuncInfo( frameDisplayRegister(Js::Constants::NoRegister), funcObjRegister(Js::Constants::NoRegister), localClosureReg(Js::Constants::NoRegister), + awaitRegister(Js::Constants::NoRegister), yieldRegister(Js::Constants::NoRegister), firstTmpReg(Js::Constants::NoRegister), curTmpReg(Js::Constants::NoRegister), diff --git a/lib/Runtime/ByteCode/FuncInfo.h b/lib/Runtime/ByteCode/FuncInfo.h index 560f1f0960c..ecc63f08b18 100644 --- a/lib/Runtime/ByteCode/FuncInfo.h +++ b/lib/Runtime/ByteCode/FuncInfo.h @@ -114,6 +114,7 @@ class FuncInfo Js::RegSlot frameDisplayRegister; // location, if any, of the display of nested frames Js::RegSlot funcObjRegister; Js::RegSlot localClosureReg; + Js::RegSlot awaitRegister; Js::RegSlot yieldRegister; Js::RegSlot firstTmpReg; Js::RegSlot curTmpReg; @@ -568,6 +569,13 @@ class FuncInfo return this->falseConstantRegister; } + Js::RegSlot AssignAwaitRegister() + { + AssertMsg(this->awaitRegister == Js::Constants::NoRegister, "await register should only be assigned once by FinalizeRegisters()"); + this->awaitRegister = NextVarRegister(); + return this->awaitRegister; + } + Js::RegSlot AssignYieldRegister() { AssertMsg(this->yieldRegister == Js::Constants::NoRegister, "yield register should only be assigned once by FinalizeRegisters()"); diff --git a/lib/Runtime/ByteCode/OpCodes.h b/lib/Runtime/ByteCode/OpCodes.h index 519290ac10b..8208a170e87 100755 --- a/lib/Runtime/ByteCode/OpCodes.h +++ b/lib/Runtime/ByteCode/OpCodes.h @@ -625,7 +625,7 @@ MACRO_EXTEND_WMS( NewScGenFuncHomeObj, ElementSlot, OpSideEffec MACRO_EXTEND_WMS( NewInnerScFuncHomeObj, ElementSlotI3, OpSideEffect) // Create new ScriptFunction instance that has home object MACRO_EXTEND_WMS( NewInnerScGenFuncHomeObj, ElementSlotI3, OpSideEffect) // Create new JavascriptGeneratorFunction instance that has home object MACRO_EXTEND_WMS( NewAsyncFromSyncIterator, Reg2, OpSideEffect) // Create new JavascriptAsyncFromSyncOperator instance -MACRO_EXTEND_WMS( NewAwaitObject, Reg2, OpSideEffect) // Create new internal await object instance +MACRO_EXTEND_WMS( NewAwaitObject, Reg1, OpSideEffect) // Create new internal await object instance MACRO_BACKEND_ONLY( NewScopeObject, Reg1, None) // Create new NewScopeObject MACRO_BACKEND_ONLY( InitCachedScope, Reg2Aux, None) // Retrieve cached scope; create if not cached MACRO_BACKEND_ONLY( InitLetCachedScope, Reg2Aux, OpSideEffect) // Retrieve cached scope; create if not cached (formals are let-like instead of var-like) diff --git a/lib/Runtime/Language/InterpreterHandler.inl b/lib/Runtime/Language/InterpreterHandler.inl index 157c06d9163..da9139a88a9 100644 --- a/lib/Runtime/Language/InterpreterHandler.inl +++ b/lib/Runtime/Language/InterpreterHandler.inl @@ -349,7 +349,7 @@ EXDEF2_WMS(GET_SLOT_FB_HMO, NewScGenFuncHomeObj, JavascriptGenera EXDEF2_WMS(GET_ELEM_SLOT_FB_HMO, NewInnerScFuncHomeObj, ScriptFunction::OP_NewScFuncHomeObj) EXDEF2_WMS(GET_ELEM_SLOT_FB_HMO, NewInnerScGenFuncHomeObj, JavascriptGeneratorFunction::OP_NewScGenFuncHomeObj) EXDEF2_WMS(A1toA1Mem, NewAsyncFromSyncIterator, JavascriptOperators::OP_NewAsyncFromSyncIterator) -EXDEF2_WMS(A1toA1Mem, NewAwaitObject, JavascriptOperators::OP_NewAwaitObject) +EXDEF2_WMS(XXtoA1Mem, NewAwaitObject, JavascriptOperators::OP_NewAwaitObject) DEF2_WMS(A1U1toXX, InitForInEnumerator, OP_InitForInEnumerator) DEF2_WMS(A1U1toXXWithCache, ProfiledInitForInEnumerator,OP_InitForInEnumeratorWithCache) DEF2_WMS(A1toXXMem, Throw, JavascriptExceptionOperators::OP_Throw) diff --git a/lib/Runtime/Language/JavascriptOperators.cpp b/lib/Runtime/Language/JavascriptOperators.cpp index c04b587498d..b1f483ac522 100644 --- a/lib/Runtime/Language/JavascriptOperators.cpp +++ b/lib/Runtime/Language/JavascriptOperators.cpp @@ -10358,13 +10358,12 @@ using namespace Js; JIT_HELPER_END(ImportCall); } - Var JavascriptOperators::OP_NewAwaitObject(Var value, ScriptContext* scriptContext) + Var JavascriptOperators::OP_NewAwaitObject(ScriptContext* scriptContext) { JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(NewAwaitObject); auto* awaitObject = DynamicObject::New( scriptContext->GetRecycler(), scriptContext->GetLibrary()->GetAwaitObjectType()); - awaitObject->SetSlot(SetSlotArguments(Js::PropertyIds::value, 0, value)); return awaitObject; JIT_HELPER_END(NewAwaitObject); } diff --git a/lib/Runtime/Language/JavascriptOperators.h b/lib/Runtime/Language/JavascriptOperators.h index b056ad365d9..6a90292ed45 100644 --- a/lib/Runtime/Language/JavascriptOperators.h +++ b/lib/Runtime/Language/JavascriptOperators.h @@ -651,7 +651,7 @@ namespace Js static Var OP_LdFuncObjProto(Var aRight, ScriptContext* scriptContext); static Var OP_ImportCall(__in JavascriptFunction *function, __in Var specifier, __in ScriptContext* scriptContext); - static Var OP_NewAwaitObject(Var value, ScriptContext* scriptContext); + static Var OP_NewAwaitObject(ScriptContext* scriptContext); static Var OP_NewAsyncFromSyncIterator(Var syncIterator, ScriptContext* scriptContext); template From 413db718a51f445ca4238be45008085b57b7d72c Mon Sep 17 00:00:00 2001 From: rhuanjl Date: Sat, 13 Jun 2020 13:54:45 +0100 Subject: [PATCH 3/6] Remove Dead code --- lib/Backend/amd64/LinearScanMD.h | 1 - lib/Backend/arm/LinearScanMD.cpp | 6 - lib/Backend/arm/LinearScanMD.h | 1 - lib/Backend/arm64/LinearScanMD.cpp | 6 - lib/Backend/arm64/LinearScanMD.h | 1 - lib/Backend/i386/LinearScanMD.cpp | 181 ----------------------------- lib/Backend/i386/LinearScanMD.h | 1 - 7 files changed, 197 deletions(-) diff --git a/lib/Backend/amd64/LinearScanMD.h b/lib/Backend/amd64/LinearScanMD.h index 2c9cbfb1ea2..db80343ab08 100644 --- a/lib/Backend/amd64/LinearScanMD.h +++ b/lib/Backend/amd64/LinearScanMD.h @@ -37,7 +37,6 @@ class LinearScanMD : public LinearScanMDShared void GenerateBailOut(IR::Instr * instr, __in_ecount(registerSaveSymsCount) StackSym ** registerSaveSyms, uint registerSaveSymsCount); - IR::Instr* GenerateBailInForGeneratorYield(IR::Instr* resumeLabelInstr, BailOutInfo* bailOutInfo); private: static void SaveAllRegisters(BailOutRecord *const bailOutRecord); diff --git a/lib/Backend/arm/LinearScanMD.cpp b/lib/Backend/arm/LinearScanMD.cpp index be42e2df265..11cf61a4b77 100644 --- a/lib/Backend/arm/LinearScanMD.cpp +++ b/lib/Backend/arm/LinearScanMD.cpp @@ -345,12 +345,6 @@ LinearScanMD::GenerateBailOut( instr->ReplaceSrc1(IR::RegOpnd::New(nullptr, RegLR, TyMachPtr, func)); } -IR::Instr * -LinearScanMD::GenerateBailInForGeneratorYield(IR::Instr * resumeLabelInstr, BailOutInfo * bailOutInfo) -{ - Js::Throw::NotImplemented(); -} - uint LinearScanMD::GetRegisterSaveIndex(RegNum reg) { if (RegTypes[reg] == TyFloat64) diff --git a/lib/Backend/arm/LinearScanMD.h b/lib/Backend/arm/LinearScanMD.h index 2be05f9aa59..dd0891fa5dc 100644 --- a/lib/Backend/arm/LinearScanMD.h +++ b/lib/Backend/arm/LinearScanMD.h @@ -39,7 +39,6 @@ class LinearScanMD : public LinearScanMDShared void LegalizeConstantUse(IR::Instr * instr, IR::Opnd * opnd) { /* no op for arm */ } void GenerateBailOut(IR::Instr * instr, __in_ecount(registerSaveSymsCount) StackSym ** registerSaveSyms, uint registerSaveSymsCount); - IR::Instr *GenerateBailInForGeneratorYield(IR::Instr * resumeLabelInstr, BailOutInfo * bailOutInfo); private: static void SaveAllRegisters(BailOutRecord *const bailOutRecord); public: diff --git a/lib/Backend/arm64/LinearScanMD.cpp b/lib/Backend/arm64/LinearScanMD.cpp index a299da15b39..9c023fdd54a 100644 --- a/lib/Backend/arm64/LinearScanMD.cpp +++ b/lib/Backend/arm64/LinearScanMD.cpp @@ -341,12 +341,6 @@ LinearScanMD::GenerateBailOut( instr->ReplaceSrc1(IR::RegOpnd::New(nullptr, RegLR, TyMachPtr, func)); } -IR::Instr * -LinearScanMD::GenerateBailInForGeneratorYield(IR::Instr * resumeLabelInstr, BailOutInfo * bailOutInfo) -{ - Js::Throw::NotImplemented(); -} - uint LinearScanMD::GetRegisterSaveIndex(RegNum reg) { return reg; diff --git a/lib/Backend/arm64/LinearScanMD.h b/lib/Backend/arm64/LinearScanMD.h index e2b90f4fe1d..479111822c4 100644 --- a/lib/Backend/arm64/LinearScanMD.h +++ b/lib/Backend/arm64/LinearScanMD.h @@ -46,7 +46,6 @@ class LinearScanMD : public LinearScanMDShared void LegalizeConstantUse(IR::Instr * instr, IR::Opnd * opnd) { /* no op for arm */ } void GenerateBailOut(IR::Instr * instr, __in_ecount(registerSaveSymsCount) StackSym ** registerSaveSyms, uint registerSaveSymsCount); - IR::Instr *GenerateBailInForGeneratorYield(IR::Instr * resumeLabelInstr, BailOutInfo * bailOutInfo); private: static void SaveAllRegisters(BailOutRecord *const bailOutRecord); public: diff --git a/lib/Backend/i386/LinearScanMD.cpp b/lib/Backend/i386/LinearScanMD.cpp index b00126f7ef8..e3036621da3 100644 --- a/lib/Backend/i386/LinearScanMD.cpp +++ b/lib/Backend/i386/LinearScanMD.cpp @@ -180,187 +180,6 @@ LinearScanMD::GenerateBailOut(IR::Instr * instr, __in_ecount(registerSaveSymsCou } } -IR::Instr * -LinearScanMD::GenerateBailInForGeneratorYield(IR::Instr * resumeLabelInstr, BailOutInfo * bailOutInfo) -{ - BailOutRecord * bailOutRecord = bailOutInfo->bailOutRecord; - IR::Instr * instrAfter = resumeLabelInstr->m_next; - IR::Instr * newInstr; - - // if (argOuts) { - // sub esp, numArgOutsTimesMachSizeMachAligned - // } - // mov eax, prm1 - // mov eax, [eax + offset of JavascriptGenerator::frame] - // - - IntConstType stackAdjustSize = 0; - bailOutRecord->MapStartCallParamCounts([&stackAdjustSize](uint startCallParamCount) { - IntConstType sizeValue = startCallParamCount; - int32 stackAlignment = Math::Align(sizeValue*MachPtr, MachStackAlignment) - sizeValue*MachPtr; - if (stackAlignment != 0) - { - sizeValue += 1; - } - sizeValue *= MachPtr; - stackAdjustSize += sizeValue; - }); - - if (stackAdjustSize != 0) - { - if ((uint32)stackAdjustSize > AutoSystemInfo::PageSize) - { - // mov eax, sizeOpnd->m_value - // call _chkstk - IR::RegOpnd *eaxOpnd = IR::RegOpnd::New(nullptr, RegEAX, TyMachReg, this->func); - newInstr = IR::Instr::New(Js::OpCode::MOV, eaxOpnd, IR::IntConstOpnd::New(stackAdjustSize, TyInt32, this->func), this->func); - instrAfter->InsertBefore(newInstr); - - newInstr = IR::Instr::New(Js::OpCode::CALL, this->func); - newInstr->SetSrc1(IR::HelperCallOpnd::New(IR::HelperCRT_chkstk, this->func)); - instrAfter->InsertBefore(newInstr); - this->func->SetHasCallsOnSelfAndParents(); - } - else - { - // lea esp, [esp - sizeValue] - IR::RegOpnd * espOpnd = IR::RegOpnd::New(nullptr, LowererMD::GetRegStackPointer(), TyMachReg, this->func); - newInstr = IR::Instr::New(Js::OpCode::LEA, espOpnd, IR::IndirOpnd::New(espOpnd, -stackAdjustSize, TyMachReg, this->func), this->func); - instrAfter->InsertBefore(newInstr); - } - } - - IR::RegOpnd * eaxRegOpnd = IR::RegOpnd::New(nullptr, RegEAX, TyMachPtr, this->func); - IR::RegOpnd * ecxRegOpnd = IR::RegOpnd::New(nullptr, RegECX, TyVar, this->func); - - StackSym * sym = StackSym::NewParamSlotSym(1, this->func); - this->func->SetArgOffset(sym, LowererMD::GetFormalParamOffset() * MachPtr); - IR::SymOpnd * symOpnd = IR::SymOpnd::New(sym, TyMachPtr, this->func); - newInstr = IR::Instr::New(Js::OpCode::MOV, eaxRegOpnd, symOpnd, this->func); - instrAfter->InsertBefore(newInstr); - - IR::IndirOpnd * indirOpnd = IR::IndirOpnd::New(eaxRegOpnd, Js::JavascriptGenerator::GetFrameOffset(), TyMachPtr, this->func); - newInstr = IR::Instr::New(Js::OpCode::MOV, eaxRegOpnd, indirOpnd, this->func); - instrAfter->InsertBefore(newInstr); - - - // eax points to the frame, restore stack syms and registers except eax, restore eax last - - IR::Instr * eaxRestoreInstr = nullptr; - IR::Instr * instrInsertStackSym = instrAfter; - IR::Instr * instrInsertRegSym = instrAfter; - - Assert(bailOutInfo->capturedValues->constantValues.Empty()); - Assert(bailOutInfo->capturedValues->copyPropSyms.Empty()); - - auto restoreSymFn = [this, &eaxRegOpnd, &ecxRegOpnd, &eaxRestoreInstr, &instrInsertStackSym, &instrInsertRegSym](SymID symId) - { - StackSym * stackSym = this->func->m_symTable->FindStackSym(symId); - Assert(stackSym->IsVar()); - - Assert(stackSym->HasByteCodeRegSlot()); - Js::RegSlot byteCodeReg = stackSym->GetByteCodeRegSlot(); - int32 offset = byteCodeReg * sizeof(Js::Var) + Js::InterpreterStackFrame::GetOffsetOfLocals(); - - IR::Opnd * srcOpnd = IR::IndirOpnd::New(eaxRegOpnd, offset, stackSym->GetType(), this->func); - Lifetime * lifetime = stackSym->scratch.linearScan.lifetime; - - if (lifetime->isSpilled) - { - // stack restores require an extra register since we can't move an indir directly to an indir on x86 - IR::Instr * instr = IR::Instr::New(Js::OpCode::MOV, ecxRegOpnd, srcOpnd, this->func); - instrInsertStackSym->InsertBefore(instr); - - IR::SymOpnd * dstOpnd = IR::SymOpnd::New(stackSym, stackSym->GetType(), this->func); - instr = IR::Instr::New(Js::OpCode::MOV, dstOpnd, ecxRegOpnd, this->func); - instrInsertStackSym->InsertBefore(instr); - } - else - { - // register restores must come after stack restores so that we have EAX and ECX free to - // use for stack restores and further EAX must be restored last since it holds the - // pointer to the InterpreterStackFrame from which we are restoring values. - // We must also track these restores using RecordDef in case the symbols are spilled. - - IR::RegOpnd * dstRegOpnd = IR::RegOpnd::New(stackSym, stackSym->GetType(), this->func); - dstRegOpnd->SetReg(lifetime->reg); - - IR::Instr * instr = IR::Instr::New(Js::OpCode::MOV, dstRegOpnd, srcOpnd, this->func); - instrInsertRegSym->InsertBefore(instr); - - if (instrInsertRegSym == instrInsertStackSym) - { - // this is the first register sym, make sure we don't insert stack stores - // after this instruction so we can ensure eax and ecx remain free to use - // for restoring spilled stack syms. - instrInsertStackSym = instr; - } - - if (lifetime->reg == RegEAX) - { - // ensure eax is restored last - Assert(instrInsertRegSym != instrInsertStackSym); - - instrInsertRegSym = instr; - - if (eaxRestoreInstr != nullptr) - { - AssertMsg(false, "this is unexpected until copy prop is enabled"); - // eax was mapped to multiple bytecode registers. Obviously only the first - // restore we do will work so change all following stores to `mov eax, eax`. - // We still need to keep them around for RecordDef in case the corresponding - // dst sym is spilled later on. - eaxRestoreInstr->FreeSrc1(); - eaxRestoreInstr->SetSrc1(eaxRegOpnd); - } - - eaxRestoreInstr = instr; - } - - this->linearScan->RecordDef(lifetime, instr, 0); - } - }; - - FOREACH_BITSET_IN_SPARSEBV(symId, bailOutInfo->byteCodeUpwardExposedUsed) - { - restoreSymFn(symId); - } - NEXT_BITSET_IN_SPARSEBV; - - if (bailOutInfo->capturedValues->argObjSyms) - { - FOREACH_BITSET_IN_SPARSEBV(symId, bailOutInfo->capturedValues->argObjSyms) - { - restoreSymFn(symId); - } - NEXT_BITSET_IN_SPARSEBV; - } - - // Restore out params. - // Would be nice to use restoreSymFn on a walk of the SymIds where the walk matches - // the logic in LinearScan::FillBailOutRecord, but the walk is very complicated and - // requires state to enumerate the exact syms that are actually mapped in the bail - // out record. So instead, since we have disabled most of GlobOpt for the time - // being, just enumerate the argouts from the BailOutRecord and ignore the syms. - // This may need to be improved to use the syms when the optimizations are brought - // online. - bailOutRecord->MapArgOutOffsets([this, &eaxRegOpnd, &ecxRegOpnd, &instrInsertStackSym](Js::RegSlot regSlot, int32 stackOffset) { - // mov ecx, [eax + bytecode reg offset] - // mov [ebp + native stack offset], ecx - int32 regSlotOffset = Js::InterpreterStackFrame::GetOffsetOfLocals() + (this->func->GetJITFunctionBody()->GetLocalsCount() + regSlot) * sizeof(Js::Var); - IR::IndirOpnd * indirOpnd = IR::IndirOpnd::New(eaxRegOpnd, regSlotOffset, TyVar, this->func); - IR::Instr * instr = IR::Instr::New(Js::OpCode::MOV, ecxRegOpnd, indirOpnd, this->func); - instrInsertStackSym->InsertBefore(instr); - - IR::RegOpnd* ebpRegOpnd = IR::RegOpnd::New(nullptr, RegEBP, TyMachPtr, this->func); - indirOpnd = IR::IndirOpnd::New(ebpRegOpnd, stackOffset, TyVar, this->func); - instr = IR::Instr::New(Js::OpCode::RestoreOutParam, indirOpnd, ecxRegOpnd, this->func); - instrInsertStackSym->InsertBefore(instr); - }); - - return instrAfter; -} - __declspec(naked) void LinearScanMD::SaveAllRegisters(BailOutRecord *const bailOutRecord) { __asm diff --git a/lib/Backend/i386/LinearScanMD.h b/lib/Backend/i386/LinearScanMD.h index 49df0ec2151..9770af76f83 100644 --- a/lib/Backend/i386/LinearScanMD.h +++ b/lib/Backend/i386/LinearScanMD.h @@ -31,7 +31,6 @@ class LinearScanMD : public LinearScanMDShared void LegalizeUse(IR::Instr * instr, IR::Opnd * opnd) { /* A nop for x86 */ } void LegalizeConstantUse(IR::Instr * instr, IR::Opnd * opnd) { /* A nop for x86 */ } void GenerateBailOut(IR::Instr * instr, __in_ecount(registerSaveSymsCount) StackSym ** registerSaveSyms, uint registerSaveSymsCount); - IR::Instr *GenerateBailInForGeneratorYield(IR::Instr * resumeLabelInstr, BailOutInfo * bailOutInfo); void InsertOpHelperSpillAndRestores(SList *opHelperBlockList); void EndOfHelperBlock(uint32 helperSpilledLiveranges) { /* NOP */ } From 416e813471841b337516b847f20bcc8aa9a5a134 Mon Sep 17 00:00:00 2001 From: rhuanjl Date: Tue, 29 Oct 2019 01:04:24 +0000 Subject: [PATCH 4/6] Cache AsyncGenerator Continuations --- .../Library/JavascriptAsyncGenerator.cpp | 22 ++++++++++--------- .../Library/JavascriptAsyncGenerator.h | 2 ++ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/Runtime/Library/JavascriptAsyncGenerator.cpp b/lib/Runtime/Library/JavascriptAsyncGenerator.cpp index 7a32a218207..1d15b552a32 100644 --- a/lib/Runtime/Library/JavascriptAsyncGenerator.cpp +++ b/lib/Runtime/Library/JavascriptAsyncGenerator.cpp @@ -15,13 +15,24 @@ JavascriptAsyncGenerator* JavascriptAsyncGenerator::New( { auto* requestQueue = RecyclerNew(recycler, JavascriptAsyncGenerator::RequestQueue, recycler); - return RecyclerNew( + JavascriptAsyncGenerator* generator = RecyclerNew( recycler, JavascriptAsyncGenerator, generatorType, args, scriptFunction, requestQueue); + + auto* library = scriptFunction->GetLibrary(); + generator->onFulfilled = library->CreateAsyncGeneratorCallbackFunction( + EntryAwaitFulfilledCallback, + generator); + + generator->onRejected = library->CreateAsyncGeneratorCallbackFunction( + EntryAwaitRejectedCallback, + generator); + + return generator; } Var JavascriptAsyncGenerator::EntryNext(RecyclableObject* function, CallInfo callInfo, ...) @@ -295,18 +306,9 @@ void JavascriptAsyncGenerator::UnwrapValue(Var value, PendingState pendingState) this->pendingState = pendingState; auto* scriptContext = GetScriptContext(); - auto* library = scriptContext->GetLibrary(); auto* promise = JavascriptPromise::InternalPromiseResolve(value, scriptContext); auto* unused = JavascriptPromise::UnusedPromiseCapability(scriptContext); - auto* onFulfilled = library->CreateAsyncGeneratorCallbackFunction( - EntryAwaitFulfilledCallback, - this); - - auto* onRejected = library->CreateAsyncGeneratorCallbackFunction( - EntryAwaitRejectedCallback, - this); - JavascriptPromise::PerformPromiseThen(promise, unused, onFulfilled, onRejected, scriptContext); } diff --git a/lib/Runtime/Library/JavascriptAsyncGenerator.h b/lib/Runtime/Library/JavascriptAsyncGenerator.h index 22257e8326f..7f3c8f165fb 100644 --- a/lib/Runtime/Library/JavascriptAsyncGenerator.h +++ b/lib/Runtime/Library/JavascriptAsyncGenerator.h @@ -37,6 +37,8 @@ class JavascriptAsyncGenerator : public JavascriptGenerator Field(RequestQueue*) requestQueue; Field(PendingState) pendingState = PendingState::None; + Field(RuntimeFunction*) onFulfilled; + Field(RuntimeFunction*) onRejected; JavascriptAsyncGenerator( DynamicType* type, From a95394705a5c8761d901410d65dfa660c2cd2f3b Mon Sep 17 00:00:00 2001 From: rhuanjl Date: Sun, 13 Dec 2020 14:27:44 +0000 Subject: [PATCH 5/6] Remove unnecessary prototype creation --- .../Library/JavascriptAsyncGeneratorFunction.cpp | 10 ++-------- lib/Runtime/Library/JavascriptGeneratorFunction.cpp | 10 ++-------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/lib/Runtime/Library/JavascriptAsyncGeneratorFunction.cpp b/lib/Runtime/Library/JavascriptAsyncGeneratorFunction.cpp index 394f5e8482f..949a6f17bf4 100644 --- a/lib/Runtime/Library/JavascriptAsyncGeneratorFunction.cpp +++ b/lib/Runtime/Library/JavascriptAsyncGeneratorFunction.cpp @@ -49,7 +49,8 @@ Var JavascriptAsyncGeneratorFunction::EntryAsyncGeneratorFunctionImplementation( auto* asyncGeneratorFn = VarTo(function); auto* library = scriptContext->GetLibrary(); - auto* prototype = library->CreateAsyncGeneratorConstructorPrototypeObject(); + auto* prototype = VarTo(JavascriptOperators::GetPropertyNoCache( + function, Js::PropertyIds::prototype, scriptContext)); auto* scriptFn = asyncGeneratorFn->GetGeneratorVirtualScriptFunction(); auto* generator = library->CreateAsyncGenerator(args, scriptFn, prototype); @@ -62,12 +63,5 @@ Var JavascriptAsyncGeneratorFunction::EntryAsyncGeneratorFunctionImplementation( generator->SetSuspendedStart(); - // Set the prototype from constructor - JavascriptOperators::OrdinaryCreateFromConstructor( - function, - generator, - prototype, - scriptContext); - return generator; } diff --git a/lib/Runtime/Library/JavascriptGeneratorFunction.cpp b/lib/Runtime/Library/JavascriptGeneratorFunction.cpp index db4f852bf61..f5ff7f046ae 100644 --- a/lib/Runtime/Library/JavascriptGeneratorFunction.cpp +++ b/lib/Runtime/Library/JavascriptGeneratorFunction.cpp @@ -111,7 +111,8 @@ using namespace Js; auto* library = scriptContext->GetLibrary(); auto* generatorFunction = VarTo(function); - DynamicObject* prototype = library->CreateGeneratorConstructorPrototypeObject(); + DynamicObject* prototype = VarTo(JavascriptOperators::GetPropertyNoCache( + function, Js::PropertyIds::prototype, scriptContext)); JavascriptGenerator* generator = library->CreateGenerator( args, @@ -127,13 +128,6 @@ using namespace Js; generator->SetSuspendedStart(); - // Set the prototype from constructor - JavascriptOperators::OrdinaryCreateFromConstructor( - function, - generator, - prototype, - scriptContext); - return generator; } From ce1cbdc34a35d67c5db69990f9a38bc31e357c2d Mon Sep 17 00:00:00 2001 From: rhuanjl Date: Sun, 13 Dec 2020 20:20:23 +0000 Subject: [PATCH 6/6] TTD fixes for Await Object --- lib/Runtime/Debug/TTSnapObjects.cpp | 22 ++++++++++++++++++++-- lib/Runtime/Debug/TTSnapObjects.h | 2 ++ lib/Runtime/Debug/TTSnapshot.cpp | 1 + lib/Runtime/Debug/TTSupport.h | 1 + 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/lib/Runtime/Debug/TTSnapObjects.cpp b/lib/Runtime/Debug/TTSnapObjects.cpp index 2660127a336..44c53bb7696 100644 --- a/lib/Runtime/Debug/TTSnapObjects.cpp +++ b/lib/Runtime/Debug/TTSnapObjects.cpp @@ -18,7 +18,7 @@ namespace TTD TTDAssert(!obj->IsExternal(), "We are not prepared for custom external objects yet"); sobj->ObjectPtrId = TTD_CONVERT_VAR_TO_PTR_ID(obj); - sobj->SnapObjectTag = obj->GetSnapTag_TTD(); + sobj->SnapObjectTag = obj->GetTypeId() == Js::TypeId::TypeIds_AwaitObject ? SnapObjectType::SnapAwaitObject : obj->GetSnapTag_TTD(); TTD_WELLKNOWN_TOKEN lookupToken = isWellKnown ? obj->GetScriptContext()->TTDWellKnownInfo->ResolvePathForKnownObject(obj) : TTD_INVALID_WELLKNOWN_TOKEN; sobj->OptWellKnownToken = alloc.CopyRawNullTerminatedStringInto(lookupToken); @@ -39,7 +39,10 @@ namespace TTD NSSnapObjects::StdPropertyExtract_DynamicType(sobj, static_cast(obj), alloc); } - obj->ExtractSnapObjectDataInto(sobj, alloc); + if (sobj->SnapObjectTag != SnapObjectType::SnapAwaitObject) + { + obj->ExtractSnapObjectDataInto(sobj, alloc); + } } void StdPropertyExtract_StaticType(SnapObject* snpObject, Js::RecyclableObject* obj) @@ -681,6 +684,21 @@ namespace TTD } #endif + Js::RecyclableObject* DoObjectInflation_SnapAwaitObject(const SnapObject* snpObject, InflateMap* inflator) + { + Js::DynamicObject* rcObj = ReuseObjectCheckAndReset(snpObject, inflator); + if(rcObj != nullptr) + { + return rcObj; + } + else + { + Js::ScriptContext* ctx = inflator->LookupScriptContext(snpObject->SnapType->ScriptContextLogId); + + return Js::DynamicObject::New(ctx->GetRecycler(), ctx->GetLibrary()->GetAwaitObjectType()); + } + } + Js::RecyclableObject* DoObjectInflation_SnapDynamicObject(const SnapObject* snpObject, InflateMap* inflator) { Js::DynamicObject* rcObj = ReuseObjectCheckAndReset(snpObject, inflator); diff --git a/lib/Runtime/Debug/TTSnapObjects.h b/lib/Runtime/Debug/TTSnapObjects.h index b2f6c206294..115d987d6b2 100644 --- a/lib/Runtime/Debug/TTSnapObjects.h +++ b/lib/Runtime/Debug/TTSnapObjects.h @@ -155,6 +155,8 @@ namespace TTD //ParseAddtlInfo is a nop //AssertSnapEquiv is a nop + Js::RecyclableObject* DoObjectInflation_SnapAwaitObject(const SnapObject* snpObject, InflateMap* inflator); + ////////////////// //A struct that represents a script function object diff --git a/lib/Runtime/Debug/TTSnapshot.cpp b/lib/Runtime/Debug/TTSnapshot.cpp index c83b3eeb8d0..59e3d430a25 100644 --- a/lib/Runtime/Debug/TTSnapshot.cpp +++ b/lib/Runtime/Debug/TTSnapshot.cpp @@ -303,6 +303,7 @@ namespace TTD //For the objects that have inflators this->m_snapObjectVTableArray[(uint32)NSSnapObjects::SnapObjectType::SnapDynamicObject] = { &NSSnapObjects::DoObjectInflation_SnapDynamicObject, nullptr, nullptr, nullptr }; + this->m_snapObjectVTableArray[(uint32)NSSnapObjects::SnapObjectType::SnapAwaitObject] = { &NSSnapObjects::DoObjectInflation_SnapAwaitObject, nullptr, nullptr, nullptr }; this->m_snapObjectVTableArray[(uint32)NSSnapObjects::SnapObjectType::SnapExternalObject] = { &NSSnapObjects::DoObjectInflation_SnapExternalObject, nullptr, nullptr, nullptr }; this->m_snapObjectVTableArray[(uint32)NSSnapObjects::SnapObjectType::SnapScriptFunctionObject] = { &NSSnapObjects::DoObjectInflation_SnapScriptFunctionInfo, &NSSnapObjects::DoAddtlValueInstantiation_SnapScriptFunctionInfo, &NSSnapObjects::EmitAddtlInfo_SnapScriptFunctionInfo, &NSSnapObjects::ParseAddtlInfo_SnapScriptFunctionInfo }; diff --git a/lib/Runtime/Debug/TTSupport.h b/lib/Runtime/Debug/TTSupport.h index bf07f7f5678..ac7557490f3 100644 --- a/lib/Runtime/Debug/TTSupport.h +++ b/lib/Runtime/Debug/TTSupport.h @@ -255,6 +255,7 @@ namespace TTD SnapGeneratorVirtualScriptFunction, SnapAsyncFunction, SnapGenerator, + SnapAwaitObject, JavascriptAsyncSpawnExecutorFunction, JavascriptAsyncSpawnStepFunction,