#521: baseline parser support for async/await, with toggle, without bytecode (passes tests)

This commit is contained in:
Cameron Kaiser 2019-08-18 14:26:54 -07:00
parent 3fd15a87ad
commit 0e5746aaf0
39 changed files with 1236 additions and 91 deletions

View File

@ -0,0 +1,51 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
function AsyncFunction_wrap(genFunction) {
var wrapper = function() {
// The try block is required to handle throws in default arguments properly.
try {
return AsyncFunction_start(callFunction(std_Function_apply, genFunction, this, arguments));
} catch (e) {
return Promise_reject(e);
}
};
/* not yet
std_Object_setPrototypeOf(wrapper, GetBuiltinPrototype("AsyncFunction"));
*/
_DefineDataProperty(wrapper, "length", genFunction.length);
SetFunctionExtendedSlot(genFunction, 1, wrapper);
var name = GetFunName(genFunction);
if (typeof name !== 'undefined')
SetFunName(wrapper, name);
return wrapper;
}
function AsyncFunction_start(generator) {
return AsyncFunction_resume(generator, undefined, generator.next);
}
function AsyncFunction_resume(gen, v, method) {
let result;
try {
// get back into async function, run to next await point
result = callFunction(method, gen, v);
} catch (exc) {
// The async function itself failed.
return Promise_reject(exc);
}
if (result.done)
return Promise_resolve(result.value);
// If we get here, `await` occurred. `gen` is paused at a yield point.
return callFunction(Promise_then,
Promise_resolve(result.value),
function(val) {
return AsyncFunction_resume(gen, val, gen.next);
}, function (err) {
return AsyncFunction_resume(gen, err, gen.throw);
});
}

142
js/src/builtin/Promise.cpp Normal file
View File

@ -0,0 +1,142 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "builtin/Promise.h"
#include "jscntxt.h"
#include "builtin/SelfHostingDefines.h"
#include "jsobjinlines.h"
using namespace js;
static const JSFunctionSpec promise_methods[] = {
JS_SELF_HOSTED_FN("catch", "Promise_catch", 1, 0),
JS_SELF_HOSTED_FN("then", "Promise_then", 2, 0),
JS_FS_END
};
static const JSFunctionSpec promise_static_methods[] = {
JS_SELF_HOSTED_FN("all", "Promise_all", 1, 0),
JS_SELF_HOSTED_FN("race", "Promise_race", 1, 0),
JS_SELF_HOSTED_FN("reject", "Promise_reject", 1, 0),
JS_SELF_HOSTED_FN("resolve", "Promise_resolve", 1, 0),
JS_FS_END
};
namespace js {
bool
PromiseConstructor(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() == 0) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
"ShellPromise.constructor", "0", "s");
return false;
}
HandleValue fn = args.get(0);
if (!IsCallable(fn)) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_CALLABLE,
"Argument 1 of ShellPromise.constructor");
return false;
}
RootedObject promise(cx, NewBuiltinClassInstance(cx, &ShellPromiseObject::class_));
if (!promise)
return false;
JS_SetReservedSlot(promise, PROMISE_STATE_SLOT, NumberValue(PROMISE_STATE_PENDING));
JS_SetReservedSlot(promise, PROMISE_VALUE_SLOT, NullValue());
JS_SetReservedSlot(promise, PROMISE_DEFERREDS_SLOT, NullValue());
RootedValue initRval(cx);
JS::AutoValueVector argValues(cx);
argValues.append(ObjectValue(*promise));
argValues.append(fn);
HandleValueArray arr(argValues);
JSAtom* promiseInitAtom;
if (!(promiseInitAtom = Atomize(cx, "Promise_init", 12)))
return false;
RootedPropertyName name(cx, promiseInitAtom->asPropertyName());
RootedValue selfHostedFun(cx);
if (!GlobalObject::getIntrinsicValue(cx, cx->global(), name, &selfHostedFun))
return false;
if (!JS_CallFunctionValue(cx, promise, selfHostedFun, arr, &initRval))
return false;
args.rval().setObject(*promise);
return true;
}
}
static JSObject*
CreatePromisePrototype(JSContext* cx, JSProtoKey key)
{
return cx->global()->createBlankPrototype(cx, &ShellPromiseObject::protoClass_);
}
const Class ShellPromiseObject::class_ = {
"ShellPromise",
JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_ShellPromise),
nullptr, /* addProperty */
nullptr, /* delProperty */
nullptr, /* getProperty */
nullptr, /* setProperty */
nullptr, /* enumerate */
nullptr, /* resolve */
nullptr, /* mayResolve */
nullptr, /* finalize */
nullptr, /* call */
nullptr, /* hasInstance */
nullptr, /* construct */
nullptr, /* trace */
{
GenericCreateConstructor<PromiseConstructor, 2, gc::AllocKind::FUNCTION>,
CreatePromisePrototype,
promise_static_methods,
nullptr,
promise_methods
}
};
const Class ShellPromiseObject::protoClass_ = {
"ShellPromise",
JSCLASS_HAS_CACHED_PROTO(JSProto_ShellPromise),
nullptr, /* addProperty */
nullptr, /* delProperty */
nullptr, /* getProperty */
nullptr, /* setProperty */
nullptr, /* enumerate */
nullptr, /* resolve */
nullptr, /* mayResolve */
nullptr, /* finalize */
nullptr, /* call */
nullptr, /* hasInstance */
nullptr, /* construct */
nullptr, /* trace */
{
DELEGATED_CLASSSPEC(&ShellPromiseObject::class_.spec),
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
ClassSpec::IsDelegated
}
};

29
js/src/builtin/Promise.h Normal file
View File

@ -0,0 +1,29 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef builtin_Promise_h
#define builtin_Promise_h
#include "vm/NativeObject.h"
namespace js {
class AutoSetNewObjectMetadata;
class ShellPromiseObject : public NativeObject
{
public:
static const unsigned RESERVED_SLOTS = 3;
static const Class class_;
static const Class protoClass_;
};
bool
PromiseConstructor(JSContext* cx, unsigned argc, Value* vp);
} // namespace js
#endif /* builtin_Promise_h */

253
js/src/builtin/Promise.js Normal file
View File

@ -0,0 +1,253 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* This implementation is by no means complete and is using a polyfill.
* In particular, it doesn't fully comply to ES6 Promises spec.
* The list of incompatibilities may not be complete and includes:
*
* - no [Symbol.species] implementation
* - implementation is not really async at all, but all is executed
* in correct order
* - Promise.race is not implemented (not necessary for async/await)
* - Promise.all implementation currently only handles arrays, no other
* iterables.
*/
/**
* This polyfill implements the Promise specification in the following way:
* At first, Promise is initialized with the intrinsic Promise constructor,
* with its initialization completed by calling Promise_init. The goal is to
* completely resolve/reject the promise. There are certain helper functions:
* - resolveOneStep() executes "one step" of the promise - which means
* getting either the final value or next promise towards complete execution.
* - resolveCompletely() executes the entire promise, which merely means
* resolving one step at a time, until the final step no longer resolves
* to a promise (which means either resolving to a value or rejecting).
*
* Once resolution is finished, resolveCompletely() is called in order to
* update state of the promise. It also spawns callbacks that may have been
* deferred with a then() - they are NOT normally taken into consideration,
* because resolveCompletely() just runs one path.
*/
/**
* Below is a simple simulation of an event loop. Events enqueued using
* this setTimeout implementation must be manually triggered by calling
* runEvents().
*/
var eventQueue = new List();
function runEvents() {
while (eventQueue.length > 0) {
var evt = callFunction(std_Array_pop, eventQueue);
evt();
}
}
function setTimeout(cb, interval) {
if (!IsCallable(cb))
ThrowTypeError(JSMSG_NOT_CALLABLE, "Argument 0");
if (interval !== 0)
ThrowTypeError(JSMSG_SETTIMEOUT_INTERVAL_NONZERO);
callFunction(std_Array_push, eventQueue, cb);
}
function Handler(onFulfilled, onRejected, resolve, reject) {
this.onFulfilled = IsCallable(onFulfilled) ? onFulfilled : null;
this.onRejected = IsCallable(onRejected) ? onRejected : null;
this.resolve = resolve;
this.reject = reject;
}
MakeConstructible(Handler, std_Object_create(null));
function Promise_setState(promise, state) {
UnsafeSetReservedSlot(promise, PROMISE_STATE_SLOT, state);
}
function Promise_getState(promise) {
return UnsafeGetReservedSlot(promise, PROMISE_STATE_SLOT);
}
function Promise_setDeferreds(promise, deferreds) {
UnsafeSetReservedSlot(promise, PROMISE_DEFERREDS_SLOT, deferreds);
}
function Promise_getDeferreds(promise) {
return UnsafeGetReservedSlot(promise, PROMISE_DEFERREDS_SLOT);
}
function Promise_setValue(promise, value) {
UnsafeSetReservedSlot(promise, PROMISE_VALUE_SLOT, value);
}
function Promise_getValue(promise) {
return UnsafeGetReservedSlot(promise, PROMISE_VALUE_SLOT);
}
function Promise_init(promise, fn) {
Promise_setDeferreds(promise, new List());
resolveOneStep(fn,
function(value) { resolveCompletely(promise, value); },
function(reason) { reject(promise, reason); });
}
function Promise_isThenable(valueOrPromise) {
if (valueOrPromise && (typeof valueOrPromise === 'object' || IsCallable(valueOrPromise))) {
var then = valueOrPromise.then;
if (IsCallable(then))
return true;
}
return false;
}
function Promise_all(promises) {
var length = promises.length;
var results = [];
return NewPromise(function (resolve, reject) {
if (length === 0)
return resolve([]);
var remaining = length;
function resolveChain(index, valueOrPromise) {
try {
if (Promise_isThenable(valueOrPromise)) {
callFunction(valueOrPromise.then, valueOrPromise,
function (valueOrPromise) { resolveChain(index, valueOrPromise); },
reject);
} else {
_DefineDataProperty(results, index, valueOrPromise);
if (--remaining === 0)
resolve(results);
}
} catch (ex) {
reject(ex);
}
}
for (var i = 0; i < length; i++)
resolveChain(i, promises[i]);
});
}
function Promise_race(values) {
ThrowTypeError(JSMSG_NOT_IMPLEMENTED, "Promise.race");
}
function Promise_reject(value) {
return NewPromise(function (resolve, reject) {
reject(value);
});
}
function Promise_resolve(value) {
if (value && typeof value === 'object' && IsPromise(value))
return value;
return NewPromise(function (resolve) {
resolve(value);
});
}
function Promise_catch(onRejected) {
return callFunction(Promise_then, this, undefined, onRejected);
}
function asap(cb) {
setTimeout(cb, 0);
}
function deferOrExecute(promise, deferred) {
if (Promise_getState(promise) === PROMISE_STATE_PENDING) {
/* work around "self-hosted code may not contain direct method calls"
Promise_getDeferreds(promise).push(deferred); */
callFunction(std_Array_push, Promise_getDeferreds(promise), deferred);
return;
}
asap(function() {
var cb = Promise_getState(promise) === PROMISE_STATE_RESOLVED ?
deferred.onFulfilled : deferred.onRejected;
if (cb === null) {
var value = Promise_getValue(promise);
/* (Promise_getState(promise) === PROMISE_STATE_RESOLVED ? deferred.resolve : deferred.reject)(value); */
if (Promise_getState(promise) == PROMISE_STATE_RESOLVED) {
callFunction(deferred.resolve, deferred, value);
} else {
callFunction(deferred.reject, deferred, value);
}
return;
}
var returnValue;
try {
returnValue = cb(Promise_getValue(promise));
} catch (e) {
/* sigh: deferred.reject(e); */
callFunction(deferred.reject, deferred, e);
return;
}
/* deferred.resolve(returnValue); */
callFunction(deferred.resolve, deferred, returnValue);
});
}
function resolveOneStep(fn, onFulfilled, onRejected) {
var done = false;
var callOnce = function(cb) {
return function(value) {
if (done) return;
done = true;
cb(value);
};
};
try {
fn(callOnce(onFulfilled), callOnce(onRejected));
} catch (ex) {
callOnce(onRejected)(ex);
}
}
function resolveCompletely(promise, valueOrPromise) {
try {
// FIXME this is probably not a type error
if (valueOrPromise === promise)
ThrowTypeError(JSMSG_PROMISE_RESOLVED_WITH_ITSELF);
if (Promise_isThenable(valueOrPromise)) {
resolveOneStep(function(resolve, reject) {
/* valueOrPromise.then(resolve, reject); */
callFunction(valueOrPromise.then, valueOrPromise, resolve, reject);
},
function(value) { resolveCompletely(promise, value); },
function(value) { reject(promise, value); });
}
else
callFunction(resolvingFinished, promise, PROMISE_STATE_RESOLVED, valueOrPromise);
} catch (ex) {
callFunction(reject, promise, ex);
}
}
function reject(promise, reason) {
callFunction(resolvingFinished, promise, PROMISE_STATE_REJECTED, reason);
}
function resolvingFinished(state, newValue) {
Promise_setState(this, state);
Promise_setValue(this, newValue);
var deferreds = Promise_getDeferreds(this);
for (var i = 0, len = deferreds.length; i < len; i++)
deferOrExecute(this, deferreds[i]);
Promise_setDeferreds(this, null);
}
function Promise_then(onFulfilled, onRejected) {
var promise = this;
var newPromise = NewPromise(function(resolve, reject) {
deferOrExecute(promise,
new Handler(onFulfilled, onRejected, resolve, reject));
});
runEvents();
return newPromise;
}

View File

@ -93,6 +93,7 @@ enum UnaryOperator {
UNOP_BITNOT,
UNOP_TYPEOF,
UNOP_VOID,
UNOP_AWAIT,
UNOP_LIMIT
};
@ -162,7 +163,8 @@ static const char* const unopNames[] = {
"!", /* UNOP_NOT */
"~", /* UNOP_BITNOT */
"typeof", /* UNOP_TYPEOF */
"void" /* UNOP_VOID */
"void", /* UNOP_VOID */
"await" /* UNOP_AWAIT */
};
static const char* const nodeTypeNames[] = {
@ -466,7 +468,7 @@ class NodeBuilder
bool function(ASTType type, TokenPos* pos,
HandleValue id, NodeVector& args, NodeVector& defaults,
HandleValue body, HandleValue rest, GeneratorStyle generatorStyle,
bool isExpression, MutableHandleValue dst);
bool isAsync, bool isExpression, MutableHandleValue dst);
bool variableDeclarator(HandleValue id, HandleValue init, TokenPos* pos,
MutableHandleValue dst);
@ -1602,7 +1604,7 @@ bool
NodeBuilder::function(ASTType type, TokenPos* pos,
HandleValue id, NodeVector& args, NodeVector& defaults,
HandleValue body, HandleValue rest,
GeneratorStyle generatorStyle, bool isExpression,
GeneratorStyle generatorStyle, bool isAsync, bool isExpression,
MutableHandleValue dst)
{
RootedValue array(cx), defarray(cx);
@ -1613,6 +1615,7 @@ NodeBuilder::function(ASTType type, TokenPos* pos,
bool isGenerator = generatorStyle != GeneratorStyle::None;
RootedValue isGeneratorVal(cx, BooleanValue(isGenerator));
RootedValue isAsyncVal(cx, BooleanValue(isAsync));
RootedValue isExpressionVal(cx, BooleanValue(isExpression));
RootedValue cb(cx, callbacks[type]);
@ -1636,6 +1639,7 @@ NodeBuilder::function(ASTType type, TokenPos* pos,
"body", body,
"rest", rest,
"generator", isGeneratorVal,
"async", isAsyncVal,
"style", styleVal,
"expression", isExpressionVal,
dst);
@ -1648,6 +1652,7 @@ NodeBuilder::function(ASTType type, TokenPos* pos,
"body", body,
"rest", rest,
"generator", isGeneratorVal,
"async", isAsyncVal,
"expression", isExpressionVal,
dst);
}
@ -1818,6 +1823,7 @@ class ASTSerializer
bool function(ParseNode* pn, ASTType type, MutableHandleValue dst);
bool functionArgsAndBody(ParseNode* pn, NodeVector& args, NodeVector& defaults,
bool isAsync, bool isExpression,
MutableHandleValue body, MutableHandleValue rest);
bool functionBody(ParseNode* pn, TokenPos* pos, MutableHandleValue dst);
@ -1893,6 +1899,9 @@ ASTSerializer::unop(ParseNodeKind kind, JSOp op)
if (IsTypeofKind(kind))
return UNOP_TYPEOF;
if (kind == PNK_AWAIT)
return UNOP_AWAIT;
switch (op) {
case JSOP_NEG:
return UNOP_NEG;
@ -2974,6 +2983,7 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
case PNK_NOT:
case PNK_BITNOT:
case PNK_POS:
case PNK_AWAIT:
case PNK_NEG: {
MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos));
@ -3464,6 +3474,7 @@ ASTSerializer::function(ParseNode* pn, ASTType type, MutableHandleValue dst)
: GeneratorStyle::ES6)
: GeneratorStyle::None;
bool isAsync = pn->pn_funbox->isAsync();
bool isExpression =
#if JS_HAS_EXPR_CLOSURES
func->isExprBody();
@ -3484,13 +3495,14 @@ ASTSerializer::function(ParseNode* pn, ASTType type, MutableHandleValue dst)
rest.setUndefined();
else
rest.setNull();
return functionArgsAndBody(pn->pn_body, args, defaults, &body, &rest) &&
return functionArgsAndBody(pn->pn_body, args, defaults, isAsync, isExpression, &body, &rest) &&
builder.function(type, &pn->pn_pos, id, args, defaults, body,
rest, generatorStyle, isExpression, dst);
rest, generatorStyle, isAsync, isExpression, dst);
}
bool
ASTSerializer::functionArgsAndBody(ParseNode* pn, NodeVector& args, NodeVector& defaults,
bool isAsync, bool isExpression,
MutableHandleValue body, MutableHandleValue rest)
{
ParseNode* pnargs;
@ -3521,6 +3533,14 @@ ASTSerializer::functionArgsAndBody(ParseNode* pn, NodeVector& args, NodeVector&
pnstart = pnstart->pn_next;
}
// Async arrow with expression body is converted into STATEMENTLIST
// to insert initial yield.
if (isAsync && isExpression) {
MOZ_ASSERT(pnstart->getKind() == PNK_RETURN);
return functionArgs(pn, pnargs, pnbody, args, defaults, rest) &&
expression(pnstart->pn_kid, body);
}
return functionArgs(pn, pnargs, pnbody, args, defaults, rest) &&
functionBody(pnstart, &pnbody->pn_pos, body);
}

View File

@ -42,6 +42,15 @@
// Stores the private WeakMap slot used for WeakSets
#define WEAKSET_MAP_SLOT 0
// Slots for use with promises implementation.
#define PROMISE_STATE_SLOT 0
#define PROMISE_VALUE_SLOT 1
#define PROMISE_DEFERREDS_SLOT 2
#define PROMISE_STATE_PENDING 0
#define PROMISE_STATE_RESOLVED 1
#define PROMISE_STATE_REJECTED 2
#define ITERATOR_SLOT_TARGET 0
// Used for collection iterators.
#define ITERATOR_SLOT_RANGE 1

View File

@ -2058,6 +2058,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
case PNK_YIELD_STAR:
case PNK_YIELD:
case PNK_AWAIT:
MOZ_ASSERT(pn->isArity(PN_BINARY));
*answer = true;
return true;
@ -6488,6 +6489,11 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
return true;
}
bool
BytecodeEmitter::emitAsyncWrapper(unsigned index, bool needsHomeObject) {
MOZ_CRASH("NYI");
}
bool
BytecodeEmitter::emitDo(ParseNode* pn)
{
@ -8472,6 +8478,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote)
break;
case PNK_YIELD:
case PNK_AWAIT:
if (!emitYield(pn))
return false;
break;

View File

@ -514,6 +514,8 @@ struct BytecodeEmitter
bool emitPropOp(ParseNode* pn, JSOp op);
bool emitPropIncDec(ParseNode* pn);
bool emitAsyncWrapper(unsigned index, bool isMethod);
bool emitComputedPropertyName(ParseNode* computedPropName);
// Emit bytecode to put operands for a JSOP_GETELEM/CALLELEM/SETELEM/DELELEM

View File

@ -330,6 +330,7 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
case PNK_CONDITIONAL:
case PNK_TYPEOFNAME:
case PNK_TYPEOFEXPR:
case PNK_AWAIT:
case PNK_VOID:
case PNK_NOT:
case PNK_BITNOT:
@ -1855,6 +1856,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
return Fold(cx, &pn->pn_left, parser, inGenexpLambda);
case PNK_YIELD:
case PNK_AWAIT:
MOZ_ASSERT(pn->isArity(PN_BINARY));
MOZ_ASSERT((pn->pn_right->isKind(PNK_NAME) && !pn->pn_right->isAssigned()) ||
(pn->pn_right->isKind(PNK_ASSIGN) &&

View File

@ -450,6 +450,11 @@ class FullParseHandler
return new_<BinaryNode>(PNK_YIELD_STAR, JSOP_NOP, pos, value, gen);
}
ParseNode* newAwaitExpression(uint32_t begin, ParseNode* value, ParseNode* gen) {
TokenPos pos(begin, value ? value->pn_pos.end : begin + 1);
return new_<BinaryNode>(PNK_AWAIT, JSOP_YIELD, pos, value, gen);
}
// Statements
ParseNode* newStatementList(unsigned blockid, const TokenPos& pos) {

View File

@ -493,6 +493,7 @@ class NameResolver
break;
case PNK_YIELD:
case PNK_AWAIT:
MOZ_ASSERT(cur->isArity(PN_BINARY));
if (cur->pn_left) {
if (!resolve(cur->pn_left, prefix))

View File

@ -319,7 +319,8 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
// variable, or an assignment of a PNK_GENERATOR node to the '.generator'
// local, for a synthesized, prepended initial yield. Yum!
case PNK_YIELD_STAR:
case PNK_YIELD: {
case PNK_YIELD:
case PNK_AWAIT: {
MOZ_ASSERT(pn->isArity(PN_BINARY));
MOZ_ASSERT(pn->pn_right);
MOZ_ASSERT(pn->pn_right->isKind(PNK_NAME) ||

View File

@ -185,6 +185,7 @@ class PackedScopeCoordinate
F(VOID) \
F(NOT) \
F(BITNOT) \
F(AWAIT) \
\
/* \
* Binary operators. \
@ -425,7 +426,8 @@ IsTypeofKind(ParseNodeKind kind)
* PNK_NEG
* PNK_VOID, unary pn_kid: UNARY expr
* PNK_NOT,
* PNK_BITNOT
* PNK_BITNOT,
* PNK_AWAIT
* PNK_TYPEOFNAME, unary pn_kid: UNARY expr
* PNK_TYPEOFEXPR
* PNK_PREINCREMENT, unary pn_kid: MEMBER expr

View File

@ -958,7 +958,8 @@ Parser<ParseHandler>::checkStrictBinding(PropertyName* name, Node pn)
if (!pc->sc->needStrictChecks())
return true;
if (name == context->names().eval || name == context->names().arguments || IsKeyword(name)) {
if (name == context->names().eval || name == context->names().arguments ||
(IsKeyword(name) && name != context->names().await)) {
JSAutoByteString bytes;
if (!AtomToPrintableString(context, name, &bytes))
return false;
@ -969,6 +970,7 @@ Parser<ParseHandler>::checkStrictBinding(PropertyName* name, Node pn)
return true;
}
/* ::moduleBody */
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::standaloneModule(HandleModuleObject module)
@ -979,6 +981,7 @@ Parser<ParseHandler>::standaloneModule(HandleModuleObject module)
if (!mn)
return null();
AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, true);
ModuleBox* modulebox = newModuleBox(mn, module);
if (!modulebox)
return null();
@ -1139,6 +1142,16 @@ Parser<SyntaxParseHandler>::defineFunctionThis()
return true;
}
static YieldHandling
GetYieldHandling(GeneratorKind generatorKind, FunctionAsyncKind asyncKind)
{
if (asyncKind == AsyncFunction)
return YieldIsName;
if (generatorKind == NotGenerator)
return YieldIsName;
return YieldIsKeyword;
}
template <>
ParseNode*
Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun,
@ -1176,7 +1189,7 @@ Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun,
return null();
}
YieldHandling yieldHandling = generatorKind != NotGenerator ? YieldIsKeyword : YieldIsName;
YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
ParseNode* pn = functionBody(InAllowed, yieldHandling, Statement, StatementListBody);
if (!pn)
return null();
@ -1341,6 +1354,16 @@ Parser<ParseHandler>::functionBody(InHandling inHandling, YieldHandling yieldHan
} else {
MOZ_ASSERT(type == ExpressionBody);
// Async functions are implemented as star generators, and star
// generators are assumed to be statement lists, to prepend initial
// `yield`.
Node stmtList = null();
if (pc->isAsync()) {
stmtList = handler.newStatementList(pc->blockid(), pos());
if (!stmtList)
return null();
}
Node kid = assignExpr(inHandling, yieldHandling, TripledotProhibited);
if (!kid)
return null();
@ -1348,6 +1371,11 @@ Parser<ParseHandler>::functionBody(InHandling inHandling, YieldHandling yieldHan
pn = handler.newReturnStatement(kid, handler.getPosition(kid));
if (!pn)
return null();
if (pc->isAsync()) {
handler.addStatementToList(stmtList, pn, pc);
pn = stmtList;
}
}
switch (pc->generatorKind()) {
@ -1372,13 +1400,13 @@ Parser<ParseHandler>::functionBody(InHandling inHandling, YieldHandling yieldHan
break;
case StarGenerator:
MOZ_ASSERT(kind != Arrow);
MOZ_ASSERT(type == StatementListBody);
MOZ_ASSERT_IF(!pc->isAsync(), kind != Arrow);
MOZ_ASSERT_IF(!pc->isAsync(), type == StatementListBody);
break;
}
if (pc->isGenerator()) {
MOZ_ASSERT(type == StatementListBody);
MOZ_ASSERT_IF(!pc->isAsync(), type == StatementListBody);
Node generator = newName(context->names().dotGenerator);
if (!generator)
return null();
@ -2060,19 +2088,47 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
*hasRest = false;
bool parenFreeArrow = false;
TokenStream::Modifier modifier = TokenStream::None;
// Modifier for the following tokens.
// TokenStream::None for the following cases:
// async a => 1
// ^
//
// (a) => 1
// ^
//
// async (a) => 1
// ^
//
// function f(a) {}
// ^
//
// TokenStream::Operand for the following case:
// a => 1
// ^
TokenStream::Modifier firstTokenModifier = TokenStream::None;
// Modifier for the the first token in each argument.
// can be changed to TokenStream::None for the following case:
// async a => 1
// ^
TokenStream::Modifier argModifier = TokenStream::Operand;
if (kind == Arrow) {
TokenKind tt;
if (!tokenStream.peekToken(&tt, TokenStream::Operand))
// In async function, the first token after `async` is already gotten
// with TokenStream::None.
// In sync function, the first token is already gotten with
// TokenStream::Operand.
firstTokenModifier = funbox->isAsync() ? TokenStream::None : TokenStream::Operand;
if (!tokenStream.peekToken(&tt, firstTokenModifier))
return false;
if (tt == TOK_NAME)
if (tt == TOK_NAME || tt == TOK_YIELD) { // from bug 1305566 -r 347016
parenFreeArrow = true;
else
modifier = TokenStream::Operand;
argModifier = firstTokenModifier;
}
}
if (!parenFreeArrow) {
TokenKind tt;
if (!tokenStream.getToken(&tt, modifier))
if (!tokenStream.getToken(&tt, firstTokenModifier))
return false;
if (tt != TOK_LP) {
report(ParseError, false, null(),
@ -2117,9 +2173,11 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
}
TokenKind tt;
if (!tokenStream.getToken(&tt, TokenStream::Operand))
if (!tokenStream.getToken(&tt, argModifier))
return false;
MOZ_ASSERT_IF(parenFreeArrow, tt == TOK_NAME);
argModifier = TokenStream::Operand;
// from bug 1305566 -r 347016
MOZ_ASSERT_IF(parenFreeArrow, tt == TOK_NAME || tt == TOK_YIELD);
switch (tt) {
case TOK_LB:
case TOK_LC:
@ -2141,8 +2199,7 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
*/
BindData<ParseHandler> data(context);
data.initDestructuring(JSOP_DEFVAR);
Node destruct = destructuringExprWithoutYield(yieldHandling, &data, tt,
JSMSG_YIELD_IN_DEFAULT);
Node destruct = destructuringExprWithoutYieldOrAwait(yieldHandling, &data, tt);
if (!destruct)
return false;
@ -2198,12 +2255,21 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
goto TOK_NAME;
}
TOK_NAME:
TOK_NAME: /* from TOK_YIELD above */
case TOK_NAME:
{
if (parenFreeArrow)
funbox->setStart(tokenStream);
if (funbox->isAsync() && tokenStream.currentName() == context->names().await) {
// `await` is already gotten as TOK_NAME for the following
// case:
//
// async await => 1
report(ParseError, false, null(), JSMSG_RESERVED_ID, "await");
return false;
}
RootedPropertyName name(context, tokenStream.currentName());
if (!defineArg(funcpn, name, disallowDuplicateArgs, &duplicatedArg))
return false;
@ -2241,7 +2307,7 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
// before the first default argument.
funbox->length = pc->numArgs() - 1;
}
Node def_expr = assignExprWithoutYield(yieldHandling, JSMSG_YIELD_IN_DEFAULT);
Node def_expr = assignExprWithoutYieldOrAwait(yieldHandling);
if (!def_expr)
return false;
if (!handler.setLastFunctionArgumentDefault(funcpn, def_expr))
@ -2745,6 +2811,7 @@ Parser<ParseHandler>::functionDef(InHandling inHandling, YieldHandling yieldHand
Node* assignmentForAnnexBOut)
{
MOZ_ASSERT_IF(kind == Statement, funName);
MOZ_ASSERT_IF(asyncKind == AsyncFunction, generatorKind == StarGenerator);
/* Make a TOK_FUNCTION node. */
Node pn = handler.newFunctionDefinition();
@ -2890,7 +2957,7 @@ Parser<FullParseHandler>::functionArgsAndBody(InHandling inHandling, ParseNode*
if (kind == DerivedClassConstructor)
funbox->setDerivedClassConstructor();
YieldHandling yieldHandling = generatorKind != NotGenerator ? YieldIsKeyword : YieldIsName;
YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
// We need to roll back the block scope vector if syntax parsing fails.
uint32_t oldBlockScopesLength = blockScopes.length();
@ -2993,7 +3060,7 @@ Parser<SyntaxParseHandler>::functionArgsAndBody(InHandling inHandling, Node pn,
if (!funpc.init(*this))
return false;
YieldHandling yieldHandling = generatorKind != NotGenerator ? YieldIsKeyword : YieldIsName;
YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
if (!functionArgsAndBodyGeneric(inHandling, yieldHandling, pn, fun, kind))
return false;
@ -3038,8 +3105,13 @@ Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, bool strict
return null();
// Our tokenStream has no current token, so pn's position is garbage.
// Substitute the position of the first token in our source.
if (!tokenStream.peekTokenPos(&pn->pn_pos))
// Substitute the position of the first token in our source. If the function
// is a not-async arrow, use TokenStream::Operand to keep
// verifyConsistentModifier from complaining (we will use
// TokenStream::Operand in functionArguments).
TokenStream::Modifier modifier = (fun->isArrow() && asyncKind == SyncFunction)
? TokenStream::Operand : TokenStream::None;
if (!tokenStream.peekTokenPos(&pn->pn_pos, modifier))
return null();
RootedObject enclosing(context, fun->lazyScript()->enclosingScope());
@ -3058,7 +3130,7 @@ Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, bool strict
if (!funpc.init(*this))
return null();
YieldHandling yieldHandling = generatorKind != NotGenerator ? YieldIsKeyword : YieldIsName;
YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
FunctionSyntaxKind syntaxKind = Statement;
if (fun->isClassConstructor())
syntaxKind = ClassConstructor;
@ -3102,12 +3174,13 @@ Parser<ParseHandler>::functionArgsAndBodyGeneric(InHandling inHandling,
// function without concern for conversion to strict mode, use of lazy
// parsing and such.
FunctionBox* funbox = pc->sc->asFunctionBox();
AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, funbox->isAsync());
bool hasRest;
if (!functionArguments(yieldHandling, kind, pn, &hasRest))
return false;
FunctionBox* funbox = pc->sc->asFunctionBox();
fun->setArgCount(pc->numArgs());
if (hasRest)
fun->setHasRest();
@ -3128,7 +3201,7 @@ Parser<ParseHandler>::functionArgsAndBodyGeneric(InHandling inHandling,
if (!tokenStream.getToken(&tt, TokenStream::Operand))
return false;
if (tt != TOK_LC) {
if (funbox->isStarGenerator() || kind == Method ||
if ((funbox->isStarGenerator() && !funbox->isAsync()) || kind == Method ||
kind == GetterNoExpressionClosure || kind == SetterNoExpressionClosure ||
IsConstructorKind(kind)) {
report(ParseError, false, null(), JSMSG_CURLY_BEFORE_BODY);
@ -3137,7 +3210,7 @@ Parser<ParseHandler>::functionArgsAndBodyGeneric(InHandling inHandling,
if (kind != Arrow) {
#if JS_HAS_EXPR_CLOSURES
addTelemetry(JSCompartment::DeprecatedExpressionClosure);
//addTelemetry(JSCompartment::DeprecatedExpressionClosure);
if (!warnOnceAboutExprClosure())
return false;
#else
@ -3201,7 +3274,8 @@ Parser<ParseHandler>::checkYieldNameValidity()
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling defaultHandling)
Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling defaultHandling,
FunctionAsyncKind asyncKind)
{
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
@ -3224,12 +3298,16 @@ Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling
}
RootedPropertyName name(context);
GeneratorKind generatorKind = NotGenerator;
GeneratorKind generatorKind = asyncKind == AsyncFunction ? StarGenerator : NotGenerator;
TokenKind tt;
if (!tokenStream.getToken(&tt))
return null();
if (tt == TOK_MUL) {
if (asyncKind != SyncFunction) {
report(ParseError, false, null(), JSMSG_ASYNC_GENERATOR);
return null();
}
generatorKind = StarGenerator;
if (!tokenStream.getToken(&tt))
return null();
@ -3251,8 +3329,9 @@ Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling
}
Node assignmentForAnnexB;
Node fun = functionDef(InAllowed, yieldHandling, name, Statement, generatorKind,
SyncFunction, PredictUninvoked, &assignmentForAnnexB);
YieldHandling newYieldHandling = GetYieldHandling(generatorKind, asyncKind);
Node fun = functionDef(InAllowed, newYieldHandling, name, Statement, generatorKind,
asyncKind, PredictUninvoked, &assignmentForAnnexB);
if (!fun)
return null();
@ -3283,21 +3362,28 @@ Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::functionExpr(InvokedPrediction invoked)
Parser<ParseHandler>::functionExpr(InvokedPrediction invoked, FunctionAsyncKind asyncKind)
{
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
GeneratorKind generatorKind = NotGenerator;
AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, asyncKind == AsyncFunction);
GeneratorKind generatorKind = asyncKind == AsyncFunction ? StarGenerator : NotGenerator;
TokenKind tt;
if (!tokenStream.getToken(&tt))
return null();
if (tt == TOK_MUL) {
if (asyncKind != SyncFunction) {
report(ParseError, false, null(), JSMSG_ASYNC_GENERATOR);
return null();
}
generatorKind = StarGenerator;
if (!tokenStream.getToken(&tt))
return null();
}
YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
RootedPropertyName name(context);
if (tt == TOK_NAME) {
name = tokenStream.currentName();
@ -3309,8 +3395,7 @@ Parser<ParseHandler>::functionExpr(InvokedPrediction invoked)
tokenStream.ungetToken();
}
YieldHandling yieldHandling = generatorKind != NotGenerator ? YieldIsKeyword : YieldIsName;
return functionDef(InAllowed, yieldHandling, name, Expression, generatorKind, SyncFunction, invoked);
return functionDef(InAllowed, yieldHandling, name, Expression, generatorKind, asyncKind, invoked);
}
/*
@ -4397,16 +4482,21 @@ Parser<ParseHandler>::destructuringExpr(YieldHandling yieldHandling, BindData<Pa
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::destructuringExprWithoutYield(YieldHandling yieldHandling,
BindData<ParseHandler>* data, TokenKind tt,
unsigned msg)
Parser<ParseHandler>::destructuringExprWithoutYieldOrAwait(YieldHandling yieldHandling,
BindData<ParseHandler>* data, TokenKind tt)
{
uint32_t startYieldOffset = pc->lastYieldOffset;
uint32_t startAwaitOffset = pc->lastAwaitOffset;
Node res = destructuringExpr(yieldHandling, data, tt);
if (res && pc->lastYieldOffset != startYieldOffset) {
reportWithOffset(ParseError, false, pc->lastYieldOffset,
msg, js_yield_str);
return null();
if (res) {
if (pc->lastYieldOffset != startYieldOffset) {
reportWithOffset(ParseError, false, pc->lastYieldOffset, JSMSG_YIELD_IN_DEFAULT);
return null();
}
if (pc->lastAwaitOffset != startAwaitOffset) {
reportWithOffset(ParseError, false, pc->lastAwaitOffset, JSMSG_AWAIT_IN_DEFAULT);
return null();
}
}
return res;
}
@ -5581,7 +5671,24 @@ Parser<FullParseHandler>::exportDeclaration()
if (!kid)
return null();
break;
default:
default: {
if (tt == TOK_NAME && tokenStream.currentName() == context->names().async) {
TokenKind nextSameLine = TOK_EOF;
if (!tokenStream.peekTokenSameLine(&nextSameLine))
return null();
if (nextSameLine == TOK_FUNCTION) {
if (!warnOnceAboutAsyncFuncs())
return null();
tokenStream.consumeKnownToken(nextSameLine);
kid = functionStmt(YieldIsName, AllowDefaultName, AsyncFunction);
if (!kid)
return null();
break;
}
}
tokenStream.ungetToken();
RootedPropertyName name(context, context->names().starDefaultStar);
binding = makeInitializedLexicalBinding(name, PrepareConst, pos());
@ -5593,6 +5700,7 @@ Parser<FullParseHandler>::exportDeclaration()
if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
return null();
break;
}
}
return handler.newExportDefaultDeclaration(kid, binding, TokenPos(begin, pos().end));
@ -6424,6 +6532,18 @@ Parser<ParseHandler>::newYieldExpression(uint32_t begin, typename ParseHandler::
return handler.newYieldExpression(begin, expr, generator);
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::newAwaitExpression(uint32_t begin, typename ParseHandler::Node expr)
{
Node generator = newName(context->names().dotGenerator);
if (!generator)
return null();
if (!noteNameUse(context->names().dotGenerator, generator))
return null();
return handler.newAwaitExpression(begin, expr, generator);
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::yieldExpression(InHandling inHandling)
@ -6873,6 +6993,7 @@ JSOpFromPropertyType(PropertyType propType)
case PropertyType::Normal:
case PropertyType::Method:
case PropertyType::GeneratorMethod:
case PropertyType::AsyncMethod:
case PropertyType::Constructor:
case PropertyType::DerivedConstructor:
return JSOP_INITPROP;
@ -6894,8 +7015,8 @@ FunctionSyntaxKindFromPropertyType(PropertyType propType)
case PropertyType::SetterNoExpressionClosure:
return SetterNoExpressionClosure;
case PropertyType::Method:
return Method;
case PropertyType::GeneratorMethod:
case PropertyType::AsyncMethod:
return Method;
case PropertyType::Constructor:
return ClassConstructor;
@ -6909,7 +7030,19 @@ FunctionSyntaxKindFromPropertyType(PropertyType propType)
static GeneratorKind
GeneratorKindFromPropertyType(PropertyType propType)
{
return propType == PropertyType::GeneratorMethod ? StarGenerator : NotGenerator;
if (propType == PropertyType::GeneratorMethod)
return StarGenerator;
if (propType == PropertyType::AsyncMethod)
return StarGenerator;
return NotGenerator;
}
static FunctionAsyncKind
AsyncKindFromPropertyType(PropertyType propType)
{
if (propType == PropertyType::AsyncMethod)
return AsyncFunction;
return SyncFunction;
}
template <>
@ -7023,6 +7156,7 @@ Parser<FullParseHandler>::classDefinition(YieldHandling yieldHandling,
if (propType != PropertyType::Getter && propType != PropertyType::Setter &&
propType != PropertyType::Method && propType != PropertyType::GeneratorMethod &&
propType != PropertyType::AsyncMethod &&
propType != PropertyType::Constructor && propType != PropertyType::DerivedConstructor)
{
report(ParseError, false, null(), JSMSG_BAD_METHOD_DEF);
@ -7183,6 +7317,7 @@ Parser<ParseHandler>::peekShouldParseLetDeclaration(bool* parseDeclOut,
return true;
}
/* ::statementListItem */
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::statement(YieldHandling yieldHandling, bool canHaveDirectives)
@ -7266,6 +7401,19 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling, bool canHaveDirecti
return lexicalDeclaration(yieldHandling, /* isConst = */ false);
}
if (tokenStream.currentName() == context->names().async) {
TokenKind nextSameLine = TOK_EOF;
if (!tokenStream.peekTokenSameLine(&nextSameLine))
return null();
if (nextSameLine == TOK_FUNCTION) {
if (!warnOnceAboutAsyncFuncs())
return null();
tokenStream.consumeKnownToken(TOK_FUNCTION);
return functionStmt(yieldHandling, NameRequired, AsyncFunction);
}
}
TokenKind next;
if (!tokenStream.peekToken(&next))
return null();
@ -7726,6 +7874,16 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
if (tt == TOK_YIELD && yieldExpressionsSupported())
return yieldExpression(inHandling);
bool maybeAsyncArrow = false;
if (tt == TOK_NAME && tokenStream.currentName() == context->names().async) {
TokenKind nextSameLine = TOK_EOF;
if (!tokenStream.peekTokenSameLine(&nextSameLine))
return null();
if (nextSameLine == TOK_NAME || nextSameLine == TOK_YIELD)
maybeAsyncArrow = true;
}
tokenStream.ungetToken();
// Save the tokenizer state in case we find an arrow function and have to
@ -7734,9 +7892,35 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
tokenStream.tell(&start);
PossibleError possibleErrorInner(*this);
Node lhs = condExpr1(inHandling, yieldHandling, tripledotHandling, &possibleErrorInner, invoked);
if (!lhs) {
return null();
Node lhs;
if (maybeAsyncArrow) {
tokenStream.consumeKnownToken(TOK_NAME, TokenStream::Operand);
MOZ_ASSERT(tokenStream.currentName() == context->names().async);
TokenKind tt;
if (!tokenStream.getToken(&tt))
return null();
MOZ_ASSERT(tt == TOK_NAME || tt == TOK_YIELD);
// Check yield validity here.
if (!checkYieldNameValidity())
return null();
if (!tokenStream.getToken(&tt))
return null();
if (tt != TOK_ARROW) {
report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
"'=>' after argument list", TokenKindToDesc(tt));
return null();
}
if (!warnOnceAboutAsyncFuncs())
return null();
} else {
lhs = condExpr1(inHandling, yieldHandling, tripledotHandling, &possibleErrorInner, invoked);
if (!lhs) {
return null();
}
}
ParseNodeKind kind;
@ -7778,11 +7962,35 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
if (!abortIfSyntaxParser())
return null();
TokenKind ignored;
if (!tokenStream.peekToken(&ignored, TokenStream::Operand))
if (!tokenStream.peekToken(&next, TokenStream::Operand))
return null();
Node arrowFunc = functionDef(inHandling, yieldHandling, nullptr, Arrow, NotGenerator, SyncFunction);
GeneratorKind generatorKind = NotGenerator;
FunctionAsyncKind asyncKind = SyncFunction;
if (next == TOK_NAME) {
tokenStream.consumeKnownToken(next, TokenStream::Operand);
if (tokenStream.currentName() == context->names().async) {
TokenKind nextSameLine = TOK_EOF;
if (!tokenStream.peekTokenSameLine(&nextSameLine))
return null();
if (!warnOnceAboutAsyncFuncs())
return null();
if (nextSameLine == TOK_ARROW) {
tokenStream.ungetToken();
} else {
generatorKind = StarGenerator;
asyncKind = AsyncFunction;
}
} else {
tokenStream.ungetToken();
}
}
Node arrowFunc = functionDef(inHandling, yieldHandling, nullptr,
Arrow, generatorKind, asyncKind);
if (!arrowFunc)
return null();
@ -7811,6 +8019,7 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
// has block body. An arrow function not ending in such, ends in
// another AssignmentExpression that we can inductively assume was
// peeked consistently.
TokenKind ignored;
if (!tokenStream.peekToken(&ignored, TokenStream::Operand))
return null();
tokenStream.addModifierException(TokenStream::NoneIsOperand);
@ -8062,6 +8271,21 @@ Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, TripledotHandling t
return handler.newDelete(begin, expr);
}
case TOK_AWAIT: {
TokenKind nextSameLine = TOK_EOF;
if (!tokenStream.peekTokenSameLine(&nextSameLine, TokenStream::Operand))
return null();
if (nextSameLine != TOK_EOL) {
Node kid = unaryExpr(yieldHandling, tripledotHandling, possibleError, invoked);
if (!kid)
return null();
pc->lastAwaitOffset = begin;
return newAwaitExpression(begin, kid);
}
report(ParseError, false, null(), JSMSG_LINE_BREAK_AFTER_AWAIT);
return null();
}
default: {
Node pn = memberExpr(yieldHandling, tripledotHandling, possibleError, tt, /* allowCallSyntax = */ true,
invoked);
@ -9003,14 +9227,20 @@ Parser<ParseHandler>::generatorComprehension(uint32_t begin)
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::assignExprWithoutYield(YieldHandling yieldHandling, unsigned msg)
Parser<ParseHandler>::assignExprWithoutYieldOrAwait(YieldHandling yieldHandling)
{
uint32_t startYieldOffset = pc->lastYieldOffset;
uint32_t startAwaitOffset = pc->lastAwaitOffset;
Node res = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
if (res && pc->lastYieldOffset != startYieldOffset) {
reportWithOffset(ParseError, false, pc->lastYieldOffset,
msg, js_yield_str);
return null();
if (res) {
if (pc->lastYieldOffset != startYieldOffset) {
reportWithOffset(ParseError, false, pc->lastYieldOffset, JSMSG_YIELD_IN_DEFAULT);
return null();
}
if (pc->lastAwaitOffset != startAwaitOffset) {
reportWithOffset(ParseError, false, pc->lastAwaitOffset, JSMSG_AWAIT_IN_DEFAULT);
return null();
}
}
return res;
}
@ -9572,15 +9802,53 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
return null();
// TOK_RC should be handled in caller.
MOZ_ASSERT(ltok != TOK_RC);
MOZ_ASSERT(ltok != TOK_RC, "caller should have handled TOK_RC");
bool isGenerator = false;
bool isAsync = false;
if (ltok == TOK_MUL) {
isGenerator = true;
if (!tokenStream.getToken(&ltok, TokenStream::KeywordIsName))
return null();
}
if (ltok == TOK_NAME && tokenStream.currentName() == context->names().async) {
// AsyncMethod[Yield, Await]:
// async [no LineTerminator here] PropertyName[?Yield, ?Await] ...
//
// PropertyName:
// LiteralPropertyName
// ComputedPropertyName[?Yield, ?Await]
//
// LiteralPropertyName:
// IdentifierName
// StringLiteral
// NumericLiteral
//
// ComputedPropertyName[Yield, Await]:
// [ ...
TokenKind tt = TOK_EOF;
if (!tokenStream.peekTokenSameLine(&tt, TokenStream::KeywordIsName))
return null();
if (tt == TOK_STRING || tt == TOK_NUMBER || tt == TOK_LB ||
tt == TOK_NAME || tt == TOK_YIELD)
{
isAsync = true;
tokenStream.consumeKnownToken(tt, TokenStream::KeywordIsName);
ltok = tt;
} else {
tokenStream.addModifierException(TokenStream::NoneIsKeywordIsName);
}
}
if (isAsync) {
if (isGenerator) {
report(ParseError, false, null(), JSMSG_ASYNC_GENERATOR);
return null();
}
if (!warnOnceAboutAsyncFuncs()) return null(); // or fail safe?
}
propAtom.set(nullptr);
Node propName;
switch (ltok) {
@ -9602,7 +9870,7 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
case TOK_NAME: {
propAtom.set(tokenStream.currentName());
// Do not look for accessor syntax on generators
if (isGenerator ||
if (isGenerator || isAsync ||
!(propAtom.get() == context->names().get ||
propAtom.get() == context->names().set))
{
@ -9721,7 +9989,12 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
if (tt == TOK_LP) {
tokenStream.ungetToken();
*propType = isGenerator ? PropertyType::GeneratorMethod : PropertyType::Method;
if (isGenerator)
*propType = PropertyType::GeneratorMethod;
else if (isAsync)
*propType = PropertyType::AsyncMethod;
else
*propType = PropertyType::Method;
return propName;
}
@ -9899,6 +10172,7 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError*
return literal;
}
/* XXX: yieldHandling no longer used */
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::methodDefinition(YieldHandling yieldHandling, PropertyType propType,
@ -9906,7 +10180,9 @@ Parser<ParseHandler>::methodDefinition(YieldHandling yieldHandling, PropertyType
{
FunctionSyntaxKind kind = FunctionSyntaxKindFromPropertyType(propType);
GeneratorKind generatorKind = GeneratorKindFromPropertyType(propType);
return functionDef(InAllowed, yieldHandling, funName, kind, generatorKind, SyncFunction);
FunctionAsyncKind asyncKind = AsyncKindFromPropertyType(propType);
YieldHandling newYieldHandling = GetYieldHandling(generatorKind, asyncKind);
return functionDef(InAllowed, newYieldHandling, funName, kind, generatorKind, asyncKind);
}
template <typename ParseHandler>
@ -10030,8 +10306,22 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling
if (!checkYieldNameValidity())
return null();
// Fall through.
case TOK_NAME:
case TOK_NAME: {
if (tokenStream.currentName() == context->names().async) {
TokenKind nextSameLine = TOK_EOF;
if (!tokenStream.peekTokenSameLine(&nextSameLine))
return null();
if (nextSameLine == TOK_FUNCTION) {
if (!warnOnceAboutAsyncFuncs())
return null();
tokenStream.consumeKnownToken(TOK_FUNCTION);
return functionExpr(PredictUninvoked, AsyncFunction);
}
}
return identifierName(yieldHandling);
}
case TOK_REGEXP:
return newRegExp();
@ -10233,6 +10523,27 @@ Parser<ParseHandler>::warnOnceAboutExprClosure()
return true;
}
template <typename ParseHandler>
bool
Parser<ParseHandler>::warnOnceAboutAsyncFuncs()
{
JSContext* cx = context->maybeJSContext();
if (!cx)
return true;
if (!cx->runtime()->options().asyncFuncs()) {
report(ParseError, false, null(), JSMSG_ASYNC_DISABLED);
return false;
}
if (!cx->compartment()->warnedAboutAsyncFuncs) {
if (!report(ParseWarning, false, null(), JSMSG_ASYNC_WARNING))
return false;
cx->compartment()->warnedAboutAsyncFuncs = true;
}
return true;
}
template class Parser<FullParseHandler>;
template class Parser<SyntaxParseHandler>;

View File

@ -116,6 +116,11 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
static const uint32_t NoYieldOffset = UINT32_MAX;
uint32_t lastYieldOffset;
// lastAwaitOffset stores the offset of the last await that was parsed.
// NoAwaitOffset is its initial value.
static const uint32_t NoAwaitOffset = UINT32_MAX;
uint32_t lastAwaitOffset;
// Most functions start off being parsed as non-generators.
// Non-generators transition to LegacyGenerator on parsing "yield" in JS 1.7.
// An ES6 generator is marked as a "star generator" before its body is parsed.
@ -130,6 +135,10 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
return sc->isFunctionBox() && sc->asFunctionBox()->isAsync();
}
FunctionAsyncKind asyncKind() const {
return isAsync() ? AsyncFunction : SyncFunction;
}
bool isArrowFunction() const {
return sc->isFunctionBox() && sc->asFunctionBox()->function()->isArrow();
}
@ -267,6 +276,7 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
stmtStack(prs->context),
maybeFunction(maybeFunction),
lastYieldOffset(NoYieldOffset),
lastAwaitOffset(NoAwaitOffset),
blockScopeDepth(0),
blockNode(ParseHandler::null()),
decls_(prs->context, prs->alloc),
@ -375,6 +385,7 @@ enum class PropertyType {
SetterNoExpressionClosure,
Method,
GeneratorMethod,
AsyncMethod,
Constructor,
DerivedConstructor
};
@ -653,6 +664,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
inline Node newName(PropertyName* name);
inline Node newYieldExpression(uint32_t begin, Node expr, bool isYieldStar = false);
inline Node newAwaitExpression(uint32_t begin, Node expr);
inline bool abortIfSyntaxParser();
@ -705,7 +717,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
// inside a star generator.
bool checkYieldNameValidity();
bool yieldExpressionsSupported() {
return versionNumber() >= JSVERSION_1_7 || pc->isGenerator();
return (versionNumber() >= JSVERSION_1_7 || pc->isGenerator()) && !pc->isAsync();
}
virtual bool strictMode() { return pc->sc->strict(); }
@ -739,8 +751,10 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
* Some parsers have two versions: an always-inlined version (with an 'i'
* suffix) and a never-inlined version (with an 'n' suffix).
*/
Node functionStmt(YieldHandling yieldHandling, DefaultHandling defaultHandling);
Node functionExpr(InvokedPrediction invoked = PredictUninvoked);
Node functionStmt(YieldHandling yieldHandling, DefaultHandling defaultHandling,
FunctionAsyncKind asyncKind = SyncFunction);
Node functionExpr(InvokedPrediction invoked = PredictUninvoked,
FunctionAsyncKind asyncKind = SyncFunction);
Node statements(YieldHandling yieldHandling);
Node blockStatement(YieldHandling yieldHandling);
@ -844,7 +858,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
Node assignExpr(InHandling inHandling, YieldHandling yieldHandling,
TripledotHandling tripledotHandling,
InvokedPrediction invoked = PredictUninvoked);
Node assignExprWithoutYield(YieldHandling yieldHandling, unsigned err);
Node assignExprWithoutYieldOrAwait(YieldHandling yieldHandling);
Node yieldExpression(InHandling inHandling);
Node condExpr1(InHandling inHandling, YieldHandling yieldHandling,
TripledotHandling tripledotHandling,
@ -915,8 +929,8 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
bool argumentList(YieldHandling yieldHandling, Node listNode, bool* isSpread);
Node destructuringExpr(YieldHandling yieldHandling, BindData<ParseHandler>* data,
TokenKind tt);
Node destructuringExprWithoutYield(YieldHandling yieldHandling, BindData<ParseHandler>* data,
TokenKind tt, unsigned msg);
Node destructuringExprWithoutYieldOrAwait(YieldHandling yieldHandling, BindData<ParseHandler>* data,
TokenKind tt);
Node newBoundImportForCurrentName();
bool namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet);
@ -1072,6 +1086,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
void addTelemetry(JSCompartment::DeprecatedLanguageExtension e);
bool warnOnceAboutExprClosure();
bool warnOnceAboutAsyncFuncs();
friend class LegacyCompExprTransplanter;
friend struct BindData<ParseHandler>;

View File

@ -299,6 +299,7 @@ class SyntaxParseHandler
bool addClassMethodDefinition(Node literal, Node name, Node fn, JSOp op, bool isStatic) { return true; }
Node newYieldExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; }
Node newYieldStarExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; }
Node newAwaitExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; }
// Statements

View File

@ -2067,7 +2067,7 @@ CodeGenerator::visitLambda(LLambda* lir)
emitLambdaInit(output, scopeChain, info);
if (info.flags & JSFunction::EXTENDED) {
MOZ_ASSERT(info.fun->allowSuperProperty());
MOZ_ASSERT(info.fun->allowSuperProperty() || info.fun->isAsync());
static_assert(FunctionExtended::NUM_EXTENDED_SLOTS == 2, "All slots must be initialized");
masm.storeValue(UndefinedValue(), Address(output, FunctionExtended::offsetOfExtendedSlot(0)));
masm.storeValue(UndefinedValue(), Address(output, FunctionExtended::offsetOfExtendedSlot(1)));

View File

@ -107,6 +107,8 @@ MSG_DEF(JSMSG_PROTO_NOT_OBJORNULL, 1, JSEXN_TYPEERR, "{0}.prototype is not a
MSG_DEF(JSMSG_CANT_CALL_CLASS_CONSTRUCTOR, 0, JSEXN_TYPEERR, "class constructors must be invoked with |new|")
MSG_DEF(JSMSG_UNINITIALIZED_THIS, 1, JSEXN_REFERENCEERR, "|this| used uninitialized in {0} class constructor")
MSG_DEF(JSMSG_BAD_DERIVED_RETURN, 1, JSEXN_TYPEERR, "derived class constructor returned invalid value {0}")
MSG_DEF(JSMSG_NOT_CALLABLE, 1, JSEXN_TYPEERR, "{0} is not callable")
MSG_DEF(JSMSG_NOT_IMPLEMENTED, 1, JSEXN_INTERNALERR, "{0} is not implemented")
// JSON
MSG_DEF(JSMSG_JSON_BAD_PARSE, 3, JSEXN_SYNTAXERR, "JSON.parse: {0} at line {1} column {2} of the JSON data")
@ -188,6 +190,10 @@ MSG_DEF(JSMSG_ARRAY_COMP_LEFTSIDE, 0, JSEXN_SYNTAXERR, "invalid array compre
MSG_DEF(JSMSG_ARRAY_INIT_TOO_BIG, 0, JSEXN_INTERNALERR, "array initializer too large")
MSG_DEF(JSMSG_AS_AFTER_IMPORT_STAR, 0, JSEXN_SYNTAXERR, "missing keyword 'as' after import *")
MSG_DEF(JSMSG_AS_AFTER_RESERVED_WORD, 1, JSEXN_SYNTAXERR, "missing keyword 'as' after reserved word '{0}'")
MSG_DEF(JSMSG_ASYNC_GENERATOR, 0, JSEXN_SYNTAXERR, "generator function or method can't be async")
MSG_DEF(JSMSG_ASYNC_DISABLED, 0, JSEXN_SYNTAXERR, "async functions are not enabled in the parser")
MSG_DEF(JSMSG_ASYNC_WARNING, 0, JSEXN_NONE, "async functions are not fully supported in TenFourFox")
MSG_DEF(JSMSG_AWAIT_IN_DEFAULT, 0, JSEXN_SYNTAXERR, "await can't be used in default expression")
MSG_DEF(JSMSG_BAD_ANON_GENERATOR_RETURN, 0, JSEXN_TYPEERR, "anonymous generator function returns a value")
MSG_DEF(JSMSG_BAD_ARROW_ARGS, 0, JSEXN_SYNTAXERR, "invalid arrow-function arguments (parentheses around the arrow-function may help)")
MSG_DEF(JSMSG_BAD_BINDING, 1, JSEXN_SYNTAXERR, "redefining {0} is deprecated")
@ -273,6 +279,7 @@ MSG_DEF(JSMSG_LET_CLASS_BINDING, 0, JSEXN_SYNTAXERR, "'let' is not a valid
MSG_DEF(JSMSG_LET_COMP_BINDING, 0, JSEXN_SYNTAXERR, "'let' is not a valid name for a comprehension variable")
MSG_DEF(JSMSG_LEXICAL_DECL_NOT_IN_BLOCK, 1, JSEXN_SYNTAXERR, "{0} declaration not directly within block")
MSG_DEF(JSMSG_LEXICAL_DECL_LABEL, 1, JSEXN_SYNTAXERR, "{0} declarations cannot be labelled")
MSG_DEF(JSMSG_LINE_BREAK_AFTER_AWAIT, 0, JSEXN_SYNTAXERR, "no line break is allowed after 'await'")
MSG_DEF(JSMSG_FUNCTION_LABEL, 0, JSEXN_SYNTAXERR, "functions cannot be labelled")
MSG_DEF(JSMSG_SLOPPY_FUNCTION_LABEL, 0, JSEXN_SYNTAXERR, "functions can only be labelled inside blocks")
MSG_DEF(JSMSG_LINE_BREAK_AFTER_THROW, 0, JSEXN_SYNTAXERR, "no line break is allowed between 'throw' and its expression")
@ -526,6 +533,10 @@ MSG_DEF(JSMSG_NO_INDEXED_SETTER, 2, JSEXN_TYPEERR, "{0} doesn't have an
MSG_DEF(JSMSG_CANT_DELETE_SUPER, 0, JSEXN_REFERENCEERR, "invalid delete involving 'super'")
MSG_DEF(JSMSG_REINIT_THIS, 0, JSEXN_REFERENCEERR, "super() called twice in derived class constructor")
// Promise
MSG_DEF(JSMSG_PROMISE_RESOLVED_WITH_ITSELF, 0, JSEXN_TYPEERR, "A promise cannot be resolved with itself")
MSG_DEF(JSMSG_SETTIMEOUT_INTERVAL_NONZERO, 0, JSEXN_TYPEERR, "Intervals other than 0 are not supported")
// Modules
MSG_DEF(JSMSG_BAD_DEFAULT_EXPORT, 0, JSEXN_SYNTAXERR, "default export cannot be provided by export *")
MSG_DEF(JSMSG_MISSING_INDIRECT_EXPORT, 1, JSEXN_SYNTAXERR, "indirect export '{0}' not found")

View File

@ -1092,6 +1092,7 @@ class JS_PUBLIC_API(RuntimeOptions) {
nativeRegExp_(true),
unboxedArrays_(false),
asyncStack_(true),
asyncFuncs_(false),
werror_(false),
strictMode_(false),
extraWarnings_(false)
@ -1156,6 +1157,12 @@ class JS_PUBLIC_API(RuntimeOptions) {
return *this;
}
bool asyncFuncs() const { return asyncFuncs_; }
RuntimeOptions& setAsyncFuncs(bool flag) {
asyncFuncs_ = flag;
return *this;
}
bool werror() const { return werror_; }
RuntimeOptions& setWerror(bool flag) {
werror_ = flag;
@ -1194,6 +1201,7 @@ class JS_PUBLIC_API(RuntimeOptions) {
bool nativeRegExp_ : 1;
bool unboxedArrays_ : 1;
bool asyncStack_ : 1;
bool asyncFuncs_ : 1;
bool werror_ : 1;
bool strictMode_ : 1;
bool extraWarnings_ : 1;

View File

@ -51,6 +51,7 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options =
marked(true),
warnedAboutFlagsArgument(false),
warnedAboutExprClosure(false),
warnedAboutAsyncFuncs(false),
addonId(options.addonIdOrNull()),
#ifdef DEBUG
firedOnNewGlobalObject(false),

View File

@ -288,6 +288,7 @@ struct JSCompartment
bool marked;
bool warnedAboutFlagsArgument;
bool warnedAboutExprClosure;
bool warnedAboutAsyncFuncs;
// A null add-on ID means that the compartment is not associated with an
// add-on.

View File

@ -998,6 +998,11 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool lambdaParen)
}
}
if (fun->isAsync()) {
if (!out.append("async "))
return nullptr;
}
bool funIsMethodOrNonArrowLambda = (fun->isLambda() && !fun->isArrow()) || fun->isMethod() ||
fun->isGetter() || fun->isSetter();
@ -1007,7 +1012,12 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool lambdaParen)
return nullptr;
}
if (!fun->isArrow()) {
if (!(fun->isStarGenerator() ? out.append("function* ") : out.append("function ")))
bool ok;
if (fun->isStarGenerator() && !fun->isAsync())
ok = out.append("function* ");
else
ok = out.append("function ");
if (!ok)
return nullptr;
}
if (fun->atom()) {

View File

@ -102,6 +102,7 @@ IF_BDATA(real,imaginary)(SIMD, 41, InitSIMDClass, OCLASP(SI
real(TypedArray, 43, InitViaClassSpec, &js::TypedArrayObject::sharedTypedArrayPrototypeClass) \
IF_SAB(real,imaginary)(Atomics, 44, InitAtomicsClass, OCLASP(Atomics)) \
real(SavedFrame, 45, InitViaClassSpec, &js::SavedFrame::class_) \
real(ShellPromise, 46, InitViaClassSpec, OCLASP(ShellPromise)) \
#define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro)

View File

@ -3010,6 +3010,7 @@ JSScript::fullyInitFromEmitter(ExclusiveContext* cx, HandleScript script, Byteco
MOZ_ASSERT(script->functionNonDelazifying() == funbox->function());
MOZ_ASSERT(script->isGeneratorExp_ == funbox->inGenexpLambda);
MOZ_ASSERT(script->generatorKind() == funbox->generatorKind());
MOZ_ASSERT(script->asyncKind() == funbox->asyncKind());
} else {
MOZ_ASSERT(!script->funHasExtensibleScope_);
MOZ_ASSERT(!script->funNeedsDeclEnvObject_);

View File

@ -345,6 +345,8 @@ UNIFIED_SOURCES += [
# jsarray.cpp and jsatom.cpp cannot be built in unified mode because
# xpcshell is broken during packaging when compiled with gcc-4.8.2
# builtin/Promise.cpp cannot be built in unified mode because it makes
# certain parts of RegExp* go together which shouldn't.
# builtin/RegExp.cpp cannot be built in unified mode because it is built
# without PGO
# frontend/Parser.cpp cannot be built in unified mode because of explicit
@ -357,6 +359,7 @@ UNIFIED_SOURCES += [
# instantiations may or may not be needed depending on what it gets bundled
# with.
SOURCES += [
'builtin/Promise.cpp',
'builtin/RegExp.cpp',
'frontend/Parser.cpp',
'gc/StoreBuffer.cpp',
@ -693,6 +696,7 @@ selfhosted.inputs = [
'builtin/SelfHostingDefines.h',
'builtin/Utilities.js',
'builtin/Array.js',
'builtin/AsyncFunctions.js',
'builtin/Date.js',
'builtin/Error.js',
'builtin/Generator.js',
@ -703,6 +707,7 @@ selfhosted.inputs = [
'builtin/Module.js',
'builtin/Number.js',
'builtin/Object.js',
'builtin/Promise.js',
'builtin/Reflect.js',
'builtin/RegExp.js',
'builtin/String.js',

View File

@ -170,6 +170,7 @@ static bool enableIon = false;
static bool enableAsmJS = false;
static bool enableNativeRegExp = false;
static bool enableUnboxedArrays = false;
static bool enableAsyncFuncs = true;
#ifdef JS_GC_ZEAL
static char gZealStr[128];
#endif
@ -1690,6 +1691,68 @@ PrintErr(JSContext* cx, unsigned argc, Value* vp)
return PrintInternal(cx, args, gErrFile);
}
static bool
SetTimeout(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() < 2) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
"setTimeout", args.length() == 0 ? "0" : "1", "s");
return false;
}
RootedValue rval(cx);
JS::AutoValueVector argValues(cx);
argValues.append(args.get(0));
argValues.append(args.get(1));
HandleValueArray arr(argValues);
JSAtom* setTimeoutAtom;
if (!(setTimeoutAtom = Atomize(cx, "setTimeout", 10)))
return false;
RootedPropertyName name(cx, setTimeoutAtom->asPropertyName());
RootedValue selfHostedFun(cx);
if (!GlobalObject::getIntrinsicValue(cx, cx->global(), name, &selfHostedFun))
return false;
RootedObject undef(cx);
if (!JS_CallFunctionValue(cx, undef, selfHostedFun, arr, &rval))
return false;
args.rval().set(rval);
return true;
}
static bool
RunEvents(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedValue rval(cx);
JS::AutoValueVector argValues(cx);
HandleValueArray arr(argValues);
JSAtom* runEvents;
if (!(runEvents = Atomize(cx, "runEvents", 9)))
return false;
RootedPropertyName name(cx, runEvents->asPropertyName());
RootedValue selfHostedFun(cx);
if (!GlobalObject::getIntrinsicValue(cx, cx->global(), name, &selfHostedFun))
return false;
RootedObject undef(cx);
if (!JS_CallFunctionValue(cx, undef, selfHostedFun, arr, &rval))
return false;
args.rval().set(rval);
return true;
}
static bool
Help(JSContext* cx, unsigned argc, Value* vp);
@ -4861,6 +4924,15 @@ static const JSFunctionSpecWithHelp shell_functions[] = {
"dateNow()",
" Return the current time with sub-ms precision."),
JS_FN_HELP("setTimeout", SetTimeout, 2, 2,
"setTimeout(fn, timeout)",
" Execute a function after a specified timeout. Currently only 0 is supported."),
JS_FN_HELP("runEvents", RunEvents, 2, 2,
"runEvents()",
" Run events that were scheduled using setTimeout() calls.\n"
" This call is required, because there is no real event loop."),
JS_FN_HELP("help", Help, 0, 0,
"help([name ...])",
" Display usage and help messages."),
@ -6244,11 +6316,13 @@ SetRuntimeOptions(JSRuntime* rt, const OptionParser& op)
enableAsmJS = !op.getBoolOption("no-asmjs");
enableNativeRegExp = !op.getBoolOption("no-native-regexp");
enableUnboxedArrays = op.getBoolOption("unboxed-arrays");
enableAsyncFuncs = !op.getBoolOption("no-async-funcs");
JS::RuntimeOptionsRef(rt).setBaseline(enableBaseline)
.setIon(enableIon)
.setAsmJS(enableAsmJS)
.setNativeRegExp(enableNativeRegExp)
.setAsyncFuncs(enableAsyncFuncs)
.setUnboxedArrays(enableUnboxedArrays);
if (op.getBoolOption("no-unboxed-objects"))
@ -6495,6 +6569,7 @@ SetWorkerRuntimeOptions(JSRuntime* rt)
.setIon(enableIon)
.setAsmJS(enableAsmJS)
.setNativeRegExp(enableNativeRegExp)
.setAsyncFuncs(enableAsyncFuncs)
.setUnboxedArrays(enableUnboxedArrays);
rt->setOffthreadIonCompilationEnabled(offthreadCompilation);
rt->profilingScripts = enableCodeCoverage || enableDisassemblyDumps;
@ -6653,6 +6728,8 @@ main(int argc, char** argv, char** envp)
|| !op.addBoolOption('\0', "no-asmjs", "Disable asm.js compilation")
|| !op.addBoolOption('\0', "no-native-regexp", "Disable native regexp compilation")
|| !op.addBoolOption('\0', "no-unboxed-objects", "Disable creating unboxed plain objects")
|| !op.addBoolOption('\0', "no-async-funcs",
"Make async/await a syntax error (TenFourFox #521)")
|| !op.addBoolOption('\0', "unboxed-arrays", "Allow creating unboxed arrays")
|| !op.addStringOption('\0', "ion-shared-stubs", "on/off",
"Use shared stubs (default: off, on to enable)")

View File

@ -0,0 +1,38 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Basic syntactic tests for issue 521. */
var Promise = ShellPromise;
function assertThrowsSE(code) {
assertThrows(() => Reflect.parse(code), SyntaxError);
}
assertThrowsSE("'use strict'; async function eval() {}");
assertThrowsSE("'use strict'; async function arguments() {}");
assertThrowsSE("async function a(k = super.prop) { }");
assertThrowsSE("async function a() { super.prop(); }");
assertThrowsSE("async function a() { super(); }");
assertThrowsSE("async function a(k = await 3) {}");
async function test() { }
var anon = async function() { }
assertEq(test.name, "test");
assertEq(anon.name, "");
function okok(x, y, z) { return (typeof y === "function"); }
assertEq(okok(5, async function(w) { await w+w; }, "ok"), true);
assertEq(okok(6, (async(w)=>{await w+w}), "ok"), true);
assertEq(okok(7, ()=>{!async function(){ }}, "ok"), true);
function yoyo(k) { return new Promise(resolve => { resolve(k+1); }); }
async function dodo(k) { return await yoyo(k+1); }
// Just make sure this executes. It currently returns ({ }) in the shell.
dodo(5);
if (typeof reportCompare === "function")
reportCompare(true, true);

View File

@ -0,0 +1,49 @@
var BUGNUMBER = 1185106;
var summary = "async name token in property and object destructuring pattern";
print(BUGNUMBER + ": " + summary);
{
let a = { async: 10 };
assertEq(a.async, 10);
}
{
let a = { async() {} };
assertEq(a.async instanceof Function, true);
assertEq(a.async.name, "async");
}
{
let async = 11;
let a = { async };
assertEq(a.async, 11);
}
{
let { async } = { async: 12 };
assertEq(async, 12);
}
{
let { async = 13 } = {};
assertEq(async, 13);
}
{
let { async: a = 14 } = {};
assertEq(a, 14);
}
{
let { async, other } = { async: 15, other: 16 };
assertEq(async, 15);
assertEq(other, 16);
let a = { async, other };
assertEq(a.async, 15);
assertEq(a.other, 16);
}
if (typeof reportCompare === "function")
reportCompare(true, true);

View File

@ -10,6 +10,8 @@ var summary = 'GC not called during non-fatal out of memory';
var actual = '';
var expect = 'Normal Exit';
// NOTE: This test is probably bogus now after TenFourFox issue 521.
printBugNumber(BUGNUMBER);
printStatus (summary);
print ('This test should never fail explicitly. ' +
@ -23,9 +25,9 @@ var testFuncTimerId;
var maxTests = 5;
var currTest = 0;
if (typeof setTimeout == 'undefined')
if (typeof shellSetTimeout == 'undefined')
{
setTimeout = function() {};
shellSetTimeout = function() {};
clearTimeout = function() {};
actual = 'Normal Exit';
reportCompare(expect, actual, summary);
@ -36,7 +38,7 @@ else
// delay test driver end
gDelayTestDriverEnd = true;
setTimeout(testFuncWatcher, 1000);
shellSetTimeout(testFuncWatcher, 1000);
}
function testFuncWatcher()
@ -60,8 +62,8 @@ function testFuncWatcher()
print('Executing test ' + currTest + '\n');
testFuncWatcherId = setTimeout("testFuncWatcher()", timeOut);
testFuncTimerId = setTimeout(testFunc, interval);
testFuncWatcherId = shellSetTimeout("testFuncWatcher()", timeOut);
testFuncTimerId = shellSetTimeout(testFunc, interval);
}
@ -120,7 +122,7 @@ function testFunc()
if (testFuncTimerId)
{
testFuncTimerId = setTimeout(testFunc, interval);
testFuncTimerId = shellSetTimeout(testFunc, interval);
}
}

View File

@ -6,14 +6,16 @@
//-----------------------------------------------------------------------------
var BUGNUMBER = 314401;
var summary = 'setTimeout(eval,0,"",null)|setTimeout(Script,0,"",null) should not crash';
var summary = 'shellSetTimeout(eval,0,"",null)|shellSetTimeout(Script,0,"",null) should not crash';
var actual = 'No Crash';
var expect = 'No Crash';
// NOTE: probably a bogus test now after TenFourFox issue 521
printBugNumber(BUGNUMBER);
printStatus (summary);
if (typeof setTimeout == 'undefined')
if (typeof shellSetTimeout == 'undefined')
{
reportCompare(expect, actual, 'Test Skipped.');
}
@ -24,26 +26,26 @@ else
try
{
setTimeout(eval, 0, '', null);
shellSetTimeout(eval, 0, '', null);
}
catch(ex)
{
printStatus(ex+'');
}
reportCompare(expect, actual, 'setTimeout(eval, 0, "", null)');
reportCompare(expect, actual, 'shellSetTimeout(eval, 0, "", null)');
if (typeof Script != 'undefined')
{
try
{
setTimeout(Script, 0, '', null);
shellSetTimeout(Script, 0, '', null);
}
catch(ex)
{
printStatus(ex+'');
}
reportCompare(expect, actual, 'setTimeout(Script, 0, "", null)');
reportCompare(expect, actual, 'shellSetTimeout(Script, 0, "", null)');
}
try
@ -68,5 +70,5 @@ else
}
reportCompare(expect, actual, 'setInterval(Script, 0, "", null)');
}
setTimeout('gDelayTestDriverEnd = false; jsTestDriverEnd();', 0);
shellSetTimeout('gDelayTestDriverEnd = false; jsTestDriverEnd();', 0);
}

View File

@ -24,6 +24,7 @@
macro(ArrayValues, ArrayValues, "ArrayValues") \
macro(ArrayValuesAt, ArrayValuesAt, "ArrayValuesAt") \
macro(Async, Async, "Async") \
macro(async, async, "async") \
macro(await, await, "await") \
macro(breakdown, breakdown, "breakdown") \
macro(buffer, buffer, "buffer") \

View File

@ -23,6 +23,7 @@
#include "builtin/MapObject.h"
#include "builtin/ModuleObject.h"
#include "builtin/Object.h"
#include "builtin/Promise.h"
#include "builtin/RegExp.h"
#include "builtin/SIMD.h"
#include "builtin/SymbolObject.h"

View File

@ -389,6 +389,13 @@ class GlobalObject : public NativeObject
return &global->getPrototype(JSProto_Symbol).toObject().as<NativeObject>();
}
static NativeObject* getOrCreatePromisePrototype(JSContext* cx, Handle<GlobalObject*> global)
{
if (!ensureConstructor(cx, global, JSProto_ShellPromise))
return nullptr;
return &global->getPrototype(JSProto_ShellPromise).toObject().as<NativeObject>();
}
static NativeObject* getOrCreateRegExpPrototype(JSContext* cx, Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_RegExp))
return nullptr;

View File

@ -239,7 +239,12 @@ CallObject::createForFunction(JSContext* cx, HandleObject enclosing, HandleFunct
* object holding function's name.
*/
if (callee->isNamedLambda()) {
scopeChain = DeclEnvObject::create(cx, scopeChain, callee);
if (callee->isAsync()) {
RootedFunction fun(cx, &callee->getExtendedSlot(1).toObject().as<JSFunction>());
scopeChain = DeclEnvObject::create(cx, scopeChain, fun);
} else {
scopeChain = DeclEnvObject::create(cx, scopeChain, callee);
}
if (!scopeChain)
return nullptr;
}

View File

@ -25,6 +25,7 @@
#include "builtin/MapObject.h"
#include "builtin/ModuleObject.h"
#include "builtin/Object.h"
#include "builtin/Promise.h"
#include "builtin/Reflect.h"
#include "builtin/SelfHostingDefines.h"
#include "builtin/SIMD.h"
@ -493,6 +494,54 @@ intrinsic_GetIteratorPrototype(JSContext* cx, unsigned argc, Value* vp)
return true;
}
static bool
intrinsic_NewPromise(JSContext* cx, unsigned argc, Value* vp)
{
return js::PromiseConstructor(cx, argc, vp);
}
static bool
intrinsic_IsPromise(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(args[0].isObject());
bool isPromise = args[0].toObject().getClass() == &ShellPromiseObject::class_;
args.rval().setBoolean(isPromise);
return true;
}
static bool
intrinsic_SetFunName(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
MOZ_ASSERT(args[0].isObject() && args[0].toObject().is<JSFunction>());
MOZ_ASSERT(args[1].isString());
JSAtom* atom = AtomizeString(cx, args[1].toString());
if (atom == nullptr)
return false;
RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
fun->setFlags(fun->flags() & ~JSFunction::HAS_GUESSED_ATOM);
fun->initAtom(atom);
return true;
}
static bool
intrinsic_GetFunName(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(args[0].isObject() && args[0].toObject().is<JSFunction>());
PropertyName* name = args[0].toObject().as<JSFunction>().name();
if (!name)
args.rval().setUndefined();
else
args.rval().setString(name);
return true;
}
static bool
intrinsic_NewArrayIterator(JSContext* cx, unsigned argc, Value* vp)
{
@ -1208,6 +1257,20 @@ CallNonGenericSelfhostedMethod(JSContext* cx, unsigned argc, Value* vp)
return CallNonGenericMethod<Test, CallSelfHostedNonGenericMethod>(cx, args);
}
bool
intrinsic_SetFunctionExtendedSlot(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 3);
MOZ_ASSERT(args[0].isObject());
MOZ_ASSERT(args[0].toObject().is<JSFunction>());
MOZ_ASSERT(args[1].isInt32());
args[0].toObject().as<JSFunction>().setExtendedSlot(args[1].toPrivateUint32(), args[2]);
args.rval().setUndefined();
return true;
}
/**
* Returns the default locale as a well-formed, but not necessarily canonicalized,
* BCP-47 language tag.
@ -1552,6 +1615,7 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("_NameForTypedArray", intrinsic_NameForTypedArray, 1,0),
JS_FN("DecompileArg", intrinsic_DecompileArg, 2,0),
JS_FN("RuntimeDefaultLocale", intrinsic_RuntimeDefaultLocale, 0,0),
JS_FN("SetFunctionExtendedSlot", intrinsic_SetFunctionExtendedSlot, 3,0),
JS_FN("LocalTZA", intrinsic_LocalTZA, 0,0),
JS_INLINABLE_FN("_IsConstructing", intrinsic_IsConstructing, 0,0,
@ -1661,6 +1725,12 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("CallWeakSetMethodIfWrapped",
CallNonGenericSelfhostedMethod<Is<WeakSetObject>>, 2, 0),
JS_FN("NewPromise", intrinsic_NewPromise, 1,0),
JS_FN("IsPromise", intrinsic_IsPromise, 1,0),
JS_FN("SetFunName", intrinsic_SetFunName, 2,0),
JS_FN("GetFunName", intrinsic_GetFunName, 1,0),
// See builtin/TypedObject.h for descriptors of the typedobj functions.
JS_FN("NewOpaqueTypedObject", js::NewOpaqueTypedObject, 1, 0),
JS_FN("NewDerivedTypedObject", js::NewDerivedTypedObject, 3, 0),

View File

@ -29,11 +29,11 @@ namespace js {
*
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
*/
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 338;
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 339;
static const uint32_t XDR_BYTECODE_VERSION =
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
static_assert(JSErr_Limit == 435,
static_assert(JSErr_Limit == 444,
"GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
"removed MSG_DEFs from js.msg, you should increment "
"XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "

View File

@ -1564,6 +1564,8 @@ ReloadPrefsCallback(const char* pref, void* data)
bool useAsyncStack = Preferences::GetBool(JS_OPTIONS_DOT_STR "asyncstack");
bool useAsyncFuncs = Preferences::GetBool(JS_OPTIONS_DOT_STR "asyncfuncs");
bool werror = Preferences::GetBool(JS_OPTIONS_DOT_STR "werror");
bool extraWarnings = Preferences::GetBool(JS_OPTIONS_DOT_STR "strict");
@ -1577,6 +1579,7 @@ ReloadPrefsCallback(const char* pref, void* data)
.setThrowOnAsmJSValidationFailure(throwOnAsmJSValidationFailure)
.setNativeRegExp(useNativeRegExp)
.setAsyncStack(useAsyncStack)
.setAsyncFuncs(useAsyncFuncs)
.setWerror(werror)
.setExtraWarnings(extraWarnings);

View File

@ -1132,6 +1132,7 @@ pref("javascript.options.asyncstack", true);
#else
pref("javascript.options.asyncstack", false);
#endif
pref("javascript.options.asyncfuncs", true); // TenFourFox issue 521
pref("javascript.options.throw_on_asmjs_validation_failure", false);
pref("javascript.options.ion.offthread_compilation", true);
// This preference instructs the JS engine to discard the