Bug 1414805 - Use proper modifiers when parsing default expressions that end in an arrow function with block body, in function parameters. r=arai
authorJeff Walden <jwalden@mit.edu>
Tue, 07 Nov 2017 16:12:33 -0800
changeset 444110 af596055be2169def86749be7d8c8e4fbf3ede2f
parent 444109 eed73ebe0266c7263da00ce634959983edfb8f35
child 444111 3bcd1bde9a0233e24ab133967fc7feb9aa338728
push id1618
push userCallek@gmail.com
push dateThu, 11 Jan 2018 17:45:48 +0000
treeherdermozilla-release@882ca853e05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarai
bugs1414805
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1414805 - Use proper modifiers when parsing default expressions that end in an arrow function with block body, in function parameters. r=arai
js/src/frontend/Parser.cpp
js/src/tests/ecma_6/ArrowFunctions/arrow-not-as-end-of-statement.js
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -3125,18 +3125,27 @@ Parser<ParseHandler, CharT>::functionArg
               }
             }
 
             if (positionalFormals.length() >= ARGNO_LIMIT) {
                 error(JSMSG_TOO_MANY_FUN_ARGS);
                 return false;
             }
 
+            // The next step is to detect arguments with default expressions,
+            // e.g. |function parseInt(str, radix = 10) {}|.  But if we have a
+            // parentheses-free arrow function, |a => ...|, the '=' necessary
+            // for a default expression would really be an assignment operator:
+            // that is, |a = b => 42;| would parse as |a = (b => 42);|.  So we
+            // should stop parsing arguments here.
+            if (parenFreeArrow)
+                break;
+
             bool matched;
-            if (!tokenStream.matchToken(&matched, TOK_ASSIGN))
+            if (!tokenStream.matchToken(&matched, TOK_ASSIGN, TokenStream::Operand))
                 return false;
             if (matched) {
                 // A default argument without parentheses would look like:
                 // a = expr => body, but both operators are right-associative, so
                 // that would have been parsed as a = (expr => body) instead.
                 // Therefore it's impossible to get here with parenFreeArrow.
                 MOZ_ASSERT(!parenFreeArrow);
 
@@ -3161,37 +3170,36 @@ Parser<ParseHandler, CharT>::functionArg
 
                 Node def_expr = assignExprWithoutYieldOrAwait(yieldHandling);
                 if (!def_expr)
                     return false;
                 if (!handler.setLastFunctionFormalParameterDefault(funcpn, def_expr))
                     return false;
             }
 
-            if (parenFreeArrow || IsSetterKind(kind))
+            // Setter syntax uniquely requires exactly one argument.
+            if (IsSetterKind(kind))
                 break;
 
-            if (!tokenStream.matchToken(&matched, TOK_COMMA))
+            if (!tokenStream.matchToken(&matched, TOK_COMMA, TokenStream::Operand))
                 return false;
             if (!matched)
                 break;
 
             if (!hasRest) {
                 if (!tokenStream.peekToken(&tt, TokenStream::Operand))
                     return null();
-                if (tt == TOK_RP) {
-                    tokenStream.addModifierException(TokenStream::NoneIsOperand);
+                if (tt == TOK_RP)
                     break;
-                }
             }
         }
 
         if (!parenFreeArrow) {
             TokenKind tt;
-            if (!tokenStream.getToken(&tt))
+            if (!tokenStream.getToken(&tt, TokenStream::Operand))
                 return false;
             if (tt != TOK_RP) {
                 if (IsSetterKind(kind)) {
                     error(JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
                     return false;
                 }
 
                 error(JSMSG_PAREN_AFTER_FORMAL);
--- a/js/src/tests/ecma_6/ArrowFunctions/arrow-not-as-end-of-statement.js
+++ b/js/src/tests/ecma_6/ArrowFunctions/arrow-not-as-end-of-statement.js
@@ -156,16 +156,49 @@ assertEq(typeof [0 ? 1 : a => {}][0], "f
 
 Function.prototype[Symbol.iterator] = function() { return { next() { return { done: true }; } }; };
 assertEq([...0 ? 1 : a => {}].length, 0);
 delete Function.prototype[Symbol.iterator];
 
 var props = Object.getOwnPropertyNames({ ...0 ? 1 : a => {} }).sort();
 assertEq(props.length, 0);
 
+function f1(x = 0 ? 1 : a => {}) { return x; }
+assertEq(typeof f1(), "function");
+assertEq(f1(5), 5);
+
+var g1 = (x = 0 ? 1 : a => {}) => { return x; };
+assertEq(typeof g1(), "function");
+assertEq(g1(5), 5);
+
+var h1 = async (x = 0 ? 1 : a => {}) => { return x; };
+assertEq(typeof h1, "function");
+
+function f2(m, x = 0 ? 1 : a => {}) { return x; }
+assertEq(typeof f2(1), "function");
+assertEq(f2(1, 5), 5);
+
+var g2 = (m, x = 0 ? 1 : a => {}) => { return x; };
+assertEq(typeof g2(1), "function");
+assertEq(g2(1, 5), 5);
+
+var h2 = async (m, x = 0 ? 1 : a => {}) => { return x; };
+assertEq(typeof h2, "function");
+
+function f3(x = 0 ? 1 : a => {}, q) { return x; }
+assertEq(typeof f3(), "function");
+assertEq(f3(5), 5);
+
+var g3 = (x = 0 ? 1 : a => {}, q) => { return x; };
+assertEq(typeof g3(), "function");
+assertEq(g3(5), 5);
+
+var h3 = async (x = 0 ? 1 : a => {}, q) => { return x; };
+assertEq(typeof h3, "function");
+
 var asyncf = async () => {};
 assertEq(typeof asyncf, "function");
 
 var { [0 ? 1 : a => {}]: h } = { "a => {}": "boo-urns!" };
 assertEq(h, "boo-urns!");
 
 var gencomp = (for (prop of [0]) 0 ? 1 : a => {});
 assertEq(typeof gencomp.next().value, "function");