Bug 1250589 - Remove the must-be-parenthesized requirement from yield expressions. r=waldo
authorDaniel Näslund <dannas@dannas.name>
Fri, 26 Feb 2016 22:57:02 +0100
changeset 286378 5e0e7a6b6f10e4435569d33e92326b9c8d530da7
parent 286377 f859932803e86f449793e3f99d905cf43bac0771
child 286379 15631f4834576bbfb5ef37126a9bb48e4f9c097d
push id72752
push usercbook@mozilla.com
push dateWed, 02 Mar 2016 10:58:17 +0000
treeherdermozilla-inbound@15631f483457 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswaldo
bugs1250589
milestone47.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 1250589 - Remove the must-be-parenthesized requirement from yield expressions. r=waldo
js/src/frontend/Parser.cpp
js/src/frontend/SyntaxParseHandler.h
js/src/js.msg
js/src/tests/ecma_6/Comprehensions/error-messages.js
js/src/tests/js1_7/geniter/regress-351514.js
js/src/vm/Xdr.h
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -7470,20 +7470,16 @@ Parser<ParseHandler>::expr(InHandling in
         return null();
     if (!matched)
         return pn;
 
     Node seq = handler.newCommaExpressionList(pn);
     if (!seq)
         return null();
     while (true) {
-        if (handler.isUnparenthesizedYieldExpression(pn)) {
-            report(ParseError, false, pn, JSMSG_BAD_YIELD_SYNTAX);
-            return null();
-        }
 
         pn = assignExpr(inHandling, yieldHandling, tripledotHandling);
         if (!pn)
             return null();
         handler.addList(seq, pn);
 
         if (!tokenStream.matchToken(&matched, TOK_COMMA))
             return null();
@@ -8452,26 +8448,16 @@ Parser<ParseHandler>::argumentList(Yield
         if (!argNode)
             return false;
         if (spread) {
             argNode = handler.newUnary(PNK_SPREAD, JSOP_NOP, begin, argNode);
             if (!argNode)
                 return false;
         }
 
-        if (handler.isUnparenthesizedYieldExpression(argNode)) {
-            TokenKind tt;
-            if (!tokenStream.peekToken(&tt))
-                return false;
-            if (tt == TOK_COMMA) {
-                report(ParseError, false, argNode, JSMSG_BAD_YIELD_SYNTAX);
-                return false;
-            }
-        }
-
         handler.addList(listNode, argNode);
 
         bool matched;
         if (!tokenStream.matchToken(&matched, TOK_COMMA))
             return false;
         if (!matched)
             break;
     }
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -112,24 +112,16 @@ class SyntaxParseHandler
         // Legacy generator expressions of the form |(expr for (...))| and
         // array comprehensions of the form |[expr for (...)]|) don't permit
         // |expr| to be a comma expression.  Thus we need this to treat
         // |(a(), b for (x in []))| as a syntax error and
         // |((a(), b) for (x in []))| as a generator that calls |a| and then
         // yields |b| each time it's resumed.
         NodeUnparenthesizedCommaExpr,
 
-        // Yield expressions currently (but not in ES6 -- a SpiderMonkey bug to
-        // fix) must generally be parenthesized.  (See the uses of
-        // isUnparenthesizedYieldExpression in Parser.cpp for the rare
-        // exceptions.)  Thus we need this to treat |yield 1, 2;| as a syntax
-        // error and |(yield 1), 2;| as a comma expression that will yield 1,
-        // then evaluate to 2.
-        NodeUnparenthesizedYieldExpr,
-
         // Assignment expressions in condition contexts could be typos for
         // equality checks.  (Think |if (x = y)| versus |if (x == y)|.)  Thus
         // we need this to treat |if (x = y)| as a possible typo and
         // |if ((x = y))| as a deliberate assignment within a condition.
         //
         // (Technically this isn't needed, as these are *only* extraWarnings
         // warnings, and parsing with that option disables syntax parsing.  But
         // it seems best to be consistent, and perhaps the syntax parser will
@@ -283,17 +275,17 @@ class SyntaxParseHandler
     Node newPosHolder(const TokenPos& pos) { return NodeGeneric; }
     Node newSuperBase(Node thisName, const TokenPos& pos) { return NodeSuperBase; }
 
     bool addPrototypeMutation(Node literal, uint32_t begin, Node expr) { return true; }
     bool addPropertyDefinition(Node literal, Node name, Node expr) { return true; }
     bool addShorthand(Node literal, Node name, Node expr) { return true; }
     bool addObjectMethodDefinition(Node literal, Node name, Node fn, JSOp op) { return true; }
     bool addClassMethodDefinition(Node literal, Node name, Node fn, JSOp op, bool isStatic) { return true; }
-    Node newYieldExpression(uint32_t begin, Node value, Node gen) { return NodeUnparenthesizedYieldExpr; }
+    Node newYieldExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; }
     Node newYieldStarExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; }
 
     // Statements
 
     Node newStatementList(unsigned blockid, const TokenPos& pos) { return NodeGeneric; }
     void addStatementToList(Node list, Node stmt, ParseContext<SyntaxParseHandler>* pc) {}
     void addCaseStatementToList(Node list, Node stmt, ParseContext<SyntaxParseHandler>* pc) {}
     bool prependInitialYield(Node stmtList, Node gen) { return true; }
@@ -478,20 +470,16 @@ class SyntaxParseHandler
     Node newAssignment(ParseNodeKind kind, Node lhs, Node rhs,
                        ParseContext<SyntaxParseHandler>* pc, JSOp op)
     {
         if (kind == PNK_ASSIGN)
             return NodeUnparenthesizedAssignment;
         return newBinary(kind, lhs, rhs, op);
     }
 
-    bool isUnparenthesizedYieldExpression(Node node) {
-        return node == NodeUnparenthesizedYieldExpr;
-    }
-
     bool isUnparenthesizedCommaExpression(Node node) {
         return node == NodeUnparenthesizedCommaExpr;
     }
 
     bool isUnparenthesizedAssignment(Node node) {
         return node == NodeUnparenthesizedAssignment;
     }
 
@@ -529,17 +517,16 @@ class SyntaxParseHandler
             return NodeParenthesizedArray;
         if (node == NodeUnparenthesizedObject)
             return NodeParenthesizedObject;
 
         // Other nodes need not be recognizable after parenthesization; convert
         // them to a generic node.
         if (node == NodeUnparenthesizedString ||
             node == NodeUnparenthesizedCommaExpr ||
-            node == NodeUnparenthesizedYieldExpr ||
             node == NodeUnparenthesizedAssignment)
         {
             return NodeGeneric;
         }
 
         // In all other cases, the parenthesized form of |node| is equivalent
         // to the unparenthesized form: return |node| unchanged.
         return node;
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -200,17 +200,16 @@ MSG_DEF(JSMSG_BAD_DESTRUCT_TARGET,     0
 MSG_DEF(JSMSG_BAD_DESTRUCT_PARENS,     0, JSEXN_SYNTAXERR, "destructuring patterns in assignments can't be parenthesized")
 MSG_DEF(JSMSG_BAD_DESTRUCT_DECL,       0, JSEXN_SYNTAXERR, "missing = in destructuring declaration")
 MSG_DEF(JSMSG_BAD_DUP_ARGS,            0, JSEXN_SYNTAXERR, "duplicate argument names not allowed in this context")
 MSG_DEF(JSMSG_BAD_FOR_EACH_LOOP,       0, JSEXN_SYNTAXERR, "invalid for each loop")
 MSG_DEF(JSMSG_BAD_FOR_LEFTSIDE,        0, JSEXN_SYNTAXERR, "invalid for-in/of left-hand side")
 MSG_DEF(JSMSG_LEXICAL_DECL_DEFINES_LET,0, JSEXN_SYNTAXERR, "a lexical declaration can't define a 'let' binding")
 MSG_DEF(JSMSG_LET_STARTING_FOROF_LHS,  0, JSEXN_SYNTAXERR, "an expression X in 'for (X of Y)' must not start with 'let'")
 MSG_DEF(JSMSG_BAD_GENERATOR_RETURN,    1, JSEXN_TYPEERR,   "generator function {0} returns a value")
-MSG_DEF(JSMSG_BAD_YIELD_SYNTAX,        0, JSEXN_SYNTAXERR, "yield expression must be parenthesized")
 MSG_DEF(JSMSG_BAD_GENERATOR_SYNTAX,    0, JSEXN_SYNTAXERR, "generator expression must be parenthesized")
 MSG_DEF(JSMSG_BAD_GENEXP_BODY,         1, JSEXN_SYNTAXERR, "illegal use of {0} in generator expression")
 MSG_DEF(JSMSG_BAD_INCOP_OPERAND,       0, JSEXN_REFERENCEERR, "invalid increment/decrement operand")
 MSG_DEF(JSMSG_BAD_METHOD_DEF,          0, JSEXN_SYNTAXERR, "bad method definition")
 MSG_DEF(JSMSG_BAD_OCTAL,               1, JSEXN_SYNTAXERR, "{0} is not a legal ECMA-262 octal constant")
 MSG_DEF(JSMSG_BAD_OPERAND,             1, JSEXN_SYNTAXERR, "invalid {0} operand")
 MSG_DEF(JSMSG_BAD_PROP_ID,             0, JSEXN_SYNTAXERR, "invalid property id")
 MSG_DEF(JSMSG_BAD_RETURN_OR_YIELD,     1, JSEXN_SYNTAXERR, "{0} not in function")
--- a/js/src/tests/ecma_6/Comprehensions/error-messages.js
+++ b/js/src/tests/ecma_6/Comprehensions/error-messages.js
@@ -20,17 +20,16 @@ function error(str) {
 }
 
 const YIELD_PAREN          = error("(function*(){(for (y of (yield 1, 2)) y)})").message;
 const GENEXP_YIELD         = error("(function*(){(for (x of yield 1) x)})").message;
 const TOP_YIELD            = error("yield").message;
 const GENERIC              = error("(for)").message;
 const BAD_GENERATOR_SYNTAX = error("(for (x of []) x, 1)").message;
 const MISSING_SEMI         = error("yield 1").message;
-const MISSING_PAREN        = error("(yield 1)").message;
 const PAREN_PAREN          = error("(foo").message;
 const FOR_OF_PAREN         = error("(for (x of y, z) w)").message;
 
 const cases = [
 // Expressions involving yield without a value, not currently implemented.  Many
 // of these errors would need to be updated.  (Note: line numbers below might be
 // mere placeholders, not actually the expected correct behavior -- check before
 // blindly uncommenting.)
@@ -50,23 +49,23 @@ const cases = [
 //{ expr: "(for (x of []) 1, (2, yield))",   top: [TOP_YIELD, 777], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], desc: "simple yield at end of list, parenthesized in list in genexp" },
 //{ expr: "(for (x of []) (yield, 1))",      top: [TOP_YIELD, 777], fun: [YIELD_PAREN, 2],  gen: [YIELD_PAREN, 2],  desc: "simple yield in list, parenthesized in genexp" },
 //{ expr: "(for (x of []) 1, (yield, 2))",   top: [TOP_YIELD, 777], fun: [YIELD_PAREN, 2],  gen: [YIELD_PAREN, 2],  desc: "simple yield in list, parenthesized in list in genexp" },
 //{ expr: "(for (x of []) (function*() { yield }))",           top: null, fun: null, gen: null, desc: "legal yield in nested function" },
 
   // yield expressions
   { expr: "yield 1",      top: [MISSING_SEMI, 2], fun: [MISSING_SEMI, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg" },
   { expr: "1, yield 2",   top: [MISSING_SEMI, 2], fun: [MISSING_SEMI, 2], gen: null, genexp: [FOR_OF_PAREN, 1], desc: "yield w/ arg at end of list" },
-  { expr: "yield 1, 2",   top: [MISSING_SEMI, 2], fun: [MISSING_SEMI, 2], gen: [YIELD_PAREN, 3], genexp: [FOR_OF_PAREN, 3], desc: "yield w/ arg in list" },
-  { expr: "(yield 1)",    top: [MISSING_PAREN, 2], fun: [MISSING_PAREN, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg, parenthesized" },
-  { expr: "(1, yield 2)", top: [MISSING_PAREN, 2], fun: [MISSING_PAREN, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg at end of list, parenthesized" },
-  { expr: "(yield 1, 2)", top: [MISSING_PAREN, 2], fun: [MISSING_PAREN, 2], gen: [YIELD_PAREN, 3], genexp: [YIELD_PAREN, 2], desc: "yield w/ arg in list, parenthesized" },
+  { expr: "yield 1, 2",   top: [MISSING_SEMI, 2], fun: [MISSING_SEMI, 2], gen: null, genexp: [FOR_OF_PAREN, 3], desc: "yield w/ arg in list" },
+  { expr: "(yield 1)",    top: [PAREN_PAREN, 2], fun:  [PAREN_PAREN, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg, parenthesized" },
+  { expr: "(1, yield 2)", top: [PAREN_PAREN, 2], fun:  [PAREN_PAREN, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg at end of list, parenthesized" },
+  { expr: "(yield 1, 2)", top: [PAREN_PAREN, 2], fun:  [PAREN_PAREN, 2], gen: null, genexp: [YIELD_PAREN, 2], desc: "yield w/ arg in list, parenthesized" },
 
   // deeply nested yield expressions
-  { expr: "((((yield 1))))", top: [MISSING_PAREN, 2], fun: [MISSING_PAREN, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "deeply nested yield w/ arg" },
+  { expr: "((((yield 1))))", top: [PAREN_PAREN, 2], fun: [PAREN_PAREN, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "deeply nested yield w/ arg" },
 
   // arguments
   { expr: "arguments",    top: null, fun: null, gen: null, genexp: null, desc: "arguments in list" },
   { expr: "1, arguments", top: null, fun: null, gen: null, genexp: [FOR_OF_PAREN, 1], desc: "arguments in list" },
 
   // yield in generator expressions
   { expr: "(for (x of []) yield 1)",         top: [GENEXP_YIELD, 2], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg in genexp" },
   { expr: "(for (x of []) yield 1, 2)",      top: [GENEXP_YIELD, 2], fun: [GENEXP_YIELD, 2],  gen: [GENEXP_YIELD, 2], genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg in list in genexp" },
deleted file mode 100644
--- a/js/src/tests/js1_7/geniter/regress-351514.js
+++ /dev/null
@@ -1,36 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* 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/. */
-
-//-----------------------------------------------------------------------------
-var BUGNUMBER = 351514;
-var summary = 'Finalize yield syntax to match ES4/JS2 proposal';
-var actual = '';
-var expect = '';
-
-
-//-----------------------------------------------------------------------------
-test();
-//-----------------------------------------------------------------------------
-
-function test()
-{
-  enterFunc ('test');
-  printBugNumber(BUGNUMBER);
-  printStatus (summary);
- 
-  expect = /SyntaxError: yield expression must be parenthesized/;
-  try
-  {
-    eval('function f() { yield g(yield 1, 2) };');
-  }
-  catch(ex)
-  {
-    actual = ex + '';
-  }
-
-  reportMatch(expect, actual, summary);
-
-  exitFunc ('test');
-}
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -26,21 +26,21 @@ namespace js {
  *
  * When you change this, run make_opcode_doc.py and copy the new output into
  * this wiki page:
  *
  *  https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
  *
  * (If you're wondering, 0xb973c0de is used because it looks like "bytecode".)
  */
-static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 347;
+static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 348;
 static const uint32_t XDR_BYTECODE_VERSION =
     uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
 
-static_assert(JSErr_Limit == 446,
+static_assert(JSErr_Limit == 445,
               "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 "
               "expected JSErr_Limit value.");
 
 class XDRBuffer {
   public:
     explicit XDRBuffer(JSContext* cx)