Bug 1316098 - Optimize out result object allocation for await/return in async function.

Tag #1287
This commit is contained in:
Gaming4JC 2019-12-13 20:59:32 -05:00 committed by Roy Tam
commit c9f1fa2cda
10 changed files with 39 additions and 138 deletions

View file

@ -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 <i>value</i> must
conform to the iterator protocol: it must be a non-proxy object of the form
If the frame is a generator function, then <i>value</i> must conform to the
iterator protocol: it must be a non-proxy object of the form
<code>{ done: <i>boolean</i>, value: <i>v</i> }</code>, where
both `done` and `value` are ordinary properties.

View file

@ -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)
{
@ -10631,11 +10646,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;

View file

@ -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);

View file

@ -553,7 +553,7 @@ class FunctionBox : public ObjectBox, public SharedContext
}
bool needsIteratorResult() const {
return isStarGenerator() || isAsync();
return isStarGenerator();
}
bool isAsync() const { return asyncKind() == AsyncFunction; }

View file

@ -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.
// Return with resumption value.
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.
dbg.onExceptionUnwind = function(frame) {
currentFrame = frame;
return {
return: frame.eval("({ done: true, value: 10 })").return
return: 10
};
};
var val = g.eval(`

View file

@ -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}")

View file

@ -171,22 +171,14 @@ AsyncFunctionResume(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleV
: cx->names().StarGeneratorThrow;
FixedInvokeArgs<1> args(cx);
args[0].set(valueOrReason);
RootedValue result(cx);
if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &result))
RootedValue value(cx);
if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &value))
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 (generatorVal.toObject().as<GeneratorObject>().isAfterAwait())
return AsyncFunctionAwait(cx, resultPromise, value);
if (doneVal.toBoolean())
return AsyncFunctionReturned(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);
}

View file

@ -35,9 +35,6 @@ MOZ_MUST_USE bool
AsyncFunctionAwaitedRejected(JSContext* cx, Handle<PromiseObject*> resultPromise,
HandleValue generatorVal, HandleValue reason);
MOZ_MUST_USE bool
CheckAsyncResumptionValue(JSContext* cx, HandleValue v);
} // namespace js
#endif /* vm_AsyncFunction_h */

View file

@ -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<HandleVa
JSTrapStatus status, MutableHandleValue vp)
{
if (status == JSTRAP_RETURN && frame && frame.isFunctionFrame()) {
// Don't let a { return: ... } resumption value make a generator or
// async function violate the iterator protocol. The return value from
// Don't let a { return: ... } resumption value make a generator
// function violate the iterator protocol. The return value from
// such a frame must have the form { done: <bool>, value: <anything> }.
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;

View file

@ -2130,12 +2130,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) \