summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGaming4JC <g4jc@hyperbola.info>2019-12-13 20:59:32 -0500
committerGaming4JC <g4jc@hyperbola.info>2019-12-17 06:25:25 -0500
commitd0d9a4f43dbd58c694c706b17996157fede20bdf (patch)
treedb9e4dce1b1c92b477720f1eba9a9ec04b3f8e2d
parent79b5eb14bc5f8495d20147957cda5f4e5fc8186a (diff)
downloaduxp-d0d9a4f43dbd58c694c706b17996157fede20bdf.tar.gz
Bug 1316098 - Optimize out result object allocation for await/return in async function.
Tag #1287
-rw-r--r--js/src/doc/Debugger/Conventions.md4
-rw-r--r--js/src/frontend/BytecodeEmitter.cpp25
-rw-r--r--js/src/frontend/BytecodeEmitter.h1
-rw-r--r--js/src/frontend/SharedContext.h2
-rw-r--r--js/src/jit-test/tests/debug/onExceptionUnwind-resumption-async.js99
-rw-r--r--js/src/js.msg1
-rw-r--r--js/src/vm/AsyncFunction.cpp24
-rw-r--r--js/src/vm/AsyncFunction.h3
-rw-r--r--js/src/vm/Debugger.cpp12
-rw-r--r--js/src/vm/Opcodes.h6
10 files changed, 39 insertions, 138 deletions
diff --git a/js/src/doc/Debugger/Conventions.md b/js/src/doc/Debugger/Conventions.md
index e8bd3ee430..5de7bc1715 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 <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.
diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
index 77a480bff6..a98016d630 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,13 +8574,28 @@ 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)
{
MOZ_ASSERT(sc->isFunctionBox());
@@ -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 08638f48a7..f3f78df164 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 2d478308ec..8a5f6c0bd3 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 d4e7e85766..0b2ee766bf 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 e2d923bc4f..7465129bfb 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 be7a36ecc9..2bcec54657 100644
--- a/js/src/vm/AsyncFunction.cpp
+++ b/js/src/vm/AsyncFunction.cpp
@@ -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))
- 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<GeneratorObject>().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 d7f2c13117..de7c87d130 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<PromiseObject*> 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 7c09c50b06..2680699a46 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<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;
diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h
index b446280808..583d32beb1 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) \