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/lexical | |
parent | Initial commit. (diff) | |
download | thunderbird-upstream.tar.xz thunderbird-upstream.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 '')
55 files changed, 2201 insertions, 0 deletions
diff --git a/js/src/tests/non262/lexical-conventions/browser.js b/js/src/tests/non262/lexical-conventions/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/lexical-conventions/browser.js diff --git a/js/src/tests/non262/lexical-conventions/lexical-001.js b/js/src/tests/non262/lexical-conventions/lexical-001.js new file mode 100644 index 0000000000..308b7a2a82 --- /dev/null +++ b/js/src/tests/non262/lexical-conventions/lexical-001.js @@ -0,0 +1,146 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +/* + * Date: 26 November 2000 + * + *SUMMARY: Testing numeric literals that begin with 0. + *This test arose from Bugzilla bug 49233. + *The best explanation is from jsscan.c: + * + * "We permit 08 and 09 as decimal numbers, which makes + * our behaviour a superset of the ECMA numeric grammar. + * We might not always be so permissive, so we warn about it." + * + *Thus an expression 010 will evaluate, as always, as an octal (to 8). + *However, 018 will evaluate as a decimal, to 18. Even though the + *user began the expression as an octal, he later used a non-octal + *digit. We forgive this and assume he intended a decimal. If the + *JavaScript "strict" option is set though, we will give a warning. + */ + +//------------------------------------------------------------------------------------------------- +var BUGNUMBER = '49233'; +var summary = 'Testing numeric literals that begin with 0'; +var statprefix = 'Testing '; +var quote = "'"; +var asString = new Array(); +var actual = new Array(); +var expect = new Array(); + + + asString[0]='01' + actual[0]=01 + expect[0]=1 + + asString[1]='07' + actual[1]=07 + expect[1]=7 + + asString[2]='08' + actual[2]=08 + expect[2]=8 + + asString[3]='09' + actual[3]=09 + expect[3]=9 + + asString[4]='010' + actual[4]=010 + expect[4]=8 + + asString[5]='017' + actual[5]=017 + expect[5]=15 + + asString[6]='018' + actual[6]=018 + expect[6]=18 + + asString[7]='019' + actual[7]=019 + expect[7]=19 + + asString[8]='079' + actual[8]=079 + expect[8]=79 + + asString[9]='0079' + actual[9]=0079 + expect[9]=79 + + asString[10]='099' + actual[10]=099 + expect[10]=99 + + asString[11]='0099' + actual[11]=0099 + expect[11]=99 + + asString[12]='000000000077' + actual[12]=000000000077 + expect[12]=63 + + asString[13]='000000000078' + actual[13]=000000000078 + expect[13]=78 + + asString[14]='0000000000770000' + actual[14]=0000000000770000 + expect[14]=258048 + + asString[15]='0000000000780000' + actual[15]=0000000000780000 + expect[15]=780000 + + asString[16]='0765432198' + actual[16]=0765432198 + expect[16]=765432198 + + asString[17]='00076543219800' + actual[17]=00076543219800 + expect[17]=76543219800 + + asString[18]='0000001001007' + actual[18]=0000001001007 + expect[18]=262663 + + asString[19]='0000001001009' + actual[19]=0000001001009 + expect[19]=1001009 + + asString[20]='070' + actual[20]=070 + expect[20]=56 + + asString[21]='080' + actual[21]=080 + expect[21]=80 + + + +//------------------------------------------------------------------------------------------------- + test(); +//------------------------------------------------------------------------------------------------- + + +function showStatus(msg) +{ + return (statprefix + quote + msg + quote); +} + + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + + for (i=0; i !=asString.length; i++) + { + reportCompare (expect[i], actual[i], showStatus(asString[i])); + } +} diff --git a/js/src/tests/non262/lexical-conventions/regress-177314.js b/js/src/tests/non262/lexical-conventions/regress-177314.js new file mode 100644 index 0000000000..ce677b77a4 --- /dev/null +++ b/js/src/tests/non262/lexical-conventions/regress-177314.js @@ -0,0 +1,73 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * + * Date: 30 Oct 2002 + * SUMMARY: '\400' should lex as a 2-digit octal escape + '0' + * See http://bugzilla.mozilla.org/show_bug.cgi?id=177314 + * + * Bug was that Rhino interpreted '\400' as a 3-digit octal escape. As such + * it is invalid, since octal escapes may only run from '\0' to '\377'. But + * the lexer should interpret this as '\40' + '0' instead, and throw no error. + * + */ +//----------------------------------------------------------------------------- +var UBound = 0; +var BUGNUMBER = 177314; +var summary = "'\\" + "400' should lex as a 2-digit octal escape + '0'"; +var status = ''; +var statusitems = []; +var actual = ''; +var actualvalues = []; +var expect= ''; +var expectedvalues = []; + + +// the last valid octal escape is '\377', which should equal hex escape '\xFF' +status = inSection(1); +actual = '\377'; +expect = '\xFF'; +addThis(); + +// now exercise the lexer by going one higher in the last digit +status = inSection(2); +actual = '\378'; +expect = '\37' + '8'; +addThis(); + +// trickier: 400 is a valid octal number, but '\400' isn't a valid octal escape +status = inSection(3); +actual = '\400'; +expect = '\40' + '0'; +addThis(); + + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + + + +function addThis() +{ + statusitems[UBound] = status; + actualvalues[UBound] = actual; + expectedvalues[UBound] = expect; + UBound++; +} + + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus(summary); + + for (var i=0; i<UBound; i++) + { + reportCompare(expectedvalues[i], actualvalues[i], statusitems[i]); + } +} diff --git a/js/src/tests/non262/lexical-conventions/regress-469940.js b/js/src/tests/non262/lexical-conventions/regress-469940.js new file mode 100644 index 0000000000..d8169671c6 --- /dev/null +++ b/js/src/tests/non262/lexical-conventions/regress-469940.js @@ -0,0 +1,35 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 469940; +var summary = 'Do not insert semi-colon after var with multiline initializer'; +var actual = ''; +var expect = ''; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + expect = 'SyntaxError: unexpected token: identifier'; + + var s = 'var x = function f() { \n return 42; } print(x);'; + + try + { + eval(s); + } + catch(ex) + { + actual = ex + ''; + } + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/lexical-conventions/shell.js b/js/src/tests/non262/lexical-conventions/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/lexical-conventions/shell.js diff --git a/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-arguments.js b/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-arguments.js new file mode 100644 index 0000000000..efc263b66f --- /dev/null +++ b/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-arguments.js @@ -0,0 +1,10 @@ +// Test that Annex B function interaction with 'arguments'. + +(function() { + assertEq(typeof arguments, "object"); + { function arguments() {} } + assertEq(typeof arguments, "function"); +})(); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-eval.js b/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-eval.js new file mode 100644 index 0000000000..9f4e4d7fe8 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-eval.js @@ -0,0 +1,51 @@ +var log = ""; + +function f() { + log += g(); + function g() { return "outer-g"; } + + var o = { g: function () { return "with-g"; } }; + with (o) { + // Annex B.3.3.3 says g should be set on the nearest VariableEnvironment, + // and so should not change o.g. + eval(`{ + function g() { return "eval-g"; } + }`); + } + + log += g(); + log += o.g(); +} + +f(); + +function h() { + eval(` + // Should return true, as var bindings introduced by eval are configurable. + log += (delete q); + { + function q() { log += "q"; } + // Should return false, as lexical bindings introduced by eval are not + // configurable. + log += (delete q); + } + `); + return q; +} + +h()(); + +function f2() { + // Should not throw, just simply not synthesize an Annex B var in the eval + // because there's an outer const. + eval("{ function a() {} }"); + const a = 1; +} + +function f3() { + // As above, but for let. + eval("{ function a() {} }"); + let a; +} + +reportCompare(log, "outer-geval-gwith-gtruefalseq"); diff --git a/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-generators.js b/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-generators.js new file mode 100644 index 0000000000..dbef82bcfc --- /dev/null +++ b/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-generators.js @@ -0,0 +1,26 @@ +// Tests by André Bargull <andrebargull@googlemail.com> + +// Annex B.3.3.1 +function f1() { + { function* g() {} } + assertEq(typeof g, "undefined"); +} +f1(); + +// Annex B.3.3.2 +{ function* g() {} } +assertEq(typeof g, "undefined"); + +// Annex B.3.3.3 +function f2() { + eval("{ function* g() {} }"); + assertEq(typeof g, "undefined"); +} +f2(); + +// Annex B.3.3.3 +eval("{ function* g() {} }"); +assertEq(typeof g, "undefined"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-if.js b/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-if.js new file mode 100644 index 0000000000..ce63aeeea2 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-if.js @@ -0,0 +1,42 @@ +var log = ""; + +function f(x) { + if (x) + function g() { return "g0"; } + else + function g() { return "g1"; } + + log += g(); + + if (x) + function g() { return "g2"; } + else { + } + + log += g(); + + if (x) { + } else + function g() { return "g3"; } + + log += g(); + + if (x) + function g() { return "g4"; } + + log += g(); +} + +f(true); +f(false); + +try { + eval(` + if (1) + l: function foo() {} + `); +} catch (e) { + log += "e"; +} + +reportCompare(log, "g0g2g2g4g1g1g3g3e"); diff --git a/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-label.js b/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-label.js new file mode 100644 index 0000000000..0fe9f45fca --- /dev/null +++ b/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-label.js @@ -0,0 +1,43 @@ +function expectSyntaxError(str) { + var threwSyntaxError; + try { + eval(str); + } catch (e) { + threwSyntaxError = e instanceof SyntaxError; + } + assertEq(threwSyntaxError, true); + + try { + eval('"use strict";' + str); + } catch (e) { + threwSyntaxError = e instanceof SyntaxError; + } + assertEq(threwSyntaxError, true); +} + +function expectSloppyPass(str) { + eval(str); + + try { + eval('"use strict";' + str); + } catch (e) { + threwSyntaxError = e instanceof SyntaxError; + } + assertEq(threwSyntaxError, true); +} + +expectSloppyPass(`l: function f1() {}`); +expectSloppyPass(`l0: l: function f1() {}`); +expectSloppyPass(`{ f1(); l: function f1() {} }`); +expectSloppyPass(`{ f1(); l0: l: function f1() {} }`); +expectSloppyPass(`{ f1(); l: function f1() { return 42; } } assertEq(f1(), 42);`); +expectSloppyPass(`eval("fe(); l: function fe() {}")`); +expectSyntaxError(`if (1) l: function f2() {}`); +expectSyntaxError(`if (1) {} else l: function f3() {}`); +expectSyntaxError(`do l: function f4() {} while (0)`); +expectSyntaxError(`while (0) l: function f5() {}`); +expectSyntaxError(`for (;;) l: function f6() {}`); +expectSloppyPass(`switch (1) { case 1: l: function f7() {} }`); +expectSloppyPass(`switch (1) { case 1: assertEq(f8(), 'f8'); case 2: l: function f8() { return 'f8'; } } assertEq(f8(), 'f8');`); + +reportCompare(0, 0); diff --git a/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-notapplicable.js b/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-notapplicable.js new file mode 100644 index 0000000000..4c5e2527a8 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-notapplicable.js @@ -0,0 +1,14 @@ +// Test that functions in block that do not exhibit Annex B do not override +// previous functions that do exhibit Annex B. + +function f() { + var outerX; + { function x() {1} outerX = x; } + { { function x() {2}; } let x; } + { let x; { function x() {3}; } } + assertEq(x, outerX); +} +f(); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-parameter.js b/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-parameter.js new file mode 100644 index 0000000000..ae7fbe879c --- /dev/null +++ b/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-parameter.js @@ -0,0 +1,21 @@ +// Annex B.3.3.1 disallows Annex B lexical function behavior when redeclaring a +// parameter. + +(function(f) { + if (true) function f() { } + assertEq(f, 123); +}(123)); + +(function(f) { + { function f() { } } + assertEq(f, 123); +}(123)); + +(function(f = 123) { + assertEq(f, 123); + { function f() { } } + assertEq(f, 123); +}()); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-property.js b/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-property.js new file mode 100644 index 0000000000..a295de5081 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-property.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!xulRuntime.shell) + +// Define a global getter without a setter. +Object.defineProperty(this, "x", { + get: function () { return "get-x"; }, + configurable: true +}); + +// Simulate loading a 2nd script with evaluate, else we would DEFVAR the x and +// the above defineProperty would fail in trying to redefine a non-configurable +// property on the global. +evaluate(`{ + function x() { return "fun-x"; } +}`); + +// Annex B is supposed to be like an assignment. Should not blow away the +// existing setter-less getter. +reportCompare(x, "get-x"); diff --git a/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-same-name.js b/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-same-name.js new file mode 100644 index 0000000000..b89f91c59a --- /dev/null +++ b/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-same-name.js @@ -0,0 +1,7 @@ +{ + function f() { return "inner"; } +} + +function f() { return "outer"; } + +reportCompare(f(), "inner"); diff --git a/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-with.js b/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-with.js new file mode 100644 index 0000000000..d9bdd98dc9 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b-with.js @@ -0,0 +1,18 @@ +var o = { f: "string-f" }; +with (o) { + var desc = Object.getOwnPropertyDescriptor(this, "f"); + assertEq(desc.value, undefined); + assertEq(desc.writable, true); + assertEq(desc.enumerable, true); + assertEq(desc.configurable, false); + function f() { + return "fun-f"; + } +} + +// Annex B explicitly assigns to the nearest VariableEnvironment, so the +// with-object "o" should have its property unchanged. +assertEq(o.f, "string-f"); +assertEq(f(), "fun-f"); + +reportCompare(true, true) diff --git a/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b.js b/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b.js new file mode 100644 index 0000000000..16c40774ca --- /dev/null +++ b/js/src/tests/non262/lexical-environment/block-scoped-functions-annex-b.js @@ -0,0 +1,31 @@ +var log = ""; + +log += typeof f; + +{ + log += f(); + + function f() { + return "f1"; + } +} + +log += f(); + +function g() { + log += typeof h; + + { + log += h(); + + function h() { + return "h1"; + } + } + + log += h(); +} + +g(); + +reportCompare(log, "undefinedf1f1undefinedh1h1"); diff --git a/js/src/tests/non262/lexical-environment/block-scoped-functions-deprecated-redecl.js b/js/src/tests/non262/lexical-environment/block-scoped-functions-deprecated-redecl.js new file mode 100644 index 0000000000..af32be5d3d --- /dev/null +++ b/js/src/tests/non262/lexical-environment/block-scoped-functions-deprecated-redecl.js @@ -0,0 +1,78 @@ +{ + assertEq(f(), 4); + function f() { return 3; } + assertEq(f(), 4); + function f() { return 4; } + assertEq(f(), 4); +} + +// Annex B still works. +assertEq(f(), 4); + +// The same thing with labels. +{ + assertEq(f(), 4); + function f() { return 3; } + assertEq(f(), 4); + l: function f() { return 4; } + assertEq(f(), 4); +} + +// Annex B still works. +assertEq(f(), 4); + +function test() { + { + assertEq(f(), 2); + function f() { return 1; } + assertEq(f(), 2); + function f() { return 2; } + assertEq(f(), 2); + } + + // Annex B still works. + assertEq(f(), 2); +} + +test(); + +var log = ''; + +try { + // Strict mode still cannot redeclare. + eval(`"use strict"; + { + function f() { } + function f() { } + }`); +} catch (e) { + assertEq(e instanceof SyntaxError, true); + log += 'e'; +} + +try { + // Redeclaring an explicitly 'let'-declared binding doesn't work. + eval(`{ + let x = 42; + function x() {} + }`); +} catch (e) { + assertEq(e instanceof SyntaxError, true); + log += 'e'; +} + +try { + // Redeclaring an explicitly 'const'-declared binding doesn't work. + eval(`{ + const x = 42; + function x() {} + }`); +} catch (e) { + assertEq(e instanceof SyntaxError, true); + log += 'e'; +} + +assertEq(log, 'eee'); + +if ('reportCompare' in this) + reportCompare(true, true); diff --git a/js/src/tests/non262/lexical-environment/block-scoped-functions-hoisted-tdz.js b/js/src/tests/non262/lexical-environment/block-scoped-functions-hoisted-tdz.js new file mode 100644 index 0000000000..e5f9baf446 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/block-scoped-functions-hoisted-tdz.js @@ -0,0 +1,30 @@ +var log = ""; +try { + (function() { + { + let y = f(); + function f() { y; } + } + })() +} catch (e) { + log += e instanceof ReferenceError; +} + +try { + function f() { + switch (1) { + case 0: + let x; + case 1: + (function() { x; })(); + } + } + f(); +} catch (e) { + log += e instanceof ReferenceError; +} + +assertEq(log, "truetrue"); + +if ("reportCompare" in this) + reportCompare(true, true); diff --git a/js/src/tests/non262/lexical-environment/block-scoped-functions-strict.js b/js/src/tests/non262/lexical-environment/block-scoped-functions-strict.js new file mode 100644 index 0000000000..2b780d7dd1 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/block-scoped-functions-strict.js @@ -0,0 +1,45 @@ +"use strict" + +var log = ""; + +function f() { + return "f0"; +} + +log += f(); + +{ + log += f(); + + function f() { + return "f1"; + } + + log += f(); +} + +log += f(); + +function g() { + function h() { + return "h0"; + } + + log += h(); + + { + log += h(); + + function h() { + return "h1"; + } + + log += h(); + } + + log += h(); +} + +g(); + +reportCompare(log, "f0f1f1f0h0h1h1h0"); diff --git a/js/src/tests/non262/lexical-environment/browser.js b/js/src/tests/non262/lexical-environment/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/browser.js diff --git a/js/src/tests/non262/lexical-environment/bug-1216623.js b/js/src/tests/non262/lexical-environment/bug-1216623.js new file mode 100644 index 0000000000..cbdbe9722e --- /dev/null +++ b/js/src/tests/non262/lexical-environment/bug-1216623.js @@ -0,0 +1,19 @@ +// Scoping in the head of for(let;;) statements. + +let x = 0; +for (let i = 0, a = () => i; i < 4; i++) { + assertEq(i, x++); + assertEq(a(), 0); +} +assertEq(x, 4); + +x = 11; +let q = 0; +for (let {[++q]: r} = [0, 11, 22], s = () => r; r < 13; r++) { + assertEq(r, x++); + assertEq(s(), 11); +} +assertEq(x, 13); +assertEq(q, 1); + +reportCompare(0, 0); diff --git a/js/src/tests/non262/lexical-environment/catch-body.js b/js/src/tests/non262/lexical-environment/catch-body.js new file mode 100644 index 0000000000..43b9d2bfe3 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/catch-body.js @@ -0,0 +1,19 @@ +function f() { + var probeParam, probeBlock; + let x = 'outside'; + + try { + throw []; + } catch ([_ = probeParam = function() { return x; }]) { + probeBlock = function() { return x; }; + let x = 'inside'; + } + + assertEq(probeBlock(), 'inside'); + assertEq(probeParam(), 'outside'); +} + +f(); + +if (typeof reportCompare === 'function') + reportCompare(true, true); diff --git a/js/src/tests/non262/lexical-environment/const-declaration-in-for-loop.js b/js/src/tests/non262/lexical-environment/const-declaration-in-for-loop.js new file mode 100644 index 0000000000..4001220ba3 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/const-declaration-in-for-loop.js @@ -0,0 +1,88 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +var gTestfile = "const-declaration-in-for-loop.js"; +//----------------------------------------------------------------------------- +var BUGNUMBER = 1146644; +var summary = + "Don't crash compiling a non-body-level for-loop whose loop declaration is " + + "a const"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +// Don't attempt execution as a script if we can't properly emulate it. We +// could perhaps use eval, but eval, while also doing global execution, is its +// own can of messiness. Ongoing work on for-loop scoping for lexical +// declarations will likely make these tests redundant with other tests to be +// added, anyway, in the very short term. +var executeGlobalScript = typeof evaluate === "function" + ? evaluate + : function(s) {}; + +for (const a1 = 3; false; ) + continue; + +Function(`for (const a2 = 3; false; ) + continue; + `)(); + +if (true) +{ + for (const a3 = 3; false; ) + continue; +} + +Function(`if (true) + { + for (const a4 = 3; false; ) + continue; + }`)(); + +executeGlobalScript(`for (const a5 of []) + continue;`); + +Function(`for (const a6 of []) + continue;`)(); + +executeGlobalScript(`if (true) + { + for (const a7 of []) + continue; + }`); + +Function(`if (true) + { + for (const a8 of []) + continue; + }`)(); + +executeGlobalScript(`for (const a9 in {}) + continue;`); + +Function(`for (const a10 in {}) + continue;`)(); + +executeGlobalScript(`if (true) + { + for (const a11 in {}) + continue; + }`); + +Function(`if (true) + { + for (const a12 in {}) + continue; + }`)(); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/lexical-environment/eval-has-lexical-environment.js b/js/src/tests/non262/lexical-environment/eval-has-lexical-environment.js new file mode 100644 index 0000000000..16ffb06d19 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/eval-has-lexical-environment.js @@ -0,0 +1,45 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +var gTestfile = "eval-has-lexical-environment.js" +//----------------------------------------------------------------------------- +var BUGNUMBER = 1193583; +var summary = + "Eval always has a lexical environment"; + +/************** + * BEGIN TEST * + **************/ + +eval(` +let foo = 42; +const kay = foo; +var bar = 84; +function f() { + return foo + kay; +} + `); + +(1, eval)(` +let foo2 = 42; +const kay2 = foo2; +`); + +// Lexical declarations should not have escaped eval. +assertEq(typeof foo, "undefined"); +assertEq(typeof kay, "undefined"); +assertEq(typeof foo2, "undefined"); +assertEq(typeof kay2, "undefined"); + +// Eval'd functions can close over lexical bindings. +assertEq(f(), 84); + +// Var can escape direct eval. +assertEq(bar, 84); + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/lexical-environment/eval-nondefinable-function.js b/js/src/tests/non262/lexical-environment/eval-nondefinable-function.js new file mode 100644 index 0000000000..bd5dcf7978 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/eval-nondefinable-function.js @@ -0,0 +1,10 @@ +try { + eval("var shouldNotBeDefined1; function NaN(){}; var shouldNotBeDefined2;"); +} catch (e) { +} + +assertEq(Object.getOwnPropertyDescriptor(this, 'shouldNotBeDefined2'), undefined); +assertEq(Object.getOwnPropertyDescriptor(this, 'shouldNotBeDefined1'), undefined); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/lexical-environment/for-loop-with-bindings-added-at-runtime.js b/js/src/tests/non262/lexical-environment/for-loop-with-bindings-added-at-runtime.js new file mode 100644 index 0000000000..f6abf3d0cd --- /dev/null +++ b/js/src/tests/non262/lexical-environment/for-loop-with-bindings-added-at-runtime.js @@ -0,0 +1,125 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +var gTestfile = "for-loop-with-bindings-added-at-runtime.js"; +//----------------------------------------------------------------------------- +var BUGNUMBER = 1149797; +var summary = + "Don't assert when freshening the scope chain for a for-loop whose head " + + "contains a lexical declaration, where the loop body might add more " + + "bindings at runtime"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +for (let x = 0; x < 9; ++x) + eval("var y"); + +{ + for (let x = 0; x < 9; ++x) + eval("var y"); +} + +function f1() +{ + for (let x = 0; x < 9; ++x) + eval("var y"); +} +f1(); + +function f2() +{ + { + for (let x = 0; x < 9; ++x) + eval("var y"); + } +} +f2(); + +for (let x = 0; x < 9; ++x) +{ + // deliberately inside a block statement + eval("var y"); +} + +{ + for (let x = 0; x < 9; ++x) + { + // deliberately inside a block statement + eval("var y"); + } +} + +function g1() +{ + for (let x = 0; x < 9; ++x) + { + // deliberately inside a block statement + eval("var y"); + } +} +g1(); + +function g2() +{ + { + for (let x = 0; x < 9; ++x) + { + // deliberately inside a block statement + eval("var y"); + } + } +} +g2(); + +for (let x = 0; x < 9; ++x) { + (function() { + eval("var y"); + })(); +} + +{ + for (let x = 0; x < 9; ++x) + { + // deliberately inside a block statement + (function() { + eval("var y"); + })(); + } +} + +function h1() +{ + for (let x = 0; x < 9; ++x) + { + // deliberately inside a block statement + (function() { + eval("var y"); + })(); + } +} +h1(); + +function h2() +{ + { + for (let x = 0; x < 9; ++x) + { + // deliberately inside a block statement + (function() { eval("var y"); })(); + } + } +} +h2(); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/lexical-environment/for-loop.js b/js/src/tests/non262/lexical-environment/for-loop.js new file mode 100644 index 0000000000..60e3799d62 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/for-loop.js @@ -0,0 +1,121 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +var gTestfile = "for-loop.js"; +//----------------------------------------------------------------------------- +var BUGNUMBER = 985733; +var summary = + "ES6 for-loop semantics for for(;;) loops whose heads contain lexical " + "declarations"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +function isError(code, type) +{ + try + { + Function(code); + throw new Error("didn't throw"); + } + catch (e) + { + assertEq(e instanceof type, true, + "unexpected error for `" + code + "`: got " + e); + } +} + +function isOK(code) +{ + Function(code); +} + +isError("for (const x; ; ) ;", SyntaxError); +isError("for (const x = 5, y; ; ) ;", SyntaxError); +isError("for (const [z]; ; ) ;", SyntaxError); +//isError("for (const [z, z]; ; ) ;", SyntaxError); +//isError("for (const [z, z] = [0, 1]; ; ) ;", SyntaxError); + +isOK("for (let x; ; ) ;"); +isOK("for (let x = 5, y; ; ) ;"); + +// I'm fairly sure this is supposed to work: the negative-lookahead rules in +// IterationStatement ensure that |for (let| *always* is a loop header starting +// with a lexical declaration. But I'm not 100% certain, so these tests might +// need to be fixed when we implement the negative-lookahead restrictions. +isOK("for (let [z] = [3]; ; ) ;"); +isError("for (let [z, z]; ; ) ;", SyntaxError); // because missing initializer + +isError("for (let [z, z] = [0, 1]; ; ) ;", SyntaxError); + +// A for-loop with lexical declarations, with a mixture of bindings that are and +// aren't aliased. (The mixture stress-tests any code that incorrectly assumes +// all bindings are aliased.) +var funcs = []; +for (let [i, j, k] = [0, 1, 2]; i < 10; i++) + funcs.push(() => i); + +assertEq(funcs[0](), 0); +assertEq(funcs[1](), 1); +assertEq(funcs[2](), 2); +assertEq(funcs[3](), 3); +assertEq(funcs[4](), 4); +assertEq(funcs[5](), 5); +assertEq(funcs[6](), 6); +assertEq(funcs[7](), 7); +assertEq(funcs[8](), 8); +assertEq(funcs[9](), 9); + +var outer = "OUTER V IGNORE"; +var save; +for (let outer = (save = function() { return outer; }); ; ) + break; +assertEq(save(), save); + +var funcs = []; +function t(i, name, expect) +{ + assertEq(funcs[i].name, name); + assertEq(funcs[i](), expect); +} + +if (save() !== "OUTER V IGNORE") +{ + var v = "OUTER V IGNORE"; + var i = 0; + for (let v = (funcs.push(function init() { return v; }), + 0); + v = (funcs.push(function test() { return v; }), + v + 1); + v = (funcs.push(function incr() { return v; }), + v + 1)) + { + v = (funcs.push(function body() { return v; }), + v + 1); + i++; + if (i >= 3) + break; + } + t(0, "init", 0); + t(1, "test", 2); + t(2, "body", 2); + t(3, "incr", 5); + t(4, "test", 5); + t(5, "body", 5); + t(6, "incr", 8); + t(7, "test", 8); + t(8, "body", 8); + assertEq(funcs.length, 9); +} + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/lexical-environment/implicit-this-in-with.js b/js/src/tests/non262/lexical-environment/implicit-this-in-with.js new file mode 100644 index 0000000000..7c112e2447 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/implicit-this-in-with.js @@ -0,0 +1,18 @@ +// Test that callees that resolve to bindings on the global object or the +// global lexical environment get an 'undefined' this inside with scopes. + +let g = function () { "use strict"; assertEq(this, undefined); } +function f() { "use strict"; assertEq(this, undefined); } + +with ({}) { + // f is resolved on the global object + f(); + // g is resolved on the global lexical environment + g(); +} + +f(); +g(); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/lexical-environment/nondefinable-function-same-script.js b/js/src/tests/non262/lexical-environment/nondefinable-function-same-script.js new file mode 100644 index 0000000000..5d569fe4a0 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/nondefinable-function-same-script.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!xulRuntime.shell) + +function assertEvaluateAndIndirectEvalThrows(str) { + assertThrowsInstanceOf(() => evaluate(str), TypeError); + assertThrowsInstanceOf(() => (1,eval)(str), TypeError); +} + +// Regular vars +assertEvaluateAndIndirectEvalThrows(`var NaN; function NaN() {}`); + +// for-of vars +assertEvaluateAndIndirectEvalThrows(`for (var NaN of []); function NaN() {}`); + +// Annex B.3.3 synthesized vars +assertEvaluateAndIndirectEvalThrows(`{ function NaN() {} } function NaN() {}`); + +// Non-data properties +Object.defineProperty(this, 'foo', { set: function() {} }); +assertEvaluateAndIndirectEvalThrows(`var foo; function foo() {}`); +assertEvaluateAndIndirectEvalThrows(`for (var foo of []); function foo() {}`); +assertEvaluateAndIndirectEvalThrows(`{ function foo() {} } function foo() {}`); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/lexical-environment/redeclaring-global-properties.js b/js/src/tests/non262/lexical-environment/redeclaring-global-properties.js new file mode 100644 index 0000000000..7fb2f82db1 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/redeclaring-global-properties.js @@ -0,0 +1,64 @@ +// |reftest| skip-if(!xulRuntime.shell) -- needs evaluate +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +// Attempting to lexically redefine a var is a syntax error. +evaluate("var a;"); +assertThrowsInstanceOf(() => evaluate("let a;"), SyntaxError); + +// Attempting to lexically redefine a configurable global property that's not a +// var is okay. +this.b = 42; +assertEq(b, 42); +evaluate("let b = 17;"); +assertEq(b, 17); + +// Attempting to lexically redefine a configurable global property that wasn't +// a var initially but was later declared as one, isn't okay. +this.c = 8675309; +assertEq(c, 8675309); +evaluate("var c;"); +assertThrowsInstanceOf(() => evaluate("let c;"), SyntaxError); + +// Attempting to lexically redefine a var added by eval code isn't okay. +assertEq(typeof d, "undefined"); +eval("var d = 33;"); +assertEq(d, 33); +assertThrowsInstanceOf(() => evaluate("let d;"), SyntaxError); + +// Attempting to lexically redefine a var added by eval code, then deleted *as a +// name*, is okay. (The |var| will add the name to the global environment +// record's [[VarNames]], but deletion will go through the global environment +// record's DeleteBinding and so will remove it.) +assertEq(typeof e, "undefined"); +eval("var e = 'ohia';"); +assertEq(e, "ohia"); +delete e; +assertEq(this.hasOwnProperty("e"), false); +evaluate("let e = 3.141592654;"); +assertEq(e, 3.141592654); + +// Attempting to lexically redefine a var added by eval code, then deleted *as a +// property*, isn't okay. (Deletion by property doesn't go through the global +// environment record's DeleteBinding algorithm, and so the name isn't removed +// from [[VarNames]].) And it remains non-okay even if the var is subsequently +// deleted as a name, because if the property doesn't exist, it's not removed +// from [[VarNames]]. But if we add the global property again and then delete +// by name, it *will* get removed from [[VarNames]]. +assertEq(typeof f, "undefined"); +eval("var f = 8675309;"); +assertEq(f, 8675309); +delete this.f; +assertEq(this.hasOwnProperty("f"), false); +assertThrowsInstanceOf(() => evaluate("let f;"), SyntaxError); +delete f; +assertThrowsInstanceOf(() => evaluate("let f;"), SyntaxError); +this.f = 999; +assertThrowsInstanceOf(() => evaluate("let f;"), SyntaxError); +delete f; +evaluate("let f;"); + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/lexical-environment/shell.js b/js/src/tests/non262/lexical-environment/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/shell.js diff --git a/js/src/tests/non262/lexical-environment/unscopables-basics.js b/js/src/tests/non262/lexical-environment/unscopables-basics.js new file mode 100644 index 0000000000..4032b27746 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/unscopables-basics.js @@ -0,0 +1,22 @@ +// Basics of @@unscopables support. + +// In with(obj), if obj[@@unscopables][id] is truthy, then the identifier id +// is not present as a binding in the with-block's scope. +var x = "global"; +with ({x: "with", [Symbol.unscopables]: {x: true}}) + assertEq(x, "global"); + +// But if obj[@@unscopables][id] is false or not present, there is a binding. +with ({y: "with", z: "with", [Symbol.unscopables]: {y: false}}) { + assertEq(y, "with"); + assertEq(z, "with"); +} + +// ToBoolean(obj[@@unscopables][id]) determines whether there's a binding. +let someValues = [0, -0, NaN, "", undefined, null, "x", {}, []]; +for (let v of someValues) { + with ({x: "with", [Symbol.unscopables]: {x: v}}) + assertEq(x, v ? "global" : "with"); +} + +reportCompare(0, 0); diff --git a/js/src/tests/non262/lexical-environment/unscopables-closures.js b/js/src/tests/non262/lexical-environment/unscopables-closures.js new file mode 100644 index 0000000000..bdade1f113 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/unscopables-closures.js @@ -0,0 +1,23 @@ +// @@unscopables continues to work after exiting the relevant `with` block, +// if the environment is captured by a closure. + +let env = { + x: 9000, + [Symbol.unscopables]: {x: true} +}; + +function make_adder(x) { + with (env) + return function (y) { return x + y; }; +} +assertEq(make_adder(3)(10), 13); + +// Same test, but with a bunch of different parts for bad luck +let x = 500; +function make_adder_with_eval() { + with (env) + return eval('y => eval("x + y")'); +} +assertEq(make_adder_with_eval()(10), 510); + +reportCompare(0, 0); diff --git a/js/src/tests/non262/lexical-environment/unscopables-const.js b/js/src/tests/non262/lexical-environment/unscopables-const.js new file mode 100644 index 0000000000..7e1d0e07c6 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/unscopables-const.js @@ -0,0 +1,8 @@ +// @@unscopables prevents a property from having any effect on assigning to a +// const binding (which is an error). + +const x = 1; +with ({x: 1, [Symbol.unscopables]: {x: true}}) + assertThrowsInstanceOf(() => {x = 2;}, TypeError); + +reportCompare(0, 0); diff --git a/js/src/tests/non262/lexical-environment/unscopables-delete.js b/js/src/tests/non262/lexical-environment/unscopables-delete.js new file mode 100644 index 0000000000..3cd296f4b9 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/unscopables-delete.js @@ -0,0 +1,27 @@ +// If obj[@@unscopables][id], then `delete id` works across `with (obj)` scope. + +this.niche = 7; +let obj = { niche: 8, [Symbol.unscopables]: { niche: true } }; +with (obj) { + delete niche; +} + +assertEq(obj.niche, 8); +assertEq("niche" in this, false); + +// Same thing, but delete a variable introduced by sloppy direct eval. +this.niche = 9; +function f() { + eval("var niche = 10;"); + with (obj) { + assertEq(niche, 10); + delete niche; + } + assertEq(niche, 9); +} + +// Of course none of this affects a qualified delete. +assertEq(delete this.niche, true); +assertEq("niche" in this, false); + +reportCompare(0, 0); diff --git a/js/src/tests/non262/lexical-environment/unscopables-getters.js b/js/src/tests/non262/lexical-environment/unscopables-getters.js new file mode 100644 index 0000000000..1360787983 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/unscopables-getters.js @@ -0,0 +1,41 @@ +// @@unscopables checks can call getters. + +// The @@unscopables property itself can be a getter. +let hit1 = 0; +let x = "global x"; +let env1 = { + x: "env1.x", + get [Symbol.unscopables]() { + hit1++; + return {x: true}; + } +}; +with (env1) + assertEq(x, "global x"); +assertEq(hit1, 1); + +// It can throw; the exception is propagated out. +function Fit() {} +with ({x: 0, get [Symbol.unscopables]() { throw new Fit; }}) + assertThrowsInstanceOf(() => x, Fit); + +// Individual properties on the @@unscopables object can have getters. +let hit2 = 0; +let env2 = { + x: "env2.x", + [Symbol.unscopables]: { + get x() { + hit2++; + return true; + } + } +}; +with (env2) + assertEq(x, "global x"); +assertEq(hit2, 1); + +// And they can throw. +with ({x: 0, [Symbol.unscopables]: {get x() { throw new Fit; }}}) + assertThrowsInstanceOf(() => x, Fit); + +reportCompare(0, 0); diff --git a/js/src/tests/non262/lexical-environment/unscopables-global.js b/js/src/tests/non262/lexical-environment/unscopables-global.js new file mode 100644 index 0000000000..1aa4a52bda --- /dev/null +++ b/js/src/tests/non262/lexical-environment/unscopables-global.js @@ -0,0 +1,18 @@ +// @@unscopables does not affect the global environment. + +this.x = "global property x"; +let y = "global lexical y"; +this[Symbol.unscopables] = {x: true, y: true}; +assertEq(x, "global property x"); +assertEq(y, "global lexical y"); +assertEq(eval("x"), "global property x"); +assertEq(eval("y"), "global lexical y"); + +// But it does affect `with` statements targeting the global object. +{ + let x = "local x"; + with (this) + assertEq(x, "local x"); +} + +reportCompare(0, 0); diff --git a/js/src/tests/non262/lexical-environment/unscopables-ignored.js b/js/src/tests/non262/lexical-environment/unscopables-ignored.js new file mode 100644 index 0000000000..08b042a3b5 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/unscopables-ignored.js @@ -0,0 +1,22 @@ +// In these cases, @@unscopables should not be consulted. + +// Because obj has no properties `assertEq` or `x`, +// obj[@@unscopables] is not checked here: +var obj = { + get [Symbol.unscopables]() { + throw "tried to read @@unscopables"; + } +}; +var x = 3; +with (obj) + assertEq(x, 3); + +// If @@unscopables is present but not an object, it is ignored: +for (let nonObject of [undefined, null, "nothing", Symbol.for("moon")]) { + let y = 4; + let obj2 = {[Symbol.unscopables]: nonObject, y: 5}; + with (obj2) + assertEq(y, 5); +} + +reportCompare(0, 0); diff --git a/js/src/tests/non262/lexical-environment/unscopables-miss.js b/js/src/tests/non262/lexical-environment/unscopables-miss.js new file mode 100644 index 0000000000..b86d510787 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/unscopables-miss.js @@ -0,0 +1,7 @@ +// Trying to access a binding that doesn't exist due to @@unscopables +// is a ReferenceError. + +with ({x: 1, [Symbol.unscopables]: {x: true}}) + assertThrowsInstanceOf(() => x, ReferenceError); + +reportCompare(0, 0); diff --git a/js/src/tests/non262/lexical-environment/unscopables-mutation-frozen.js b/js/src/tests/non262/lexical-environment/unscopables-mutation-frozen.js new file mode 100644 index 0000000000..632785c05a --- /dev/null +++ b/js/src/tests/non262/lexical-environment/unscopables-mutation-frozen.js @@ -0,0 +1,18 @@ +// When env[@@unscopables].x changes, bindings can appear even if env is inextensible. + +let x = "global"; +let unscopables = {x: true}; +let env = Object.create(null); +env[Symbol.unscopables] = unscopables; +env.x = "object"; +Object.freeze(env); + +for (let i = 0; i < 1004; i++) { + if (i === 1000) + unscopables.x = false; + with (env) { + assertEq(x, i < 1000 ? "global" : "object"); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/non262/lexical-environment/unscopables-mutation.js b/js/src/tests/non262/lexical-environment/unscopables-mutation.js new file mode 100644 index 0000000000..2f35e1dd3c --- /dev/null +++ b/js/src/tests/non262/lexical-environment/unscopables-mutation.js @@ -0,0 +1,44 @@ +// When obj[@@unscopables].x changes, bindings appear and disappear accordingly. + +let x = "global"; +function getX() { return x; } + +let unscopables = {x: true}; +let obj = {x: "obj", [Symbol.unscopables]: unscopables}; + +with (obj) { + assertEq(x, "global"); + x = "global-1"; + assertEq(x, "global-1"); + assertEq(obj.x, "obj"); + + unscopables.x = false; // suddenly x appears in the with-environment + + assertEq(x, "obj"); + x = "obj-1"; + assertEq(getX(), "global-1"); // unchanged + assertEq(obj.x, "obj-1"); + + unscopables.x = true; // *poof* + + assertEq(x, "global-1"); + x = "global-2"; + assertEq(getX(), "global-2"); + assertEq(obj.x, "obj-1"); // unchanged + + // The determination of which binding is assigned happens when the LHS of + // assignment is evaluated, before the RHS. This is observable if we make + // the binding appear or disappear during evaluation of the RHS, before + // assigning. + x = (unscopables.x = false, "global-3"); + assertEq(getX(), "global-3"); + assertEq(obj.x, "obj-1"); + + x = (unscopables.x = true, "obj-2"); + assertEq(getX(), "global-3"); + assertEq(obj.x, "obj-2"); +} + +assertEq(x, "global-3"); + +reportCompare(0, 0); diff --git a/js/src/tests/non262/lexical-environment/unscopables-proto.js b/js/src/tests/non262/lexical-environment/unscopables-proto.js new file mode 100644 index 0000000000..dbbfb712d3 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/unscopables-proto.js @@ -0,0 +1,39 @@ +// @@unscopables treats properties found on prototype chains the same as other +// properties. + +const x = "global x"; +const y = "global y"; + +// obj[@@unscopables].x works when obj.x is inherited via the prototype chain. +let proto = {x: "object x", y: "object y"}; +let env = Object.create(proto); +env[Symbol.unscopables] = {x: true, y: false}; +with (env) { + assertEq(x, "global x"); + assertEq(delete x, false); + assertEq(y, "object y"); +} +assertEq(env.x, "object x"); + +// @@unscopables works if is inherited via the prototype chain. +env = { + x: "object", + [Symbol.unscopables]: {x: true, y: true} +}; +for (let i = 0; i < 50; i++) + env = Object.create(env); +env.y = 1; +with (env) { + assertEq(x, "global x"); + assertEq(y, "global y"); +} + +// @@unscopables works if the obj[@@unscopables][id] property is inherited. +env = { + x: "object", + [Symbol.unscopables]: Object.create({x: true}) +}; +with (env) + assertEq(x, "global x"); + +reportCompare(0, 0); diff --git a/js/src/tests/non262/lexical-environment/unscopables-proxy.js b/js/src/tests/non262/lexical-environment/unscopables-proxy.js new file mode 100644 index 0000000000..fcf241ee46 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/unscopables-proxy.js @@ -0,0 +1,46 @@ +// Object operations are performed in the right order, as observed by proxies. + +let log = []; +function LoggingProxyHandlerWrapper(name, handler={}) { + return new Proxy(handler, { + get(t, id) { + let method = handler[id]; + return function (...args) { + log.push([name + "." + id, ...args.filter(v => typeof v !== "object")]); + if (method === undefined) + return Reflect[id].apply(null, args); + return method.apply(this, args); + }; + } + }); +} + +function LoggingProxy(name, target) { + return new Proxy(target, new LoggingProxyHandlerWrapper(name)); +} + +let proto = {x: 44}; +let proto_proxy = new LoggingProxy("proto", proto); +let unscopables = {x: true}; +let unscopables_proxy = new LoggingProxy("unscopables", {x: true}); +let env = Object.create(proto_proxy, { + [Symbol.unscopables]: { value: unscopables_proxy } +}); +let env_proxy = new LoggingProxy("env", env); + +let x = 11; +function f() { + with (env_proxy) + return x; +} + +assertEq(f(), 11); + +assertDeepEq(log, [ + ["env.has", "x"], + ["proto.has", "x"], + ["env.get", Symbol.unscopables], + ["unscopables.get", "x"] +]); + +reportCompare(0, 0); diff --git a/js/src/tests/non262/lexical-environment/unscopables-strict.js b/js/src/tests/non262/lexical-environment/unscopables-strict.js new file mode 100644 index 0000000000..fd0413ed7b --- /dev/null +++ b/js/src/tests/non262/lexical-environment/unscopables-strict.js @@ -0,0 +1,32 @@ +// Strict assignment to the name of a property that's masked by @@unscopables +// throws a ReferenceError. + +let env = {k: 1}; +let f; +with (env) { + f = function () { + "use strict"; + k = 2; + }; +} + +f(); +assertEq(env.k, 2); + +env[Symbol.unscopables] = {k: true}; +assertThrowsInstanceOf(f, ReferenceError); + +// @@unscopables is tested when the LHS of assignment is evaluated, so there is +// no effect on the assignment if it is changed while evaluating the RHS. +let g; +with (env) { + g = function () { + "use strict"; + k = (env[Symbol.unscopables].k = true, 3); + } +} +env[Symbol.unscopables].k = false; +g(); +assertEq(env.k, 3); + +reportCompare(0, 0); diff --git a/js/src/tests/non262/lexical-environment/unscopables-tdz.js b/js/src/tests/non262/lexical-environment/unscopables-tdz.js new file mode 100644 index 0000000000..ce6b1df929 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/unscopables-tdz.js @@ -0,0 +1,9 @@ +// Accessing an uninitialized variable due to @@unscopables is still a ReferenceError. + +with ({x: 1, [Symbol.unscopables]: {x: true}}) + assertThrowsInstanceOf(() => x, ReferenceError); + +let x; + +reportCompare(0, 0); + diff --git a/js/src/tests/non262/lexical-environment/var-in-catch-body-annex-b-eval-destructuring.js b/js/src/tests/non262/lexical-environment/var-in-catch-body-annex-b-eval-destructuring.js new file mode 100644 index 0000000000..06174013e8 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/var-in-catch-body-annex-b-eval-destructuring.js @@ -0,0 +1,10 @@ +// |reftest| skip-if(!xulRuntime.shell) + +assertThrowsInstanceOf(() => evaluate(`try { throw {} } catch ({e}) { var e; }`), SyntaxError); +assertThrowsInstanceOf(() => evaluate(`try { throw {} } catch ({e}) { eval('var e'); }`), SyntaxError); + +assertThrowsInstanceOf(() => new Function(`try { throw {} } catch ({e}) { var e; }`), SyntaxError); +assertThrowsInstanceOf(new Function(`try { throw {} } catch ({e}) { eval('var e'); }`), SyntaxError); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/lexical-environment/var-in-catch-body-annex-b-eval-for-of.js b/js/src/tests/non262/lexical-environment/var-in-catch-body-annex-b-eval-for-of.js new file mode 100644 index 0000000000..b433b68b6e --- /dev/null +++ b/js/src/tests/non262/lexical-environment/var-in-catch-body-annex-b-eval-for-of.js @@ -0,0 +1,12 @@ +// |reftest| skip-if(!xulRuntime.shell) + +evaluate(` + try { throw null; } catch (e) { eval("for (var e of []) {}") } +`); + +new Function(` + try { throw null; } catch (e) { eval("for (var e of []) {}") } +`)(); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/lexical-environment/var-in-catch-body-annex-b-eval.js b/js/src/tests/non262/lexical-environment/var-in-catch-body-annex-b-eval.js new file mode 100644 index 0000000000..e380846e25 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/var-in-catch-body-annex-b-eval.js @@ -0,0 +1,21 @@ +// Tests annex B.3.5 that introduces a var via direct eval. + +var x = "global-x"; +var log = ""; + +// Tests that direct eval works. +function g() { + try { throw 8; } catch (x) { + eval("var x = 42;"); + log += x; + } + x = "g"; + log += x; +} +g(); + +assertEq(x, "global-x"); +assertEq(log, "42g"); + +if ("reportCompare" in this) + reportCompare(true, true) diff --git a/js/src/tests/non262/lexical-environment/var-in-catch-body-annex-b.js b/js/src/tests/non262/lexical-environment/var-in-catch-body-annex-b.js new file mode 100644 index 0000000000..6e6a24f213 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/var-in-catch-body-annex-b.js @@ -0,0 +1,114 @@ +// Tests annex B.3.5. + +assertThrowsInstanceOf(function () { + eval(` + function f() { + let x; + try {} catch (x) { + var x; + } + } + `); +}, SyntaxError); + +assertThrowsInstanceOf(function () { + eval(` + function f() { + try {} catch (x) { + let y; + var y; + } + } + `); +}, SyntaxError); + +assertThrowsInstanceOf(function () { + eval(` + function f() { + try {} catch (x) { + let x; + } + } + `); +}, SyntaxError); + +assertThrowsInstanceOf(function () { + eval(` + function f() { + try {} catch (x) { + const x; + } + } + `); +}, SyntaxError); + +// Tests that redeclaring a var inside the catch is not allowed if there's a +// body-level lexical. +assertThrowsInstanceOf(function () { + eval(` + let x; + try {} catch (x) { + var x; + } + `); +}, SyntaxError); + +var log = ''; +var x = 'global-x'; + +function g() { + x = 'g'; + try { throw 8; } catch (x) { + var x = 42; + log += x; + } + log += x; +} +g(); + +// Tests that var declaration is allowed in for-in head. +function h0() { + try {} catch (e) { + for (var e in {}); + } +} +h0(); + +// Tests that var declaration is allowed in C-for head. +function h1() { + try {} catch (e) { + for (var e;;); + } +} +h1(); + +// Tests that var declaration is allowed in for-of head. +function h2() { + try {} catch (e) { + for (var e of {}); + } +} +h2(); + +// Tests that redeclaring a var inside the catch is allowed. +function h3() { + var e; + try {} catch (e) { + var e; + } +} +h3(); + +if (typeof evaluate === "function") { + assertThrowsInstanceOf(function () { + evaluate(` + let y; + try {} catch (y) { var y; } + `); + }, SyntaxError); +} + +assertEq(log, "42g"); + +if ("reportCompare" in this) + reportCompare(true, true) diff --git a/js/src/tests/non262/lexical-environment/with-global-ignores-global-let-variables.js b/js/src/tests/non262/lexical-environment/with-global-ignores-global-let-variables.js new file mode 100644 index 0000000000..042f92f688 --- /dev/null +++ b/js/src/tests/non262/lexical-environment/with-global-ignores-global-let-variables.js @@ -0,0 +1,18 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +let v = "global-v"; + +function f(v, global) +{ + with (global) + return v; +} + +assertEq(f("argument-v", this), "argument-v", + "let-var shouldn't appear in global for |with| purposes"); + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/lexical/browser.js b/js/src/tests/non262/lexical/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/lexical/browser.js diff --git a/js/src/tests/non262/lexical/regress-336376-01.js b/js/src/tests/non262/lexical/regress-336376-01.js new file mode 100644 index 0000000000..ef6a0f435d --- /dev/null +++ b/js/src/tests/non262/lexical/regress-336376-01.js @@ -0,0 +1,322 @@ +/* -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = "336376"; +var summary = "Tests reserved words in contexts in which they are not reserved"; +var actual, expect; + +printBugNumber(BUGNUMBER); +printStatus(summary); + +/************** + * TEST SETUP * + **************/ + +// +// New tests go in Tester.prototype._tests. A test is called with a single +// argument, the keyword to test in the syntax tested by that test. Tests +// should not return anything, and they should signal failure by throwing an +// explanatory exception and success by not throwing one. +// +// If you define a new test, make sure to name it using an informative string +// for ease of use if any keywords ever manually define the array of tests they +// should pass, and add it as a string to ALL_TESTS. +// + +// all tests +const ALL_TESTS = + [ + "CONTEXT_OBJECT_LITERAL_PROPERTY", + "CONTEXT_OBJECT_PROPERTY_DOT_REFERENCE", + "CONTEXT_OBJECT_PROPERTY_DOT_REFERENCE_IS_FUNCTION", + "CONTEXT_OBJECT_PROPERTY_DOT_GET", + "CONTEXT_OBJECT_PROPERTY_DOT_SET", + ]; + +function r(keyword, tests) +{ + /** + * @param keyword + * the keyword as a string + * @param tests + * array of test numbers against it, or leave undefined to run all tests + * against it + */ + function Reserved(keyword, tests) + { + this.keyword = keyword; + if (tests) + this.tests = tests; + else + this.tests = ALL_TESTS; + } + Reserved.prototype = + { + toString: + function() + { + return "'" + this.keyword + "' being run against tests " + + this.tests; + } + }; + return new Reserved(keyword, tests); +} + +// ECMA-262, 3rd. ed. keywords -- see 7.5.2 +const ECMA_262_3_KEYWORD = + [ + r("break"), + r("case"), + r("catch"), + r("continue"), + r("default"), + r("delete"), + r("do"), + r("else"), + r("finally"), + r("for"), + r("function"), + r("if"), + r("in"), + r("instanceof"), + r("new"), + r("return"), + r("switch"), + r("this"), + r("throw"), + r("try"), + r("typeof"), + r("var"), + r("void"), + r("while"), + r("with"), + ]; + +// ECMA-262, 3rd. ed. future reserved keywords -- see 7.5.3 +const ECMA_262_3_FUTURERESERVEDKEYWORD = + [ + r("abstract"), + r("boolean"), + r("byte"), + r("char"), + r("class"), + r("const"), + r("debugger"), + r("double"), + r("enum"), + r("export"), + r("extends"), + r("final"), + r("float"), + r("goto"), + r("implements"), + r("import"), + r("int"), + r("interface"), + r("long"), + r("native"), + r("package"), + r("private"), + r("protected"), + r("public"), + r("short"), + r("static"), + r("super"), + r("synchronized"), + r("throws"), + r("transient"), + r("volatile"), + ]; + +// like reserved words, but not quite reserved words +const PSEUDO_RESERVED = + [ + r("true"), + r("false"), + r("null"), + ]; + +// new-in-ES4 reserved words -- fill this as each is implemented +const ECMA_262_4_RESERVED_WORDS = + [ + r("let") + ]; + + + +/** + * @param keyword + * string containing the tested keyword + * @param test + * the number of the failing test + * @param error + * the exception thrown when running the test + */ +function Failure(keyword, test, error) +{ + this.keyword = keyword; + this.test = test; + this.error = error; +} +Failure.prototype = +{ + toString: + function() + { + return "*** FAILURE on '" + this.keyword + "'!\n" + + "* test: " + this.test + "\n" + + "* error: " + this.error + "\n"; + } +}; + +function Tester() +{ + this._failedTests = []; +} +Tester.prototype = +{ + testReservedWords: + function(reservedArray) + { + var rv; + for (var i = 0, sz = reservedArray.length; i < sz; i++) + { + var res = reservedArray[i]; + if (!res) + continue; + + var tests = res.tests; + for (var j = 0, sz2 = tests.length; j < sz2; j++) + { + var test = tests[j]; + if (!test) + continue; + + try + { + this._tests[test](res.keyword); + } + catch (e) + { + this._failedTests.push(new Failure(res.keyword, test, e)); + } + } + } + }, + flushErrors: + function () + { + if (this._failedTests.length > 0) { + var except = "*************************\n" + + "* FAILURES ENCOUNTERED! *\n" + + "*************************\n"; + for (var i = 0, sz = this._failedTests.length; i < sz; i++) + except += this._failedTests[i]; + throw except; + } + }, + _tests: + { + CONTEXT_OBJECT_LITERAL_PROPERTY: + function(keyword) + { + try + { + eval("var o = { " + keyword + ": 17 };\n" + + "if (o['" + keyword + "'] != 17)\n" + + "throw \"o['" + keyword + "'] == 17\";"); + } + catch (e) + { + throw e; + } + }, + CONTEXT_OBJECT_PROPERTY_DOT_REFERENCE: + function(keyword) + { + try + { + eval("var o = { \"" + keyword + "\": 17, baz: null };\n" + + "if (o." + keyword + " != 17)\n" + + "throw \"o." + keyword + " == 17\";"); + } + catch (e) + { + throw e; + } + }, + CONTEXT_OBJECT_PROPERTY_DOT_REFERENCE_IS_FUNCTION: + function(keyword) + { + try + { + eval("var o = { '" + keyword + "': function() { return 17; }, baz: null };\n" + + "if (o." + keyword + "() != 17)\n" + + "throw \"o." + keyword + " == 17\";"); + } + catch (e) + { + throw e; + } + }, + CONTEXT_OBJECT_PROPERTY_DOT_GET: + function(keyword) + { + try + { + var o = {}; + eval("o['" + keyword + "'] = 17;\n" + + "if (o." + keyword + " != 17)\n" + + "throw \"'o." + keyword + " != 17' failed!\";"); + } + catch (e) + { + throw e; + } + }, + CONTEXT_OBJECT_PROPERTY_DOT_SET: + function(keyword) + { + try + { + var o = {}; + eval("o." + keyword + " = 17;\n" + + "if (o['" + keyword + "'] != 17)\n" + + "throw \"'o." + keyword + " = 17' failed!\";"); + } + catch (e) + { + throw e; + } + }, + } +}; + + +/*************** + * BEGIN TESTS * + ***************/ + +var failed = false; + +try +{ + var tester = new Tester(); + tester.testReservedWords(ECMA_262_3_KEYWORD); + tester.testReservedWords(ECMA_262_3_FUTURERESERVEDKEYWORD); + tester.testReservedWords(PSEUDO_RESERVED); + tester.testReservedWords(ECMA_262_4_RESERVED_WORDS); + tester.flushErrors(); +} +catch (e) +{ + failed = e; +} + +expect = false; +actual = failed; + +reportCompare(expect, actual, summary); diff --git a/js/src/tests/non262/lexical/regress-346642-04.js b/js/src/tests/non262/lexical/regress-346642-04.js new file mode 100644 index 0000000000..6898864904 --- /dev/null +++ b/js/src/tests/non262/lexical/regress-346642-04.js @@ -0,0 +1,33 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 346642; +var summary = 'decompilation of destructuring assignment'; +var actual = ''; +var expect = ''; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + expect = 'No Crash'; + actual = 'No Crash'; + try + { + (function() { for (var [a, b] in []) for ([c, d] in []) { } }); + } + catch(ex) + { + actual = ex + ''; + } + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/lexical/regress-351515.js b/js/src/tests/non262/lexical/regress-351515.js new file mode 100644 index 0000000000..47690b7455 --- /dev/null +++ b/js/src/tests/non262/lexical/regress-351515.js @@ -0,0 +1,94 @@ +/* -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 351515; +var summary = 'Invalid uses of yield, let keywords in js17'; +var actual = ''; +var expect = ''; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +try +{ + expect = "No Error"; + eval('yield = 1;'); + actual = 'No Error'; +} +catch(ex) +{ + actual = ex.name; +} +reportCompare(expect, actual, summary + ': global: yield = 1'); + +try +{ + expect = "No Error"; + eval('(function(){yield = 1;})'); + actual = 'No Error'; +} +catch(ex) +{ + actual = ex.name; +} +reportCompare(expect, actual, summary + ': local: yield = 1'); + +try +{ + expect = "No Error"; + eval('let = 1;'); + actual = 'No Error'; +} +catch(ex) +{ + actual = ex.name; +} +reportCompare(expect, actual, summary + ': global: let = 1'); + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + try + { + expect = "No Error"; + eval('function f(yield, let) { return yield+let; }'); + actual = 'No Error'; + } + catch(ex) + { + actual = ex.name; + } + reportCompare(expect, actual, summary + + ': function f(yield, let) { return yield+let; }'); + + try + { + expect = "No Error"; + eval('var yield = 1;'); + actual = 'No Error'; + } + catch(ex) + { + actual = ex.name; + } + reportCompare(expect, actual, summary + ': function () {var yield;}'); + + try + { + expect = "No Error"; + eval('var let = 1;'); + actual = 'No Error'; + } + catch(ex) + { + actual = ex.name; + } + reportCompare(expect, actual, summary + ': function () { var let;}'); +} diff --git a/js/src/tests/non262/lexical/shell.js b/js/src/tests/non262/lexical/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/lexical/shell.js |