diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /js/src/tests/non262/syntax | |
parent | Initial commit. (diff) | |
download | thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/tests/non262/syntax')
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"); |