From d0d9a4f43dbd58c694c706b17996157fede20bdf Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Fri, 13 Dec 2019 20:59:32 -0500 Subject: Bug 1316098 - Optimize out result object allocation for await/return in async function. Tag #1287 --- js/src/doc/Debugger/Conventions.md | 4 +- js/src/frontend/BytecodeEmitter.cpp | 25 +++++- js/src/frontend/BytecodeEmitter.h | 1 + js/src/frontend/SharedContext.h | 2 +- .../debug/onExceptionUnwind-resumption-async.js | 99 +--------------------- js/src/js.msg | 1 - js/src/vm/AsyncFunction.cpp | 24 ++---- js/src/vm/AsyncFunction.h | 3 - js/src/vm/Debugger.cpp | 12 +-- js/src/vm/Opcodes.h | 6 +- 10 files changed, 39 insertions(+), 138 deletions(-) (limited to 'js') diff --git a/js/src/doc/Debugger/Conventions.md b/js/src/doc/Debugger/Conventions.md index e8bd3ee43..5de7bc171 100644 --- a/js/src/doc/Debugger/Conventions.md +++ b/js/src/doc/Debugger/Conventions.md @@ -110,8 +110,8 @@ resumption value has one of the following forms: the `new` expression returns the frame's `this` value. Similarly, if the function is the constructor for a subclass, then a non-object value may result in a TypeError. - If the frame is a generator or async function, then value must - conform to the iterator protocol: it must be a non-proxy object of the form + If the frame is a generator function, then value must conform to the + iterator protocol: it must be a non-proxy object of the form { done: boolean, value: v }, where both `done` and `value` are ordinary properties. diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 77a480bff..a98016d63 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -8552,7 +8552,7 @@ bool BytecodeEmitter::emitYield(ParseNode* pn) { MOZ_ASSERT(sc->isFunctionBox()); - MOZ_ASSERT(pn->getOp() == JSOP_YIELD || pn->getOp() == JSOP_AWAIT); + MOZ_ASSERT(pn->getOp() == JSOP_YIELD); bool needsIteratorResult = sc->asFunctionBox()->needsIteratorResult(); if (needsIteratorResult) { @@ -8574,12 +8574,27 @@ BytecodeEmitter::emitYield(ParseNode* pn) if (!emitGetDotGenerator()) return false; - if (!emitYieldOp(pn->getOp())) + if (!emitYieldOp(JSOP_YIELD)) return false; return true; } +bool +BytecodeEmitter::emitAwait(ParseNode* pn) +{ + MOZ_ASSERT(sc->isFunctionBox()); + MOZ_ASSERT(pn->getOp() == JSOP_AWAIT); + + if (!emitTree(pn->pn_kid)) + return false; + if (!emitGetDotGenerator()) + return false; + if (!emitYieldOp(JSOP_AWAIT)) + return false; + return true; +} + bool BytecodeEmitter::emitYieldStar(ParseNode* iter) { @@ -10630,11 +10645,15 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage:: break; case PNK_YIELD: - case PNK_AWAIT: if (!emitYield(pn)) return false; break; + case PNK_AWAIT: + if (!emitAwait(pn)) + return false; + break; + case PNK_STATEMENTLIST: if (!emitStatementList(pn)) return false; diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 08638f48a..f3f78df16 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -613,6 +613,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter MOZ_MUST_USE bool emitYield(ParseNode* pn); MOZ_MUST_USE bool emitYieldOp(JSOp op); MOZ_MUST_USE bool emitYieldStar(ParseNode* iter); + MOZ_MUST_USE bool emitAwait(ParseNode* pn); MOZ_MUST_USE bool emitPropLHS(ParseNode* pn); MOZ_MUST_USE bool emitPropOp(ParseNode* pn, JSOp op); diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h index 2d478308e..8a5f6c0bd 100644 --- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -553,7 +553,7 @@ class FunctionBox : public ObjectBox, public SharedContext } bool needsIteratorResult() const { - return isStarGenerator() || isAsync(); + return isStarGenerator(); } bool isAsync() const { return asyncKind() == AsyncFunction; } diff --git a/js/src/jit-test/tests/debug/onExceptionUnwind-resumption-async.js b/js/src/jit-test/tests/debug/onExceptionUnwind-resumption-async.js index d4e7e8576..0b2ee766b 100644 --- a/js/src/jit-test/tests/debug/onExceptionUnwind-resumption-async.js +++ b/js/src/jit-test/tests/debug/onExceptionUnwind-resumption-async.js @@ -9,28 +9,6 @@ async function f() { } `); -// To continue testing after uncaught exception, remember the exception and -// return normal completeion. -var currentFrame; -var uncaughtException; -dbg.uncaughtExceptionHook = function(e) { - uncaughtException = e; - return { - return: currentFrame.eval("({ done: true, value: 'uncaught' })").return - }; -}; -function testUncaughtException() { - uncaughtException = undefined; - var val = g.eval(` -var val; -f().then(v => { val = v }); -drainJobQueue(); -val; -`); - assertEq(val, "uncaught"); - assertEq(uncaughtException instanceof TypeError, true); -} - // Just continue dbg.onExceptionUnwind = function(frame) { return undefined; @@ -42,83 +20,10 @@ drainJobQueue(); assertEq(exc instanceof ReferenceError, true); `); -// Should return object. -dbg.onExceptionUnwind = function(frame) { - currentFrame = frame; - return { - return: "foo" - }; -}; -testUncaughtException(); - -// The object should have `done` property and `value` property. -dbg.onExceptionUnwind = function(frame) { - currentFrame = frame; - return { - return: frame.eval("({})").return - }; -}; -testUncaughtException(); - -// The object should have `done` property. -dbg.onExceptionUnwind = function(frame) { - currentFrame = frame; - return { - return: frame.eval("({ value: 10 })").return - }; -}; -testUncaughtException(); - -// The object should have `value` property. -dbg.onExceptionUnwind = function(frame) { - currentFrame = frame; - return { - return: frame.eval("({ done: true })").return - }; -}; -testUncaughtException(); - -// `done` property should be a boolean value. -dbg.onExceptionUnwind = function(frame) { - currentFrame = frame; - return { - return: frame.eval("({ done: 10, value: 10 })").return - }; -}; -testUncaughtException(); - -// `done` property shouldn't be an accessor. -dbg.onExceptionUnwind = function(frame) { - currentFrame = frame; - return { - return: frame.eval("({ get done() { return true; }, value: 10 })").return - }; -}; -testUncaughtException(); - -// `value` property shouldn't be an accessor. -dbg.onExceptionUnwind = function(frame) { - currentFrame = frame; - return { - return: frame.eval("({ done: true, get value() { return 10; } })").return - }; -}; -testUncaughtException(); - -// The object shouldn't be a Proxy. -dbg.onExceptionUnwind = function(frame) { - currentFrame = frame; - return { - return: frame.eval("new Proxy({ done: true, value: 10 }, {})").return - }; -}; -testUncaughtException(); - -// Correct resumption value. +// Return with resumption value. dbg.onExceptionUnwind = function(frame) { - currentFrame = frame; return { - return: frame.eval("({ done: true, value: 10 })").return + return: 10 }; }; var val = g.eval(` diff --git a/js/src/js.msg b/js/src/js.msg index e2d923bc4..7465129bf 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -438,7 +438,6 @@ MSG_DEF(JSMSG_SC_SAB_DISABLED, 0, JSEXN_TYPEERR, "SharedArrayBuffer not // Debugger MSG_DEF(JSMSG_ASSIGN_FUNCTION_OR_NULL, 1, JSEXN_TYPEERR, "value assigned to {0} must be a function or null") -MSG_DEF(JSMSG_DEBUG_BAD_AWAIT, 0, JSEXN_TYPEERR, "await expression received invalid value") MSG_DEF(JSMSG_DEBUG_BAD_LINE, 0, JSEXN_TYPEERR, "invalid line number") MSG_DEF(JSMSG_DEBUG_BAD_OFFSET, 0, JSEXN_TYPEERR, "invalid script offset") MSG_DEF(JSMSG_DEBUG_BAD_REFERENT, 2, JSEXN_TYPEERR, "{0} does not refer to {1}") diff --git a/js/src/vm/AsyncFunction.cpp b/js/src/vm/AsyncFunction.cpp index be7a36ecc..2bcec5465 100644 --- a/js/src/vm/AsyncFunction.cpp +++ b/js/src/vm/AsyncFunction.cpp @@ -171,22 +171,14 @@ AsyncFunctionResume(JSContext* cx, Handle resultPromise, HandleV : cx->names().StarGeneratorThrow; FixedInvokeArgs<1> args(cx); args[0].set(valueOrReason); - RootedValue result(cx); - if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &result)) - return AsyncFunctionThrown(cx, resultPromise); - - RootedObject resultObj(cx, &result.toObject()); - RootedValue doneVal(cx); RootedValue value(cx); - if (!GetProperty(cx, resultObj, resultObj, cx->names().done, &doneVal)) - return false; - if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value)) - return false; + if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &value)) + return AsyncFunctionThrown(cx, resultPromise); - if (doneVal.toBoolean()) - return AsyncFunctionReturned(cx, resultPromise, value); + if (generatorVal.toObject().as().isAfterAwait()) + return AsyncFunctionAwait(cx, resultPromise, value); - return AsyncFunctionAwait(cx, resultPromise, value); + return AsyncFunctionReturned(cx, resultPromise, value); } // Async Functions proposal 2.2 steps 3-8. @@ -242,9 +234,3 @@ js::IsWrappedAsyncFunction(JSFunction* fun) { return fun->maybeNative() == WrappedAsyncFunction; } - -MOZ_MUST_USE bool -js::CheckAsyncResumptionValue(JSContext* cx, HandleValue v) -{ - return CheckStarGeneratorResumptionValue(cx, v); -} diff --git a/js/src/vm/AsyncFunction.h b/js/src/vm/AsyncFunction.h index d7f2c1311..de7c87d13 100644 --- a/js/src/vm/AsyncFunction.h +++ b/js/src/vm/AsyncFunction.h @@ -35,9 +35,6 @@ MOZ_MUST_USE bool AsyncFunctionAwaitedRejected(JSContext* cx, Handle resultPromise, HandleValue generatorVal, HandleValue reason); -MOZ_MUST_USE bool -CheckAsyncResumptionValue(JSContext* cx, HandleValue v); - } // namespace js #endif /* vm_AsyncFunction_h */ diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 7c09c50b0..2680699a4 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -32,7 +32,6 @@ #include "js/Vector.h" #include "proxy/ScriptedProxyHandler.h" #include "vm/ArgumentsObject.h" -#include "vm/AsyncFunction.h" #include "vm/DebuggerMemory.h" #include "vm/GeneratorObject.h" #include "vm/SPSProfiler.h" @@ -1560,16 +1559,11 @@ CheckResumptionValue(JSContext* cx, AbstractFramePtr frame, const Maybe, value: }. RootedFunction callee(cx, frame.callee()); - if (callee->isAsync()) { - if (!CheckAsyncResumptionValue(cx, vp)) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_AWAIT); - return false; - } - } else if (callee->isStarGenerator() || callee->isAsync()) { + if (callee->isStarGenerator()) { if (!CheckStarGeneratorResumptionValue(cx, vp)) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_YIELD); return false; diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index b44628080..583d32beb 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -2120,12 +2120,12 @@ */ \ macro(JSOP_DEBUGAFTERYIELD, 208, "debugafteryield", NULL, 1, 0, 0, JOF_BYTE) \ /* - * Pops the generator and the return value 'result', stops interpretation - * and returns 'result'. Pushes resolved value onto the stack. + * Pops the generator and the return value 'promise', stops interpretation + * and returns 'promise'. Pushes resolved value onto the stack. * Category: Statements * Type: Generator * Operands: uint24_t yieldAndAwaitIndex - * Stack: result, gen => resolved + * Stack: promise, gen => resolved */ \ macro(JSOP_AWAIT, 209, "await", NULL, 4, 2, 1, JOF_UINT24) \ macro(JSOP_UNUSED210, 210, "unused210", NULL, 1, 0, 0, JOF_BYTE) \ -- cgit v1.2.3