mirror of
https://github.com/classilla/tenfourfox.git
synced 2025-04-12 01:37:17 +00:00
#521: baseline parser support for async/await, with toggle, without bytecode (passes tests)
This commit is contained in:
parent
3fd15a87ad
commit
0e5746aaf0
51
js/src/builtin/AsyncFunctions.js
Normal file
51
js/src/builtin/AsyncFunctions.js
Normal 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
142
js/src/builtin/Promise.cpp
Normal 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
29
js/src/builtin/Promise.h
Normal 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
253
js/src/builtin/Promise.js
Normal 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;
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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) &&
|
||||
|
@ -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) {
|
||||
|
@ -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))
|
||||
|
@ -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) ||
|
||||
|
@ -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
|
||||
|
@ -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(<ok, 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>;
|
||||
|
||||
|
@ -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>;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)));
|
||||
|
@ -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")
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
|
@ -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.
|
||||
|
@ -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()) {
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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_);
|
||||
|
@ -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',
|
||||
|
@ -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)")
|
||||
|
38
js/src/tests/ecma_7/AsyncFunctions/104_basic.js
Normal file
38
js/src/tests/ecma_7/AsyncFunctions/104_basic.js
Normal 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);
|
||||
|
49
js/src/tests/ecma_7/AsyncFunctions/property.js
Normal file
49
js/src/tests/ecma_7/AsyncFunctions/property.js
Normal 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);
|
0
js/src/tests/ecma_7/AsyncFunctions/shell.js
Normal file
0
js/src/tests/ecma_7/AsyncFunctions/shell.js
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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") \
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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 "
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user