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/expressions | |
parent | Initial commit. (diff) | |
download | thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/tests/non262/expressions')
61 files changed, 4980 insertions, 0 deletions
diff --git a/js/src/tests/non262/expressions/11.1.5-01.js b/js/src/tests/non262/expressions/11.1.5-01.js new file mode 100644 index 0000000000..7db12aada3 --- /dev/null +++ b/js/src/tests/non262/expressions/11.1.5-01.js @@ -0,0 +1,37 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 520696; +var summary = + 'Implement support for string literals as names for properties defined ' + + 'using ES5 get/set syntax'; + +print(BUGNUMBER + ": " + summary); + + +var o; + +o = { get "a b c"() { return 17; } }; +assertEq("get" in Object.getOwnPropertyDescriptor(o, "a b c"), true); + +o = eval('({ get "a b c"() { return 17; } })'); +assertEq("get" in Object.getOwnPropertyDescriptor(o, "a b c"), true); + +var f = eval("(function literalInside() { return { set 'c d e'(q) { } }; })"); +f = function literalInside() { return { set 'c d e'(q) { } }; }; + +function checkO() +{ + assertEq(3.141592654 in o, true, "fractional-named property isn't in object"); + assertEq(10000 in o, true, "exponential-named property is in object"); + assertEq(0xdeadbeef in o, true, "hex-named property is in object"); + assertEq("Infinity" in o, true, "numeric index stringified correctly"); +} + +o = eval('({ 3.141592654: "pi", 1e4: 17, 0xdeadbeef: "hex", 1e3000: "Infinity" })'); +checkO(); +o = { 3.141592654: "pi", 1e4: 17, 0xdeadbeef: "hex", 1e3000: "Infinity" }; +checkO(); + +reportCompare(true, true); diff --git a/js/src/tests/non262/expressions/ToPropertyKey-symbols.js b/js/src/tests/non262/expressions/ToPropertyKey-symbols.js new file mode 100644 index 0000000000..32b3994b3c --- /dev/null +++ b/js/src/tests/non262/expressions/ToPropertyKey-symbols.js @@ -0,0 +1,90 @@ +var symbols = [ + Symbol(), Symbol("iterator"), Symbol.for("iterator"), Symbol.iterator +]; + +for (var sym of symbols) { + var key = { + toString() { return sym; } + }; + + // Test that ToPropertyKey can return a symbol in each of the following + // contexts. + + // Computed property names. + var obj = {[key]: 13}; + var found = Reflect.ownKeys(obj); + assertEq(found.length, 1); + assertEq(found[0], sym); + + // Computed accessor property names. + var obj2 = { + get [key]() { return "got"; }, + set [key](v) { this.v = v; } + }; + assertEq(obj2[sym], "got"); + obj2[sym] = 33; + assertEq(obj2.v, 33); + + // Getting and setting properties. + assertEq(obj[key], 13); + obj[key] = 19; + assertEq(obj[sym], 19); + (function () { "use strict"; obj[key] = 20; })(); + assertEq(obj[sym], 20); + obj[key]++; + assertEq(obj[sym], 21); + + // Getting properties of primitive values. + Number.prototype[sym] = "success"; + assertEq(Math.PI[key], "success"); + delete Number.prototype[sym]; + + // Getting a super property. + class X { + [sym]() { return "X"; } + } + class Y extends X { + [sym]() { return super[key]() + "Y"; } + } + var y = new Y(); + assertEq(y[sym](), "XY"); + + // Setting a super property. + class Z { + set [sym](v) { + this.self = this; + this.value = v; + } + } + class W extends Z { + set [sym](v) { + this.isW = true; + super[key] = v; + } + } + var w = new W(); + w[key] = "ok"; + assertEq(w.self, w); + assertEq(w.value, "ok"); + assertEq(w.isW, true); + + // Deleting properties. + obj = {[sym]: 1}; + assertEq(delete obj[key], true); + assertEq(sym in obj, false); + + // LHS of `in` expressions. + assertEq(key in {iterator: 0}, false); + assertEq(key in {[sym]: 0}, true); + + // Methods of Object and Object.prototype + obj = {}; + Object.defineProperty(obj, key, {value: "ok", enumerable: true}); + assertEq(obj[sym], "ok"); + assertEq(obj.hasOwnProperty(key), true); + assertEq(obj.propertyIsEnumerable(key), true); + var desc = Object.getOwnPropertyDescriptor(obj, key); + assertEq(desc.value, "ok"); +} + +reportCompare(0, 0); diff --git a/js/src/tests/non262/expressions/binary-literals.js b/js/src/tests/non262/expressions/binary-literals.js new file mode 100644 index 0000000000..df1f2ed6f3 --- /dev/null +++ b/js/src/tests/non262/expressions/binary-literals.js @@ -0,0 +1,115 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 894026; +var summary = "Implement ES6 binary literals"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var chars = ['b', 'B']; + +for (var i = 0; i < 2; i++) +{ + if (i === 2) + { + chars.forEach(function(v) + { + try + { + eval('0' + v + i); + throw "didn't throw"; + } + catch (e) + { + assertEq(e instanceof SyntaxError, true, + "no syntax error evaluating 0" + v + i + ", " + + "got " + e); + } + }); + continue; + } + + for (var j = 0; j < 2; j++) + { + if (j === 2) + { + chars.forEach(function(v) + { + try + { + eval('0' + v + i + j); + throw "didn't throw"; + } + catch (e) + { + assertEq(e instanceof SyntaxError, true, + "no syntax error evaluating 0" + v + i + j + ", " + + "got " + e); + } + }); + continue; + } + + for (var k = 0; k < 2; k++) + { + if (k === 2) + { + chars.forEach(function(v) + { + try + { + eval('0' + v + i + j + k); + throw "didn't throw"; + } + catch (e) + { + assertEq(e instanceof SyntaxError, true, + "no syntax error evaluating 0" + v + i + j + k + ", " + + "got " + e); + } + }); + continue; + } + + chars.forEach(function(v) + { + assertEq(eval('0' + v + i + j + k), i * 4 + j * 2 + k); + }); + } + } +} + +chars.forEach(function(v) +{ + try + { + } + catch (e) + { + assertEq(e instanceof SyntaxError, true, + "no syntax error evaluating 0" + v + ", got " + e); + } +}); + +// Off-by-one check: '/' immediately precedes '0'. +assertEq(0b110/1, 6); +assertEq(0B10110/1, 22); + +function strict() +{ + "use strict"; + return 0b11010101; +} +assertEq(strict(), 128 + 64 + 16 + 4 + 1); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/expressions/browser.js b/js/src/tests/non262/expressions/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/expressions/browser.js diff --git a/js/src/tests/non262/expressions/computed-property-side-effects.js b/js/src/tests/non262/expressions/computed-property-side-effects.js new file mode 100644 index 0000000000..54dec7416b --- /dev/null +++ b/js/src/tests/non262/expressions/computed-property-side-effects.js @@ -0,0 +1,35 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 1199695; +var summary = + "Computed property names must be considered as always effectful even when " + + "the name expression isn't effectful, because calling ToPropertyKey on " + + "some non-effectful expressions has user-modifiable behavior"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +RegExp.prototype.toString = () => { throw 42; }; +assertThrowsValue(function() { + ({ [/regex/]: 0 }); // ToPropertyKey(/regex/) throws 42 +}, 42); + +function Q() { + ({ [new.target]: 0 }); // new.target will be Q, ToPropertyKey(Q) throws 17 +} +Q.toString = () => { throw 17; }; +assertThrowsValue(function() { + new Q; +}, 17); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/expressions/constant-folded-labeled-statement.js b/js/src/tests/non262/expressions/constant-folded-labeled-statement.js new file mode 100644 index 0000000000..245693b50d --- /dev/null +++ b/js/src/tests/non262/expressions/constant-folded-labeled-statement.js @@ -0,0 +1,14 @@ +var BUGNUMBER = 1499448; +var summary = "Constant folder should fold labeled statements"; + +print(BUGNUMBER + ": " + summary); + +if (typeof disassemble === "function") { + var code = disassemble(() => { x: 2+2; }); + + if (typeof reportCompare === "function") + reportCompare(true, /Int8 4/.test(code)); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/expressions/delete-constant-folded-and-or.js b/js/src/tests/non262/expressions/delete-constant-folded-and-or.js new file mode 100644 index 0000000000..9576413a68 --- /dev/null +++ b/js/src/tests/non262/expressions/delete-constant-folded-and-or.js @@ -0,0 +1,41 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 1183400; +var summary = + "Deletion of a && or || expression that constant-folds to a name must not " + + "attempt to delete the name"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +Object.defineProperty(this, "nonconfigurable", { value: 42 }); +assertEq(nonconfigurable, 42); + +assertEq(delete nonconfigurable, false); +assertEq(delete (true && nonconfigurable), true); + +function nested() +{ + assertEq(delete nonconfigurable, false); + assertEq(delete (true && nonconfigurable), true); +} +nested(); + +function nestedStrict() +{ + "use strict"; + assertEq(delete (true && nonconfigurable), true); +} +nestedStrict(); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/expressions/delete-name-parenthesized-early-error-strict-mode.js b/js/src/tests/non262/expressions/delete-name-parenthesized-early-error-strict-mode.js new file mode 100644 index 0000000000..d28afb665c --- /dev/null +++ b/js/src/tests/non262/expressions/delete-name-parenthesized-early-error-strict-mode.js @@ -0,0 +1,76 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 1111101; +var summary = + "delete (foo), delete ((foo)), and so on are strict mode early errors"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +function checkSyntaxError(code) +{ + function helper(maker) + { + var earlyError = false; + try + { + var f = maker(code); + + var error = "no early error, created a function with code <" + code + ">"; + try + { + f(); + error += ", and the function can be called without error"; + } + catch (e) + { + error +=", and calling the function throws " + e; + } + + throw new Error(error); + } + catch (e) + { + assertEq(e instanceof SyntaxError, true, + "expected syntax error, got " + e); + } + } + + helper(Function); + helper(eval); +} + +checkSyntaxError("function f() { 'use strict'; delete escape; } f();"); +checkSyntaxError("function f() { 'use strict'; delete escape; }"); +checkSyntaxError("function f() { 'use strict'; delete (escape); } f();"); +checkSyntaxError("function f() { 'use strict'; delete (escape); }"); +checkSyntaxError("function f() { 'use strict'; delete ((escape)); } f();"); +checkSyntaxError("function f() { 'use strict'; delete ((escape)); }"); + +// Meanwhile, non-strict all of these should work + +function checkFine(code) +{ + Function(code); + (1, eval)(code); // indirect, to be consistent w/above +} + +checkFine("function f() { delete escape; } f();"); +checkFine("function f() { delete escape; }"); +checkFine("function f() { delete (escape); } f();"); +checkFine("function f() { delete (escape); }"); +checkFine("function f() { delete ((escape)); } f();"); +checkFine("function f() { delete ((escape)); }"); + + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/expressions/destructuring-array-default-call.js b/js/src/tests/non262/expressions/destructuring-array-default-call.js new file mode 100644 index 0000000000..a3a3622054 --- /dev/null +++ b/js/src/tests/non262/expressions/destructuring-array-default-call.js @@ -0,0 +1,10 @@ +var BUGNUMBER = 1184922; +var summary = "Array destructuring with various default values in various context - call/new expression"; + +print(BUGNUMBER + ": " + summary); + +testDestructuringArrayDefault("func()"); +testDestructuringArrayDefault("new func()"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/expressions/destructuring-array-default-class.js b/js/src/tests/non262/expressions/destructuring-array-default-class.js new file mode 100644 index 0000000000..8083089286 --- /dev/null +++ b/js/src/tests/non262/expressions/destructuring-array-default-class.js @@ -0,0 +1,72 @@ +var BUGNUMBER = 1184922; +var summary = "Array destructuring with various default values in various context - class expression and super/new.target"; + +print(BUGNUMBER + ": " + summary); + +testDestructuringArrayDefault(`class E { + constructor() {} + method() {} + get v() {} + set v(_) {} + static method() {} + static get v() {} + static set v(_) {} +}`); + +testDestructuringArrayDefault(`class E extends C { + constructor() {} + method() {} + get v() {} + set v(_) {} + static method() {} + static get v() {} + static set v(_) {} +}`); + +var opt = { + no_plain: true, + no_func: true, + no_func_arg: true, + no_gen: true, + no_gen_arg: true, + no_ctor: true, + no_method: true, + no_pre_super: true, + no_comp: true, + + no_derived_ctor: false, +}; +testDestructuringArrayDefault("super()", opt); + +opt = { + no_plain: true, + no_func: true, + no_func_arg: true, + no_gen: true, + no_gen_arg: true, + no_ctor: true, + no_comp: true, + + no_derived_ctor: false, + no_method: false, + no_pre_super: false, +}; +testDestructuringArrayDefault("super.foo()", opt); + +opt = { + no_plain: true, + + no_func: false, + no_func_arg: false, + no_gen: false, + no_gen_arg: false, + no_ctor: false, + no_derived_ctor: false, + no_method: false, + no_pre_super: false, + no_comp: false, +}; +testDestructuringArrayDefault("new.target", opt); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/expressions/destructuring-array-default-function-nested.js b/js/src/tests/non262/expressions/destructuring-array-default-function-nested.js new file mode 100644 index 0000000000..99260b9895 --- /dev/null +++ b/js/src/tests/non262/expressions/destructuring-array-default-function-nested.js @@ -0,0 +1,11 @@ +var BUGNUMBER = 1184922; +var summary = "Array destructuring with various default values in various context - function expression with nested objects"; + +print(BUGNUMBER + ": " + summary); + +testDestructuringArrayDefault("function f() { return { f() {}, *g() {}, r: /a/ }; }"); +testDestructuringArrayDefault("function* g() { return { f() {}, *g() {}, r: /b/ }; }"); +testDestructuringArrayDefault("() => { return { f() {}, *g() {}, r: /c/ }; }"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/expressions/destructuring-array-default-function.js b/js/src/tests/non262/expressions/destructuring-array-default-function.js new file mode 100644 index 0000000000..5d2ffef188 --- /dev/null +++ b/js/src/tests/non262/expressions/destructuring-array-default-function.js @@ -0,0 +1,11 @@ +var BUGNUMBER = 1184922; +var summary = "Array destructuring with various default values in various context - function expression"; + +print(BUGNUMBER + ": " + summary); + +testDestructuringArrayDefault("function f() {}"); +testDestructuringArrayDefault("function* g() {}"); +testDestructuringArrayDefault("() => {}"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/expressions/destructuring-array-default-simple.js b/js/src/tests/non262/expressions/destructuring-array-default-simple.js new file mode 100644 index 0000000000..50c6c37f3d --- /dev/null +++ b/js/src/tests/non262/expressions/destructuring-array-default-simple.js @@ -0,0 +1,16 @@ +var BUGNUMBER = 1184922; +var summary = "Array destructuring with various default values in various context - simple literal"; + +print(BUGNUMBER + ": " + summary); + +testDestructuringArrayDefault("'foo'"); +testDestructuringArrayDefault("`foo`"); +testDestructuringArrayDefault("func`foo`"); + +testDestructuringArrayDefault("/foo/"); + +testDestructuringArrayDefault("{}"); +testDestructuringArrayDefault("[]"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/expressions/destructuring-array-default-yield.js b/js/src/tests/non262/expressions/destructuring-array-default-yield.js new file mode 100644 index 0000000000..57ff9e9472 --- /dev/null +++ b/js/src/tests/non262/expressions/destructuring-array-default-yield.js @@ -0,0 +1,22 @@ +var BUGNUMBER = 1184922; +var summary = "Array destructuring with various default values in various context - yield expression"; + +print(BUGNUMBER + ": " + summary); + +var opt = { + no_plain: true, + no_func: true, + no_func_arg: true, + no_gen_arg: true, + no_ctor: true, + no_derived_ctor: true, + no_method: true, + no_pre_super: true, + no_comp: true, + + no_gen: false, +}; +testDestructuringArrayDefault("yield 1", opt); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/expressions/destructuring-array-done.js b/js/src/tests/non262/expressions/destructuring-array-done.js new file mode 100644 index 0000000000..f2c7e9410e --- /dev/null +++ b/js/src/tests/non262/expressions/destructuring-array-done.js @@ -0,0 +1,319 @@ +var BUGNUMBER = 1184922; +var summary = "iterator.next() should not be called when after iterator completes"; + +print(BUGNUMBER + ": " + summary); + +var log; +function reset() { + log = ""; +} +var obj = new Proxy({}, { + set(that, name, value) { + var v; + if (value instanceof Function || value instanceof RegExp) + v = value.toString(); + else + v = JSON.stringify(value); + log += "set:" + name + "=" + v + ","; + } +}); +function createIterable(n) { + return { + i: 0, + [Symbol.iterator]() { + return this; + }, + next() { + log += "next,"; + this.i++; + if (this.i <= n) + return {value: this.i, done: false}; + return {value: 0, done: true}; + } + }; +} + +// Simple pattern. + +reset(); +[obj.a, obj.b, obj.c] = createIterable(0); +assertEq(log, + "next," + + "set:a=undefined," + + "set:b=undefined," + + "set:c=undefined,"); + +reset(); +[obj.a, obj.b, obj.c] = createIterable(1); +assertEq(log, + "next," + + "set:a=1," + + "next," + + "set:b=undefined," + + "set:c=undefined,"); + +reset(); +[obj.a, obj.b, obj.c] = createIterable(2); +assertEq(log, + "next," + + "set:a=1," + + "next," + + "set:b=2," + + "next," + + "set:c=undefined,"); + +reset(); +[obj.a, obj.b, obj.c] = createIterable(3); +assertEq(log, + "next," + + "set:a=1," + + "next," + + "set:b=2," + + "next," + + "set:c=3,"); + +// Elision. + +reset(); +[obj.a, , obj.b, , , obj.c, ,] = createIterable(0); +assertEq(log, + "next," + + "set:a=undefined," + + "set:b=undefined," + + "set:c=undefined,"); + +reset(); +[obj.a, , obj.b, , , obj.c, ,] = createIterable(1); +assertEq(log, + "next," + + "set:a=1," + + "next," + + "set:b=undefined," + + "set:c=undefined,"); + +reset(); +[obj.a, , obj.b, , , obj.c, ,] = createIterable(2); +assertEq(log, + "next," + + "set:a=1," + + "next," + + "next," + + "set:b=undefined," + + "set:c=undefined,"); + +reset(); +[obj.a, , obj.b, , , obj.c, ,] = createIterable(3); +assertEq(log, + "next," + + "set:a=1," + + "next," + + "next," + + "set:b=3," + + "next," + + "set:c=undefined,"); + +reset(); +[obj.a, , obj.b, , , obj.c, ,] = createIterable(4); +assertEq(log, + "next," + + "set:a=1," + + "next," + + "next," + + "set:b=3," + + "next," + + "next," + + "set:c=undefined,"); + +reset(); +[obj.a, , obj.b, , , obj.c, ,] = createIterable(5); +assertEq(log, + "next," + + "set:a=1," + + "next," + + "next," + + "set:b=3," + + "next," + + "next," + + "next," + + "set:c=undefined,"); + +reset(); +[obj.a, , obj.b, , , obj.c, ,] = createIterable(6); +assertEq(log, + "next," + + "set:a=1," + + "next," + + "next," + + "set:b=3," + + "next," + + "next," + + "next," + + "set:c=6," + + "next,"); + +reset(); +[obj.a, , obj.b, , , obj.c, ,] = createIterable(7); +assertEq(log, + "next," + + "set:a=1," + + "next," + + "next," + + "set:b=3," + + "next," + + "next," + + "next," + + "set:c=6," + + "next,"); + +// Rest. + +reset(); +[...obj.r] = createIterable(0); +assertEq(log, + "next," + + "set:r=[],"); + +reset(); +[...obj.r] = createIterable(1); +assertEq(log, + "next," + + "next," + + "set:r=[1],"); + +reset(); +[obj.a, ...obj.r] = createIterable(0); +assertEq(log, + "next," + + "set:a=undefined," + + "set:r=[],"); + +reset(); +[obj.a, ...obj.r] = createIterable(1); +assertEq(log, + "next," + + "set:a=1," + + "next," + + "set:r=[],"); + +reset(); +[obj.a, ...obj.r] = createIterable(2); +assertEq(log, + "next," + + "set:a=1," + + "next," + + "next," + + "set:r=[2],"); + +reset(); +[obj.a, obj.b, ...obj.r] = createIterable(0); +assertEq(log, + "next," + + "set:a=undefined," + + "set:b=undefined," + + "set:r=[],"); + +reset(); +[obj.a, obj.b, ...obj.r] = createIterable(1); +assertEq(log, + "next," + + "set:a=1," + + "next," + + "set:b=undefined," + + "set:r=[],"); + +reset(); +[obj.a, obj.b, ...obj.r] = createIterable(2); +assertEq(log, + "next," + + "set:a=1," + + "next," + + "set:b=2," + + "next," + + "set:r=[],"); + +reset(); +[obj.a, obj.b, ...obj.r] = createIterable(3); +assertEq(log, + "next," + + "set:a=1," + + "next," + + "set:b=2," + + "next," + + "next," + + "set:r=[3],"); + +// Rest and elision. + +reset(); +[, ...obj.r] = createIterable(0); +assertEq(log, + "next," + + "set:r=[],"); + +reset(); +[, ...obj.r] = createIterable(1); +assertEq(log, + "next," + + "next," + + "set:r=[],"); + +reset(); +[, ...obj.r] = createIterable(2); +assertEq(log, + "next," + + "next," + + "next," + + "set:r=[2],"); + +reset(); +[obj.a, obj.b, , ...obj.r] = createIterable(0); +assertEq(log, + "next," + + "set:a=undefined," + + "set:b=undefined," + + "set:r=[],"); + +reset(); +[obj.a, obj.b, , ...obj.r] = createIterable(1); +assertEq(log, + "next," + + "set:a=1," + + "next," + + "set:b=undefined," + + "set:r=[],"); + +reset(); +[obj.a, obj.b, , ...obj.r] = createIterable(2); +assertEq(log, + "next," + + "set:a=1," + + "next," + + "set:b=2," + + "next," + + "set:r=[],"); + +reset(); +[obj.a, obj.b, , ...obj.r] = createIterable(3); +assertEq(log, + "next," + + "set:a=1," + + "next," + + "set:b=2," + + "next," + + "next," + + "set:r=[],"); + +reset(); +[obj.a, obj.b, , ...obj.r] = createIterable(4); +assertEq(log, + "next," + + "set:a=1," + + "next," + + "set:b=2," + + "next," + + "next," + + "next," + + "set:r=[4],"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/expressions/destructuring-array-lexical.js b/js/src/tests/non262/expressions/destructuring-array-lexical.js new file mode 100644 index 0000000000..133858a09d --- /dev/null +++ b/js/src/tests/non262/expressions/destructuring-array-lexical.js @@ -0,0 +1,12 @@ +var BUGNUMBER = 1184922; +var summary = "Array destructuring with accessing uninitialized lexical binding."; + +print(BUGNUMBER + ": " + summary); + +assertThrowsInstanceOf(() => { let y = [y] = []; }, + ReferenceError); +assertThrowsInstanceOf(() => { let y = [y] = [,]; }, + ReferenceError); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/expressions/destructuring-object-__proto__-1.js b/js/src/tests/non262/expressions/destructuring-object-__proto__-1.js new file mode 100644 index 0000000000..1e36a3b9bb --- /dev/null +++ b/js/src/tests/non262/expressions/destructuring-object-__proto__-1.js @@ -0,0 +1,82 @@ +// Test __proto__ is destructuring binding initialization. + +// __proto__ shorthand, no default. +{ + let {__proto__} = {}; + assertEq(__proto__, Object.prototype); +} + +// __proto__ shorthand, with default. +{ + let {__proto__ = 0} = {}; + assertEq(__proto__, Object.prototype); +} + +{ + let {__proto__ = 0} = Object.create(null); + assertEq(__proto__, 0); +} + +// __proto__ keyed, no default. +{ + let {__proto__: p} = {}; + assertEq(p, Object.prototype); +} + +// __proto__ keyed, with default. +{ + let {__proto__: p = 0} = {}; + assertEq(p, Object.prototype); +} + +// __proto__ keyed, with default. +{ + let {__proto__: p = 0} = Object.create(null); + assertEq(p, 0); +} + +// Repeat the cases from above, but this time with a rest property. + +// __proto__ shorthand, no default. +{ + let {__proto__, ...rest} = {}; + assertEq(__proto__, Object.prototype); + assertEq(Reflect.ownKeys(rest).length, 0); +} + +// __proto__ shorthand, with default. +{ + let {__proto__ = 0, ...rest} = {}; + assertEq(__proto__, Object.prototype); + assertEq(Reflect.ownKeys(rest).length, 0); +} + +{ + let {__proto__ = 0, ...rest} = Object.create(null); + assertEq(__proto__, 0); + assertEq(Reflect.ownKeys(rest).length, 0); +} + +// __proto__ keyed, no default. +{ + let {__proto__: p, ...rest} = {}; + assertEq(p, Object.prototype); + assertEq(Reflect.ownKeys(rest).length, 0); +} + +// __proto__ keyed, with default. +{ + let {__proto__: p = 0, ...rest} = {}; + assertEq(p, Object.prototype); + assertEq(Reflect.ownKeys(rest).length, 0); +} + +// __proto__ keyed, with default. +{ + let {__proto__: p = 0, ...rest} = Object.create(null); + assertEq(p, 0); + assertEq(Reflect.ownKeys(rest).length, 0); +} + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/expressions/destructuring-object-__proto__-2.js b/js/src/tests/non262/expressions/destructuring-object-__proto__-2.js new file mode 100644 index 0000000000..36f3e06ebe --- /dev/null +++ b/js/src/tests/non262/expressions/destructuring-object-__proto__-2.js @@ -0,0 +1,94 @@ +// Test __proto__ is destructuring assignment. + +// __proto__ shorthand, no default. +{ + let __proto__; + ({__proto__} = {}); + assertEq(__proto__, Object.prototype); +} + +// __proto__ shorthand, with default. +{ + let __proto__; + ({__proto__ = 0} = {}); + assertEq(__proto__, Object.prototype); +} + +{ + let __proto__; + ({__proto__ = 0} = Object.create(null)); + assertEq(__proto__, 0); +} + +// __proto__ keyed, no default. +{ + let p; + ({__proto__: p} = {}); + assertEq(p, Object.prototype); +} + +// __proto__ keyed, with default. +{ + let p; + ({__proto__: p = 0} = {}); + assertEq(p, Object.prototype); +} + +// __proto__ keyed, with default. +{ + let p; + ({__proto__: p = 0} = Object.create(null)); + assertEq(p, 0); +} + +// Repeat the cases from above, but this time with a rest property. + +// __proto__ shorthand, no default. +{ + let __proto__, rest; + ({__proto__, ...rest} = {}); + assertEq(__proto__, Object.prototype); + assertEq(Reflect.ownKeys(rest).length, 0); +} + +// __proto__ shorthand, with default. +{ + let __proto__, rest; + ({__proto__ = 0, ...rest} = {}); + assertEq(__proto__, Object.prototype); + assertEq(Reflect.ownKeys(rest).length, 0); +} + +{ + let __proto__, rest; + ({__proto__ = 0, ...rest} = Object.create(null)); + assertEq(__proto__, 0); + assertEq(Reflect.ownKeys(rest).length, 0); +} + +// __proto__ keyed, no default. +{ + let p, rest; + ({__proto__: p, ...rest} = {}); + assertEq(p, Object.prototype); + assertEq(Reflect.ownKeys(rest).length, 0); +} + +// __proto__ keyed, with default. +{ + let p, rest; + ({__proto__: p = 0, ...rest} = {}); + assertEq(p, Object.prototype); + assertEq(Reflect.ownKeys(rest).length, 0); +} + +// __proto__ keyed, with default. +{ + let p, rest; + ({__proto__: p = 0, ...rest} = Object.create(null)); + assertEq(p, 0); + assertEq(Reflect.ownKeys(rest).length, 0); +} + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/expressions/destructuring-pattern-parenthesized.js b/js/src/tests/non262/expressions/destructuring-pattern-parenthesized.js new file mode 100644 index 0000000000..529fb5f71e --- /dev/null +++ b/js/src/tests/non262/expressions/destructuring-pattern-parenthesized.js @@ -0,0 +1,137 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 1146136; +var summary = + 'Parenthesized "destructuring patterns" are not usable as destructuring ' + + 'patterns'; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +// Don't pollute the top-level script with eval references. +var savedEval = this[String.fromCharCode(101, 118, 97, 108)]; + +function checkError(code, nonstrictErr, strictErr) +{ + function helper(exec, prefix, err) + { + var fullCode = prefix + code; + try + { + var f = exec(fullCode); + + var error = + "no early error, parsed code <" + fullCode + "> using " + exec.name; + if (typeof f === "function") + { + try + { + f(); + error += ", and the function can be called without error"; + } + catch (e) + { + error +=", and calling the function throws " + e; + } + } + + throw new Error(error); + } + catch (e) + { + assertEq(e instanceof err, true, + "expected " + err.name + ", got " + e + " " + + "for code <" + fullCode + "> when parsed using " + exec.name); + } + } + + helper(Function, "", nonstrictErr); + helper(Function, "'use strict'; ", strictErr); + helper(savedEval, "", nonstrictErr); + helper(savedEval, "'use strict'; ", strictErr); +} + +// Parenthesized destructuring patterns don't trigger grammar refinement, so we +// get the usual SyntaxError for an invalid assignment target, per +// 12.14.1 second bullet. +checkError("var a, b; ([a, b]) = [1, 2];", SyntaxError, SyntaxError); +checkError("var a, b; ({a, b}) = { a: 1, b: 2 };", SyntaxError, SyntaxError); + +// *Nested* parenthesized destructuring patterns, on the other hand, do trigger +// grammar refinement. But subtargets in a destructuring pattern must be +// either object/array literals that match the destructuring pattern refinement +// *or* valid simple assignment targets (or such things with a default, with the +// entire subtarget unparenthesized: |a = 3| is fine, |(a) = 3| is fine for +// destructuring in an expression, |(a = 3)| is forbidden). Parenthesized +// object/array patterns are neither. And so 12.14.5.1 third bullet requires an +// early SyntaxError. +checkError("var a, b; ({ a: ({ b: b }) } = { a: { b: 42 } });", SyntaxError, SyntaxError); +checkError("var a, b; ({ a: { b: (b = 7) } } = { a: {} });", SyntaxError, SyntaxError); +checkError("var a, b; ({ a: ([b]) } = { a: [42] });", SyntaxError, SyntaxError); +checkError("var a, b; [(a = 5)] = [1];", SyntaxError, SyntaxError); +checkError("var a, b; ({ a: (b = 7)} = { b: 1 });", SyntaxError, SyntaxError); + +Function("var a, b; [(a), b] = [1, 2];")(); +Function("var a, b; [(a) = 5, b] = [1, 2];")(); +Function("var a, b; [(arguments), b] = [1, 2];")(); +Function("var a, b; [(arguments) = 5, b] = [1, 2];")(); +Function("var a, b; [(eval), b] = [1, 2];")(); +Function("var a, b; [(eval) = 5, b] = [1, 2];")(); + +var repair = {}, demolition = {}; + +Function("var a, b; [(repair.man), b] = [1, 2];")(); +Function("var a, b; [(demolition['man']) = 'motel', b] = [1, 2];")(); +Function("var a, b; [(demolition['man' + {}]) = 'motel', b] = [1, 2];")(); // evade constant-folding + +function classesEnabled() +{ + try + { + new Function("class B { constructor() { } }; class D extends B { constructor() { super(); } }"); + return true; + } + catch (e) { + if (!(e instanceof SyntaxError)) + throw e; + return false; + } +} + +if (classesEnabled()) +{ + Function("var a, b; var obj = { x() { [(super.man), b] = [1, 2]; } };")(); + Function("var a, b; var obj = { x() { [(super[8]) = 'motel', b] = [1, 2]; } };")(); + Function("var a, b; var obj = { x() { [(super[8 + {}]) = 'motel', b] = [1, 2]; } };")(); // evade constant-folding +} + +// As noted above, when the assignment element has an initializer, the +// assignment element must not be parenthesized. +checkError("var a, b; [(repair.man = 17)] = [1];", SyntaxError, SyntaxError); +checkError("var a, b; [(demolition['man'] = 'motel')] = [1, 2];", SyntaxError, SyntaxError); +checkError("var a, b; [(demolition['man' + {}] = 'motel')] = [1];", SyntaxError, SyntaxError); // evade constant-folding +if (classesEnabled()) +{ + checkError("var a, b; var obj = { x() { [(super.man = 5)] = [1]; } };", SyntaxError, SyntaxError); + checkError("var a, b; var obj = { x() { [(super[8] = 'motel')] = [1]; } };", SyntaxError, SyntaxError); + checkError("var a, b; var obj = { x() { [(super[8 + {}] = 'motel')] = [1]; } };", SyntaxError, SyntaxError); // evade constant-folding +} + +checkError("var a, b; [f() = 'ohai', b] = [1, 2];", SyntaxError, SyntaxError); +checkError("var a, b; [(f()) = 'kthxbai', b] = [1, 2];", SyntaxError, SyntaxError); + +Function("var a, b; ({ a: (a), b} = { a: 1, b: 2 });")(); +Function("var a, b; ({ a: (a) = 5, b} = { a: 1, b: 2 });")(); + + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/expressions/destructuring-scope.js b/js/src/tests/non262/expressions/destructuring-scope.js new file mode 100644 index 0000000000..374c1bec98 --- /dev/null +++ b/js/src/tests/non262/expressions/destructuring-scope.js @@ -0,0 +1,67 @@ +/* -*- 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 = 'none'; +var summary = 'Test destructuring assignments for differing scopes'; +var actual = ''; +var expect = ''; + +printBugNumber(BUGNUMBER); +printStatus (summary); + +function f() { + var x = 3; + if (x > 0) { + let {a:x} = {a:7}; + if (x != 7) + throw "fail"; + } + if (x != 3) + throw "fail"; +} + +function g() { + // Before JS1.7's destructuring for…in was fixed to match JS1.8's, + // the expected values were a == "x" and b == 7. + for (var [a,b] in {x:7}) { + if (a !== "x" || typeof b !== "undefined") + throw "fail"; + } + + { + // Before JS1.7's destructuring for…in was fixed to match JS1.8's, + // the expected values were a == "y" and b == 8. + for (let [a,b] in {y:8}) { + if (a !== "y" || typeof b !== "undefined") + throw "fail"; + } + } + + if (a !== "x" || typeof b !== "undefined") + throw "fail"; +} + +f(); +g(); + +if (typeof a != "undefined" || typeof b != "undefined" || typeof x != "undefined") + throw "fail"; + +function h() { + // Before JS1.7's destructuring for…in was fixed to match JS1.8's, + // the expected values were a == "x" and b == 9. + for ([a,b] in {z:9}) { + if (a !== "z" || typeof b !== "undefined") + throw "fail"; + } +} + +h(); + +if (a !== "z" || typeof b !== "undefined") + throw "fail"; + +reportCompare(expect, actual, summary); diff --git a/js/src/tests/non262/expressions/exponentiation-unparenthesised-unary.js b/js/src/tests/non262/expressions/exponentiation-unparenthesised-unary.js new file mode 100644 index 0000000000..a673521264 --- /dev/null +++ b/js/src/tests/non262/expressions/exponentiation-unparenthesised-unary.js @@ -0,0 +1,106 @@ +const AsyncFunction = async function(){}.constructor; + +function assertNoError(f, msg) { + try { + f(); + } catch (e) { + assertEq(true, false, `${msg}: ${e}`); + } +} + +function assertSyntaxError(code) { + assertThrowsInstanceOf(function () { Function(code); }, SyntaxError, "Function:" + code); + assertThrowsInstanceOf(function () { AsyncFunction(code); }, SyntaxError, "AsyncFunction:" + code); +} + +function assertNoSyntaxError(code) { + assertNoError(function () { Function(code); }, "Function:" + code); + assertNoError(function () { AsyncFunction(code); }, "AsyncFunction:" + code); +} + +function assertNoSyntaxErrorAsyncContext(code) { + assertNoError(function () { AsyncFunction(code); }, "AsyncFunction:" + code); +} + +const invalidTestCases = [ + // UnaryExpression : delete UnaryExpression + // + // Test all possible `delete` expression kinds. + "delete a ** 0", + "delete a.prop ** 0", + "delete a[0] ** 0", + "delete a?.prop ** 0", + "delete 0 ** 0", + + // UnaryExpression : void UnaryExpression + "void a ** 0", + + // UnaryExpression : typeof UnaryExpression + // + // Test all possible `typeof` expression kinds. + "typeof a ** 0", + "typeof 0 ** 0", + + // UnaryExpression : + UnaryExpression + "+a ** 0", + + // UnaryExpression : - UnaryExpression + "-a ** 0", + + // UnaryExpression : ~ UnaryExpression + "~a ** 0", + + // UnaryExpression : ! UnaryExpression + "!a ** 0", + + // UnaryExpression : AwaitExpression + "await a ** 0", +]; + +for (let source of invalidTestCases) { + assertSyntaxError(source); +} + +const validTestCases = [ + // UnaryExpression : delete UnaryExpression + "(delete a) ** 0", + "(delete a.prop) ** 0", + "(delete a[0]) ** 0", + "(delete a?.prop) ** 0", + "(delete 0) ** 0", + + // UnaryExpression : void UnaryExpression + "(void a) ** 0", + + // UnaryExpression : typeof UnaryExpression + "(typeof a) ** 0", + "(typeof 0) ** 0", + + // UnaryExpression : + UnaryExpression + "(+a) ** 0", + + // UnaryExpression : - UnaryExpression + "(-a) ** 0", + + // UnaryExpression : ~ UnaryExpression + "(~a) ** 0", + + // UnaryExpression : ! UnaryExpression + "(!a) ** 0", +]; + +for (let source of validTestCases) { + assertNoSyntaxError(source); +} + +const validTestCasesAsync = [ + // UnaryExpression : AwaitExpression + "(await a) ** 0", +]; + +for (let source of validTestCasesAsync) { + assertNoSyntaxErrorAsyncContext(source); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/expressions/inNotObjectError.js b/js/src/tests/non262/expressions/inNotObjectError.js new file mode 100644 index 0000000000..3394ac0db7 --- /dev/null +++ b/js/src/tests/non262/expressions/inNotObjectError.js @@ -0,0 +1,46 @@ +var BUGNUMBER = 1352429; +var summary = 'Error message should provide enough infomation for use of in operator'; + +print(BUGNUMBER + ": " + summary); + +function checkErr(substr, str, messageSubstr, messageStr) { + var caught = false; + try { + substr in str; + } catch (e) { + caught = true; + assertEq(e.message.includes(messageSubstr), true); + assertEq(e.message.includes(messageStr), true); + assertEq(e.message.length < 100, true); + } + assertEq(caught, true); +} + +// These test cases check if long string is omitted properly. +checkErr('subString', 'base', 'subString', 'base'); +checkErr('this is subString', 'base', 'this is subStrin...', 'base'); +checkErr('subString', 'this is baseString', 'subString', 'this is baseStri...'); +checkErr('this is subString', 'this is base', 'this is subStrin...', 'this is base'); +checkErr('HEAD' + 'subString'.repeat(30000), 'HEAD' + 'base'.repeat(30000), 'HEADsubStringsub...', 'HEADbasebasebase...'); + +// These test cases check if it does not crash and throws appropriate error. +assertThrowsInstanceOf(() => { 1 in 'hello' }, TypeError); +assertThrowsInstanceOf(() => { 'hello' in 1 }, TypeError); +assertThrowsInstanceOf(() => { 'hello' in null }, TypeError); +assertThrowsInstanceOf(() => { null in 'hello' }, TypeError); +assertThrowsInstanceOf(() => { null in null }, TypeError); +assertThrowsInstanceOf(() => { 'hello' in true }, TypeError); +assertThrowsInstanceOf(() => { false in 1.1 }, TypeError); +assertThrowsInstanceOf(() => { Symbol.iterator in undefined }, TypeError); +assertThrowsInstanceOf(() => { [] in undefined }, TypeError); +assertThrowsInstanceOf(() => { /a/ in 'hello' }, TypeError); +var str = 'hello'; +assertThrowsInstanceOf(() => { str in 'hello' }, TypeError); +class A {}; +assertThrowsInstanceOf(() => { new A() in undefined }, TypeError); +var a = new A(); +a.b = 1.1; +assertThrowsInstanceOf(() => { a.b in 1.1 }, TypeError); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/expressions/named-accessor-function.js b/js/src/tests/non262/expressions/named-accessor-function.js new file mode 100644 index 0000000000..d6a055487b --- /dev/null +++ b/js/src/tests/non262/expressions/named-accessor-function.js @@ -0,0 +1,49 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ +// Contributor: +// Jeff Walden <jwalden+code@mit.edu> + +//----------------------------------------------------------------------------- +var BUGNUMBER = 999999; +var summary = '{ get x y() { } } is not valid getter syntax'; + +print(BUGNUMBER + ": " + summary); + +var BAD_CODE = ["({ get x y() { } })", "({ set x y(v) { } })"]; + +for (var i = 0, sz = BAD_CODE.length; i < sz; i++) +{ + var code = BAD_CODE[i]; + + var err = "no exception"; + try + { + eval(code); + } + catch (e) + { + err = e; + } + if (!(err instanceof SyntaxError)) + { + assertEq(true, false, + "bad or no exception thrown for eval(" + code + "): " + err); + } + + err = "no exception"; + try + { + new Function(code); + } + catch (e) + { + err = e; + } + if (!(err instanceof SyntaxError)) + { + assertEq(true, false, + "bad or no exception thrown for Function(" + code + "): " + err); + } +} + +reportCompare(true, true); diff --git a/js/src/tests/non262/expressions/nested-delete-name-in-evalcode.js b/js/src/tests/non262/expressions/nested-delete-name-in-evalcode.js new file mode 100644 index 0000000000..8226ba7c8b --- /dev/null +++ b/js/src/tests/non262/expressions/nested-delete-name-in-evalcode.js @@ -0,0 +1,163 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 616294; +var summary = + "|delete x| inside a function in eval code, where that eval code includes " + + "|var x| at top level, actually does delete the binding for x"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var f; + +function testOuterVar() +{ + return eval("var x; (function() { return delete x; })"); +} + +f = testOuterVar(); + +assertEq(f(), true); // configurable, so remove => true +assertEq(f(), true); // not there => true (only non-configurable => false) + + +function testOuterFunction() +{ + return eval("function x() { } (function() { return delete x; })"); +} + +f = testOuterFunction(); + +assertEq(f(), true); // configurable, so remove => true +assertEq(f(), true); // not there => true (only non-configurable => false) + + +function testOuterForVar() +{ + return eval("for (var x; false; ); (function() { return delete x; })"); +} + +f = testOuterForVar(); + +assertEq(f(), true); // configurable, so remove => true +assertEq(f(), true); // not there => true (only non-configurable => false) + + +function testOuterForInVar() +{ + return eval("for (var x in {}); (function() { return delete x; })"); +} + +f = testOuterForInVar(); + +assertEq(f(), true); // configurable, so remove => true +assertEq(f(), true); // not there => true (only non-configurable => false) + + +function testOuterNestedVar() +{ + return eval("for (var q = 0; q < 5; q++) { var x; } (function() { return delete x; })"); +} + +f = testOuterNestedVar(); + +assertEq(f(), true); // configurable, so remove => true +assertEq(f(), true); // not there => true (only non-configurable => false) + + +function testOuterNestedConditionalVar() +{ + return eval("for (var q = 0; q < 5; q++) { if (false) { var x; } } (function() { return delete x; })"); +} + +f = testOuterNestedConditionalVar(); + +assertEq(f(), true); // configurable, so remove => true +assertEq(f(), true); // not there => true (only non-configurable => false) + + +function testVarInWith() +{ + return eval("with ({}) { var x; } (function() { return delete x; })"); +} + +f = testVarInWith(); + +assertEq(f(), true); // configurable, so remove => true +assertEq(f(), true); // not there => true (only non-configurable => false) + + +function testForVarInWith() +{ + return eval("with ({}) { for (var x = 0; x < 5; x++); } (function() { return delete x; })"); +} + +f = testForVarInWith(); + +assertEq(f(), true); // configurable, so remove => true +assertEq(f(), true); // not there => true (only non-configurable => false) + + +function testForInVarInWith() +{ + return eval("with ({}) { for (var x in {}); } (function() { return delete x; })"); +} + +f = testForInVarInWith(); + +assertEq(f(), true); // configurable, so remove => true +assertEq(f(), true); // not there => true (only non-configurable => false) + + +function testUnknown() +{ + return eval("nameToDelete = 17; (function() { return delete nameToDelete; })"); +} + +f = testUnknown(); + +assertEq(f(), true); // configurable global property, so remove => true +assertEq(f(), true); // not there => true (only non-configurable => false) + + +function testArgumentShadow() +{ + return eval("var x; (function(x) { return delete x; })"); +} + +f = testArgumentShadow(); + +assertEq(f(), false); // non-configurable argument => false + + +function testArgument() +{ + return eval("(function(x) { return delete x; })"); +} + +f = testArgument(); + +assertEq(f(), false); // non-configurable argument => false + + +function testFunctionLocal() +{ + return eval("(function() { var x; return delete x; })"); +} + +f = testFunctionLocal(); + +assertEq(f(), false); // defined by function code => not configurable => false + + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("All tests passed!"); diff --git a/js/src/tests/non262/expressions/nullish-coalescing.js b/js/src/tests/non262/expressions/nullish-coalescing.js new file mode 100644 index 0000000000..4d3ea8f644 --- /dev/null +++ b/js/src/tests/non262/expressions/nullish-coalescing.js @@ -0,0 +1,112 @@ +var BUGNUMBER = 1566141; +var summary = "Implement the Nullish Coalescing operator (??) proposal"; + +print(BUGNUMBER + ": " + summary); + +// These tests are originally from webkit. +// webkit specifics have been removed and a test for `document.all` has +// been added. +function shouldBe(actual, expected) { + if (actual !== expected) + throw new Error(`expected ${expected} but got ${actual}`); +} + +function shouldNotThrow(script) { + eval(script); +} + +function shouldThrowSyntaxError(script) { + let error; + try { + eval(script); + } catch (e) { + error = e; + } + + if (!(error instanceof SyntaxError)) + throw new Error('Expected SyntaxError!'); +} + +function testBasicCases() { + shouldBe(undefined ?? 3, 3); + shouldBe(null ?? 3, 3); + shouldBe(true ?? 3, true); + shouldBe(false ?? 3, false); + shouldBe(0 ?? 3, 0); + shouldBe(1 ?? 3, 1); + shouldBe('' ?? 3, ''); + shouldBe('hi' ?? 3, 'hi'); + shouldBe(({} ?? 3) instanceof Object, true); + shouldBe(({ x: 'hi' } ?? 3).x, 'hi'); + shouldBe(([] ?? 3) instanceof Array, true); + shouldBe((['hi'] ?? 3)[0], 'hi'); + // test document.all, which has odd behavior + shouldBe(typeof(createIsHTMLDDA() ?? 3), "undefined"); +} + +for (let i = 0; i < 1e5; i++) + testBasicCases(); + +shouldBe(1 | null ?? 3, 1); +shouldBe(1 ^ null ?? 3, 1); +shouldBe(1 & null ?? 3, 0); +shouldBe(3 == null ?? 3, false); +shouldBe(3 != null ?? 3, true); +shouldBe(3 === null ?? 3, false); +shouldBe(3 !== null ?? 3, true); +shouldBe(1 < null ?? 3, false); +shouldBe(1 > null ?? 3, true); +shouldBe(1 <= null ?? 3, false); +shouldBe(1 >= null ?? 3, true); +shouldBe(1 << null ?? 3, 1); +shouldBe(1 >> null ?? 3, 1); +shouldBe(1 >>> null ?? 3, 1); +shouldBe(1 + null ?? 3, 1); +shouldBe(1 - null ?? 3, 1); +shouldBe(1 * null ?? 3, 0); +shouldBe(1 / null ?? 3, Infinity); +shouldBe(isNaN(1 % null ?? 3), true); +shouldBe(1 ** null ?? 3, 1); +shouldBe((void 0) ?? 3, 3); + +const obj = { + count: 0, + get x() { this.count++; return 'x'; } +}; +false ?? obj.x; +shouldBe(obj.count, 0); +null ?? obj.x; +shouldBe(obj.count, 1); +obj.x ?? obj.x; +shouldBe(obj.count, 2); + +shouldThrowSyntaxError('0 || 1 ?? 2'); +shouldThrowSyntaxError('0 && 1 ?? 2'); +shouldThrowSyntaxError('0 ?? 1 || 2'); +shouldThrowSyntaxError('0 ?? 1 && 2'); +shouldNotThrow('(0 || 1) ?? 2'); +shouldNotThrow('0 || (1 ?? 2)'); +shouldNotThrow('(0 && 1) ?? 2'); +shouldNotThrow('0 && (1 ?? 2)'); +shouldNotThrow('(0 ?? 1) || 2'); +shouldNotThrow('0 ?? (1 || 2)'); +shouldNotThrow('(0 ?? 1) && 2'); +shouldNotThrow('0 ?? (1 && 2)'); + +shouldNotThrow('0 || 1 && 2 | 3 ^ 4 & 5 == 6 != 7 === 8 !== 9 < 0 > 1 <= 2 >= 3 << 4 >> 5 >>> 6 + 7 - 8 * 9 / 0 % 1 ** 2'); +shouldThrowSyntaxError('0 || 1 && 2 | 3 ^ 4 & 5 == 6 != 7 === 8 !== 9 < 0 > 1 <= 2 >= 3 << 4 >> 5 >>> 6 + 7 - 8 * 9 / 0 % 1 ** 2 ?? 3'); +shouldThrowSyntaxError('3 ?? 2 ** 1 % 0 / 9 * 8 - 7 + 6 >>> 5 >> 4 << 3 >= 2 <= 1 > 0 < 9 !== 8 === 7 != 6 == 5 & 4 ^ 3 | 2 && 1 || 0'); + +shouldBe(null?.x ?? 3, 3); +shouldBe(({})?.x ?? 3, 3); +shouldBe(({ x: 0 })?.x ?? 3, 0); +shouldBe(null?.() ?? 3, 3); +shouldBe((() => 0)?.() ?? 3, 0); +shouldBe(({ x: 0 })?.[null?.a ?? 'x'] ?? 3, 0); +shouldBe((() => 0)?.(null?.a ?? 'x') ?? 3, 0); + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); + diff --git a/js/src/tests/non262/expressions/object-literal-__proto__.js b/js/src/tests/non262/expressions/object-literal-__proto__.js new file mode 100644 index 0000000000..531ef7fc5c --- /dev/null +++ b/js/src/tests/non262/expressions/object-literal-__proto__.js @@ -0,0 +1,267 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 1061853; +var summary = + "__proto__ in object literals in non-__proto__:v contexts doesn't modify " + + "[[Prototype]]"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +function hasOwn(obj, prop) +{ + return Object.getOwnPropertyDescriptor(obj, prop) !== undefined; +} + +var objectStart = "{ "; +var objectEnd = " }"; + +var members = + { + nullProto: "__proto__: null", + functionProtoProto: "__proto__: Function.prototype", + computedNull: "['__proto__']: null", + method: "__proto__() {}", + computedMethod: "['__proto__']() {}", + generatorMethod: "*__proto__() {}", + computedGenerator: "*['__proto__']() {}", + shorthand: "__proto__", + getter: "get __proto__() { return 42; }", + getterComputed: "get ['__proto__']() { return 42; }", + setter: "set __proto__(v) { }", + setterComputed: "set ['__proto__'](v) { }", + }; + +function isProtoMutation(key) +{ + return key === "nullProto" || key === "functionProtoProto"; +} + +function isGetter(key) +{ + return key === "getter" || key === "getterComputed"; +} + +function isSetter(key) +{ + return key === "setter" || key === "setterComputed"; +} + +function isData(key) +{ + return !isProtoMutation(key) && !isGetter(key) && !isSetter(key); +} + +var __proto__ = "string value"; + +function typeOfProto(key) +{ + if (key === "computedNull") + return "object"; + if (key === "method" || key === "computedMethod" || + key === "computedGenerator" || key === "generatorMethod") + { + return "function"; + } + if (key === "getter" || key === "getterComputed") + return "number"; + assertEq(key, "shorthand", "bug in test!"); + return "string"; +} + +for (var first in members) +{ + var fcode = "return " + objectStart + members[first] + objectEnd; + var f = Function(fcode); + var oneProp = f(); + + if (first === "nullProto") + { + assertEq(Object.getPrototypeOf(oneProp), null); + assertEq(hasOwn(oneProp, "__proto__"), false); + } + else if (first === "functionProtoProto") + { + assertEq(Object.getPrototypeOf(oneProp), Function.prototype); + assertEq(hasOwn(oneProp, "__proto__"), false); + } + else if (isSetter(first)) + { + assertEq(Object.getPrototypeOf(oneProp), Object.prototype); + assertEq(hasOwn(oneProp, "__proto__"), true); + assertEq(typeof Object.getOwnPropertyDescriptor(oneProp, "__proto__").set, + "function"); + } + else + { + assertEq(Object.getPrototypeOf(oneProp), Object.prototype); + assertEq(hasOwn(oneProp, "__proto__"), true); + assertEq(typeof oneProp.__proto__, typeOfProto(first)); + } + + for (var second in members) + { + try + { + var gcode = "return " + objectStart + members[first] + ", " + + members[second] + objectEnd; + var g = Function(gcode); + } + catch (e) + { + assertEq(e instanceof SyntaxError, true, + "__proto__ member conflicts should be syntax errors, got " + e); + assertEq(+(first === "nullProto" || first === "functionProtoProto") + + +(second === "nullProto" || second === "functionProtoProto") > 1, + true, + "unexpected conflict between members: " + first + ", " + second); + continue; + } + + var twoProps = g(); + + if (first === "nullProto" || second === "nullProto") + assertEq(Object.getPrototypeOf(twoProps), null); + else if (first === "functionProtoProto" || second === "functionProtoProto") + assertEq(Object.getPrototypeOf(twoProps), Function.prototype); + else + assertEq(Object.getPrototypeOf(twoProps), Object.prototype); + + if (isSetter(second)) + { + assertEq(hasOwn(twoProps, "__proto__"), true); + assertEq(typeof Object.getOwnPropertyDescriptor(twoProps, "__proto__").get, + isGetter(first) ? "function" : "undefined"); + } + else if (!isProtoMutation(second)) + { + assertEq(hasOwn(twoProps, "__proto__"), true); + assertEq(typeof twoProps.__proto__, typeOfProto(second)); + if (isGetter(second)) + { + assertEq(typeof Object.getOwnPropertyDescriptor(twoProps, "__proto__").get, + "function"); + assertEq(typeof Object.getOwnPropertyDescriptor(twoProps, "__proto__").set, + isSetter(first) ? "function" : "undefined"); + } + } + else if (isSetter(first)) + { + assertEq(hasOwn(twoProps, "__proto__"), true); + assertEq(typeof Object.getOwnPropertyDescriptor(twoProps, "__proto__").set, + "function"); + assertEq(typeof Object.getOwnPropertyDescriptor(twoProps, "__proto__").get, + "undefined"); + } + else if (!isProtoMutation(first)) + { + assertEq(hasOwn(twoProps, "__proto__"), true); + assertEq(typeof twoProps.__proto__, typeOfProto(first)); + } + else + { + assertEq(true, false, "should be unreachable: " + first + ", " + second); + } + + for (var third in members) + { + try + { + var hcode = "return " + objectStart + members[first] + ", " + + members[second] + ", " + + members[third] + objectEnd; + var h = Function(hcode); + } + catch (e) + { + assertEq(e instanceof SyntaxError, true, + "__proto__ member conflicts should be syntax errors, got " + e); + assertEq(+(first === "nullProto" || first === "functionProtoProto") + + +(second === "nullProto" || second === "functionProtoProto") + + +(third === "nullProto" || third === "functionProtoProto") > 1, + true, + "unexpected conflict among members: " + + first + ", " + second + ", " + third); + continue; + } + + var threeProps = h(); + + if (first === "nullProto" || second === "nullProto" || + third === "nullProto") + { + assertEq(Object.getPrototypeOf(threeProps), null); + } + else if (first === "functionProtoProto" || + second === "functionProtoProto" || + third === "functionProtoProto") + { + assertEq(Object.getPrototypeOf(threeProps), Function.prototype); + } + else + { + assertEq(Object.getPrototypeOf(threeProps), Object.prototype); + } + + if (isSetter(third)) + { + assertEq(hasOwn(threeProps, "__proto__"), true); + assertEq(typeof Object.getOwnPropertyDescriptor(threeProps, "__proto__").get, + isGetter(second) || (!isData(second) && isGetter(first)) + ? "function" + : "undefined", + "\n" + hcode); + } + else if (!isProtoMutation(third)) + { + assertEq(hasOwn(threeProps, "__proto__"), true); + assertEq(typeof threeProps.__proto__, typeOfProto(third), first + ", " + second + ", " + third); + if (isGetter(third)) + { + var desc = Object.getOwnPropertyDescriptor(threeProps, "__proto__"); + assertEq(typeof desc.get, "function"); + assertEq(typeof desc.set, + isSetter(second) || (!isData(second) && isSetter(first)) + ? "function" + : "undefined"); + } + } + else if (isSetter(second)) + { + assertEq(hasOwn(threeProps, "__proto__"), true); + assertEq(typeof Object.getOwnPropertyDescriptor(threeProps, "__proto__").get, + isGetter(first) ? "function" : "undefined"); + } + else if (!isProtoMutation(second)) + { + assertEq(hasOwn(threeProps, "__proto__"), true); + assertEq(typeof threeProps.__proto__, typeOfProto(second)); + if (isGetter(second)) + { + var desc = Object.getOwnPropertyDescriptor(threeProps, "__proto__"); + assertEq(typeof desc.get, "function"); + assertEq(typeof desc.set, + isSetter(first) ? "function" : "undefined"); + } + } + else + { + assertEq(true, false, + "should be unreachable: " + + first + ", " + second + ", " + third); + } + } + } +} + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/expressions/object-literal-accessor-arguments.js b/js/src/tests/non262/expressions/object-literal-accessor-arguments.js new file mode 100644 index 0000000000..5b6e7e672f --- /dev/null +++ b/js/src/tests/non262/expressions/object-literal-accessor-arguments.js @@ -0,0 +1,42 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +var gTestfile = 'object-literal-accessor-arguments.js'; +//----------------------------------------------------------------------------- +var BUGNUMBER = 536472; +var summary = + 'ES5: { get x(v) { } } and { set x(v, v2) { } } should be syntax errors'; + +print(BUGNUMBER + ": " + summary); + +//----------------------------------------------------------------------------- + +function expectSyntaxError(s) +{ + try + { + eval(s); + throw new Error("no error thrown"); + } + catch (e) + { + assertEq(e instanceof SyntaxError, true, + "expected syntax error parsing '" + s + "', got: " + e); + } +} + +expectSyntaxError("({ get x(a) { } })"); +expectSyntaxError("({ get x(a, a) { } })"); +expectSyntaxError("({ get x(a, b) { } })"); +expectSyntaxError("({ get x(a, a, b) { } })"); +expectSyntaxError("({ get x(a, b, c) { } })"); + +expectSyntaxError("({ set x() { } })"); +expectSyntaxError("({ set x(a, a) { } })"); +expectSyntaxError("({ set x(a, b) { } })"); +expectSyntaxError("({ set x(a, a, b) { } })"); +expectSyntaxError("({ set x(a, b, c) { } })"); + +//----------------------------------------------------------------------------- + +reportCompare(true, true); diff --git a/js/src/tests/non262/expressions/object-literal-accessor-property-name.js b/js/src/tests/non262/expressions/object-literal-accessor-property-name.js new file mode 100644 index 0000000000..11a7f20e04 --- /dev/null +++ b/js/src/tests/non262/expressions/object-literal-accessor-property-name.js @@ -0,0 +1,29 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +var gTestfile = 'object-literal-accessor-property-name.js'; +//----------------------------------------------------------------------------- +var BUGNUMBER = 715682; +var summary = + "Permit numbers and strings containing numbers as accessor property names"; +print(BUGNUMBER + ": " + summary); + +//----------------------------------------------------------------------------- + +({ get "0"() { } }); +({ get 0() { } }); +({ get 0.0() { } }); +({ get 0.() { } }); +({ get 1.() { } }); +({ get 5.2322341234123() { } }); + +({ set "0"(q) { } }); +({ set 0(q) { } }); +({ set 0.0(q) { } }); +({ set 0.(q) { } }); +({ set 1.(q) { } }); +({ set 5.2322341234123(q) { } }); + +//----------------------------------------------------------------------------- + +reportCompare(true, true); diff --git a/js/src/tests/non262/expressions/object-literal-computed-property-evaluation.js b/js/src/tests/non262/expressions/object-literal-computed-property-evaluation.js new file mode 100644 index 0000000000..d26783d355 --- /dev/null +++ b/js/src/tests/non262/expressions/object-literal-computed-property-evaluation.js @@ -0,0 +1,38 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 1199546; +var summary = + "Convert computed property name expressions to property key before " + + "evaluating the property's value"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var s = "foo"; +var convertsToS = { toString() { return s; } }; + +var o = { + [convertsToS]: // after ToPropertyKey becomes "foo" + (function() { + s = 'bar'; + return 'abc'; // so we have "foo": "bar" for the first property + })(), + + [convertsToS]: // |s| was set above to "bar", so after ToPropertyKey, "bar" + 'efg' // so we have "bar": "efg" for the second property +}; + +assertEq(o.foo, "abc"); +assertEq(o.bar, "efg"); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/expressions/octal-literals.js b/js/src/tests/non262/expressions/octal-literals.js new file mode 100644 index 0000000000..abeef8736f --- /dev/null +++ b/js/src/tests/non262/expressions/octal-literals.js @@ -0,0 +1,103 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 894026; +var summary = "Implement ES6 octal literals"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var chars = ['o', 'O']; + +for (var i = 0; i < 8; i++) +{ + if (i === 8) + { + chars.forEach(function(v) + { + try + { + eval('0' + v + i); + throw "didn't throw"; + } + catch (e) + { + assertEq(e instanceof SyntaxError, true, + "no syntax error evaluating 0" + v + i + ", " + + "got " + e); + } + }); + continue; + } + + for (var j = 0; j < 8; j++) + { + if (j === 8) + { + chars.forEach(function(v) + { + try + { + eval('0' + v + i + j); + throw "didn't throw"; + } + catch (e) + { + assertEq(e instanceof SyntaxError, true, + "no syntax error evaluating 0" + v + i + j + ", " + + "got " + e); + } + }); + continue; + } + + for (var k = 0; k < 8; k++) + { + if (k === 8) + { + chars.forEach(function(v) + { + try + { + eval('0' + v + i + j + k); + throw "didn't throw"; + } + catch (e) + { + assertEq(e instanceof SyntaxError, true, + "no syntax error evaluating 0" + v + i + j + k + ", " + + "got " + e); + } + }); + continue; + } + + chars.forEach(function(v) + { + assertEq(eval('0' + v + i + j + k), i * 64 + j * 8 + k); + }); + } + } +} + +// Off-by-one check: '/' immediately precedes '0'. +assertEq(0o110/2, 36); +assertEq(0O644/2, 210); + +function strict() +{ + "use strict"; + return 0o755; +} +assertEq(strict(), 7 * 64 + 5 * 8 + 5); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/expressions/optional-chain-class-heritage.js b/js/src/tests/non262/expressions/optional-chain-class-heritage.js new file mode 100644 index 0000000000..14a99c6392 --- /dev/null +++ b/js/src/tests/non262/expressions/optional-chain-class-heritage.js @@ -0,0 +1,10 @@ +// Optional expression can be part of a class heritage expression. + +var a = {b: null}; + +class C extends a?.b {} + +assertEq(Object.getPrototypeOf(C.prototype), null); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/expressions/optional-chain-first-expression.js b/js/src/tests/non262/expressions/optional-chain-first-expression.js new file mode 100644 index 0000000000..89912aec4a --- /dev/null +++ b/js/src/tests/non262/expressions/optional-chain-first-expression.js @@ -0,0 +1,92 @@ +// Verify bytecode emitter accepts all valid optional chain first expressions. + +const expressions = [ + // https://tc39.es/ecma262/#sec-primary-expression + "this", + "ident", + "null", + "true", + "false", + "123", + "123n", + "'str'", + "[]", + "{}", + "function(){}", + "class{}", + "function*(){}", + "async function(){}", + "async function*(){}", + "/a/", + "`str`", + "(a + b)", + + // https://tc39.es/ecma262/#sec-left-hand-side-expressions + "a[b]", + "a.b", + "a``", + "super[a]", + "super.a", + "new.target", + "import.meta", + "new C()", + "new C", + "f()", + "super()", + "a?.b", + "a?.[b]", + "a?.()", + "a?.``", +]; + +function tryParse(s, f = Function) { + try { f(s); } catch {} +} + +function tryRun(s, f = Function) { + try { f(s)(); } catch {} +} + +for (let expr of expressions) { + // Evaluate in an expression context. + tryRun(`void (${expr}?.());`); + tryRun(`void (${expr}?.p());`); + + // Also try parenthesized. + tryRun(`void ((${expr})?.());`); + tryRun(`void ((${expr})?.p());`); +} + +function inClassConstructor(s) { + return `class C { constructor() { ${s} } }`; +} + +for (let expr of ["super[a]", "super.a", "super()"]) { + // Evaluate in an expression context. + tryRun(inClassConstructor(`void (${expr}?.());`)); + tryRun(inClassConstructor(`void (${expr}?.p());`)); + + // Also try parenthesized. + tryRun(inClassConstructor(`void ((${expr})?.());`)); + tryRun(inClassConstructor(`void ((${expr})?.p());`)); +} + +if (typeof parseModule === "function") { + const expressions = [ + "import.meta", + "import('')", + ]; + + for (let expr of expressions) { + // Evaluate in an expression context. + tryParse(`void (${expr}?.());`, parseModule); + tryParse(`void (${expr}?.p());`, parseModule); + + // Also try parenthesized. + tryParse(`void ((${expr})?.());`, parseModule); + tryParse(`void ((${expr})?.p());`, parseModule); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/expressions/optional-chain-super-elem.js b/js/src/tests/non262/expressions/optional-chain-super-elem.js new file mode 100644 index 0000000000..3b912a9ada --- /dev/null +++ b/js/src/tests/non262/expressions/optional-chain-super-elem.js @@ -0,0 +1,12 @@ +// Don't assert. + +var obj = { + m() { + super[0]?.a + } +}; + +obj.m(); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/expressions/optional-chain-tdz.js b/js/src/tests/non262/expressions/optional-chain-tdz.js new file mode 100644 index 0000000000..e12d0fb860 --- /dev/null +++ b/js/src/tests/non262/expressions/optional-chain-tdz.js @@ -0,0 +1,28 @@ +// Test TDZ for optional chaining. + +// TDZ for lexical |let| bindings with optional chaining. +{ + assertThrowsInstanceOf(() => { + const Null = null; + Null?.[b]; + b = 0; + let b; + }, ReferenceError); + + assertThrowsInstanceOf(() => { + const Null = null; + Null?.[b](); + b = 0; + let b; + }, ReferenceError); + + assertThrowsInstanceOf(() => { + const Null = null; + delete Null?.[b]; + b = 0; + let b; + }, ReferenceError); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/expressions/optional-chain.js b/js/src/tests/non262/expressions/optional-chain.js new file mode 100644 index 0000000000..0845758022 --- /dev/null +++ b/js/src/tests/non262/expressions/optional-chain.js @@ -0,0 +1,263 @@ +var BUGNUMBER = 1566143; +var summary = "Implement the Optional Chain operator (?.) proposal"; + +print(BUGNUMBER + ": " + summary); + +// These tests are originally from webkit. +// webkit specifics have been removed and error messages have been updated. +function shouldBe(actual, expected) { + if (actual !== expected) + throw new Error(`expected ${expected} but got ${actual}`); +} + +function shouldThrowSyntaxError(script) { + let error; + try { + eval(script); + } catch (e) { + error = e; + } + + if (!(error instanceof SyntaxError)) + throw new Error('Expected SyntaxError!'); +} + +function shouldNotThrowSyntaxError(script) { + let error; + try { + eval(script); + } catch (e) { + error = e; + } + + if ((error instanceof SyntaxError)) + throw new Error('Unxpected SyntaxError!'); +} + +function shouldThrowTypeError(func, messagePrefix) { + let error; + try { + func(); + } catch (e) { + error = e; + } + + if (!(error instanceof TypeError)) + throw new Error('Expected TypeError!'); + + if (!error.message.includes(messagePrefix)) + throw new Error(`TypeError has wrong message!, expected ${messagePrefix} but got ${error.message}`); +} + +function shouldThrowReferenceError(script) { + let error; + try { + eval(script); + } catch (e) { + error = e; + } + + if (!(error instanceof ReferenceError)) + throw new Error('Expected ReferenceError!'); +} + +function testBasicSuccessCases() { + shouldBe(undefined?.valueOf(), undefined); + shouldBe(null?.valueOf(), undefined); + shouldBe(true?.valueOf(), true); + shouldBe(false?.valueOf(), false); + shouldBe(0?.valueOf(), 0); + shouldBe(1?.valueOf(), 1); + shouldBe(''?.valueOf(), ''); + shouldBe('hi'?.valueOf(), 'hi'); + shouldBe(({})?.constructor, Object); + shouldBe(({ x: 'hi' })?.x, 'hi'); + shouldBe([]?.length, 0); + shouldBe(['hi']?.length, 1); + + shouldBe(undefined?.['valueOf'](), undefined); + shouldBe(null?.['valueOf'](), undefined); + shouldBe(true?.['valueOf'](), true); + shouldBe(false?.['valueOf'](), false); + shouldBe(0?.['valueOf'](), 0); + shouldBe(1?.['valueOf'](), 1); + shouldBe(''?.['valueOf'](), ''); + shouldBe('hi'?.['valueOf'](), 'hi'); + shouldBe(({})?.['constructor'], Object); + shouldBe(({ x: 'hi' })?.['x'], 'hi'); + shouldBe([]?.['length'], 0); + shouldBe(['hi']?.[0], 'hi'); + + shouldBe(undefined?.(), undefined); + shouldBe(null?.(), undefined); + shouldBe((() => 3)?.(), 3); +} + +function testBasicFailureCases() { + shouldThrowTypeError(() => true?.(), 'true is not a function'); + shouldThrowTypeError(() => false?.(), 'false is not a function'); + shouldThrowTypeError(() => 0?.(), '0 is not a function'); + shouldThrowTypeError(() => 1?.(), '1 is not a function'); + shouldThrowTypeError(() => ''?.(), '"" is not a function'); + shouldThrowTypeError(() => 'hi'?.(), '"hi" is not a function'); + shouldThrowTypeError(() => ({})?.(), '({}) is not a function'); + shouldThrowTypeError(() => ({ x: 'hi' })?.(), '({x:"hi"}) is not a function'); + shouldThrowTypeError(() => []?.(), '[] is not a function'); + shouldThrowTypeError(() => ['hi']?.(), '[...] is not a function'); +} + +testBasicSuccessCases(); + +testBasicFailureCases(); + +shouldThrowTypeError(() => ({})?.i(), '(intermediate value).i is not a function'); +shouldBe(({}).i?.(), undefined); +shouldBe(({})?.i?.(), undefined); +shouldThrowTypeError(() => ({})?.['i'](), '(intermediate value)["i"] is not a function'); +shouldBe(({})['i']?.(), undefined); +shouldBe(({})?.['i']?.(), undefined); + +shouldThrowTypeError(() => ({})?.a['b'], '(intermediate value).a is undefined'); +shouldBe(({})?.a?.['b'], undefined); +shouldBe(null?.a['b']().c, undefined); +shouldThrowTypeError(() => ({})?.['a'].b, '(intermediate value)["a"] is undefined'); +shouldBe(({})?.['a']?.b, undefined); +shouldBe(null?.['a'].b()['c'], undefined); +shouldBe(null?.()().a['b'], undefined); + +const o0 = { a: { b() { return this._b.bind(this); }, _b() { return this.__b; }, __b: { c: 42 } } }; +shouldBe(o0?.a?.['b']?.()?.()?.c, 42); +shouldBe(o0?.i?.['j']?.()?.()?.k, undefined); +shouldBe((o0.a?._b)?.().c, 42); +shouldBe((o0.a?._b)().c, 42); +shouldBe((o0.a?.b?.())?.().c, 42); +shouldBe((o0.a?.['b']?.())?.().c, 42); + +shouldBe(({ undefined: 3 })?.[null?.a], 3); +shouldBe((() => 3)?.(null?.a), 3); + +const o1 = { count: 0, get x() { this.count++; return () => {}; } }; +o1.x?.y; +shouldBe(o1.count, 1); +o1.x?.['y']; +shouldBe(o1.count, 2); +o1.x?.(); +shouldBe(o1.count, 3); +null?.(o1.x); +shouldBe(o1.count, 3); + +shouldBe(delete undefined?.foo, true); +shouldBe(delete null?.foo, true); +shouldBe(delete undefined?.['foo'], true); +shouldBe(delete null?.['foo'], true); +shouldBe(delete undefined?.(), true); +shouldBe(delete null?.(), true); +shouldBe(delete ({}).a?.b?.b, true); +shouldBe(delete ({a : {b: undefined}}).a?.b?.b, true); +shouldBe(delete ({a : {b: undefined}}).a?.["b"]?.["b"], true); + +const o2 = { x: 0, y: 0, z() {} }; +shouldBe(delete o2?.x, true); +shouldBe(o2.x, undefined); +shouldBe(o2.y, 0); +shouldBe(delete o2?.x, true); +shouldBe(delete o2?.['y'], true); +shouldBe(o2.y, undefined); +shouldBe(delete o2?.['y'], true); +shouldBe(delete o2.z?.(), true); + +function greet(name) { return `hey, ${name}${this.suffix ?? '.'}`; } +shouldBe(eval?.('greet("world")'), 'hey, world.'); +shouldBe(greet?.call({ suffix: '!' }, 'world'), 'hey, world!'); +shouldBe(greet.call?.({ suffix: '!' }, 'world'), 'hey, world!'); +shouldBe(null?.call({ suffix: '!' }, 'world'), undefined); +shouldBe(({}).call?.({ suffix: '!' }, 'world'), undefined); +shouldBe(greet?.apply({ suffix: '?' }, ['world']), 'hey, world?'); +shouldBe(greet.apply?.({ suffix: '?' }, ['world']), 'hey, world?'); +shouldBe(null?.apply({ suffix: '?' }, ['world']), undefined); +shouldBe(({}).apply?.({ suffix: '?' }, ['world']), undefined); +shouldThrowSyntaxError('class C {} class D extends C { foo() { return super?.bar; } }'); +shouldThrowSyntaxError('class C {} class D extends C { foo() { return super?.["bar"]; } }'); +shouldThrowSyntaxError('class C {} class D extends C { constructor() { super?.(); } }'); +shouldThrowSyntaxError('const o = { C: class {} }; new o?.C();') +shouldThrowSyntaxError('const o = { C: class {} }; new o?.["C"]();') +shouldThrowSyntaxError('class C {} new C?.();') +shouldThrowSyntaxError('function foo() { new?.target; }'); +shouldThrowSyntaxError('function tag() {} tag?.``;'); +shouldThrowSyntaxError('const o = { tag() {} }; o?.tag``;'); +shouldThrowReferenceError('`${G}`?.r'); + +// NOT an optional chain +shouldBe(false?.4:5, 5); + +// Special case: binary operators that follow a binary expression +shouldThrowReferenceError('(0 || 1 << x)?.$'); +shouldThrowReferenceError('(0 || 1 >> x)?.$'); +shouldThrowReferenceError('(0 || 1 >>> x)?.$'); +shouldThrowReferenceError('(0 || 1 + x)?.$'); +shouldThrowReferenceError('(0 || 1 - x)?.$'); +shouldThrowReferenceError('(0 || 1 % x)?.$'); +shouldThrowReferenceError('(0 || 1 / x)?.$'); +shouldThrowReferenceError('(0 || 1 == x)?.$'); +shouldThrowReferenceError('(0 || 1 != x)?.$'); +shouldThrowReferenceError('(0 || 1 !== x)?.$'); +shouldThrowReferenceError('(0 || 1 === x)?.$'); +shouldThrowReferenceError('(0 || 1 <= x)?.$'); +shouldThrowReferenceError('(0 || 1 >= x)?.$'); +shouldThrowReferenceError('(0 || 1 ** x)?.$'); +shouldThrowReferenceError('(0 || 1 | x)?.$'); +shouldThrowReferenceError('(0 || 1 & x)?.$'); +shouldThrowReferenceError('(0 || 1 instanceof x)?.$'); +shouldThrowReferenceError('(0 || "foo" in x)?.$'); + +function testSideEffectCountFunction() { + let count = 0; + let a = { + b: { + c: { + d: () => { + count++; + return a; + } + } + } + } + + a.b.c.d?.()?.b?.c?.d + + shouldBe(count, 1); +} + +function testSideEffectCountGetters() { + let count = 0; + let a = { + get b() { + count++; + return { c: {} }; + } + } + + a.b?.c?.d; + shouldBe(count, 1); + a.b?.c?.d; + shouldBe(count, 2); +} + +testSideEffectCountFunction(); +testSideEffectCountGetters(); + +// stress test SM +shouldBe(({a : {b: undefined}}).a.b?.()()(), undefined); +shouldBe(({a : {b: undefined}}).a.b?.()?.()(), undefined); +shouldBe(({a : {b: () => undefined}}).a.b?.()?.(), undefined); +shouldThrowTypeError(() => delete ({a : {b: undefined}}).a?.b.b.c, '(intermediate value).a.b is undefined'); +shouldBe(delete ({a : {b: undefined}}).a?.["b"]?.["b"], true); +shouldBe(delete undefined ?.x[y+1], true); +shouldThrowTypeError(() => (({a : {b: () => undefined}}).a.b?.())(), 'undefined is not a function'); +shouldThrowTypeError(() => (delete[1]?.r[delete[1]?.r1]), "[...].r is undefined"); +shouldThrowTypeError(() => (delete[1]?.r[[1]?.r1]), "[...].r is undefined"); + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/expressions/primitive-this-boxing-behavior.js b/js/src/tests/non262/expressions/primitive-this-boxing-behavior.js new file mode 100644 index 0000000000..36cc0e9e8b --- /dev/null +++ b/js/src/tests/non262/expressions/primitive-this-boxing-behavior.js @@ -0,0 +1,106 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 732669; +var summary = "Primitive values don't box correctly"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var t; +function returnThis() { return this; } + +// Boolean + +Boolean.prototype.method = returnThis; +t = true.method(); +assertEq(t !== Boolean.prototype, true); +assertEq(t.toString(), "true"); + +Object.defineProperty(Boolean.prototype, "property", { get: returnThis, configurable: true }); +t = false.property; +assertEq(t !== Boolean.prototype, true); +assertEq(t.toString(), "false"); + +delete Boolean.prototype.method; +delete Boolean.prototype.property; + + +// Number + +Number.prototype.method = returnThis; +t = 5..method(); +assertEq(t !== Number.prototype, true); +assertEq(t.toString(), "5"); + +Object.defineProperty(Number.prototype, "property", { get: returnThis, configurable: true }); +t = 17..property; +assertEq(t !== Number.prototype, true); +assertEq(t.toString(), "17"); + +delete Number.prototype.method; +delete Number.prototype.property; + + +// String + +String.prototype.method = returnThis; +t = "foo".method(); +assertEq(t !== String.prototype, true); +assertEq(t.toString(), "foo"); + +Object.defineProperty(String.prototype, "property", { get: returnThis, configurable: true }); +t = "bar".property; +assertEq(t !== String.prototype, true); +assertEq(t.toString(), "bar"); + +delete String.prototype.method; +delete String.prototype.property; + + +// Object + +Object.prototype.method = returnThis; + +t = true.method(); +assertEq(t !== Object.prototype, true); +assertEq(t !== Boolean.prototype, true); +assertEq(t.toString(), "true"); + +t = 42..method(); +assertEq(t !== Object.prototype, true); +assertEq(t !== Number.prototype, true); +assertEq(t.toString(), "42"); + +t = "foo".method(); +assertEq(t !== Object.prototype, true); +assertEq(t !== String.prototype, true); +assertEq(t.toString(), "foo"); + +Object.defineProperty(Object.prototype, "property", { get: returnThis, configurable: true }); + +t = false.property; +assertEq(t !== Object.prototype, true); +assertEq(t !== Boolean.prototype, true); +assertEq(t.toString(), "false"); + +t = 8675309..property; +assertEq(t !== Object.prototype, true); +assertEq(t !== Number.prototype, true); +assertEq(t.toString(), "8675309"); + +t = "bar".property; +assertEq(t !== Object.prototype, true); +assertEq(t !== String.prototype, true); +assertEq(t.toString(), "bar"); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/expressions/regress-192288.js b/js/src/tests/non262/expressions/regress-192288.js new file mode 100644 index 0000000000..80e2563bdb --- /dev/null +++ b/js/src/tests/non262/expressions/regress-192288.js @@ -0,0 +1,82 @@ +/* -*- 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: 07 February 2003 + * SUMMARY: Testing 0/0 inside functions + * + * See http://bugzilla.mozilla.org/show_bug.cgi?id=192288 + * + */ +//----------------------------------------------------------------------------- +var UBound = 0; +var BUGNUMBER = 192288; +var summary = 'Testing 0/0 inside functions '; +var status = ''; +var statusitems = []; +var actual = ''; +var actualvalues = []; +var expect= ''; +var expectedvalues = []; + + +function f() +{ + return 0/0; +} + +status = inSection(1); +actual = isNaN(f()); +expect = true; +addThis(); + +status = inSection(2); +actual = isNaN(f.apply(this)); +expect = true; +addThis(); + +status = inSection(3); +actual = isNaN(eval("f.apply(this)")); +expect = true; +addThis(); + +status = inSection(4); +actual = isNaN(Function('return 0/0;')()); +expect = true; +addThis(); + +status = inSection(5); +actual = isNaN(eval("Function('return 0/0;')()")); +expect = true; +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/expressions/regress-346203.js b/js/src/tests/non262/expressions/regress-346203.js new file mode 100644 index 0000000000..68d2c49077 --- /dev/null +++ b/js/src/tests/non262/expressions/regress-346203.js @@ -0,0 +1,25 @@ +/* -*- 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 = 346203; +var summary = 'Do not crash during destructuring assignment '; +var actual = 'No Crash'; +var expect = 'No Crash'; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + var {b:{c:x}}={b:{c:1}} + + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/expressions/regress-346645-01.js b/js/src/tests/non262/expressions/regress-346645-01.js new file mode 100644 index 0000000000..ac22628946 --- /dev/null +++ b/js/src/tests/non262/expressions/regress-346645-01.js @@ -0,0 +1,31 @@ +/* -*- 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 = 346645; +var summary = 'Do not crash with empty object in destructuring assign LHS'; +var actual = 'No Crash'; +var expect = 'No Crash'; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + try + { + eval('({ a:{} }) = 3;'); + } + catch(ex) + { + } + + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/expressions/regress-346645-02.js b/js/src/tests/non262/expressions/regress-346645-02.js new file mode 100644 index 0000000000..e5ba3304b9 --- /dev/null +++ b/js/src/tests/non262/expressions/regress-346645-02.js @@ -0,0 +1,31 @@ +/* -*- 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 = 346645; +var summary = 'Do not crash with empty array in destructuring assign LHS'; +var actual = 'No Crash'; +var expect = 'No Crash'; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + try + { + eval('({ a:[] }) = 3;'); + } + catch(ex) + { + } + + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/expressions/regress-346645-03.js b/js/src/tests/non262/expressions/regress-346645-03.js new file mode 100644 index 0000000000..feddf1128f --- /dev/null +++ b/js/src/tests/non262/expressions/regress-346645-03.js @@ -0,0 +1,31 @@ +/* -*- 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 = 346645; +var summary = 'Do not crash with non-empty array in destructuring assign LHS'; +var actual = 'No Crash'; +var expect = 'No Crash'; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + try + { + eval('({ a:[z] }) = 3;'); + } + catch(ex) + { + } + + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/expressions/regress-394673.js b/js/src/tests/non262/expressions/regress-394673.js new file mode 100644 index 0000000000..8b22e65f68 --- /dev/null +++ b/js/src/tests/non262/expressions/regress-394673.js @@ -0,0 +1,49 @@ +/* -*- 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 = 394673; +var summary = 'Parsing long chains of "&&" or "||"'; +var actual = 'No Error'; +var expect = 'No Error'; + +printBugNumber(BUGNUMBER); +printStatus (summary); + +var N = 70*1000; +var counter; + +counter = 0; +var x = build("&&")(); +if (x !== true) + throw "Unexpected result: x="+x; +if (counter != N) + throw "Unexpected counter: counter="+counter; + +counter = 0; +var x = build("||")(); +if (x !== true) + throw "Unexpected result: x="+x; +if (counter != 1) + throw "Unexpected counter: counter="+counter; + +function build(operation) +{ + var counter; + var a = []; + a.push("return f()"); + for (var i = 1; i != N - 1; ++i) + a.push("f()"); + a.push("f();"); + return new Function(a.join(operation)); +} + +function f() +{ + ++counter; + return true; +} + +reportCompare(expect, actual, summary); diff --git a/js/src/tests/non262/expressions/regress-418051.js b/js/src/tests/non262/expressions/regress-418051.js new file mode 100644 index 0000000000..c70462e01a --- /dev/null +++ b/js/src/tests/non262/expressions/regress-418051.js @@ -0,0 +1,31 @@ +/* -*- 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 = 418051; +var summary = 'Do not assert: (pnkey)->pn_arity == PN_NULLARY && ' + + '((pnkey)->pn_type == TOK_NUMBER || (pnkey)->pn_type == TOK_STRING || ' + + '(pnkey)->pn_type == TOK_NAME)'; +var actual = 'No Crash'; +var expect = 'No Crash'; + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + try + { + eval("({x:[]}={x}"); + } + catch(ex) + { + } + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/expressions/regress-451340.js b/js/src/tests/non262/expressions/regress-451340.js new file mode 100644 index 0000000000..02da7bb2f3 --- /dev/null +++ b/js/src/tests/non262/expressions/regress-451340.js @@ -0,0 +1,24 @@ +/* -*- 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 = 451340; +var summary = 'Do no crash [@ CheckDestructuring]'; +var actual = 'No Crash'; +var expect = 'No Crash'; + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus (summary); + + function x([y]) { } + + reportCompare(expect, actual, summary); +} diff --git a/js/src/tests/non262/expressions/regress-96526-argsub.js b/js/src/tests/non262/expressions/regress-96526-argsub.js new file mode 100644 index 0000000000..0be5878afe --- /dev/null +++ b/js/src/tests/non262/expressions/regress-96526-argsub.js @@ -0,0 +1,88 @@ +/* -*- 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: 29 Oct 2002 + * SUMMARY: Testing "use" and "set" operations on expressions like a[i][j][k] + * See http://bugzilla.mozilla.org/show_bug.cgi?id=96526#c52 + * + * Brendan: "The idea is to cover all the 'use' and 'set' (as in modify) + * operations you can do on an expression like a[i][j][k], including variations + * where you replace |a| with arguments (literally) and |i| with 0, 1, 2, etc. + * (to hit the optimization for arguments[0]... that uses JSOP_ARGSUB)." + */ +//----------------------------------------------------------------------------- +var UBound = 0; +var BUGNUMBER = 96526; +var summary = 'Testing "use" and "set" ops on expressions like a[i][j][k]'; +var status = ''; +var statusitems = []; +var actual = ''; +var actualvalues = []; +var expect= ''; +var expectedvalues = []; + +var z='magic'; +Number.prototype.magic=42; + +status = inSection(1); +actual = f(2,1,[1,2,[3,4]]); +expect = 42; +addThis(); + + +function f(j,k) +{ + status = inSection(2); + actual = formatArray(arguments[2]); + expect = formatArray([1,2,[3,4]]); + addThis(); + + status = inSection(3); + actual = formatArray(arguments[2][j]); + expect = formatArray([3,4]); + addThis(); + + status = inSection(4); + actual = arguments[2][j][k]; + expect = 4; + addThis(); + + status = inSection(5); + actual = arguments[2][j][k][z]; + expect = 42; + addThis(); + + return arguments[2][j][k][z]; +} + + + +//----------------------------------------------------------------------------- +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/expressions/regress-96526-delelem.js b/js/src/tests/non262/expressions/regress-96526-delelem.js new file mode 100644 index 0000000000..b1fb73836f --- /dev/null +++ b/js/src/tests/non262/expressions/regress-96526-delelem.js @@ -0,0 +1,88 @@ +/* -*- 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: 29 Oct 2002 + * SUMMARY: Testing "use" and "set" operations on expressions like a[i][j][k] + * See http://bugzilla.mozilla.org/show_bug.cgi?id=96526#c52 + * + * Brendan: "The idea is to cover all the 'use' and 'set' (as in modify) + * operations you can do on an expression like a[i][j][k], including variations + * where you replace |a| with arguments (literally) and |i| with 0, 1, 2, etc. + * (to hit the optimization for arguments[0]... that uses JSOP_ARGSUB)." + */ +//----------------------------------------------------------------------------- +var UBound = 0; +var BUGNUMBER = 96526; +var summary = 'Testing "use" and "set" ops on expressions like a[i][j][k]'; +var status = ''; +var statusitems = []; +var actual = ''; +var actualvalues = []; +var expect= ''; +var expectedvalues = []; + +var z='magic'; +Number.prototype.magic=42; +f(2,1,[-1,0,[1,2,[3,4]]]); + +function f(j,k,a) +{ + status = inSection(1); + actual = formatArray(a[2]); + expect = formatArray([1,2,[3,4]]); + addThis(); + + status = inSection(2); + actual = formatArray(a[2][j]); + expect = formatArray([3,4]); + addThis(); + + status = inSection(3); + actual = a[2][j][k]; + expect = 4; + addThis(); + + status = inSection(4); + actual = a[2][j][k][z]; + expect = 42; + addThis(); + + delete a[2][j][k]; + + status = inSection(5); + actual = formatArray(a[2][j]); + expect = formatArray([3, ,]); + 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/expressions/regress-96526-noargsub.js b/js/src/tests/non262/expressions/regress-96526-noargsub.js new file mode 100644 index 0000000000..e986f1a16a --- /dev/null +++ b/js/src/tests/non262/expressions/regress-96526-noargsub.js @@ -0,0 +1,88 @@ +/* -*- 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: 29 Oct 2002 + * SUMMARY: Testing "use" and "set" operations on expressions like a[i][j][k] + * See http://bugzilla.mozilla.org/show_bug.cgi?id=96526#c52 + * + * Brendan: "The idea is to cover all the 'use' and 'set' (as in modify) + * operations you can do on an expression like a[i][j][k], including variations + * where you replace |a| with arguments (literally) and |i| with 0, 1, 2, etc. + * (to hit the optimization for arguments[0]... that uses JSOP_ARGSUB)." + */ +//----------------------------------------------------------------------------- +var UBound = 0; +var BUGNUMBER = 96526; +var summary = 'Testing "use" and "set" ops on expressions like a[i][j][k]'; +var status = ''; +var statusitems = []; +var actual = ''; +var actualvalues = []; +var expect= ''; +var expectedvalues = []; + +var z='magic'; +Number.prototype.magic=42; + +status = inSection(1); +actual = f(2,1,[-1,0,[1,2,[3,4]]]); +expect = 42; +addThis(); + + +function f(j,k,a) +{ + status = inSection(2); + actual = formatArray(a[2]); + expect = formatArray([1,2,[3,4]]); + addThis(); + + status = inSection(3); + actual = formatArray(a[2][j]); + expect = formatArray([3,4]); + addThis(); + + status = inSection(4); + actual = a[2][j][k]; + expect = 4; + addThis(); + + status = inSection(5); + actual = a[2][j][k][z]; + expect = 42; + addThis(); + + return a[2][j][k][z]; +} + + + +//----------------------------------------------------------------------------- +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/expressions/shell.js b/js/src/tests/non262/expressions/shell.js new file mode 100644 index 0000000000..bc682b79cd --- /dev/null +++ b/js/src/tests/non262/expressions/shell.js @@ -0,0 +1,288 @@ +/* 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/. */ + +(function(global) { + function func() { + } + class C { + foo() { + } + static foo() { + } + } + + function test_one(pattern, val, opt) { + var stmts = []; + var i = 0; + var c; + + stmts.push(`var ${pattern} = ${val};`); + + for (var stmt of stmts) { + if (!opt.no_plain) { + eval(` +${stmt} +`); + } + + if (!opt.no_func) { + eval(` +function f${i}() { + ${stmt} +} +f${i}(); +`); + i++; + + eval(` +var f${i} = function foo() { + ${stmt} +}; +f${i}(); +`); + i++; + + eval(` +var f${i} = () => { + ${stmt} +}; +f${i}(); +`); + i++; + } + + if (!opt.no_gen) { + eval(` +function* g${i}() { + ${stmt} +} +[...g${i}()]; +`); + i++; + + eval(` +var g${i} = function* foo() { + ${stmt} +}; +[...g${i}()]; +`); + i++; + } + + if (!opt.no_ctor) { + eval(` +class D${i} { + constructor() { + ${stmt} + } +} +new D${i}(); +`); + i++; + } + + if (!opt.no_derived_ctor) { + if (opt.no_pre_super) { + eval(` +class D${i} extends C { + constructor() { + ${stmt} + try { super(); } catch (e) {} + } +} +new D${i}(); +`); + i++; + } else { + eval(` +class D${i} extends C { + constructor() { + super(); + ${stmt} + } +} +new D${i}(); +`); + i++; + } + } + + if (!opt.no_method) { + eval(` +class D${i} extends C { + method() { + ${stmt} + } + static staticMethod() { + ${stmt} + } +} +new D${i}().method(); +D${i}.staticMethod(); +`); + i++; + } + } + + if (!opt.no_func_arg) { + eval(` +function f${i}(${pattern}) {} +f${i}(${val}); +`); + i++; + + eval(` +var f${i} = function foo(${pattern}) {}; +f${i}(${val}); +`); + i++; + + eval(` +var f${i} = (${pattern}) => {}; +f${i}(${val}); +`); + i++; + } + + if (!opt.no_gen_arg) { + eval(` +function* g${i}(${pattern}) {} +[...g${i}(${val})]; +`); + i++; + + eval(` +var g${i} = function* foo(${pattern}) {}; +[...g${i}(${val})]; +`); + i++; + } + } + + function test(expr, opt={}) { + var pattern = `[a=${expr}, ...c]`; + test_one(pattern, "[]", opt); + test_one(pattern, "[1]", opt); + + pattern = `[,a=${expr}]`; + test_one(pattern, "[]", opt); + test_one(pattern, "[1]", opt); + test_one(pattern, "[1, 2]", opt); + + pattern = `[{x: [a=${expr}]}]`; + test_one(pattern, "[{x: [1]}]", opt); + + pattern = `[x=[a=${expr}]=[]]`; + test_one(pattern, "[]", opt); + test_one(pattern, "[1]", opt); + + pattern = `[x=[a=${expr}]=[1]]`; + test_one(pattern, "[]", opt); + test_one(pattern, "[1]", opt); + } + + global.testDestructuringArrayDefault = test; +})(this); + +(function(global) { + /* + * Date: 07 February 2001 + * + * Functionality common to Array testing - + */ + //----------------------------------------------------------------------------- + + + var CHAR_LBRACKET = '['; + var CHAR_RBRACKET = ']'; + var CHAR_QT_DBL = '"'; + var CHAR_QT = "'"; + var CHAR_NL = '\n'; + var CHAR_COMMA = ','; + var CHAR_SPACE = ' '; + var TYPE_STRING = typeof 'abc'; + + + /* + * If available, arr.toSource() gives more detail than arr.toString() + * + * var arr = Array(1,2,'3'); + * + * arr.toSource() + * [1, 2, "3"] + * + * arr.toString() + * 1,2,3 + * + * But toSource() doesn't exist in Rhino, so use our own imitation, below - + * + */ + function formatArray(arr) + { + try + { + return arr.toSource(); + } + catch(e) + { + return toSource(arr); + } + } + + global.formatArray = formatArray; + + /* + * Imitate SpiderMonkey's arr.toSource() method: + * + * a) Double-quote each array element that is of string type + * b) Represent |undefined| and |null| by empty strings + * c) Delimit elements by a comma + single space + * d) Do not add delimiter at the end UNLESS the last element is |undefined| + * e) Add square brackets to the beginning and end of the string + */ + function toSource(arr) + { + var delim = CHAR_COMMA + CHAR_SPACE; + var elt = ''; + var ret = ''; + var len = arr.length; + + for (i=0; i<len; i++) + { + elt = arr[i]; + + switch(true) + { + case (typeof elt === TYPE_STRING) : + ret += doubleQuote(elt); + break; + + case (elt === undefined || elt === null) : + break; // add nothing but the delimiter, below - + + default: + ret += elt.toString(); + } + + if ((i < len-1) || (elt === undefined)) + ret += delim; + } + + return CHAR_LBRACKET + ret + CHAR_RBRACKET; + } + + global.toSource = toSource; + + function doubleQuote(text) + { + return CHAR_QT_DBL + text + CHAR_QT_DBL; + } + + + function singleQuote(text) + { + return CHAR_QT + text + CHAR_QT; + } + +})(this); diff --git a/js/src/tests/non262/expressions/short-circuit-compound-assignment-anon-fns.js b/js/src/tests/non262/expressions/short-circuit-compound-assignment-anon-fns.js new file mode 100644 index 0000000000..406f143ead --- /dev/null +++ b/js/src/tests/non262/expressions/short-circuit-compound-assignment-anon-fns.js @@ -0,0 +1,42 @@ +// NamedEvaluation applies to short-circuit assignment. + +{ + let a; + a ??= function(){}; + assertEq(a.name, "a"); +} + +{ + let a = false; + a ||= function(){}; + assertEq(a.name, "a"); +} + +{ + let a = true; + a &&= function(){}; + assertEq(a.name, "a"); +} + +// No name assignments for parenthesised left-hand sides. + +{ + let a; + (a) ??= function(){}; + assertEq(a.name, ""); +} + +{ + let a = false; + (a) ||= function(){}; + assertEq(a.name, ""); +} + +{ + let a = true; + (a) &&= function(){}; + assertEq(a.name, ""); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/expressions/short-circuit-compound-assignment-const.js b/js/src/tests/non262/expressions/short-circuit-compound-assignment-const.js new file mode 100644 index 0000000000..8499e2fb74 --- /dev/null +++ b/js/src/tests/non262/expressions/short-circuit-compound-assignment-const.js @@ -0,0 +1,90 @@ +// Test assignment to const and function name bindings. The latter is kind of a +// const binding, but only throws in strict mode. + +function notEvaluated() { + throw new Error("should not be evaluated"); +} + +// AndAssignExpr with const lexical binding. +{ + const a = false; + a &&= notEvaluated(); + assertEq(a, false); + + const b = true; + assertThrowsInstanceOf(() => { b &&= 1; }, TypeError); + assertEq(b, true); +} + +// AndAssignExpr with function name binding. +{ + let f = function fn() { + fn &&= true; + assertEq(fn, f); + }; + f(); + + let g = function fn() { + "use strict"; + assertThrowsInstanceOf(() => { fn &&= 1; }, TypeError); + assertEq(fn, g); + }; + g(); +} + +// OrAssignExpr with const lexical binding. +{ + const a = true; + a ||= notEvaluated(); + assertEq(a, true); + + const b = false; + assertThrowsInstanceOf(() => { b ||= 0; }, TypeError); + assertEq(b, false); +} + +// OrAssignExpr with function name binding. +{ + let f = function fn() { + fn ||= notEvaluated(); + assertEq(fn, f); + }; + f(); + + let g = function fn() { + "use strict"; + fn ||= notEvaluated(); + assertEq(fn, g); + }; + g(); +} + +// CoalesceAssignExpr with const lexical binding. +{ + const a = true; + a ??= notEvaluated(); + assertEq(a, true); + + const b = null; + assertThrowsInstanceOf(() => { b ??= 0; }, TypeError); + assertEq(b, null); +} + +// CoalesceAssignExpr with function name binding. +{ + let f = function fn() { + fn ??= notEvaluated(); + assertEq(fn, f); + }; + f(); + + let g = function fn() { + "use strict"; + fn ??= notEvaluated(); + assertEq(fn, g); + }; + g(); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/expressions/short-circuit-compound-assignment-deleted-decl-binding.js b/js/src/tests/non262/expressions/short-circuit-compound-assignment-deleted-decl-binding.js new file mode 100644 index 0000000000..256278a938 --- /dev/null +++ b/js/src/tests/non262/expressions/short-circuit-compound-assignment-deleted-decl-binding.js @@ -0,0 +1,65 @@ +// Test when a declarative binding is deleted. + +// ES2020, 8.1.1.1.5 SetMutableBinding ( N, V, S ) +// +// 1. ... +// 2. If envRec does not have a binding for N, then +// a. ... +// b. Perform envRec.CreateMutableBinding(N, true). +// c. Perform envRec.InitializeBinding(N, V). +// d. Return NormalCompletion(empty). +// 3. ... + +// AndAssignExpr +{ + let a = 0; + + let f = function() { + eval("var a = 1;"); + + a &&= (delete a, 2); + + assertEq(a, 2); + } + + f(); + + assertEq(a, 0); +} + +// OrAssignExpr +{ + let a = 1; + + let f = function() { + eval("var a = 0;"); + + a ||= (delete a, 2); + + assertEq(a, 2); + } + + f(); + + assertEq(a, 1); +} + +// CoalesceAssignExpr +{ + let a = undefined; + + let f = function() { + eval("var a = null;"); + + a ??= (delete a, 2); + + assertEq(a, 2); + } + + f(); + + assertEq(a, undefined); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/expressions/short-circuit-compound-assignment-property-key-evaluation.js b/js/src/tests/non262/expressions/short-circuit-compound-assignment-property-key-evaluation.js new file mode 100644 index 0000000000..2026aa25d1 --- /dev/null +++ b/js/src/tests/non262/expressions/short-circuit-compound-assignment-property-key-evaluation.js @@ -0,0 +1,68 @@ +// Test that property keys are only evaluated once. + +class PropertyKey { + constructor(key) { + this.key = key; + this.count = 0; + } + + toString() { + this.count++; + return this.key; + } + + valueOf() { + throw new Error("unexpected valueOf call"); + } +} + +// AndAssignExpr +{ + let obj = {p: true}; + let pk = new PropertyKey("p"); + + obj[pk] &&= false; + + assertEq(obj.p, false); + assertEq(pk.count, 1); + + obj[pk] &&= true; + + assertEq(obj.p, false); + assertEq(pk.count, 2); +} + +// OrAssignExpr +{ + let obj = {p: false}; + let pk = new PropertyKey("p"); + + obj[pk] ||= true; + + assertEq(obj.p, true); + assertEq(pk.count, 1); + + obj[pk] ||= false; + + assertEq(obj.p, true); + assertEq(pk.count, 2); +} + +// CoalesceAssignExpr +{ + let obj = {p: null}; + let pk = new PropertyKey("p"); + + obj[pk] ??= true; + + assertEq(obj.p, true); + assertEq(pk.count, 1); + + obj[pk] ??= false; + + assertEq(obj.p, true); + assertEq(pk.count, 2); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/expressions/short-circuit-compound-assignment-scope-lookup.js b/js/src/tests/non262/expressions/short-circuit-compound-assignment-scope-lookup.js new file mode 100644 index 0000000000..dc1d7a421d --- /dev/null +++ b/js/src/tests/non262/expressions/short-circuit-compound-assignment-scope-lookup.js @@ -0,0 +1,191 @@ +// Test scope lookups are executed in the correct order. + +function createScope() { + let log = []; + let environment = {}; + let scope = new Proxy(environment, new Proxy({ + has(target, property) { + log.push({target, property}); + return Reflect.has(target, property); + }, + get(target, property, receiver) { + log.push({target, property, receiver}); + return Reflect.get(target, property, receiver); + }, + set(target, property, value, receiver) { + log.push({target, property, value, receiver}); + return Reflect.set(target, property, value, receiver); + }, + getOwnPropertyDescriptor(target, property) { + log.push({target, property}); + return Reflect.getOwnPropertyDescriptor(target, property); + }, + defineProperty(target, property, descriptor) { + log.push({target, property, descriptor}); + return Reflect.defineProperty(target, property, descriptor); + }, + }, { + get(target, property, receiver) { + log.push(property); + return Reflect.get(target, property, receiver); + } + })); + + return {log, environment, scope}; +} + +// AndAssignExpr +{ + let {log, environment, scope} = createScope(); + + environment.a = true; + + with (scope) { + a &&= false; + } + assertEq(environment.a, false); + + with (scope) { + a &&= true; + } + assertEq(environment.a, false); + + assertDeepEq(log, [ + // Execution Contexts, 8.3.2 ResolveBinding ( name [ , env ] ) + // Lexical Environments, 8.1.2.1 GetIdentifierReference ( lex, name, strict ) + // Object Environment Records, 8.1.1.2.1 HasBinding ( N ) + "has", {target: environment, property: "a"}, + "get", {target: environment, property: Symbol.unscopables, receiver: scope}, + + // Reference Type, 6.2.4.8 GetValue ( V ) + // Object Environment Records, 8.1.1.2.6 GetBindingValue ( N, S ) + "has", {target: environment, property: "a"}, + "get", {target: environment, property: "a", receiver: scope}, + + // Reference Type, 6.2.4.9 PutValue ( V, W ) + // Object Environment Records, 8.1.1.2.5 SetMutableBinding ( N, V, S ) + "set", {target: environment, property: "a", value: false, receiver: scope}, + + // Ordinary Objects, 9.1.9 [[Set]] ( P, V, Receiver ) + // Ordinary Objects, 9.1.9.1 OrdinarySet ( O, P, V, Receiver ) + // Ordinary Objects, 9.1.9.2 OrdinarySetWithOwnDescriptor ( O, P, V, Receiver, ownDesc ) + "getOwnPropertyDescriptor", {target: environment, property: "a"}, + "defineProperty", {target: environment, property: "a", descriptor: {value: false}}, + + // Execution Contexts, 8.3.2 ResolveBinding ( name [ , env ] ) + // Lexical Environments, 8.1.2.1 GetIdentifierReference ( lex, name, strict ) + // Object Environment Records, 8.1.1.2.1 HasBinding ( N ) + "has", {target: environment, property: "a"}, + "get", {target: environment, property: Symbol.unscopables, receiver: scope}, + + // Reference Type, 6.2.4.8 GetValue ( V ) + // Object Environment Records, 8.1.1.2.6 GetBindingValue ( N, S ) + "has", {target: environment, property: "a"}, + "get", {target: environment, property: "a", receiver: scope}, + ]); +} + +// OrAssignExpr +{ + let {log, environment, scope} = createScope(); + + environment.a = false; + + with (scope) { + a ||= true; + } + assertEq(environment.a, true); + + with (scope) { + a ||= false; + } + assertEq(environment.a, true); + + assertDeepEq(log, [ + // Execution Contexts, 8.3.2 ResolveBinding ( name [ , env ] ) + // Lexical Environments, 8.1.2.1 GetIdentifierReference ( lex, name, strict ) + // Object Environment Records, 8.1.1.2.1 HasBinding ( N ) + "has", {target: environment, property: "a"}, + "get", {target: environment, property: Symbol.unscopables, receiver: scope}, + + // Reference Type, 6.2.4.8 GetValue ( V ) + // Object Environment Records, 8.1.1.2.6 GetBindingValue ( N, S ) + "has", {target: environment, property: "a"}, + "get", {target: environment, property: "a", receiver: scope}, + + // Reference Type, 6.2.4.9 PutValue ( V, W ) + // Object Environment Records, 8.1.1.2.5 SetMutableBinding ( N, V, S ) + "set", {target: environment, property: "a", value: true, receiver: scope}, + + // Ordinary Objects, 9.1.9 [[Set]] ( P, V, Receiver ) + // Ordinary Objects, 9.1.9.1 OrdinarySet ( O, P, V, Receiver ) + // Ordinary Objects, 9.1.9.2 OrdinarySetWithOwnDescriptor ( O, P, V, Receiver, ownDesc ) + "getOwnPropertyDescriptor", {target: environment, property: "a"}, + "defineProperty", {target: environment, property: "a", descriptor: {value: true}}, + + // Execution Contexts, 8.3.2 ResolveBinding ( name [ , env ] ) + // Lexical Environments, 8.1.2.1 GetIdentifierReference ( lex, name, strict ) + // Object Environment Records, 8.1.1.2.1 HasBinding ( N ) + "has", {target: environment, property: "a"}, + "get", {target: environment, property: Symbol.unscopables, receiver: scope}, + + // Reference Type, 6.2.4.8 GetValue ( V ) + // Object Environment Records, 8.1.1.2.6 GetBindingValue ( N, S ) + "has", {target: environment, property: "a"}, + "get", {target: environment, property: "a", receiver: scope}, + ]); +} + +// CoalesceAssignExpr +{ + let {log, environment, scope} = createScope(); + + environment.a = null; + + with (scope) { + a ??= true; + } + assertEq(environment.a, true); + + with (scope) { + a ??= false; + } + assertEq(environment.a, true); + + assertDeepEq(log, [ + // Execution Contexts, 8.3.2 ResolveBinding ( name [ , env ] ) + // Lexical Environments, 8.1.2.1 GetIdentifierReference ( lex, name, strict ) + // Object Environment Records, 8.1.1.2.1 HasBinding ( N ) + "has", {target: environment, property: "a"}, + "get", {target: environment, property: Symbol.unscopables, receiver: scope}, + + // Reference Type, 6.2.4.8 GetValue ( V ) + // Object Environment Records, 8.1.1.2.6 GetBindingValue ( N, S ) + "has", {target: environment, property: "a"}, + "get", {target: environment, property: "a", receiver: scope}, + + // Reference Type, 6.2.4.9 PutValue ( V, W ) + // Object Environment Records, 8.1.1.2.5 SetMutableBinding ( N, V, S ) + "set", {target: environment, property: "a", value: true, receiver: scope}, + + // Ordinary Objects, 9.1.9 [[Set]] ( P, V, Receiver ) + // Ordinary Objects, 9.1.9.1 OrdinarySet ( O, P, V, Receiver ) + // Ordinary Objects, 9.1.9.2 OrdinarySetWithOwnDescriptor ( O, P, V, Receiver, ownDesc ) + "getOwnPropertyDescriptor", {target: environment, property: "a"}, + "defineProperty", {target: environment, property: "a", descriptor: {value: true}}, + + // Execution Contexts, 8.3.2 ResolveBinding ( name [ , env ] ) + // Lexical Environments, 8.1.2.1 GetIdentifierReference ( lex, name, strict ) + // Object Environment Records, 8.1.1.2.1 HasBinding ( N ) + "has", {target: environment, property: "a"}, + "get", {target: environment, property: Symbol.unscopables, receiver: scope}, + + // Reference Type, 6.2.4.8 GetValue ( V ) + // Object Environment Records, 8.1.1.2.6 GetBindingValue ( N, S ) + "has", {target: environment, property: "a"}, + "get", {target: environment, property: "a", receiver: scope}, + ]); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/expressions/short-circuit-compound-assignment-tdz.js b/js/src/tests/non262/expressions/short-circuit-compound-assignment-tdz.js new file mode 100644 index 0000000000..b4fcbe8043 --- /dev/null +++ b/js/src/tests/non262/expressions/short-circuit-compound-assignment-tdz.js @@ -0,0 +1,56 @@ +// Test TDZ for short-circuit compound assignments. + +// TDZ for lexical |let| bindings. +{ + assertThrowsInstanceOf(() => { let a = (a &&= 0); }, ReferenceError); + assertThrowsInstanceOf(() => { let a = (a ||= 0); }, ReferenceError); + assertThrowsInstanceOf(() => { let a = (a ??= 0); }, ReferenceError); +} + +// TDZ for lexical |const| bindings. +{ + assertThrowsInstanceOf(() => { const a = (a &&= 0); }, ReferenceError); + assertThrowsInstanceOf(() => { const a = (a ||= 0); }, ReferenceError); + assertThrowsInstanceOf(() => { const a = (a ??= 0); }, ReferenceError); +} + +// TDZ for parameter expressions. +{ + assertThrowsInstanceOf((a = (b &&= 0), b) => {}, ReferenceError); + assertThrowsInstanceOf((a = (b ||= 0), b) => {}, ReferenceError); + assertThrowsInstanceOf((a = (b ??= 0), b) => {}, ReferenceError); +} + +// TDZ for |class| bindings. +{ + assertThrowsInstanceOf(() => { class a extends (a &&= 0) {} }, ReferenceError); + assertThrowsInstanceOf(() => { class a extends (a ||= 0) {} }, ReferenceError); + assertThrowsInstanceOf(() => { class a extends (a ??= 0) {} }, ReferenceError); +} + +// TDZ for lexical |let| bindings with conditional assignment. +{ + assertThrowsInstanceOf(() => { + const False = false; + False &&= b; + b = 2; + let b; + }, ReferenceError); + + assertThrowsInstanceOf(() => { + const True = true; + True ||= b; + b = 2; + let b; + }, ReferenceError); + + assertThrowsInstanceOf(() => { + const NonNull = {}; + NonNull ??= b; + b = 2; + let b; + }, ReferenceError); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/expressions/short-circuit-compound-assignment.js b/js/src/tests/non262/expressions/short-circuit-compound-assignment.js new file mode 100644 index 0000000000..c08c4ddecf --- /dev/null +++ b/js/src/tests/non262/expressions/short-circuit-compound-assignment.js @@ -0,0 +1,265 @@ +const testCasesAnd = []; +const testCasesOr = []; +const testCasesCoalesce = []; + + +// Assignment to a global variable (JSOp::SetGName). +var globalVar; + +function testAnd_GlobalVar(init, value) { + globalVar = init; + return [(globalVar &&= value), globalVar]; +} +testCasesAnd.push(testAnd_GlobalVar); + +function testOr_GlobalVar(init, value) { + globalVar = init; + return [(globalVar ||= value), globalVar]; +} +testCasesOr.push(testOr_GlobalVar); + +function testCoalesce_GlobalVar(init, value) { + globalVar = init; + return [(globalVar ??= value), globalVar]; +} +testCasesCoalesce.push(testCoalesce_GlobalVar); + + +// Assignment to a local variable (JSOp::SetLocal). +function testAnd_LocalVar(init, value) { + let v = init; + return [(v &&= value), v]; +} +testCasesAnd.push(testAnd_LocalVar); + +function testOr_LocalVar(init, value) { + let v = init; + return [(v ||= value), v]; +} +testCasesOr.push(testOr_LocalVar); + +function testCoalesce_LocalVar(init, value) { + let v = init; + return [(v ??= value), v]; +} +testCasesCoalesce.push(testCoalesce_LocalVar); + + +// Assignment to a parameter (JSOp::SetArg). +function testAnd_Arg(init, value) { + function f(v) { + return [(v &&= value), v]; + } + return f(init); +} +testCasesAnd.push(testAnd_Arg); + +function testOr_Arg(init, value) { + function f(v) { + return [(v ||= value), v]; + } + return f(init); +} +testCasesOr.push(testOr_Arg); + +function testCoalesce_Arg(init, value) { + function f(v) { + return [(v ??= value), v]; + } + return f(init); +} +testCasesCoalesce.push(testCoalesce_Arg); + + +// Assignment to a closed over variable (JSOp::SetAliasedVar). +function testAnd_ClosedOver(init, value) { + let v = init; + function f() { + return (v &&= value); + } + return [f(), v]; +} +testCasesAnd.push(testAnd_ClosedOver); + +function testOr_ClosedOver(init, value) { + let v = init; + function f() { + return (v ||= value); + } + return [f(), v]; +} +testCasesOr.push(testOr_ClosedOver); + +function testCoalesce_ClosedOver(init, value) { + let v = init; + function f() { + return (v ??= value); + } + return [f(), v]; +} +testCasesCoalesce.push(testCoalesce_ClosedOver); + + +// Assignment to a dynamic name (JSOp::SetName). +function testAnd_DynamicName(init, value) { + eval("var v = init;"); + return [(v &&= value), v]; +} +testCasesAnd.push(testAnd_DynamicName); + +function testOr_DynamicName(init, value) { + eval("var v = init;"); + return [(v ||= value), v]; +} +testCasesOr.push(testOr_DynamicName); + +function testCoalesce_DynamicName(init, value) { + eval("var v = init;"); + return [(v ??= value), v]; +} +testCasesCoalesce.push(testCoalesce_DynamicName); + + +// Assignment to a property. +function testAnd_Property(init, value) { + let obj = {p: init}; + return [(obj.p &&= value), obj.p]; +} +testCasesAnd.push(testAnd_Property); + +function testOr_Property(init, value) { + let obj = {p: init}; + return [(obj.p ||= value), obj.p]; +} +testCasesOr.push(testOr_Property); + +function testCoalesce_Property(init, value) { + let obj = {p: init}; + return [(obj.p ??= value), obj.p]; +} +testCasesCoalesce.push(testCoalesce_Property); + + +// Assignment to a super property. +function testAnd_SuperProperty(init, value) { + let proto = {p: init}; + let obj = {__proto__: proto, m() { return (super.p &&= value); }}; + return [obj.m(), obj.p]; +} +testCasesAnd.push(testAnd_SuperProperty); + +function testOr_SuperProperty(init, value) { + let proto = {p: init}; + let obj = {__proto__: proto, m() { return (super.p ||= value); }}; + return [obj.m(), obj.p]; +} +testCasesOr.push(testOr_SuperProperty); + +function testCoalesce_SuperProperty(init, value) { + let proto = {p: init}; + let obj = {__proto__: proto, m() { return (super.p ??= value); }}; + return [obj.m(), obj.p]; +} +testCasesCoalesce.push(testCoalesce_SuperProperty); + + +// Assignment to an element. +function testAnd_Element(init, value) { + let p = 123; + let obj = {[p]: init}; + return [(obj[p] &&= value), obj[p]]; +} +testCasesAnd.push(testAnd_Element); + +function testOr_Element(init, value) { + let p = 123; + let obj = {[p]: init}; + return [(obj[p] ||= value), obj[p]]; +} +testCasesOr.push(testOr_Element); + +function testCoalesce_Element(init, value) { + let p = 123; + let obj = {[p]: init}; + return [(obj[p] ??= value), obj[p]]; +} +testCasesCoalesce.push(testCoalesce_Element); + + +// Assignment to a super element. +function testAnd_SuperElement(init, value) { + let p = 123; + let proto = {[p]: init}; + let obj = {__proto__: proto, m() { return (super[p] &&= value); }}; + return [obj.m(), obj[p]]; +} +testCasesAnd.push(testAnd_SuperElement); + +function testOr_SuperElement(init, value) { + let p = 123; + let proto = {[p]: init}; + let obj = {__proto__: proto, m() { return (super[p] ||= value); }}; + return [obj.m(), obj[p]]; +} +testCasesOr.push(testOr_SuperElement); + +function testCoalesce_SuperElement(init, value) { + let p = 123; + let proto = {[p]: init}; + let obj = {__proto__: proto, m() { return (super[p] ??= value); }}; + return [obj.m(), obj[p]]; +} +testCasesCoalesce.push(testCoalesce_SuperElement); + + +// Run the actual tests. + +function runTest(testCases, init, value, expected) { + for (let f of testCases) { + let [result, newValue] = f(init, value); + + assertEq(result, expected); + assertEq(newValue, expected); + } +} + +function testAnd(init, value) { + const expected = init ? value : init; + runTest(testCasesAnd, init, value, expected); +} + +function testOr(init, value) { + const expected = !init ? value : init; + runTest(testCasesOr, init, value, expected); +} + +function testCoalesce(init, value) { + const expected = init === undefined || init === null ? value : init; + runTest(testCasesCoalesce, init, value, expected); +} + + +// Repeat a number of times to ensure JITs can kick in, too. +for (let i = 0; i < 50; ++i) { + for (let thruthy of [true, 123, 123n, "hi", [], Symbol()]) { + testAnd(thruthy, "pass"); + testOr(thruthy, "fail"); + testCoalesce(thruthy, "fail"); + } + + for (let falsy of [false, 0, NaN, 0n, ""]) { + testAnd(falsy, "fail"); + testOr(falsy, "pass"); + testCoalesce(falsy, "fail"); + } + + for (let nullOrUndefined of [null, undefined]) { + testAnd(nullOrUndefined, "fail"); + testOr(nullOrUndefined, "pass"); + testCoalesce(nullOrUndefined, "pass"); + } +} + + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/expressions/string-literal-escape-sequences.js b/js/src/tests/non262/expressions/string-literal-escape-sequences.js new file mode 100644 index 0000000000..bc50b2c855 --- /dev/null +++ b/js/src/tests/non262/expressions/string-literal-escape-sequences.js @@ -0,0 +1,146 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 663300; +var summary = + "\\u and \\x must be followed by the appropriate number of hex digits or " + + "else it is a syntax error"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +function expectSyntaxError(str) +{ + try + { + eval(str); + } + catch (e) + { + assertEq(e instanceof SyntaxError, true, + "no syntax error evaluating " + str); + } +} + +expectSyntaxError('"\\x"'); +expectSyntaxError('"\\x0"'); +expectSyntaxError('"\\x1"'); +expectSyntaxError('"\\x2"'); +expectSyntaxError('"\\x3"'); +expectSyntaxError('"\\x4"'); +expectSyntaxError('"\\x5"'); +expectSyntaxError('"\\x6"'); +expectSyntaxError('"\\x7"'); +expectSyntaxError('"\\x8"'); +expectSyntaxError('"\\x9"'); +expectSyntaxError('"\\xA"'); +expectSyntaxError('"\\xB"'); +expectSyntaxError('"\\xC"'); +expectSyntaxError('"\\xD"'); +expectSyntaxError('"\\xE"'); +expectSyntaxError('"\\xF"'); +expectSyntaxError('"\\xG"'); +expectSyntaxError('"\\x0G"'); +expectSyntaxError('"\\x1G"'); +expectSyntaxError('"\\x2G"'); +expectSyntaxError('"\\x3G"'); +expectSyntaxError('"\\x4G"'); +expectSyntaxError('"\\x5G"'); +expectSyntaxError('"\\x6G"'); +expectSyntaxError('"\\x7G"'); +expectSyntaxError('"\\x8G"'); +expectSyntaxError('"\\x9G"'); +expectSyntaxError('"\\xAG"'); +expectSyntaxError('"\\xBG"'); +expectSyntaxError('"\\xCG"'); +expectSyntaxError('"\\xDG"'); +expectSyntaxError('"\\xEG"'); +expectSyntaxError('"\\xFG"'); +expectSyntaxError('"\\xGG"'); + +expectSyntaxError('"\\u"'); +expectSyntaxError('"\\u0"'); +expectSyntaxError('"\\u1"'); +expectSyntaxError('"\\u2"'); +expectSyntaxError('"\\u3"'); +expectSyntaxError('"\\u4"'); +expectSyntaxError('"\\u5"'); +expectSyntaxError('"\\u6"'); +expectSyntaxError('"\\u7"'); +expectSyntaxError('"\\u8"'); +expectSyntaxError('"\\u9"'); +expectSyntaxError('"\\uA"'); +expectSyntaxError('"\\uB"'); +expectSyntaxError('"\\uC"'); +expectSyntaxError('"\\uD"'); +expectSyntaxError('"\\uE"'); +expectSyntaxError('"\\uF"'); +expectSyntaxError('"\\uG"'); +expectSyntaxError('"\\u00"'); +expectSyntaxError('"\\u11"'); +expectSyntaxError('"\\u22"'); +expectSyntaxError('"\\u33"'); +expectSyntaxError('"\\u44"'); +expectSyntaxError('"\\u55"'); +expectSyntaxError('"\\u66"'); +expectSyntaxError('"\\u77"'); +expectSyntaxError('"\\u88"'); +expectSyntaxError('"\\u99"'); +expectSyntaxError('"\\uAA"'); +expectSyntaxError('"\\uBB"'); +expectSyntaxError('"\\uCC"'); +expectSyntaxError('"\\uDD"'); +expectSyntaxError('"\\uEE"'); +expectSyntaxError('"\\uFF"'); +expectSyntaxError('"\\uGG"'); +expectSyntaxError('"\\u000"'); +expectSyntaxError('"\\u111"'); +expectSyntaxError('"\\u222"'); +expectSyntaxError('"\\u333"'); +expectSyntaxError('"\\u444"'); +expectSyntaxError('"\\u555"'); +expectSyntaxError('"\\u666"'); +expectSyntaxError('"\\u777"'); +expectSyntaxError('"\\u888"'); +expectSyntaxError('"\\u999"'); +expectSyntaxError('"\\uAAA"'); +expectSyntaxError('"\\uBBB"'); +expectSyntaxError('"\\uCCC"'); +expectSyntaxError('"\\uDDD"'); +expectSyntaxError('"\\uEEE"'); +expectSyntaxError('"\\uFFF"'); +expectSyntaxError('"\\uGGG"'); +expectSyntaxError('"\\u000G"'); +expectSyntaxError('"\\u111G"'); +expectSyntaxError('"\\u222G"'); +expectSyntaxError('"\\u333G"'); +expectSyntaxError('"\\u444G"'); +expectSyntaxError('"\\u555G"'); +expectSyntaxError('"\\u666G"'); +expectSyntaxError('"\\u777G"'); +expectSyntaxError('"\\u888G"'); +expectSyntaxError('"\\u999G"'); +expectSyntaxError('"\\uAAAG"'); +expectSyntaxError('"\\uBBBG"'); +expectSyntaxError('"\\uCCCG"'); +expectSyntaxError('"\\uDDDG"'); +expectSyntaxError('"\\uEEEG"'); +expectSyntaxError('"\\uFFFG"'); +expectSyntaxError('"\\uGGGG"'); + +assertEq(eval('"a\\\rb"'), "ab"); +assertEq(eval('"a\\\nb"'), "ab"); +assertEq(eval('"a\\\r\nb"'), "ab"); +assertEq(eval('"a\\\u2028b"'), "ab"); +assertEq(eval('"a\\\u2029b"'), "ab"); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("All tests passed!"); diff --git a/js/src/tests/non262/expressions/tagged-template-constant-folding.js b/js/src/tests/non262/expressions/tagged-template-constant-folding.js new file mode 100644 index 0000000000..6041cbc161 --- /dev/null +++ b/js/src/tests/non262/expressions/tagged-template-constant-folding.js @@ -0,0 +1,28 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 1182373; +var summary = + "Don't let constant-folding in the MemberExpression part of a tagged " + + "template cause an incorrect |this| be passed to the callee"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var prop = "global"; + +var obj = { prop: "obj", f: function() { return this.prop; } }; + +assertEq(obj.f``, "obj"); +assertEq((true ? obj.f : null)``, "global"); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/non262/expressions/trailing_comma_arguments.js b/js/src/tests/non262/expressions/trailing_comma_arguments.js new file mode 100644 index 0000000000..612fd46a13 --- /dev/null +++ b/js/src/tests/non262/expressions/trailing_comma_arguments.js @@ -0,0 +1,85 @@ +/* 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/. */ + +// Trailing comma in Arguments production. + +// 12.3 Left-Hand-Side Expressions +// Arguments[Yield]: +// () +// ( ArgumentList[?Yield] ) +// ( ArgumentList[?Yield] , ) + + +function argsLength() { + return {value: arguments.length}; +} +function sum(...rest) { + return {value: rest.reduce((a, c) => a + c, 0)}; +} + +function call(f, argList) { + return eval(`(${f}(${argList})).value`); +} + +function newCall(F, argList) { + return eval(`(new ${F}(${argList})).value`); +} + +function superCall(superClass, argList) { + return eval(`(new class extends ${superClass} { + constructor() { + super(${argList}); + } + }).value`); +} + +// Ensure the correct number of arguments is passed. +for (let type of [call, newCall, superCall]) { + let test = type.bind(null, "argsLength"); + + assertEq(test("10, "), 1); + assertEq(test("10, 20, "), 2); + assertEq(test("10, 20, 30, "), 3); + assertEq(test("10, 20, 30, 40, "), 4); + + assertEq(test("...[10, 20], "), 2); + assertEq(test("...[10, 20], 30, "), 3); + assertEq(test("...[10, 20], ...[30], "), 3); +} + +// Ensure the arguments themselves are passed correctly. +for (let type of [call, newCall, superCall]) { + let test = type.bind(null, "sum"); + + assertEq(test("10, "), 10); + assertEq(test("10, 20, "), 30); + assertEq(test("10, 20, 30, "), 60); + assertEq(test("10, 20, 30, 40, "), 100); + + assertEq(test("...[10, 20], "), 30); + assertEq(test("...[10, 20], 30, "), 60); + assertEq(test("...[10, 20], ...[30], "), 60); +} + +// Error cases. +for (let type of [call, newCall, superCall]) { + let test = type.bind(null, "f"); + + // Trailing comma in empty arguments list. + assertThrowsInstanceOf(() => test(","), SyntaxError); + + // Leading comma. + assertThrowsInstanceOf(() => test(", a"), SyntaxError); + assertThrowsInstanceOf(() => test(", ...a"), SyntaxError); + + // Multiple trailing comma. + assertThrowsInstanceOf(() => test("a, , "), SyntaxError); + assertThrowsInstanceOf(() => test("...a, , "), SyntaxError); + + // Elision. + assertThrowsInstanceOf(() => test("a, , b"), SyntaxError); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/expressions/trailing_comma_arrow.js b/js/src/tests/non262/expressions/trailing_comma_arrow.js new file mode 100644 index 0000000000..f36b476ef4 --- /dev/null +++ b/js/src/tests/non262/expressions/trailing_comma_arrow.js @@ -0,0 +1,108 @@ +/* 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/. */ + +// Trailing comma in CoverParenthesizedExpressionAndArrowParameterList production. + +// 12.2 Primary Expression +// CoverParenthesizedExpressionAndArrowParameterList[Yield]: +// ( Expression[In, ?Yield] ) +// ( Expression[In, ?Yield] , ) +// () +// ( ...BindingIdentifier[?Yield] ) +// ( Expression[In, ?Yield] , ...BindingIdentifier[?Yield] ) + + +function arrow(argList, parameters = "", returnExpr = "") { + return eval(` + let fun = (${argList}) => { + return ${returnExpr}; + } + fun(${parameters}); + `); +} + +function arrowConcise(argList, parameters = "", returnExpr = "null") { + return eval(` + let fun = (${argList}) => ${returnExpr}; + fun(${parameters}); + `); +} + +const tests = [ + arrow, + arrowConcise, +]; + +// Ensure parameters are passed correctly. +for (let test of tests) { + assertEq(test("a, ", "10", "a"), 10); + assertEq(test("a, b, ", "10, 20", "a + b"), 30); + assertEq(test("a = 30, ", "", "a"), 30); + assertEq(test("a = 30, b = 40, ", "", "a + b"), 70); + + assertEq(test("[a], ", "[10]", "a"), 10); + assertEq(test("[a], [b], ", "[10], [20]", "a + b"), 30); + assertEq(test("[a] = [30], ", "", "a"), 30); + assertEq(test("[a] = [30], [b] = [40], ", "", "a + b"), 70); + + assertEq(test("{a}, ", "{a: 10}", "a"), 10); + assertEq(test("{a}, {b}, ", "{a: 10}, {b: 20}", "a + b"), 30); + assertEq(test("{a} = {a: 30}, ", "", "a"), 30); + assertEq(test("{a} = {a: 30}, {b} = {b: 40}, ", "", "a + b"), 70); +} + +// Ensure function length doesn't change. +for (let test of tests) { + assertEq(test("a, ", "", "fun.length"), 1); + assertEq(test("a, b, ", "", "fun.length"), 2); + + assertEq(test("[a], ", "[]", "fun.length"), 1); + assertEq(test("[a], [b], ", "[], []", "fun.length"), 2); + + assertEq(test("{a}, ", "{}", "fun.length"), 1); + assertEq(test("{a}, {b}, ", "{}, {}", "fun.length"), 2); +} + +for (let test of tests) { + // Trailing comma in empty parameters list. + assertThrowsInstanceOf(() => test(","), SyntaxError); + + // Leading comma. + assertThrowsInstanceOf(() => test(", a"), SyntaxError); + assertThrowsInstanceOf(() => test(", ...a"), SyntaxError); + + // Multiple trailing comma. + assertThrowsInstanceOf(() => test("a, , "), SyntaxError); + assertThrowsInstanceOf(() => test("a..., , "), SyntaxError); + + // Trailing comma after rest parameter. + assertThrowsInstanceOf(() => test("...a, "), SyntaxError); + assertThrowsInstanceOf(() => test("a, ...b, "), SyntaxError); + + // Elision. + assertThrowsInstanceOf(() => test("a, , b"), SyntaxError); +} + +// Trailing comma in non-parenthesized arrow head. +assertThrowsInstanceOf(() => eval("a, => {}"), SyntaxError); +assertThrowsInstanceOf(() => eval("a, => null"), SyntaxError); + +// Parenthesized expression is not an arrow function expression. +for (let trail of ["", ";", "\n => {}"]) { + assertThrowsInstanceOf(() => eval("(a,)" + trail), SyntaxError); + assertThrowsInstanceOf(() => eval("(a, b,)" + trail), SyntaxError); + assertThrowsInstanceOf(() => eval("(...a, )" + trail), SyntaxError); + assertThrowsInstanceOf(() => eval("(a, ...b, )" + trail), SyntaxError); + assertThrowsInstanceOf(() => eval("(a, , b)" + trail), SyntaxError); + + assertThrowsInstanceOf(() => eval("(,)" + trail), SyntaxError); + assertThrowsInstanceOf(() => eval("(, a)" + trail), SyntaxError); + assertThrowsInstanceOf(() => eval("(, ...a)" + trail), SyntaxError); + assertThrowsInstanceOf(() => eval("(a, , )" + trail), SyntaxError); + assertThrowsInstanceOf(() => eval("(...a, , )" + trail), SyntaxError); +} + + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/expressions/trailing_comma_getter_setter.js b/js/src/tests/non262/expressions/trailing_comma_getter_setter.js new file mode 100644 index 0000000000..258dd8df61 --- /dev/null +++ b/js/src/tests/non262/expressions/trailing_comma_getter_setter.js @@ -0,0 +1,88 @@ +/* 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/. */ + +// Trailing comma is not allowed in getter and setter methods + +// 14.3 Method Definitions +// MethodDefinition[Yield]: +// get PropertyName[?Yield] () { FunctionBody[~Yield] } +// set PropertyName[?Yield] ( PropertySetParameterList ) { FunctionBody[~Yield] } +// PropertySetParameterList: +// FormalParameter[~Yield] + +function objectGetter(argList) { + return eval(`({ + get m(${argList}) {} + })`); +} + +function objectSetter(argList) { + return eval(`({ + set m(${argList}) {} + })`); +} + +function classGetter(argList) { + return eval(`(class { + get m(${argList}) {} + })`); +} + +function classStaticGetter(argList) { + return eval(`(class { + static get m(${argList}) {} + })`); +} + +function classSetter(argList) { + return eval(`(class { + set m(${argList}) {} + })`); +} + +function classStaticSetter(argList) { + return eval(`(class { + static set m(${argList}) {} + })`); +} + +const tests = [ + objectGetter, + objectSetter, + classGetter, + classStaticGetter, + classSetter, + classStaticSetter, +]; + +for (let test of tests) { + // Trailing comma. + assertThrowsInstanceOf(() => test("a, "), SyntaxError); + assertThrowsInstanceOf(() => test("[], "), SyntaxError); + assertThrowsInstanceOf(() => test("{}, "), SyntaxError); + assertThrowsInstanceOf(() => test("a = 0, "), SyntaxError); + assertThrowsInstanceOf(() => test("[] = [], "), SyntaxError); + assertThrowsInstanceOf(() => test("{} = {}, "), SyntaxError); + + // Trailing comma in empty parameters list. + assertThrowsInstanceOf(() => test(","), SyntaxError); + + // Leading comma. + assertThrowsInstanceOf(() => test(", a"), SyntaxError); + assertThrowsInstanceOf(() => test(", ...a"), SyntaxError); + + // Multiple trailing comma. + assertThrowsInstanceOf(() => test("a, ,"), SyntaxError); + assertThrowsInstanceOf(() => test("a..., ,"), SyntaxError); + + // Trailing comma after rest parameter. + assertThrowsInstanceOf(() => test("...a ,"), SyntaxError); + assertThrowsInstanceOf(() => test("a, ...b, "), SyntaxError); + + // Elision. + assertThrowsInstanceOf(() => test("a, , b"), SyntaxError); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/expressions/trailing_comma_parameters.js b/js/src/tests/non262/expressions/trailing_comma_parameters.js new file mode 100644 index 0000000000..d0e756b862 --- /dev/null +++ b/js/src/tests/non262/expressions/trailing_comma_parameters.js @@ -0,0 +1,165 @@ +/* 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/. */ + +// Trailing comma in functions and methods. + +// 14.1 Function Definitions +// FunctionExpression: +// function BindingIdentifier[~Yield]opt ( FormalParameters[~Yield] ) { FunctionBody[~Yield] } + +// 14.3 Method Definitions +// MethodDefinition[Yield]: +// PropertyName[?Yield] ( UniqueFormalParameters[~Yield] ) { FunctionBody[~Yield] } +// GeneratorMethod[?Yield] +// PropertySetParameterList: +// FormalParameter[~Yield] + +// 14.4 Generator Function Definitions +// GeneratorExpression: +// function * BindingIdentifier[+Yield]opt ( FormalParameters[+Yield] ) { GeneratorBody } +// GeneratorMethod[Yield]: +// * PropertyName[?Yield] ( UniqueFormalParameters[+Yield] ) { GeneratorBody } + + +function functionExpression(argList, parameters = "", returnExpr = "") { + return eval(`(function f(${argList}) { + var fun = f; + return ${returnExpr}; + })(${parameters})`); +} + +function generatorExpression(argList, parameters = "", returnExpr = "") { + return eval(`(function* f(${argList}) { + var fun = f; + return ${returnExpr}; + })(${parameters}).next().value`); +} + +function objectMethod(argList, parameters = "", returnExpr = "") { + return eval(`({ + m(${argList}) { + var fun = this.m; + return ${returnExpr}; + } + }).m(${parameters})`); +} + +function objectGeneratorMethod(argList, parameters = "", returnExpr = "") { + return eval(`({ + * m(${argList}) { + var fun = this.m; + return ${returnExpr}; + } + }).m(${parameters}).next().value`); +} + +function classMethod(argList, parameters = "", returnExpr = "") { + return eval(`(new class { + m(${argList}) { + var fun = this.m; + return ${returnExpr}; + } + }).m(${parameters})`); +} + +function classStaticMethod(argList, parameters = "", returnExpr = "") { + return eval(`(class { + static m(${argList}) { + var fun = this.m; + return ${returnExpr}; + } + }).m(${parameters})`); +} + +function classGeneratorMethod(argList, parameters = "", returnExpr = "") { + return eval(`(new class { + * m(${argList}) { + var fun = this.m; + return ${returnExpr}; + } + }).m(${parameters}).next().value`); +} + +function classStaticGeneratorMethod(argList, parameters = "", returnExpr = "") { + return eval(`(class { + static * m(${argList}) { + var fun = this.m; + return ${returnExpr}; + } + }).m(${parameters}).next().value`); +} + +function classConstructorMethod(argList, parameters = "", returnExpr = "null") { + return eval(`new (class { + constructor(${argList}) { + var fun = this.constructor; + return { value: ${returnExpr} }; + } + })(${parameters}).value`); +} + +const tests = [ + functionExpression, + generatorExpression, + objectMethod, + objectGeneratorMethod, + classMethod, + classStaticMethod, + classGeneratorMethod, + classStaticGeneratorMethod, + classConstructorMethod, +]; + +// Ensure parameters are passed correctly. +for (let test of tests) { + assertEq(test("a, ", "10", "a"), 10); + assertEq(test("a, b, ", "10, 20", "a + b"), 30); + assertEq(test("a = 30, ", "", "a"), 30); + assertEq(test("a = 30, b = 40, ", "", "a + b"), 70); + + assertEq(test("[a], ", "[10]", "a"), 10); + assertEq(test("[a], [b], ", "[10], [20]", "a + b"), 30); + assertEq(test("[a] = [30], ", "", "a"), 30); + assertEq(test("[a] = [30], [b] = [40], ", "", "a + b"), 70); + + assertEq(test("{a}, ", "{a: 10}", "a"), 10); + assertEq(test("{a}, {b}, ", "{a: 10}, {b: 20}", "a + b"), 30); + assertEq(test("{a} = {a: 30}, ", "", "a"), 30); + assertEq(test("{a} = {a: 30}, {b} = {b: 40}, ", "", "a + b"), 70); +} + +// Ensure function length doesn't change. +for (let test of tests) { + assertEq(test("a, ", "", "fun.length"), 1); + assertEq(test("a, b, ", "", "fun.length"), 2); + + assertEq(test("[a], ", "[]", "fun.length"), 1); + assertEq(test("[a], [b], ", "[], []", "fun.length"), 2); + + assertEq(test("{a}, ", "{}", "fun.length"), 1); + assertEq(test("{a}, {b}, ", "{}, {}", "fun.length"), 2); +} + +for (let test of tests) { + // Trailing comma in empty parameters list. + assertThrowsInstanceOf(() => test(","), SyntaxError); + + // Leading comma. + assertThrowsInstanceOf(() => test(", a"), SyntaxError); + assertThrowsInstanceOf(() => test(", ...a"), SyntaxError); + + // Multiple trailing comma. + assertThrowsInstanceOf(() => test("a, , "), SyntaxError); + assertThrowsInstanceOf(() => test("a..., , "), SyntaxError); + + // Trailing comma after rest parameter. + assertThrowsInstanceOf(() => test("...a, "), SyntaxError); + assertThrowsInstanceOf(() => test("a, ...b, "), SyntaxError); + + // Elision. + assertThrowsInstanceOf(() => test("a, , b"), SyntaxError); +} + +if (typeof reportCompare === "function") + reportCompare(0, 0); |