diff options
Diffstat (limited to 'js/src/tests/non262/lexical-environment')
45 files changed, 1498 insertions, 0 deletions
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"); |