summaryrefslogtreecommitdiffstats
path: root/js/src/tests/non262/syntax
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/tests/non262/syntax')
-rw-r--r--js/src/tests/non262/syntax/browser.js0
-rw-r--r--js/src/tests/non262/syntax/column-numbers-in-long-lines.js403
-rw-r--r--js/src/tests/non262/syntax/declaration-forbidden-in-label.js34
-rw-r--r--js/src/tests/non262/syntax/escaped-let-static-identifier.js57
-rw-r--r--js/src/tests/non262/syntax/escaped-strict-reserved-words-and-yield.js105
-rw-r--r--js/src/tests/non262/syntax/identifier_vertical_tilde.js20
-rw-r--r--js/src/tests/non262/syntax/identifiers-with-extended-unicode-escape.js208
-rw-r--r--js/src/tests/non262/syntax/keyword-unescaped-requirement.js68
-rw-r--r--js/src/tests/non262/syntax/let-as-label.js35
-rw-r--r--js/src/tests/non262/syntax/line-number-maintenance-for-identifier-containing-escape-terminated-by-unicode-separator.js41
-rw-r--r--js/src/tests/non262/syntax/linefeed-at-eof-in-unterminated-string-or-template.js88
-rw-r--r--js/src/tests/non262/syntax/non-simple-with-strict-directive.js140
-rw-r--r--js/src/tests/non262/syntax/omitted-catch-binding.js36
-rw-r--r--js/src/tests/non262/syntax/shell.js0
-rw-r--r--js/src/tests/non262/syntax/statement-versus-statementlistitem.js148
-rw-r--r--js/src/tests/non262/syntax/syntax-parsed-arrow-then-bigint.js40
-rw-r--r--js/src/tests/non262/syntax/syntax-parsed-arrow-then-directive.js72
-rw-r--r--js/src/tests/non262/syntax/syntax-parsed-arrow-then-sourcemap-directive.js63
-rw-r--r--js/src/tests/non262/syntax/unicode_other_id_continue.js45
-rw-r--r--js/src/tests/non262/syntax/unicode_other_id_start.js48
-rw-r--r--js/src/tests/non262/syntax/yield-as-identifier.js54
21 files changed, 1705 insertions, 0 deletions
diff --git a/js/src/tests/non262/syntax/browser.js b/js/src/tests/non262/syntax/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/non262/syntax/browser.js
diff --git a/js/src/tests/non262/syntax/column-numbers-in-long-lines.js b/js/src/tests/non262/syntax/column-numbers-in-long-lines.js
new file mode 100644
index 0000000000..56ce74f86b
--- /dev/null
+++ b/js/src/tests/non262/syntax/column-numbers-in-long-lines.js
@@ -0,0 +1,403 @@
+// |reftest| skip-if(!this.hasOwnProperty('Reflect')||!Reflect.parse) -- uses Reflect.parse(..., { loc: true}) to trigger the column-computing API
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1551916;
+var summary =
+ "Optimize computing a column number as count of code points by caching " +
+ "column numbers (and whether each chunk might contain anything multi-unit) " +
+ "and counting forward from them";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+// Various testing of column-number computations, with respect to counting as
+// code points or units, for very long lines.
+//
+// This test should pass when column numbers are counts of code points (current
+// behavior) or code units (past behavior). It also *should* pass for any valid
+// |TokenStreamAnyChars::ColumnChunkLength| value (it must be at least 4 so that
+// the maximum-length code point in UTF-8/16 will fit in a single chunk),
+// because the value of that constant should be externally invisible save for
+// perf effects. (As a result, recompiling and running this test with a variety
+// of different values assigned to that constant is a good smoke-test of column
+// number computation, that doesn't require having to closely inspect any
+// column-related code.)
+//
+// However, this test is structured on the assumption that that constant has the
+// value 128, in order to exercise in targeted fashion various column number
+// computation edge cases.
+//
+// All this testing *could* be written to not be done with |Reflect.parse| --
+// backwards column computations happen even when compiling normal code, in some
+// cases. But it's much more the exception than the rule. And |Reflect.parse|
+// has *very* predictable column-computation operations (basically start/end
+// coordinates are computed immediately when the end of an AST node is reached)
+// that make it easier to recognize what the exact pattern of computations for
+// which offsets will look like.
+
+// Helper function for checking node location tersely.
+function checkLoc(node, expectedStart, expectedEnd)
+{
+ let start = node.loc.start;
+
+ assertEq(start.line, expectedStart[0],
+ "start line number must be as expected");
+ assertEq(start.column, expectedStart[1],
+ "start column number must be as expected");
+
+ let end = node.loc.end;
+
+ assertEq(end.line, expectedEnd[0], "end line number must be as expected");
+ assertEq(end.column, expectedEnd[1],
+ "end column number must be as expected");
+}
+
+function lengthInCodePoints(str)
+{
+ return [...str].length;
+}
+
+// True if column numbers are counts of code points, false otherwise. This
+// constant can be used to short-circuit testing that isn't point/unit-agnostic.
+const columnsAreCodePoints = (function()
+{
+ var columnTypes = [];
+
+ function checkColumn(actual, expectedPoints, expectedUnits)
+ {
+ if (actual === expectedPoints)
+ columnTypes.push("p");
+ else if (actual === expectedUnits)
+ columnTypes.push("u");
+ else
+ columnTypes.push("x");
+ }
+
+ var script = Reflect.parse('"😱😱😱😱";', { loc: true });
+ assertEq(script.type, "Program");
+ assertEq(script.loc.start.line, 1);
+ assertEq(script.loc.end.line, 1);
+ assertEq(script.loc.start.column, 0);
+ checkColumn(script.loc.end.column, 7, 11);
+
+ var body = script.body;
+ assertEq(body.length, 1);
+
+ var stmt = body[0];
+ assertEq(stmt.type, "ExpressionStatement");
+ assertEq(stmt.loc.start.line, 1);
+ assertEq(stmt.loc.end.line, 1);
+ assertEq(stmt.loc.start.column, 0);
+ checkColumn(stmt.loc.end.column, 7, 11);
+
+ var expr = stmt.expression;
+ assertEq(expr.type, "Literal");
+ assertEq(expr.value, "😱😱😱😱");
+ assertEq(expr.loc.start.line, 1);
+ assertEq(expr.loc.end.line, 1);
+ assertEq(expr.loc.start.column, 0);
+ checkColumn(expr.loc.end.column, 6, 10);
+
+ var checkResult = columnTypes.join(",");
+
+ assertEq(checkResult === "p,p,p" || checkResult === "u,u,u", true,
+ "columns must be wholly code points or units: " + checkResult);
+
+ return checkResult === "p,p,p";
+})();
+
+// Start with some basic sanity-testing, without regard to exactly when, how, or
+// in what order (offset => column) computations are performed.
+function testSimple()
+{
+ if (!columnsAreCodePoints)
+ return;
+
+ // Array elements within the full |simpleCode| string constructed below are
+ // one-element arrays containing the string "😱😱#x" where "#" is the
+ // character that, in C++, could be written as |'(' + i| where |i| is the
+ // index of the array within the outer array.
+ let simpleCodeArray =
+ [
+ 'var Q = [[', // column 0, offset 0
+ // REPEAT
+ '"😱😱(x"],["', // column 10, offset 10
+ '😱😱)x"],["😱', // column 20, offset 22
+ '😱*x"],["😱😱', // column 30, offset 35
+ '+x"],["😱😱,', // column 40, offset 48
+ 'x"],["😱😱-x', // column 50, offset 60
+ '"],["😱😱.x"', // column 60, offset 72
+ '],["😱😱/x"]', // column 70, offset 84
+ ',["😱😱0x"],', // column 80, offset 96
+ '["😱😱1x"],[', // column 90, offset 108
+ // REPEAT
+ '"😱😱2x"],["', // column 100, offset 120 -- chunk limit between "]
+ '😱😱3x"],["😱', // column 110, offset 132
+ '😱4x"],["😱😱', // column 120, offset 145
+ '5x"],["😱😱6', // column 130, offset 158
+ 'x"],["😱😱7x', // column 140, offset 170
+ '"],["😱😱8x"', // column 150, offset 182
+ '],["😱😱9x"]', // column 160, offset 194
+ ',["😱😱:x"],', // column 170, offset 206
+ '["😱😱;x"],[', // column 180, offset 218
+ // REPEAT
+ '"😱😱<x"],["', // column 190, offset 230
+ '😱😱=x"],["😱', // column 200, offset 242
+ '😱>x"],["😱😱', // column 210, offset 255 -- chunk limit splits first 😱
+ '?x"],["😱😱@', // column 220, offset 268
+ 'x"],["😱😱Ax', // column 230, offset 280
+ '"],["😱😱Bx"', // column 240, offset 292
+ '],["😱😱Cx"]', // column 250, offset 304
+ ',["😱😱Dx"],', // column 260, offset 316
+ '["😱😱Ex"],[', // column 270, offset 328
+ // REPEAT
+ '"😱😱Fx"],["', // column 280, offset 340
+ '😱😱Gx"],["😱', // column 290, offset 352
+ '😱Hx"],["😱😱', // column 300, offset 365
+ 'Ix"],["😱😱J', // column 310, offset 378 -- chunk limit between ["
+ 'x"],["😱😱Kx', // column 320, offset 390
+ '"],["😱😱Lx"', // column 330, offset 402
+ '],["😱😱Mx"]', // column 340, offset 414
+ ',["😱😱Nx"],', // column 350, offset 426
+ '["😱😱Ox"]];', // column 360 (10 long), offset 438 (+12 to end)
+ ];
+ let simpleCode = simpleCodeArray.join("");
+
+ // |simpleCode| overall contains this many code points. (This number is
+ // chosen to be several |TokenStreamAnyChars::ColumnChunkLength = 128| chunks
+ // long so that long-line handling is exercised, and the relevant vector
+ // increased in length, for more than one chunk [which would be too short to
+ // trigger chunking] and for more than two chunks [so that vector extension
+ // will eventually occur].)
+ const CodePointLength = 370;
+
+ assertEq(lengthInCodePoints(simpleCode), CodePointLength,
+ "code point count should be correct");
+
+ // |simpleCodeArray| contains this many REPEAT-delimited cycles.
+ const RepetitionNumber = 4;
+
+ // Each cycle consists of this many elements.
+ const ElementsPerCycle = 9;
+
+ // Each element in a cycle has at least this many 😱.
+ const MinFaceScreamingPerElementInCycle = 2;
+
+ // Each cycle consists of many elements with three 😱.
+ const ElementsInCycleWithThreeFaceScreaming = 2;
+
+ // Compute the overall number of UTF-16 code units. (UTF-16 because this is a
+ // JS string as input.)
+ const OverallCodeUnitCount =
+ CodePointLength +
+ RepetitionNumber * (ElementsPerCycle * MinFaceScreamingPerElementInCycle +
+ ElementsInCycleWithThreeFaceScreaming);
+
+ // Code units != code points.
+ assertEq(OverallCodeUnitCount > CodePointLength, true,
+ "string contains code points outside BMP, so length in units " +
+ "exceeds length in points");
+
+ // The overall computed number of code units has this exact numeric value.
+ assertEq(OverallCodeUnitCount, 450,
+ "code unit count computation produces this value");
+
+ // The overall computed number of code units matches the string length.
+ assertEq(simpleCode.length, OverallCodeUnitCount, "string length must match");
+
+ // Evaluate the string.
+ var Q;
+ eval(simpleCode);
+
+ // Verify characteristics of the resulting execution.
+ assertEq(Array.isArray(Q), true);
+
+ const NumArrayElements = 40;
+ assertEq(Q.length, NumArrayElements);
+ Q.forEach((v, i) => {
+ assertEq(Array.isArray(v), true);
+ assertEq(v.length, 1);
+ assertEq(v[0], "😱😱" + String.fromCharCode('('.charCodeAt(0) + i) + "x");
+ });
+
+ let parseTree = Reflect.parse(simpleCode, { loc: true });
+
+ // Check the overall script.
+ assertEq(parseTree.type, "Program");
+ checkLoc(parseTree, [1, 0], [1, 370]);
+ assertEq(parseTree.body.length, 1);
+
+ // Check the coordinates of the declaration.
+ let varDecl = parseTree.body[0];
+ assertEq(varDecl.type, "VariableDeclaration");
+ checkLoc(varDecl, [1, 0], [1, 369]);
+
+ // ...and its initializing expression.
+ let varInit = varDecl.declarations[0].init;
+ assertEq(varInit.type, "ArrayExpression");
+ checkLoc(varInit, [1, 8], [1, 369]);
+
+ // ...and then every literal inside it.
+ assertEq(varInit.elements.length, NumArrayElements, "array literal length");
+
+ const ItemLength = lengthInCodePoints('["😱😱#x"],');
+ assertEq(ItemLength, 9, "item length check");
+
+ for (let i = 0; i < NumArrayElements; i++)
+ {
+ let elem = varInit.elements[i];
+ assertEq(elem.type, "ArrayExpression");
+
+ let startCol = 9 + i * ItemLength;
+ let endCol = startCol + ItemLength - 1;
+ checkLoc(elem, [1, startCol], [1, endCol]);
+
+ let arrayElems = elem.elements;
+ assertEq(arrayElems.length, 1);
+
+ let str = arrayElems[0];
+ assertEq(str.type, "Literal");
+ assertEq(str.value,
+ "😱😱" + String.fromCharCode('('.charCodeAt(0) + i) + "x");
+ checkLoc(str, [1, startCol + 1], [1, endCol - 1]);
+ }
+}
+testSimple();
+
+// Test |ChunkInfo::unitsType() == UnitsType::GuaranteedSingleUnit| -- not that
+// it should be observable, precisely, but effects of mis-applying or
+// miscomputing it would in principle be observable if such were happening.
+// This test also is intended to to be useful for (manually, in a debugger)
+// verifying that the optimization is computed and kicks in correctly.
+function testGuaranteedSingleUnit()
+{
+ if (!columnsAreCodePoints)
+ return;
+
+ // Begin a few array literals in a first chunk to test column computation in
+ // that first chunk.
+ //
+ // End some of them in the first chunk to test columns *before* we know we
+ // have a long line.
+ //
+ // End one array *outside* the first chunk to test a computation inside a
+ // first chunk *after* we know we have a long line and have computed a first
+ // chunk.
+ let mixedChunksCode = "var Z = [ [ [],"; // column 0, offset 0
+ assertEq(mixedChunksCode.length, 15);
+ assertEq(lengthInCodePoints(mixedChunksCode), 15);
+
+ mixedChunksCode +=
+ " ".repeat(128 - mixedChunksCode.length); // column 15, offset 15
+ assertEq(mixedChunksCode.length, 128);
+ assertEq(lengthInCodePoints(mixedChunksCode), 128);
+
+ // Fill out a second chunk as also single-unit, with an outer array literal
+ // that begins in this chunk but finishes in the next (to test column
+ // computation in a prior, guaranteed-single-unit chunk).
+ mixedChunksCode += "[" + "[],".repeat(42) + " "; // column 128, offset 128
+ assertEq(mixedChunksCode.length, 256);
+ assertEq(lengthInCodePoints(mixedChunksCode), 256);
+
+ // Add a third chunk with one last empty nested array literal (so that we
+ // tack on another chunk, and conclude the second chunk is single-unit, before
+ // closing the enclosing array literal). Then close the enclosing array
+ // literal. Finally start a new string literal element containing
+ // multi-unit code points. For good measure, make the chunk *end* in the
+ // middle of such a code point, so that the relevant chunk limit must be
+ // retracted one code unit.
+ mixedChunksCode += "[] ], '" + "😱".repeat(61); // column 256, offset 256
+ assertEq(mixedChunksCode.length, 384 + 1);
+ assertEq(lengthInCodePoints(mixedChunksCode), 324);
+
+ // Wrap things up. Terminate the string, then terminate the nested array
+ // literal to trigger a column computation within the first chunk that can
+ // benefit from knowing the first chunk is all single-unit. Next add a *new*
+ // element to the outermost array, a string literal that contains a line
+ // terminator. The terminator invalidates the column computation cache, so
+ // when the outermost array is closed, location info for it will not hit the
+ // cache. Finally, tack on the terminating semicolon for good measure.
+ mixedChunksCode += "' ], '\u2028' ];"; // column 324, offset 385
+ assertEq(mixedChunksCode.length, 396);
+ assertEq(lengthInCodePoints(mixedChunksCode), 335);
+
+ let parseTree = Reflect.parse(mixedChunksCode, { loc: true });
+
+ // Check the overall script.
+ assertEq(parseTree.type, "Program");
+ checkLoc(parseTree, [1, 0], [2, 4]);
+ assertEq(parseTree.body.length, 1);
+
+ // Check the coordinates of the declaration.
+ let varDecl = parseTree.body[0];
+ assertEq(varDecl.type, "VariableDeclaration");
+ checkLoc(varDecl, [1, 0], [2, 3]);
+
+ // ...and its initializing expression.
+ let varInit = varDecl.declarations[0].init;
+ assertEq(varInit.type, "ArrayExpression");
+ checkLoc(varInit, [1, 8], [2, 3]);
+
+ let outerArrayElements = varInit.elements;
+ assertEq(outerArrayElements.length, 2);
+
+ {
+ // Next the first element, the array inside the initializing expression.
+ let nestedArray = varInit.elements[0];
+ assertEq(nestedArray.type, "ArrayExpression");
+ checkLoc(nestedArray, [1, 10], [1, 327]);
+
+ // Now inside that nested array.
+ let nestedArrayElements = nestedArray.elements;
+ assertEq(nestedArrayElements.length, 3);
+
+ // First the [] in chunk #0
+ let emptyArray = nestedArrayElements[0];
+ assertEq(emptyArray.type, "ArrayExpression");
+ assertEq(emptyArray.elements.length, 0);
+ checkLoc(emptyArray, [1, 12], [1, 14]);
+
+ // Then the big array of empty arrays starting in chunk #1 and ending just
+ // barely in chunk #2.
+ let bigArrayOfEmpties = nestedArrayElements[1];
+ assertEq(bigArrayOfEmpties.type, "ArrayExpression");
+ assertEq(bigArrayOfEmpties.elements.length, 42 + 1);
+ bigArrayOfEmpties.elements.forEach((elem, i) => {
+ assertEq(elem.type, "ArrayExpression");
+ assertEq(elem.elements.length, 0);
+ if (i !== 42)
+ checkLoc(elem, [1, 129 + i * 3], [1, 131 + i * 3]);
+ else
+ checkLoc(elem, [1, 256], [1, 258]); // last element was hand-placed
+ });
+
+ // Then the string literal of multi-unit code points beginning in chunk #2
+ // and ending just into chunk #3 on a second line.
+ let multiUnitStringLiteral = nestedArrayElements[2];
+ assertEq(multiUnitStringLiteral.type, "Literal");
+ assertEq(multiUnitStringLiteral.value, "😱".repeat(61));
+ checkLoc(multiUnitStringLiteral, [1, 262], [1, 325]);
+ }
+
+ {
+ // Finally, the string literal containing a line terminator as element in
+ // the outermost array.
+ let stringLiteralWithEmbeddedTerminator = outerArrayElements[1];
+ assertEq(stringLiteralWithEmbeddedTerminator.type, "Literal");
+ assertEq(stringLiteralWithEmbeddedTerminator.value, "\u2028");
+ checkLoc(stringLiteralWithEmbeddedTerminator, [1, 329], [2, 1]);
+ }
+}
+testGuaranteedSingleUnit();
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
+
+print("Testing completed");
diff --git a/js/src/tests/non262/syntax/declaration-forbidden-in-label.js b/js/src/tests/non262/syntax/declaration-forbidden-in-label.js
new file mode 100644
index 0000000000..53acb39961
--- /dev/null
+++ b/js/src/tests/non262/syntax/declaration-forbidden-in-label.js
@@ -0,0 +1,34 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1288459;
+var summary =
+ "Properly implement the spec's distinctions between StatementListItem and " +
+ "Statement grammar productions and their uses";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+assertThrowsInstanceOf(() => Function("a: let x;"), SyntaxError);
+assertThrowsInstanceOf(() => Function("b: const y = 3;"), SyntaxError);
+assertThrowsInstanceOf(() => Function("c: class z {};"), SyntaxError);
+
+assertThrowsInstanceOf(() => Function("'use strict'; d: function w() {};"), SyntaxError);
+
+// Annex B.3.2 allows this in non-strict mode code.
+Function("e: function x() {};");
+
+assertThrowsInstanceOf(() => Function("f: function* y() {}"), SyntaxError);
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
+
+print("Tests complete");
diff --git a/js/src/tests/non262/syntax/escaped-let-static-identifier.js b/js/src/tests/non262/syntax/escaped-let-static-identifier.js
new file mode 100644
index 0000000000..a4353a516f
--- /dev/null
+++ b/js/src/tests/non262/syntax/escaped-let-static-identifier.js
@@ -0,0 +1,57 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1288460;
+var summary =
+ "|let| and |static| are forbidden as Identifier only in strict mode code, " +
+ "and it's permissible to use them as Identifier (with or without " +
+ "containing escapes) in non-strict code";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+function t(code)
+{
+ var strictSemi = " 'use strict'; " + code;
+ var strictASI = " 'use strict' \n " + code;
+
+ var creationFunctions = [Function];
+ if (typeof evaluate === "function")
+ creationFunctions.push(evaluate);
+ if (typeof parseModule === "function")
+ creationFunctions.push(parseModule);
+
+ for (var func of creationFunctions)
+ {
+ if (typeof parseModule === "function" && func === parseModule)
+ assertThrowsInstanceOf(() => func(code), SyntaxError);
+ else
+ func(code);
+
+ assertThrowsInstanceOf(() => func(strictSemi), SyntaxError);
+ assertThrowsInstanceOf(() => func(strictASI), SyntaxError);
+ }
+}
+
+t("l\\u0065t: 42;");
+t("if (1) l\\u0065t: 42;");
+t("l\\u0065t = 42;");
+t("if (1) l\\u0065t = 42;");
+
+t("st\\u0061tic: 42;");
+t("if (1) st\\u0061tic: 42;");
+t("st\\u0061tic = 42;");
+t("if (1) st\\u0061tic = 42;");
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
+
+print("Tests complete");
diff --git a/js/src/tests/non262/syntax/escaped-strict-reserved-words-and-yield.js b/js/src/tests/non262/syntax/escaped-strict-reserved-words-and-yield.js
new file mode 100644
index 0000000000..9d683bd975
--- /dev/null
+++ b/js/src/tests/non262/syntax/escaped-strict-reserved-words-and-yield.js
@@ -0,0 +1,105 @@
+/* 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/. */
+
+const testCases = [
+ // Label identifier.
+ id => `${id}: ;`,
+
+ // Binding identifier.
+ id => `var ${id};`,
+ id => `let ${id};`,
+ id => `const ${id} = 0;`,
+
+ // Binding identifier in binding pattern.
+ id => `var [${id}] = [];`,
+ id => `var [${id} = 0] = [];`,
+ id => `var [...${id}] = [];`,
+ id => `var {a: ${id}} = {};`,
+ id => `var {${id}} = {};`,
+ id => `var {${id} = 0} = {};`,
+
+ id => `let [${id}] = [];`,
+ id => `let [${id} = 0] = [];`,
+ id => `let [...${id}] = [];`,
+ id => `let {a: ${id}} = {};`,
+ id => `let {${id}} = {};`,
+ id => `let {${id} = 0} = {};`,
+
+ id => `const [${id}] = [];`,
+ id => `const [${id} = 0] = [];`,
+ id => `const [...${id}] = [];`,
+ id => `const {a: ${id}} = {};`,
+ id => `const {${id}} = {};`,
+ id => `const {${id} = 0} = {};`,
+
+ // Identifier reference.
+ id => `void ${id};`,
+];
+
+const strictReservedWords = [
+ "implements",
+ "interface",
+ "package",
+ "private",
+ "protected",
+ "public",
+];
+
+function escapeWord(s) {
+ return "\\u00" + s.charCodeAt(0).toString(16) + s.substring(1);
+}
+
+for (let strictReservedWordOrYield of [...strictReservedWords, "yield"]) {
+ let escapedStrictReservedWordOrYield = escapeWord(strictReservedWordOrYield);
+
+ for (let testCase of testCases) {
+ eval(testCase(strictReservedWordOrYield));
+ eval(testCase(escapedStrictReservedWordOrYield));
+
+ assertThrowsInstanceOf(() => eval(String.raw`
+ "use strict";
+ ${testCase(strictReservedWordOrYield)}
+ `), SyntaxError);
+
+ assertThrowsInstanceOf(() => eval(String.raw`
+ "use strict";
+ ${testCase(escapedStrictReservedWordOrYield)}
+ `), SyntaxError);
+ }
+}
+
+// |yield| is always a keyword in generator functions.
+for (let testCase of testCases) {
+ let yield = "yield";
+ let escapedYield = escapeWord("yield");
+
+ assertThrowsInstanceOf(() => eval(String.raw`
+ function* g() {
+ ${testCase(yield)}
+ }
+ `), SyntaxError);
+
+ assertThrowsInstanceOf(() => eval(String.raw`
+ function* g() {
+ ${testCase(escapedYield)}
+ }
+ `), SyntaxError);
+
+ assertThrowsInstanceOf(() => eval(String.raw`
+ "use strict";
+ function* g() {
+ ${testCase(yield)}
+ }
+ `), SyntaxError);
+
+ assertThrowsInstanceOf(() => eval(String.raw`
+ "use strict";
+ function* g() {
+ ${testCase(escapedYield)}
+ }
+ `), SyntaxError);
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0, "ok");
diff --git a/js/src/tests/non262/syntax/identifier_vertical_tilde.js b/js/src/tests/non262/syntax/identifier_vertical_tilde.js
new file mode 100644
index 0000000000..94fb55f637
--- /dev/null
+++ b/js/src/tests/non262/syntax/identifier_vertical_tilde.js
@@ -0,0 +1,20 @@
+/* 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/. */
+
+// U+2E2F (VERTICAL TILDE) is in Gc=Lm, but also in [:Pattern_Syntax:].
+// http://www.unicode.org/reports/tr31/
+const verticalTilde = 0x2E2F;
+
+// Leading character in identifier.
+assertThrowsInstanceOf(() => eval(`${String.fromCodePoint(verticalTilde)}`), SyntaxError);
+assertThrowsInstanceOf(() => eval(`\\u${verticalTilde.toString(16).padStart(4, "0")}`), SyntaxError);
+assertThrowsInstanceOf(() => eval(`\\u{${verticalTilde.toString(16)}}`), SyntaxError);
+
+// Not leading character in identifier.
+assertThrowsInstanceOf(() => eval(`A${String.fromCodePoint(verticalTilde)}`), SyntaxError);
+assertThrowsInstanceOf(() => eval(`A\\u${verticalTilde.toString(16).padStart(4, "0")}`), SyntaxError);
+assertThrowsInstanceOf(() => eval(`A\\u{${verticalTilde.toString(16)}}`), SyntaxError);
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0, "ok");
diff --git a/js/src/tests/non262/syntax/identifiers-with-extended-unicode-escape.js b/js/src/tests/non262/syntax/identifiers-with-extended-unicode-escape.js
new file mode 100644
index 0000000000..e4b5f46029
--- /dev/null
+++ b/js/src/tests/non262/syntax/identifiers-with-extended-unicode-escape.js
@@ -0,0 +1,208 @@
+/* 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/. */
+
+// Simple cases, not using eval.
+{
+ let \u{61} = 123;
+ assertEq(a, 123);
+
+ let \u{6A} = 123;
+ assertEq(j, 123);
+
+ let a\u{62} = 456;
+ assertEq(ab, 456);
+
+ let \u{63}\u{6b} = 789;
+ assertEq(ck, 789);
+}
+
+const leadingZeros = [0, 1, 2, 3, 4, 100].map(c => "0".repeat(c));
+
+
+// From DerivedCoreProperties.txt (Unicode 9):
+// Derived Property: ID_Start
+// Characters that can start an identifier.
+// Generated from:
+// Lu + Ll + Lt + Lm + Lo + Nl
+// + Other_ID_Start
+// - Pattern_Syntax
+// - Pattern_White_Space
+const idStart = [
+ 0x0041, // LATIN CAPITAL LETTER A, Gc=Lu
+ 0x006A, // LATIN SMALL LETTER J, Gc=Ll
+ 0x00C9, // LATIN CAPITAL LETTER E WITH ACUTE, Gc=Lu
+ 0x00FF, // LATIN SMALL LETTER Y WITH DIAERESIS, Gc=Ll
+ 0x01C5, // LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON, Gc=Lt
+ 0x0294, // LATIN LETTER GLOTTAL STOP, Gc=Lo
+ 0x037A, // GREEK YPOGEGRAMMENI, Gc=Lm
+ 0x16EE, // RUNIC ARLAUG SYMBOL, Gc=Nl
+ 0xFF70, // HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK, Gc=Lm
+];
+
+const idStartSupplemental = [
+ 0x10140, // GREEK ACROPHONIC ATTIC ONE QUARTER, Gc=Nl
+ 0x10300, // OLD ITALIC LETTER A, Gc=Lo
+ 0x10400, // DESERET CAPITAL LETTER LONG I, Gc=Lu
+ 0x10430, // DESERET SMALL LETTER SHORT A, Gc=Ll
+ 0x16B40, // PAHAWH HMONG SIGN VOS SEEV, Gc=Lm
+];
+
+// From PropList.txt (Unicode 9):
+const otherIdStart = [
+ 0x1885, // MONGOLIAN LETTER ALI GALI BALUDA, Gc=Mn
+ 0x1886, // MONGOLIAN LETTER ALI GALI THREE BALUDA, Gc=Mn
+ 0x2118, // SCRIPT CAPITAL P, Gc=Sm
+ 0x212E, // ESTIMATED SYMBOL, Gc=So
+ 0x309B, // KATAKANA-HIRAGANA VOICED SOUND MARK, Gc=Sk
+ 0x309C, // KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK, Gc=Sk
+];
+
+// From DerivedCoreProperties.txt (Unicode 9):
+// Derived Property: ID_Continue
+// Characters that can continue an identifier.
+// Generated from:
+// ID_Start
+// + Mn + Mc + Nd + Pc
+// + Other_ID_Continue
+// - Pattern_Syntax
+// - Pattern_White_Space
+const idContinue = [
+ 0x0030, // DIGIT ZERO, Gc=Nd
+ 0x0300, // COMBINING GRAVE ACCENT, Gc=Mn
+ 0x0660, // ARABIC-INDIC DIGIT ZERO, Gc=Nd
+ 0x0903, // DEVANAGARI SIGN VISARGA, Gc=Mc
+ 0xFF10, // FULLWIDTH DIGIT ZERO, Gc=Nd
+ 0xFF3F, // FULLWIDTH LOW LINE, Gc=Pc
+];
+
+const idContinueSupplemental = [
+ 0x101FD, // PHAISTOS DISC SIGN COMBINING OBLIQUE STROKE, Gc=Mn
+ 0x104A0, // OSMANYA DIGIT ZERO, Gc=Nd
+ 0x11000, // BRAHMI SIGN CANDRABINDU, Gc=Mc
+];
+
+// From PropList.txt (Unicode 9):
+const otherIdContinue = [
+ 0x00B7, // MIDDLE DOT, Gc=Po
+ 0x0387, // GREEK ANO TELEIA, Gc=Po
+ 0x1369, // ETHIOPIC DIGIT ONE, Gc=No
+ 0x136A, // ETHIOPIC DIGIT TWO, Gc=No
+ 0x136B, // ETHIOPIC DIGIT THREE, Gc=No
+ 0x136C, // ETHIOPIC DIGIT FOUR, Gc=No
+ 0x136D, // ETHIOPIC DIGIT FIVE, Gc=No
+ 0x136E, // ETHIOPIC DIGIT SIX, Gc=No
+ 0x136F, // ETHIOPIC DIGIT SEVEN, Gc=No
+ 0x1370, // ETHIOPIC DIGIT EIGHT, Gc=No
+ 0x1371, // ETHIOPIC DIGIT NINE, Gc=No
+ 0x19DA, // NEW TAI LUE THAM DIGIT ONE, Gc=No
+];
+
+for (let ident of [...idStart, ...otherIdStart, ...idStartSupplemental]) {
+ for (let count of leadingZeros) {
+ let zeros = "0".repeat(count);
+ eval(`
+ let \\u{${zeros}${ident.toString(16)}} = 123;
+ assertEq(${String.fromCodePoint(ident)}, 123);
+ `);
+ }
+}
+
+for (let ident of [...idContinue, ...idContinueSupplemental, ...otherIdContinue]) {
+ for (let zeros of leadingZeros) {
+ assertThrowsInstanceOf(() => eval(`\\u{${zeros}${ident.toString(16)}}`), SyntaxError);
+ }
+}
+
+for (let ident of [...idStart, ...otherIdStart, ...idContinue, ...otherIdContinue, ...idStartSupplemental, ...idContinueSupplemental]) {
+ for (let zeros of leadingZeros) {
+ eval(`
+ let A\\u{${zeros}${ident.toString(16)}} = 123;
+ assertEq(${String.fromCodePoint(0x41, ident)}, 123);
+ `);
+ }
+}
+
+
+const notIdentifiers = [
+ 0x0000, // NULL, Gc=Cc
+ 0x000A, // LINE FEED (LF), Gc=Cc
+ 0x005E, // CIRCUMFLEX ACCENT, Gc=Sk
+ 0x00B1, // PLUS-MINUS SIGN, Gc=Sm
+ 0xFF61, // HALFWIDTH IDEOGRAPHIC FULL STOP, Gc=Po
+ 0x10061, // Not assigned.
+ 0x10100, // AEGEAN WORD SEPARATOR LINE, Gc=Po
+ 0x100061, // <Plane 16 Private Use>, Gc=Co
+];
+
+for (let ident of notIdentifiers) {
+ for (let zeros of leadingZeros) {
+ assertThrowsInstanceOf(() => eval(`\\u{${zeros}${ident.toString(16)}}`), SyntaxError);
+ }
+}
+
+
+const incompleteEscapes = [
+ "\\u{",
+ "\\u{6",
+ "\\u{61",
+ "\\u{061",
+ "\\u{0061",
+ "\\u{00061",
+ "\\u{000061",
+ "\\u{0000061",
+
+ "\\u}",
+];
+for (let invalid of incompleteEscapes) {
+ // Ends with EOF.
+ assertThrowsInstanceOf(() => eval(invalid), SyntaxError);
+
+ // Ends with EOL.
+ assertThrowsInstanceOf(() => eval(invalid + "\n"), SyntaxError);
+
+ // Ends with space.
+ assertThrowsInstanceOf(() => eval(invalid + " "), SyntaxError);
+}
+
+
+const invalidEscapes = [
+ // Empty escape.
+ "",
+
+ // Not hexadecimal characters.
+ "\0",
+ "G",
+ "Z",
+ "\uFFFF",
+ "\uDBFF\uDFFF",
+
+ // Has space characters.
+ " 61",
+ "61 ",
+
+ // Has newline characters.
+ "\n61",
+ "61\n",
+
+ // Exceeds 0x10FFFF, six characters.
+ "110000",
+ "110001",
+ "fffffe",
+ "ffffff",
+
+ // Exceeds 0x10FFFF, more than six characters.
+ "10ffff0",
+ "10ffffabcdef",
+];
+
+for (let invalid of invalidEscapes) {
+ for (let zeros of leadingZeros) {
+ assertThrowsInstanceOf(() => eval(`\\u{${zeros}${invalid}}`), SyntaxError);
+ assertThrowsInstanceOf(() => eval(`var \\u{${zeros}${invalid}}`), SyntaxError);
+ }
+}
+
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0, "ok");
diff --git a/js/src/tests/non262/syntax/keyword-unescaped-requirement.js b/js/src/tests/non262/syntax/keyword-unescaped-requirement.js
new file mode 100644
index 0000000000..eb6edd57db
--- /dev/null
+++ b/js/src/tests/non262/syntax/keyword-unescaped-requirement.js
@@ -0,0 +1,68 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1204027;
+var summary =
+ "Escape sequences aren't allowed in bolded grammar tokens (that is, in " +
+ "keywords, possibly contextual keywords)";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+function memberVariants(code)
+{
+ return ["(class { constructor() {} " + code + " });",
+ "({ " + code + " })"];
+}
+
+var badScripts =
+ [
+ "class { st\\u0061tic m() { return 0; } }",
+ "class { st\\u0061tic get foo() { return 0; } }",
+ "class { st\\u0061tic set foo(v) {} }",
+ "class { st\\u0061tic get ['hi']() { return 0; } }",
+ "class { st\\u0061tic set ['hi'](v) {} }",
+ "class { st\\u0061tic get 'hi'() { return 0; } }",
+ "class { st\\u0061tic set 'hi'(v) {} }",
+ "class { st\\u0061tic get 42() { return 0; } }",
+ "class { st\\u0061tic set 42(v) {} }",
+ ...memberVariants("\\u0067et foo() { return 0; }"),
+ ...memberVariants("\\u0073et foo() {}"),
+ ...memberVariants("g\\u0065t foo() { return 0; }"),
+ ...memberVariants("s\\u0065t foo() {}"),
+ ...memberVariants("g\\u0065t ['hi']() { return 0; }"),
+ ...memberVariants("s\\u0065t ['hi']() {}"),
+ ...memberVariants("g\\u0065t 'hi'() { return 0; }"),
+ ...memberVariants("s\\u0065t 'hi'() {}"),
+ ...memberVariants("g\\u0065t 42() { return 0; }"),
+ ...memberVariants("s\\u0065t 42() {}"),
+ "for (var foo o\\u0066 [1]) ;",
+ "for (var foo \\u006ff [1]) ;",
+ "for (var foo i\\u006e [1]) ;",
+ "for (var foo \\u0069n [1]) ;",
+ "function f() { return n\\u0065w.target }",
+ "function f() { return \\u006eew.target }",
+ "function f() { return new.t\\u0061rget }",
+ "function f() { return new.\\u0074arget }",
+ "function f() { return n\\u0065w Array }",
+ "function f() { return \\u006eew Array }",
+ "\\u0064o { } while (0)",
+ "[for (x \\u006ff [1]) x]",
+ "[for (x o\\u0066 [1]) x]",
+ ];
+
+for (var script of badScripts)
+ assertThrowsInstanceOf(() => Function(script), SyntaxError);
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
+
+print("Tests complete");
diff --git a/js/src/tests/non262/syntax/let-as-label.js b/js/src/tests/non262/syntax/let-as-label.js
new file mode 100644
index 0000000000..a01ad556e1
--- /dev/null
+++ b/js/src/tests/non262/syntax/let-as-label.js
@@ -0,0 +1,35 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1288459;
+var summary = "let can't be used as a label in strict mode code";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+Function("let: 42");
+Function("l\\u0065t: 42");
+assertThrowsInstanceOf(() => Function(" 'use strict'; let: 42"), SyntaxError);
+assertThrowsInstanceOf(() => Function(" 'use strict' \n let: 42"), SyntaxError);
+assertThrowsInstanceOf(() => Function(" 'use strict'; l\\u0065t: 42"), SyntaxError);
+assertThrowsInstanceOf(() => Function(" 'use strict' \n l\\u0065t: 42"), SyntaxError);
+
+eval("let: 42");
+eval("l\\u0065t: 42");
+assertThrowsInstanceOf(() => eval(" 'use strict'; let: 42"), SyntaxError);
+assertThrowsInstanceOf(() => eval(" 'use strict' \n let: 42;"), SyntaxError);
+assertThrowsInstanceOf(() => eval(" 'use strict'; l\\u0065t: 42"), SyntaxError);
+assertThrowsInstanceOf(() => eval(" 'use strict' \n l\\u0065t: 42;"), SyntaxError);
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
+
+print("Tests complete");
diff --git a/js/src/tests/non262/syntax/line-number-maintenance-for-identifier-containing-escape-terminated-by-unicode-separator.js b/js/src/tests/non262/syntax/line-number-maintenance-for-identifier-containing-escape-terminated-by-unicode-separator.js
new file mode 100644
index 0000000000..4293cbb0a2
--- /dev/null
+++ b/js/src/tests/non262/syntax/line-number-maintenance-for-identifier-containing-escape-terminated-by-unicode-separator.js
@@ -0,0 +1,41 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 9999999;
+var summary =
+ "Properly maintain the line number when tokenizing identifiers that " +
+ "contain Unicode escapes *and* are terminated by U+2028 LINE SEPARATOR " +
+ "or U+2029 PARAGRAPH SEPARATOR";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+var code = "var a\u0062c = [];\n"; // line 1
+
+for (var i = 0; i < 100; i++)
+ code += "a\\u0062c\u2028a\\u0062c\u2029"; // lines 2..2+200-1
+
+code += "@"; // line 2+200
+
+try
+{
+ eval(code);
+ throw new Error("didn't throw");
+}
+catch (e)
+{
+ assertEq(e.lineNumber, 2 + 200);
+}
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
+
+print("Tests complete");
diff --git a/js/src/tests/non262/syntax/linefeed-at-eof-in-unterminated-string-or-template.js b/js/src/tests/non262/syntax/linefeed-at-eof-in-unterminated-string-or-template.js
new file mode 100644
index 0000000000..461f61ebd3
--- /dev/null
+++ b/js/src/tests/non262/syntax/linefeed-at-eof-in-unterminated-string-or-template.js
@@ -0,0 +1,88 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1476409;
+var summary =
+ "Properly handle the case of U+005C REVERSE SOLIDUS U+000D CARRIAGE RETURN " +
+ "at the end of source text being tokenized, in the middle of a string or " +
+ "template literal, where the next code point in memory (outside the bounds " +
+ "of the source text) is U+000A LINE FEED";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+function expectSyntaxError(code)
+{
+ try
+ {
+ eval(code);
+ throw new Error("didn't throw");
+ }
+ catch (e)
+ {
+ assertEq(e instanceof SyntaxError, true,
+ "got " + e.name + ", expected SyntaxError");
+ }
+}
+
+// The fundamental requirements of this test:
+//
+// 1. The computed string that is eval'd must be a Script that ends in a string
+// literal ending with the code points U+005C REVERSE SOLIDUS U+000D CARRIAGE
+// RETURN.
+// 2. The *memory* that is actually tokenized/parsed by eval must be
+// immediately followed by U+000A LINE FEED.
+//
+// There's only one way to guarantee a U+000A LINE FEED after the source text:
+// compute the source text as a dependent string, of a larger (linear) string.
+// A simple substr will do the trick -- just as long as the substring can't fit
+// in inline storage. 53 in the tests below comfortably exceeds all inline
+// storage limits.
+//
+// One final wrinkle: because we only tokenize/parse two-byte source text right
+// now, ensuring we directly tokenize/parse the dependent string's character
+// data means the dependent string must have two-byte character data, hence the
+// '\u1234' in the strings below.
+
+function singleQuote()
+{
+ var containsBadSingleQuoteLiteral =
+ "\u1234x'01234567890123456789012345678901234567890123456789\\\r\n0123456789";
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ expectSyntaxError(containsBadSingleQuoteLiteral.substr(2, 53));
+}
+singleQuote();
+
+function doubleQuote()
+{
+ var containsBadDoubleQuoteLiteral =
+ "\u1234x\"01234567890123456789012345678901234567890123456789\\\r\n0123456789";
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ expectSyntaxError(containsBadDoubleQuoteLiteral.substr(2, 53));
+}
+doubleQuote();
+
+function template()
+{
+ var containsBadTemplateLiteral =
+ "\u1234x`01234567890123456789012345678901234567890123456789\\\r\n0123456789";
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ expectSyntaxError(containsBadTemplateLiteral.substr(2, 53));
+}
+template();
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
+
+print("Tests complete");
diff --git a/js/src/tests/non262/syntax/non-simple-with-strict-directive.js b/js/src/tests/non262/syntax/non-simple-with-strict-directive.js
new file mode 100644
index 0000000000..a0e5366879
--- /dev/null
+++ b/js/src/tests/non262/syntax/non-simple-with-strict-directive.js
@@ -0,0 +1,140 @@
+/* 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/. */
+
+const testCases = [
+ // Array destructuring.
+ "[]",
+ "[a]",
+ "x, [a]",
+ "[a], x",
+
+ // Array destructuring with defaults.
+ "[a = 0]",
+ "x, [a = 0]",
+ "[a = 0], x",
+
+ // Array destructuring with rest binding identifier.
+ "[...a]",
+ "x, [...a]",
+ "[...a], x",
+
+ // Array destructuring with rest binding pattern.
+ "[...[a]]",
+ "x, [...[a]]",
+ "[...[a]], x",
+
+ // Object destructuring.
+ "{}",
+ "{p: o}",
+ "x, {p: o}",
+ "{p: o}, x",
+
+ // Object destructuring with defaults.
+ "{p: o = 0}",
+ "x, {p: o = 0}",
+ "{p: o = 0}, x",
+
+ // Object destructuring with shorthand identifier form.
+ "{o}",
+ "x, {o}",
+ "{o}, x",
+
+ // Object destructuring with CoverInitName.
+ "{o = 0}",
+ "x, {o = 0}",
+ "{o = 0}, x",
+
+ // Default parameter.
+ "d = 0",
+ "x, d = 0",
+ "d = 0, x",
+
+ // Rest parameter.
+ "...rest",
+ "x, ...rest",
+
+ // Rest parameter with array destructuring.
+ "...[]",
+ "...[a]",
+ "x, ...[]",
+ "x, ...[a]",
+
+ // Rest parameter with object destructuring.
+ "...{}",
+ "...{p: o}",
+ "x, ...{}",
+ "x, ...{p: o}",
+
+ // All non-simple cases combined.
+ "x, d = 123, [a], {p: 0}, ...rest",
+];
+
+const GeneratorFunction = function*(){}.constructor;
+
+const functionDefinitions = [
+ // FunctionDeclaration
+ parameters => `function f(${parameters}) { "use strict"; }`,
+
+ // FunctionExpression
+ parameters => `void function(${parameters}) { "use strict"; };`,
+ parameters => `void function f(${parameters}) { "use strict"; };`,
+
+ // Function constructor
+ parameters => `Function('${parameters}', '"use strict";')`,
+
+ // GeneratorDeclaration
+ parameters => `function* g(${parameters}) { "use strict"; }`,
+
+ // GeneratorExpression
+ parameters => `void function*(${parameters}) { "use strict"; };`,
+ parameters => `void function* g(${parameters}) { "use strict"; };`,
+
+ // GeneratorFunction constructor
+ parameters => `GeneratorFunction('${parameters}', '"use strict";')`,
+
+ // MethodDefinition
+ parameters => `({ m(${parameters}) { "use strict"; } });`,
+ parameters => `(class { m(${parameters}) { "use strict"; } });`,
+ parameters => `class C { m(${parameters}) { "use strict"; } }`,
+
+ // MethodDefinition (constructors)
+ parameters => `(class { constructor(${parameters}) { "use strict"; } });`,
+ parameters => `class C { constructor(${parameters}) { "use strict"; } }`,
+
+ // MethodDefinition (getter)
+ parameters => `({ get m(${parameters}) { "use strict"; } });`,
+ parameters => `(class { get m(${parameters}) { "use strict"; } });`,
+ parameters => `class C { get m(${parameters}) { "use strict"; } }`,
+
+ // MethodDefinition (setter)
+ parameters => `({ set m(${parameters}) { "use strict"; } });`,
+ parameters => `(class { set m(${parameters}) { "use strict"; } });`,
+ parameters => `class C { set m(${parameters}) { "use strict"; } }`,
+
+ // GeneratorMethod
+ parameters => `({ *m(${parameters}) { "use strict"; } });`,
+ parameters => `(class { *m(${parameters}) { "use strict"; } });`,
+ parameters => `class C { *m(${parameters}) { "use strict"; } }`,
+
+ // ArrowFunction
+ parameters => `(${parameters}) => { "use strict"; };`,
+];
+
+for (let nonSimpleParameters of testCases) {
+ for (let def of functionDefinitions) {
+ // Non-strict script code.
+ assertThrowsInstanceOf(() => eval(`
+ ${def(nonSimpleParameters)}
+ `), SyntaxError, def(nonSimpleParameters));
+
+ // Strict script code.
+ assertThrowsInstanceOf(() => eval(`
+ "use strict";
+ ${def(nonSimpleParameters)}
+ `), SyntaxError, `"use strict"; ${def(nonSimpleParameters)}`);
+ }
+}
+
+if (typeof reportCompare === 'function')
+ reportCompare(0, 0);
diff --git a/js/src/tests/non262/syntax/omitted-catch-binding.js b/js/src/tests/non262/syntax/omitted-catch-binding.js
new file mode 100644
index 0000000000..86b3c0f4f8
--- /dev/null
+++ b/js/src/tests/non262/syntax/omitted-catch-binding.js
@@ -0,0 +1,36 @@
+var state = 'initial';
+try {
+ throw new Error('caught');
+ state = 'unreachable';
+} catch { // Note the lack of a binding
+ assertEq(state, 'initial');
+ state = 'caught';
+}
+assertEq(state, 'caught');
+
+
+var sigil1 = {};
+try {
+ throw sigil1;
+} catch (e) {
+ assertEq(e, sigil1);
+}
+
+
+var sigil2 = {};
+var reached = false;
+try {
+ try {
+ throw sigil1;
+ } catch {
+ reached = true;
+ } finally {
+ throw sigil2;
+ }
+} catch (e) {
+ assertEq(e, sigil2);
+}
+assertEq(reached, true);
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0);
diff --git a/js/src/tests/non262/syntax/shell.js b/js/src/tests/non262/syntax/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/non262/syntax/shell.js
diff --git a/js/src/tests/non262/syntax/statement-versus-statementlistitem.js b/js/src/tests/non262/syntax/statement-versus-statementlistitem.js
new file mode 100644
index 0000000000..d0565353dd
--- /dev/null
+++ b/js/src/tests/non262/syntax/statement-versus-statementlistitem.js
@@ -0,0 +1,148 @@
+// |reftest| skip-if(!xulRuntime.shell) -- needs evaluate, parseModule
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1288459;
+var summary =
+ "Properly implement the spec's distinctions between StatementListItem and " +
+ "Statement grammar productions and their uses";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+var counter = 0;
+
+// Test all the different contexts that expect a Statement.
+
+function contextAcceptsStatement(pat)
+{
+ var goodCode =
+`function f${counter++}()
+{
+ ${pat.replace("@@@", "let \n {} \n ;")}
+}
+`;
+
+ evaluate(goodCode);
+
+ var badCode =
+`function f${counter++}()
+{
+ ${pat.replace("@@@", "let {} \n ;")}
+}
+`;
+
+ try
+ {
+ evaluate(badCode);
+ throw new Error("didn't throw");
+ }
+ catch (e)
+ {
+ assertEq(e instanceof SyntaxError, true,
+ "didn't get a syntax error, instead got: " + e);
+ }
+}
+
+contextAcceptsStatement("if (0) @@@");
+contextAcceptsStatement("if (0) ; else @@@");
+contextAcceptsStatement("if (0) ; else if (0) @@@");
+contextAcceptsStatement("if (0) ; else if (0) ; else @@@");
+// Can't test do-while loops this way because the Statement isn't in trailing
+// position, so 'let' followed by newline *must* be followed by 'while'.
+contextAcceptsStatement("while (1) @@@");
+contextAcceptsStatement("for (1; 1; 0) @@@");
+contextAcceptsStatement("for (var x; 1; 0) @@@");
+contextAcceptsStatement("for (let x; 1; 0) @@@");
+contextAcceptsStatement("for (const x = 1; 1; 0) @@@");
+contextAcceptsStatement("for (x in 0) @@@");
+contextAcceptsStatement("for (var x in 0) @@@");
+contextAcceptsStatement("for (let x in 0) @@@");
+contextAcceptsStatement("for (const x in 0) @@@");
+contextAcceptsStatement("for (x of []) @@@");
+contextAcceptsStatement("for (var x of []) @@@");
+contextAcceptsStatement("for (let x of []) @@@");
+contextAcceptsStatement("for (const x of []) @@@");
+contextAcceptsStatement("with (1) @@@");
+contextAcceptsStatement("foo: @@@");
+
+// Test all the different contexts that expect a StatementListItem.
+
+function contextAcceptsStatementListItem(pat)
+{
+ var goodCode =
+`function f${counter++}()
+{
+ ${pat.replace("@@@", "let \n [] = [] ;")}
+}
+`;
+
+ evaluate(goodCode);
+
+ var moarGoodCode = pat.replace("@@@", "let \n [] = [] ;");
+ evaluate(moarGoodCode);
+
+ var goodModuleCode = pat.replace("@@@", "let \n [] = [] ;");
+ parseModule(goodModuleCode);
+
+ var badCode =
+`function f${counter++}()
+{
+ ${pat.replace("@@@", "let \n let = 3 ;")}
+}
+`;
+
+ try
+ {
+ evaluate(badCode);
+ throw new Error("didn't throw");
+ }
+ catch (e)
+ {
+ assertEq(e instanceof SyntaxError, true,
+ "didn't get a syntax error, instead got: " + e);
+ }
+}
+
+contextAcceptsStatementListItem("{ @@@ }");
+contextAcceptsStatementListItem("switch (1) { case 1: @@@ }");
+contextAcceptsStatementListItem("switch (1) { default: @@@ }");
+contextAcceptsStatementListItem("switch (1) { case 0: case 1: @@@ }");
+contextAcceptsStatementListItem("switch (1) { case 0: break; case 1: @@@ }");
+contextAcceptsStatementListItem("switch (1) { default: @@@ case 2: }");
+contextAcceptsStatementListItem("try { @@@ } catch (e) { }");
+contextAcceptsStatementListItem("try { @@@ } finally { }");
+contextAcceptsStatementListItem("try { @@@ } catch (e) { } finally { }");
+contextAcceptsStatementListItem("try { } catch (e) { @@@ }");
+contextAcceptsStatementListItem("try { } finally { @@@ }");
+contextAcceptsStatementListItem("try { } catch (e) { @@@ } finally { }");
+contextAcceptsStatementListItem("try { } catch (e) { } finally { @@@ }");
+contextAcceptsStatementListItem("_ => { @@@ }");
+contextAcceptsStatementListItem("function q() { @@@ }");
+contextAcceptsStatementListItem("function* q() { @@@ }");
+contextAcceptsStatementListItem("(function() { @@@ })");
+contextAcceptsStatementListItem("(function*() { @@@ })");
+contextAcceptsStatementListItem("({ *q() { @@@ } })");
+contextAcceptsStatementListItem("({ q() { @@@ } })");
+contextAcceptsStatementListItem("({ get q() { @@@ } })");
+contextAcceptsStatementListItem("({ set q(v) { @@@ } })");
+contextAcceptsStatementListItem("(class { f() { @@@ } })");
+contextAcceptsStatementListItem("(class { static f() { @@@ } })");
+contextAcceptsStatementListItem("(class { static *f() { @@@ } })");
+contextAcceptsStatementListItem("(class { static get f() { @@@ } })");
+contextAcceptsStatementListItem("(class { static set f(v) { @@@ } })");
+contextAcceptsStatementListItem("(class { static() { @@@ } })");
+contextAcceptsStatementListItem("@@@");
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
+
+print("Tests complete");
diff --git a/js/src/tests/non262/syntax/syntax-parsed-arrow-then-bigint.js b/js/src/tests/non262/syntax/syntax-parsed-arrow-then-bigint.js
new file mode 100644
index 0000000000..d7a4ccd092
--- /dev/null
+++ b/js/src/tests/non262/syntax/syntax-parsed-arrow-then-bigint.js
@@ -0,0 +1,40 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1596706;
+var summary =
+ "Properly evaluate a bigint literal that's initially tokenized by a syntax " +
+ "parser (because the bigint literal appears immediately after an arrow " +
+ "function with expression body)";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+// block followed by semicolon
+assertEq(eval(`x=>{};
+17n`), 17n);
+
+// block not followed by semicolon
+assertEq(eval(`x=>{}
+42n`), 42n);
+
+// expr followed by semicolon
+assertEq(eval(`x=>y;
+8675309n`), 8675309n);
+
+// expr not followed by semicolon
+assertEq(eval(`x=>y
+78051120n`), 78051120n);
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
+
+print("Tests complete");
diff --git a/js/src/tests/non262/syntax/syntax-parsed-arrow-then-directive.js b/js/src/tests/non262/syntax/syntax-parsed-arrow-then-directive.js
new file mode 100644
index 0000000000..9be9c3fca6
--- /dev/null
+++ b/js/src/tests/non262/syntax/syntax-parsed-arrow-then-directive.js
@@ -0,0 +1,72 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1596706;
+var summary =
+ "Properly apply a directive comment that's only tokenized by a syntax " +
+ "parser (because the directive comment appears immediately after an arrow " +
+ "function with expression body)";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+var stack;
+
+function reset()
+{
+ stack = "";
+}
+
+function assertStackContains(needle, msg)
+{
+ assertEq(stack.indexOf(needle) >= 0, true,
+ `stack should contain '${needle}': ${msg}`);
+}
+
+Object.defineProperty(this, "detectSourceURL", {
+ get() {
+ stack = new Error().stack;
+ return 17;
+ }
+});
+
+// block followed by semicolon
+reset();
+assertEq(eval(`x=>{};
+//# sourceURL=http://example.com/foo.js
+detectSourceURL`), 17);
+assertStackContains("http://example.com/foo.js", "block, semi");
+
+// block not followed by semicolon
+reset();
+assertEq(eval(`x=>{}
+//# sourceURL=http://example.com/bar.js
+detectSourceURL`), 17);
+assertStackContains("http://example.com/bar.js", "block, not semi");
+
+// expr followed by semicolon
+reset();
+assertEq(eval(`x=>y;
+//# sourceURL=http://example.com/baz.js
+detectSourceURL`), 17);
+assertStackContains("http://example.com/baz.js", "expr, semi");
+
+// expr not followed by semicolon
+reset();
+assertEq(eval(`x=>y
+//# sourceURL=http://example.com/quux.js
+detectSourceURL`), 17);
+assertStackContains("http://example.com/quux.js", "expr, not semi");
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
+
+print("Tests complete");
diff --git a/js/src/tests/non262/syntax/syntax-parsed-arrow-then-sourcemap-directive.js b/js/src/tests/non262/syntax/syntax-parsed-arrow-then-sourcemap-directive.js
new file mode 100644
index 0000000000..71e2b2bdaf
--- /dev/null
+++ b/js/src/tests/non262/syntax/syntax-parsed-arrow-then-sourcemap-directive.js
@@ -0,0 +1,63 @@
+// |reftest| skip-if(!xulRuntime.shell) -- needs Debugger
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1596706;
+var summary =
+ "Properly apply a source map directive that's only tokenized by a syntax " +
+ "parser (because the directive comment appears immediately after an arrow " +
+ "function with expression body)";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+var g = newGlobal({newCompartment: true});
+var dbg = new Debugger(g);
+
+var script;
+dbg.onDebuggerStatement = function (frame)
+{
+ script = frame.script;
+};
+
+function checkSourceMapFor(arrowFunc, sourceMap)
+{
+ g.evaluate(`${arrowFunc}
+//# sourceMappingURL=${sourceMap}
+debugger;`);
+
+ assertEq(script instanceof Debugger.Script, true,
+ "expected a Debugger.Script for " + arrowFunc);
+
+ var source = script.source;
+ assertEq(source instanceof Debugger.Source, true,
+ "expected a Debugger.Source for " + arrowFunc);
+
+ assertEq(source.sourceMapURL, sourceMap,
+ "unexpected source map for " + arrowFunc);
+}
+
+// block followed by semicolon
+checkSourceMapFor("x=>{};", "http://example.com/foo.js");
+
+// block not followed by semicolon
+checkSourceMapFor("x=>{}", "http://example.com/bar.js");
+
+// expr followed by semicolon
+checkSourceMapFor("x=>y;", "http://example.com/baz.js");
+
+// expr not followed by semicolon
+checkSourceMapFor("x=>y", "http://example.com/bar.js");
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
+
+print("Tests complete");
diff --git a/js/src/tests/non262/syntax/unicode_other_id_continue.js b/js/src/tests/non262/syntax/unicode_other_id_continue.js
new file mode 100644
index 0000000000..03dd09b71b
--- /dev/null
+++ b/js/src/tests/non262/syntax/unicode_other_id_continue.js
@@ -0,0 +1,45 @@
+/* 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/. */
+
+// From PropList.txt (Unicode 9):
+const otherIdContinue = [
+ 0x00B7, // MIDDLE DOT, Gc=Po
+ 0x0387, // GREEK ANO TELEIA, Gc=Po
+ 0x1369, // ETHIOPIC DIGIT ONE, Gc=No
+ 0x136A, // ETHIOPIC DIGIT TWO, Gc=No
+ 0x136B, // ETHIOPIC DIGIT THREE, Gc=No
+ 0x136C, // ETHIOPIC DIGIT FOUR, Gc=No
+ 0x136D, // ETHIOPIC DIGIT FIVE, Gc=No
+ 0x136E, // ETHIOPIC DIGIT SIX, Gc=No
+ 0x136F, // ETHIOPIC DIGIT SEVEN, Gc=No
+ 0x1370, // ETHIOPIC DIGIT EIGHT, Gc=No
+ 0x1371, // ETHIOPIC DIGIT NINE, Gc=No
+ 0x19DA, // NEW TAI LUE THAM DIGIT ONE, Gc=No
+];
+
+// Leading character in identifier.
+for (let ident of [...otherIdContinue]) {
+ assertThrowsInstanceOf(() => eval(`${String.fromCodePoint(ident)}`), SyntaxError);
+ assertThrowsInstanceOf(() => eval(`\\u${ident.toString(16).padStart(4, "0")}`), SyntaxError);
+ assertThrowsInstanceOf(() => eval(`\\u{${ident.toString(16)}}`), SyntaxError);
+}
+
+// Not leading character in identifier.
+for (let ident of [...otherIdContinue]) {
+ eval(`
+ let A${String.fromCodePoint(ident)} = 123;
+ assertEq(${String.fromCodePoint(0x41, ident)}, 123);
+ `);
+ eval(`
+ let A\\u${ident.toString(16).padStart(4, "0")} = 123;
+ assertEq(${String.fromCodePoint(0x41, ident)}, 123);
+ `);
+ eval(`
+ let A\\u{${ident.toString(16)}} = 123;
+ assertEq(${String.fromCodePoint(0x41, ident)}, 123);
+ `);
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0, "ok");
diff --git a/js/src/tests/non262/syntax/unicode_other_id_start.js b/js/src/tests/non262/syntax/unicode_other_id_start.js
new file mode 100644
index 0000000000..86790dd00e
--- /dev/null
+++ b/js/src/tests/non262/syntax/unicode_other_id_start.js
@@ -0,0 +1,48 @@
+/* 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/. */
+
+// From PropList.txt (Unicode 9):
+const otherIdStart = [
+ 0x1885, // MONGOLIAN LETTER ALI GALI BALUDA, Gc=Mn
+ 0x1886, // MONGOLIAN LETTER ALI GALI THREE BALUDA, Gc=Mn
+ 0x2118, // SCRIPT CAPITAL P, Gc=Sm
+ 0x212E, // ESTIMATED SYMBOL, Gc=So
+ 0x309B, // KATAKANA-HIRAGANA VOICED SOUND MARK, Gc=Sk
+ 0x309C, // KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK, Gc=Sk
+];
+
+// Leading character in identifier.
+for (let ident of otherIdStart) {
+ eval(`
+ let ${String.fromCodePoint(ident)} = 123;
+ assertEq(${String.fromCodePoint(ident)}, 123);
+ `);
+ eval(`
+ let \\u${ident.toString(16).padStart(4, "0")} = 123;
+ assertEq(${String.fromCodePoint(ident)}, 123);
+ `);
+ eval(`
+ let \\u{${ident.toString(16)}} = 123;
+ assertEq(${String.fromCodePoint(ident)}, 123);
+ `);
+}
+
+// Not leading character in identifier.
+for (let ident of otherIdStart) {
+ eval(`
+ let A${String.fromCodePoint(ident)} = 123;
+ assertEq(${String.fromCodePoint(0x41, ident)}, 123);
+ `);
+ eval(`
+ let A\\u${ident.toString(16).padStart(4, "0")} = 123;
+ assertEq(${String.fromCodePoint(0x41, ident)}, 123);
+ `);
+ eval(`
+ let A\\u{${ident.toString(16)}} = 123;
+ assertEq(${String.fromCodePoint(0x41, ident)}, 123);
+ `);
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0, "ok");
diff --git a/js/src/tests/non262/syntax/yield-as-identifier.js b/js/src/tests/non262/syntax/yield-as-identifier.js
new file mode 100644
index 0000000000..665b0b11c4
--- /dev/null
+++ b/js/src/tests/non262/syntax/yield-as-identifier.js
@@ -0,0 +1,54 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1288459;
+var summary = "|yield| is sometimes a valid identifier";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+function t(code)
+{
+ var strictSemi = " 'use strict'; " + code;
+ var strictASI = " 'use strict' \n " + code;
+
+ var creationFunctions = ["Function"];
+ if (typeof evaluate === "function")
+ creationFunctions.push("evaluate");
+ if (typeof parseModule === "function")
+ creationFunctions.push("parseModule");
+
+ for (var func of creationFunctions)
+ {
+ var g = newGlobal();
+ var f = g[func];
+
+ if (func === "parseModule")
+ assertThrowsInstanceOf(() => f(code), g.SyntaxError);
+ else
+ f(code);
+
+ assertThrowsInstanceOf(() => f(strictSemi), g.SyntaxError);
+ assertThrowsInstanceOf(() => f(strictASI), g.SyntaxError);
+ }
+}
+
+t("var yield = 3;");
+t("let yield = 3;");
+t("const yield = 3;");
+t("for (var yield = 3; ; ) break;");
+t("for (let yield = 3; ; ) break;");
+t("for (const yield = 3; ; ) break;");
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
+
+print("Tests complete");