From f2dfeff9777e1fc26dd615af885210ada4a4d0da Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Fri, 25 Jul 2014 18:59:19 +0300 Subject: [PATCH] Added test for BindingPattern inside ArrayComprehension (fails in esprima - https://code.google.com/p/esprima/issues/detail?id=570). --- index.html | 65 +++++++++----- test/tests-harmony.js | 203 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 245 insertions(+), 23 deletions(-) diff --git a/index.html b/index.html index ff59372d05..88ea6a3263 100644 --- a/index.html +++ b/index.html @@ -849,8 +849,10 @@ to.

break; case "ArrayPattern": - for (var i = 0; i < expr.elements.length; i++) - checkLVal(expr.elements[i], isBinding); + for (var i = 0; i < expr.elements.length; i++) { + var elem = expr.elements[i]; + if (elem) checkLVal(elem, isBinding); + } break; case "SpreadElement": @@ -1319,10 +1321,9 @@ or {}.

if (tokType !== _parenR) { val = parseExpression(); } - expect(_parenR); - if (metParenL === oldParenL && eat(_arrow)) { + expect(_parenR);

if '=>' follows '(...)', convert contents to arguments

      if (metParenL === oldParenL && eat(_arrow)) {
         val = parseArrowExpression(node, !val ? [] : val.type === "SequenceExpression" ? val.expressions : [val]);
-      } else {

disallow '()' before everything but error

        if (!val) unexpected(lastStart);
+      } else {

forbid '()' before everything but '=>'

        if (!val) unexpected(lastStart);
       }
       val.start = tokStart1;
       val.end = lastEnd;
@@ -1337,7 +1338,25 @@ or {}.

case _bracketL: var node = startNode(); - next(); + next();

check whether this is array comprehension or regular array

      if (options.ecmaVersion >= 6 && tokType === _for) {
+        node.blocks = [];
+        while (tokType === _for) {
+          var block = startNode();
+          next();
+          expect(_parenL);
+          block.left = toAssignable(parseExprAtom());
+          checkLVal(block.left, true);
+          expect(_of);

of property is here for compatibility with Esprima's AST +which also supports deprecated [for (... in ...) expr]

          block.of = true;
+          block.right = parseExpression();
+          expect(_parenR);
+          node.blocks.push(finishNode(block, "ComprehensionBlock"));
+        }
+        node.filter = eat(_if) ? parseParenExpression() : null;
+        node.body = parseExpression();
+        expect(_bracketR);
+        return finishNode(node, "ComprehensionExpression");
+      }
       node.elements = parseExprList(_bracketR, true, true);
       return finishNode(node, "ArrayExpression");
 
@@ -1361,7 +1380,7 @@ or {}.

default: unexpected(); } - }

New's precedence is slightly tricky. It must allow its argument + }

New's precedence is slightly tricky. It must allow its argument to be a [] or dot subscript expression, but not a call — at least, not without wrapping it in parentheses. Thus, it uses the

  function parseNew() {
     var node = startNode();
@@ -1370,12 +1389,12 @@ least, not without wrapping it in parentheses. Thus, it uses the

if (eat(_parenL)) node.arguments = parseExprList(_parenR, false); else node.arguments = empty; return finishNode(node, "NewExpression"); - }

Parse spread element '...expr'

  function parseSpread() {
+  }

Parse spread element '...expr'

  function parseSpread() {
     var node = startNode();
     next();
     node.argument = parseExpression(true);
     return finishNode(node, "SpreadElement");
-  }

Parse an object literal.

  function parseObj() {
+  }

Parse an object literal.

  function parseObj() {
     var node = startNode(), first = true, sawGetSet = false;
     node.properties = [];
     next();
@@ -1416,7 +1435,7 @@ least, not without wrapping it in parentheses. Thus, it uses the

addProperty(node.properties, finishNode(prop, "Property"), sawGetSet, "init"); } return finishNode(node, "ObjectExpression"); - }

Add property to list with keeping in mind and checking that + }

Add property to list with keeping in mind and checking that object/class getters and setters are not allowed to clash — either with each other or with an init property — and in strict mode, init properties are also not allowed to be repeated.

  function addProperty(props, current, sawGetSet, defaultKind) {
@@ -1447,7 +1466,7 @@ strict mode, init properties are also not allowed to be repeated.

} } prop.key = (tokType === _num || tokType === _string) ? parseExprAtom() : parseIdent(true); - }

Initialize empty function node.

  function initFunction(node) {
+  }

Initialize empty function node.

  function initFunction(node) {
     node.id = null;
     node.params = [];
     if (options.ecmaVersion >= 6) {
@@ -1455,7 +1474,7 @@ strict mode, init properties are also not allowed to be repeated.

node.rest = null; node.generator = false; } - }

Parse a function declaration or literal (depending on the + }

Parse a function declaration or literal (depending on the isStatement parameter).

  function parseFunction(node, isStatement, allowExpressionBody) {
     initFunction(node);
     if (isStatement || tokType === _name) {
@@ -1464,7 +1483,7 @@ strict mode, init properties are also not allowed to be repeated.

parseFunctionParams(node); parseFunctionBody(node, allowExpressionBody); return finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression"); - }

Parse arrow function expression with given parameters.

  function parseArrowExpression(node, params) {
+  }

Parse arrow function expression with given parameters.

  function parseArrowExpression(node, params) {
     initFunction(node);
 
     var defaults = node.defaults, hasDefaults = false;
@@ -1492,7 +1511,7 @@ strict mode, init properties are also not allowed to be repeated.

parseFunctionBody(node, true); return finishNode(node, "ArrowFunctionExpression"); - }

Parse function parameters.

  function parseFunctionParams(node) {
+  }

Parse function parameters.

  function parseFunctionParams(node) {
     var defaults = [], hasDefaults = false;
     
     expect(_parenL);
@@ -1519,19 +1538,19 @@ strict mode, init properties are also not allowed to be repeated.

} if (hasDefaults) node.defaults = defaults; - }

Parse function body and check parameters.

  function parseFunctionBody(node, allowExpression) {
+  }

Parse function body and check parameters.

  function parseFunctionBody(node, allowExpression) {
     var isExpression = allowExpression && tokType !== _braceL;
     
     if (isExpression) {
       node.body = parseExpression(true);
       node.expression = true;
-    } else {

Start a new scope with regard to labels and the inFunction + } else {

Start a new scope with regard to labels and the inFunction flag (restore them to their old value afterwards).

      var oldInFunc = inFunction, oldLabels = labels;
       inFunction = true; labels = [];
       node.body = parseBlock(true);
       node.expression = false;
       inFunction = oldInFunc; labels = oldLabels;
-    }

If this is a strict mode function, verify that argument names + }

If this is a strict mode function, verify that argument names are not repeated, and it does not try to bind the words eval or arguments.

    if (strict || !isExpression && node.body.body.length && isUseStrict(node.body.body[0])) {
       var nameHash = {};
@@ -1542,7 +1561,7 @@ or arguments.

if (node.rest) checkFunctionParam(node.rest, nameHash); } - }

Verify that argument names are not repeated, and it does not + }

Verify that argument names are not repeated, and it does not try to bind the words eval or arguments.

  function checkFunctionParam(param, nameHash) {
     switch (param.type) {
       case "Identifier":
@@ -1563,7 +1582,7 @@ try to bind the words eval or arguments.

checkFunctionParam(param.elements[i], nameHash); break; } - }

Parse a class declaration or literal (depending on the + }

Parse a class declaration or literal (depending on the isStatement parameter).

  
   function parseClass(node, isStatement) {
     next();
@@ -1589,7 +1608,7 @@ try to bind the words eval or arguments.

} node.body = finishNode(classBody, "ClassBody"); return finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression"); - }

Parses a comma-separated list of expressions, and returns them as + }

Parses a comma-separated list of expressions, and returns them as an array. close is the token type that ends the list, and allowEmpty can be turned on to allow subsequent commas with nothing in between them to be parsed as null (which is needed @@ -1605,7 +1624,7 @@ for array literals).

else elts.push(parseExpression(true)); } return elts; - }

Parse the next token as an identifier. If liberal is true (used + }

Parse the next token as an identifier. If liberal is true (used when parsing properties), it will also convert keywords into identifiers.

  function parseIdent(liberal) {
     var node = startNode();
@@ -1626,7 +1645,7 @@ identifiers.

< tokRegexpAllowed = false; next(); return finishNode(node, "Identifier"); - }

Convert existing expression atom to assignable pattern + }

Convert existing expression atom to assignable pattern if possible.

  function toAssignable(node, allowSpread) {
     if (options.ecmaVersion >= 6 && node) {
       switch (node.type) {
@@ -1662,7 +1681,7 @@ if possible.

< } } return node; - }

Checks if node can be assignable spread argument.

  function checkSpreadAssign(node) {
+  }

Checks if node can be assignable spread argument.

  function checkSpreadAssign(node) {
     if (node.type !== "Identifier" && node.type !== "ArrayPattern")
       unexpected(node.start);
   }
diff --git a/test/tests-harmony.js b/test/tests-harmony.js
index a0833d10ba..f69ae66f52 100644
--- a/test/tests-harmony.js
+++ b/test/tests-harmony.js
@@ -3700,6 +3700,209 @@ test("[for (x of array) for (y of array2) if (x === test) x]", {
   locations: true
 });
 
+test("[for ([,x] of array) for ({[start.x]: x, [start.y]: y} of array2) x]", {
+  type: "Program",
+  body: [{
+    type: "ExpressionStatement",
+    expression: {
+      type: "ComprehensionExpression",
+      filter: null,
+      blocks: [
+        {
+          type: "ComprehensionBlock",
+          left: {
+            type: "ArrayPattern",
+            elements: [
+              null,
+              {
+                type: "Identifier",
+                name: "x",
+                range: [8, 9],
+                loc: {
+                  start: {line: 1, column: 8},
+                  end: {line: 1, column: 9}
+                }
+              }
+            ],
+            range: [6, 10],
+            loc: {
+              start: {line: 1, column: 6},
+              end: {line: 1, column: 10}
+            }
+          },
+          right: {
+            type: "Identifier",
+            name: "array",
+            range: [14, 19],
+            loc: {
+              start: {line: 1, column: 14},
+              end: {line: 1, column: 19}
+            }
+          },
+          range: [1, 20],
+          loc: {
+            start: {line: 1, column: 1},
+            end: {line: 1, column: 20}
+          },
+          of: true
+        },
+        {
+          type: "ComprehensionBlock",
+          left: {
+            type: "ObjectPattern",
+            properties: [
+              {
+                type: "Property",
+                key: {
+                  type: "MemberExpression",
+                  computed: false,
+                  object: {
+                    type: "Identifier",
+                    name: "start",
+                    range: [28, 33],
+                    loc: {
+                      start: {line: 1, column: 28},
+                      end: {line: 1, column: 33}
+                    }
+                  },
+                  property: {
+                    type: "Identifier",
+                    name: "x",
+                    range: [34, 35],
+                    loc: {
+                      start: {line: 1, column: 34},
+                      end: {line: 1, column: 35}
+                    }
+                  },
+                  range: [28, 35],
+                  loc: {
+                    start: {line: 1, column: 28},
+                    end: {line: 1, column: 35}
+                  }
+                },
+                value: {
+                  type: "Identifier",
+                  name: "x",
+                  range: [38, 39],
+                  loc: {
+                    start: {line: 1, column: 38},
+                    end: {line: 1, column: 39}
+                  }
+                },
+                kind: "init",
+                method: false,
+                shorthand: false,
+                computed: true,
+                range: [27, 39],
+                loc: {
+                  start: {line: 1, column: 27},
+                  end: {line: 1, column: 39}
+                }
+              },
+              {
+                type: "Property",
+                key: {
+                  type: "MemberExpression",
+                  computed: false,
+                  object: {
+                    type: "Identifier",
+                    name: "start",
+                    range: [42, 47],
+                    loc: {
+                      start: {line: 1, column: 42},
+                      end: {line: 1, column: 47}
+                    }
+                  },
+                  property: {
+                    type: "Identifier",
+                    name: "y",
+                    range: [48, 49],
+                    loc: {
+                      start: {line: 1, column: 48},
+                      end: {line: 1, column: 49}
+                    }
+                  },
+                  range: [42, 49],
+                  loc: {
+                    start: {line: 1, column: 42},
+                    end: {line: 1, column: 49}
+                  }
+                },
+                value: {
+                  type: "Identifier",
+                  name: "y",
+                  range: [52, 53],
+                  loc: {
+                    start: {line: 1, column: 52},
+                    end: {line: 1, column: 53}
+                  }
+                },
+                kind: "init",
+                method: false,
+                shorthand: false,
+                computed: true,
+                range: [41, 53],
+                loc: {
+                  start: {line: 1, column: 41},
+                  end: {line: 1, column: 53}
+                }
+              }
+            ],
+            range: [26, 54],
+            loc: {
+              start: {line: 1, column: 26},
+              end: {line: 1, column: 54}
+            }
+          },
+          right: {
+            type: "Identifier",
+            name: "array2",
+            range: [58, 64],
+            loc: {
+              start: {line: 1, column: 58},
+              end: {line: 1, column: 64}
+            }
+          },
+          range: [21, 65],
+          loc: {
+            start: {line: 1, column: 21},
+            end: {line: 1, column: 65}
+          },
+          of: true
+        }
+      ],
+      body: {
+        type: "Identifier",
+        name: "x",
+        range: [66, 67],
+        loc: {
+          start: {line: 1, column: 66},
+          end: {line: 1, column: 67}
+        }
+      },
+      range: [0, 68],
+      loc: {
+        start: {line: 1, column: 0},
+        end: {line: 1, column: 68}
+      }
+    },
+    range: [0, 68],
+    loc: {
+      start: {line: 1, column: 0},
+      end: {line: 1, column: 68}
+    }
+  }],
+  range: [0, 68],
+  loc: {
+    start: {line: 1, column: 0},
+    end: {line: 1, column: 68}
+  }
+}, {
+  ecmaVersion: 6,
+  ranges: true,
+  locations: true
+});
+
 // Harmony: Object Literal Property Value Shorthand
 
 test("x = { y, z }", {