mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-03 22:38:41 +09:00
Bug 1316098 - Optimize out result object allocation for await/return in async function.
Tag #1287
This commit is contained in:
parent
c1bdfa62fc
commit
c9f1fa2cda
10 changed files with 39 additions and 138 deletions
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -553,7 +553,7 @@ class FunctionBox : public ObjectBox, public SharedContext
|
|||
}
|
||||
|
||||
bool needsIteratorResult() const {
|
||||
return isStarGenerator() || isAsync();
|
||||
return isStarGenerator();
|
||||
}
|
||||
|
||||
bool isAsync() const { return asyncKind() == AsyncFunction; }
|
||||
|
|
|
|||
|
|
@ -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(`
|
||||
|
|
|
|||
|
|
@ -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}")
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) \
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue