summaryrefslogtreecommitdiffstats
path: root/js/src
diff options
context:
space:
mode:
Diffstat (limited to 'js/src')
-rw-r--r--js/src/frontend/Parser.cpp59
-rw-r--r--js/src/tests/ecma_2017/AsyncFunctions/await-in-arrow-parameters.js94
2 files changed, 128 insertions, 25 deletions
diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
index 47e0f943d..0c279591f 100644
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -3623,9 +3623,14 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
FunctionBox* funbox = pc->functionBox();
RootedFunction fun(context, funbox->function());
- AutoAwaitIsKeyword<ParseHandler> awaitIsKeyword(this, funbox->isAsync());
- if (!functionArguments(yieldHandling, kind, pn))
- return false;
+ // See below for an explanation why arrow function parameters and arrow
+ // function bodies are parsed with different yield/await settings.
+ {
+ bool asyncOrArrowInAsync = funbox->isAsync() || (kind == Arrow && awaitIsKeyword());
+ AutoAwaitIsKeyword<ParseHandler> awaitIsKeyword(this, asyncOrArrowInAsync);
+ if (!functionArguments(yieldHandling, kind, pn))
+ return false;
+ }
Maybe<ParseContext::VarScope> varScope;
if (funbox->hasParameterExprs) {
@@ -3689,32 +3694,36 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
// |yield| in the parameters is either a name or keyword, depending on
// whether the arrow function is enclosed in a generator function or not.
// Whereas the |yield| in the function body is always parsed as a name.
+ // The same goes when parsing |await| in arrow functions.
YieldHandling bodyYieldHandling = GetYieldHandling(pc->generatorKind(), pc->asyncKind());
- Node body = functionBody(inHandling, bodyYieldHandling, kind, bodyType);
- if (!body)
- return false;
+ Node body;
+ {
+ AutoAwaitIsKeyword<ParseHandler> awaitIsKeyword(this, funbox->isAsync());
+ body = functionBody(inHandling, bodyYieldHandling, kind, bodyType);
+ if (!body)
+ return false;
+ }
- if ((kind != Method && !IsConstructorKind(kind)) && fun->explicitName()) {
+ if ((kind == Statement || kind == Expression) && fun->explicitName()) {
RootedPropertyName propertyName(context, fun->explicitName()->asPropertyName());
- // `await` cannot be checked at this point because of different context.
- // It should already be checked before this point.
- if (propertyName != context->names().await) {
- YieldHandling nameYieldHandling;
- if (kind == Expression) {
- // Named lambda has binding inside it.
- nameYieldHandling = bodyYieldHandling;
- } else {
- // Otherwise YieldHandling cannot be checked at this point
- // because of different context.
- // It should already be checked before this point.
- nameYieldHandling = YieldIsName;
- }
+ YieldHandling nameYieldHandling;
+ if (kind == Expression) {
+ // Named lambda has binding inside it.
+ nameYieldHandling = bodyYieldHandling;
+ } else {
+ // Otherwise YieldHandling cannot be checked at this point
+ // because of different context.
+ // It should already be checked before this point.
+ nameYieldHandling = YieldIsName;
+ }
- if (!checkBindingIdentifier(propertyName, handler.getPosition(pn).begin,
- nameYieldHandling))
- {
- return false;
- }
+ // We already use the correct await-handling at this point, therefore
+ // we don't need call AutoAwaitIsKeyword here.
+
+ if (!checkBindingIdentifier(propertyName, handler.getPosition(pn).begin,
+ nameYieldHandling))
+ {
+ return false;
}
}
diff --git a/js/src/tests/ecma_2017/AsyncFunctions/await-in-arrow-parameters.js b/js/src/tests/ecma_2017/AsyncFunctions/await-in-arrow-parameters.js
new file mode 100644
index 000000000..ebb4ea9da
--- /dev/null
+++ b/js/src/tests/ecma_2017/AsyncFunctions/await-in-arrow-parameters.js
@@ -0,0 +1,94 @@
+var ieval = eval;
+var AsyncFunction = async function(){}.constructor;
+
+var functionContext = {
+ Function: {
+ constructor: Function,
+ toSourceBody: code => `function f() { ${code} }`,
+ toSourceParameter: code => `function f(x = ${code}) { }`,
+ },
+ AsyncFunction: {
+ constructor: AsyncFunction,
+ toSourceBody: code => `async function f() { ${code} }`,
+ toSourceParameter: code => `async function f(x = ${code}) { }`,
+ },
+};
+
+function assertSyntaxError(kind, code) {
+ var {constructor, toSourceBody, toSourceParameter} = functionContext[kind];
+ var body = toSourceBody(code);
+ var parameter = toSourceParameter(code);
+
+ assertThrowsInstanceOf(() => { constructor(code); }, SyntaxError, constructor.name + ":" + code);
+ assertThrowsInstanceOf(() => { constructor(`x = ${code}`, ""); }, SyntaxError, constructor.name + ":" + code);
+
+ assertThrowsInstanceOf(() => { eval(body); }, SyntaxError, "eval:" + body);
+ assertThrowsInstanceOf(() => { ieval(body); }, SyntaxError, "indirect eval:" + body);
+
+ assertThrowsInstanceOf(() => { eval(parameter); }, SyntaxError, "eval:" + parameter);
+ assertThrowsInstanceOf(() => { ieval(parameter); }, SyntaxError, "indirect eval:" + parameter);
+}
+
+function assertNoSyntaxError(kind, code) {
+ var {constructor, toSourceBody, toSourceParameter} = functionContext[kind];
+ var body = toSourceBody(code);
+ var parameter = toSourceParameter(code);
+
+ constructor(code);
+ constructor(`x = ${code}`, "");
+
+ eval(body);
+ ieval(body);
+
+ eval(parameter);
+ ieval(parameter);
+}
+
+function assertSyntaxErrorAsync(code) {
+ assertNoSyntaxError("Function", code);
+ assertSyntaxError("AsyncFunction", code);
+}
+
+function assertSyntaxErrorBoth(code) {
+ assertSyntaxError("Function", code);
+ assertSyntaxError("AsyncFunction", code);
+}
+
+
+// Bug 1353691
+// |await| expression is invalid in arrow functions in async-context.
+// |await/r/g| first parses as |AwaitExpression RegularExpressionLiteral|, when reparsing the
+// arrow function, it is parsed as |IdentRef DIV IdentRef DIV IdentRef|. We need to ensure in this
+// case, that we still treat |await| as a keyword and hence throw a SyntaxError.
+assertSyntaxErrorAsync("(a = await/r/g) => {}");
+assertSyntaxErrorBoth("async(a = await/r/g) => {}");
+
+// Also applies when nesting arrow functions.
+assertSyntaxErrorAsync("(a = (b = await/r/g) => {}) => {}");
+assertSyntaxErrorBoth("async(a = (b = await/r/g) => {}) => {}");
+assertSyntaxErrorBoth("(a = async(b = await/r/g) => {}) => {}");
+assertSyntaxErrorBoth("async(a = async(b = await/r/g) => {}) => {}");
+
+
+// Bug 1355860
+// |await| cannot be used as rest-binding parameter in arrow functions in async-context.
+assertSyntaxErrorAsync("(...await) => {}");
+assertSyntaxErrorBoth("async(...await) => {}");
+
+assertSyntaxErrorAsync("(a, ...await) => {}");
+assertSyntaxErrorBoth("async(a, ...await) => {}");
+
+// Also test nested arrow functions.
+assertSyntaxErrorAsync("(a = (...await) => {}) => {}");
+assertSyntaxErrorBoth("(a = async(...await) => {}) => {}");
+assertSyntaxErrorBoth("async(a = (...await) => {}) => {}");
+assertSyntaxErrorBoth("async(a = async(...await) => {}) => {}");
+
+assertSyntaxErrorAsync("(a = (b, ...await) => {}) => {}");
+assertSyntaxErrorBoth("(a = async(b, ...await) => {}) => {}");
+assertSyntaxErrorBoth("async(a = (b, ...await) => {}) => {}");
+assertSyntaxErrorBoth("async(a = async(b, ...await) => {}) => {}");
+
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);