summaryrefslogtreecommitdiffstats
path: root/js/src/tests/non262/expressions
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /js/src/tests/non262/expressions
parentInitial commit. (diff)
downloadthunderbird-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')
-rw-r--r--js/src/tests/non262/expressions/11.1.5-01.js37
-rw-r--r--js/src/tests/non262/expressions/ToPropertyKey-symbols.js90
-rw-r--r--js/src/tests/non262/expressions/binary-literals.js115
-rw-r--r--js/src/tests/non262/expressions/browser.js0
-rw-r--r--js/src/tests/non262/expressions/computed-property-side-effects.js35
-rw-r--r--js/src/tests/non262/expressions/constant-folded-labeled-statement.js14
-rw-r--r--js/src/tests/non262/expressions/delete-constant-folded-and-or.js41
-rw-r--r--js/src/tests/non262/expressions/delete-name-parenthesized-early-error-strict-mode.js76
-rw-r--r--js/src/tests/non262/expressions/destructuring-array-default-call.js10
-rw-r--r--js/src/tests/non262/expressions/destructuring-array-default-class.js72
-rw-r--r--js/src/tests/non262/expressions/destructuring-array-default-function-nested.js11
-rw-r--r--js/src/tests/non262/expressions/destructuring-array-default-function.js11
-rw-r--r--js/src/tests/non262/expressions/destructuring-array-default-simple.js16
-rw-r--r--js/src/tests/non262/expressions/destructuring-array-default-yield.js22
-rw-r--r--js/src/tests/non262/expressions/destructuring-array-done.js319
-rw-r--r--js/src/tests/non262/expressions/destructuring-array-lexical.js12
-rw-r--r--js/src/tests/non262/expressions/destructuring-object-__proto__-1.js82
-rw-r--r--js/src/tests/non262/expressions/destructuring-object-__proto__-2.js94
-rw-r--r--js/src/tests/non262/expressions/destructuring-pattern-parenthesized.js137
-rw-r--r--js/src/tests/non262/expressions/destructuring-scope.js67
-rw-r--r--js/src/tests/non262/expressions/exponentiation-unparenthesised-unary.js106
-rw-r--r--js/src/tests/non262/expressions/inNotObjectError.js46
-rw-r--r--js/src/tests/non262/expressions/named-accessor-function.js49
-rw-r--r--js/src/tests/non262/expressions/nested-delete-name-in-evalcode.js163
-rw-r--r--js/src/tests/non262/expressions/nullish-coalescing.js112
-rw-r--r--js/src/tests/non262/expressions/object-literal-__proto__.js267
-rw-r--r--js/src/tests/non262/expressions/object-literal-accessor-arguments.js42
-rw-r--r--js/src/tests/non262/expressions/object-literal-accessor-property-name.js29
-rw-r--r--js/src/tests/non262/expressions/object-literal-computed-property-evaluation.js38
-rw-r--r--js/src/tests/non262/expressions/octal-literals.js103
-rw-r--r--js/src/tests/non262/expressions/optional-chain-class-heritage.js10
-rw-r--r--js/src/tests/non262/expressions/optional-chain-first-expression.js92
-rw-r--r--js/src/tests/non262/expressions/optional-chain-super-elem.js12
-rw-r--r--js/src/tests/non262/expressions/optional-chain-tdz.js28
-rw-r--r--js/src/tests/non262/expressions/optional-chain.js263
-rw-r--r--js/src/tests/non262/expressions/primitive-this-boxing-behavior.js106
-rw-r--r--js/src/tests/non262/expressions/regress-192288.js82
-rw-r--r--js/src/tests/non262/expressions/regress-346203.js25
-rw-r--r--js/src/tests/non262/expressions/regress-346645-01.js31
-rw-r--r--js/src/tests/non262/expressions/regress-346645-02.js31
-rw-r--r--js/src/tests/non262/expressions/regress-346645-03.js31
-rw-r--r--js/src/tests/non262/expressions/regress-394673.js49
-rw-r--r--js/src/tests/non262/expressions/regress-418051.js31
-rw-r--r--js/src/tests/non262/expressions/regress-451340.js24
-rw-r--r--js/src/tests/non262/expressions/regress-96526-argsub.js88
-rw-r--r--js/src/tests/non262/expressions/regress-96526-delelem.js88
-rw-r--r--js/src/tests/non262/expressions/regress-96526-noargsub.js88
-rw-r--r--js/src/tests/non262/expressions/shell.js288
-rw-r--r--js/src/tests/non262/expressions/short-circuit-compound-assignment-anon-fns.js42
-rw-r--r--js/src/tests/non262/expressions/short-circuit-compound-assignment-const.js90
-rw-r--r--js/src/tests/non262/expressions/short-circuit-compound-assignment-deleted-decl-binding.js65
-rw-r--r--js/src/tests/non262/expressions/short-circuit-compound-assignment-property-key-evaluation.js68
-rw-r--r--js/src/tests/non262/expressions/short-circuit-compound-assignment-scope-lookup.js191
-rw-r--r--js/src/tests/non262/expressions/short-circuit-compound-assignment-tdz.js56
-rw-r--r--js/src/tests/non262/expressions/short-circuit-compound-assignment.js265
-rw-r--r--js/src/tests/non262/expressions/string-literal-escape-sequences.js146
-rw-r--r--js/src/tests/non262/expressions/tagged-template-constant-folding.js28
-rw-r--r--js/src/tests/non262/expressions/trailing_comma_arguments.js85
-rw-r--r--js/src/tests/non262/expressions/trailing_comma_arrow.js108
-rw-r--r--js/src/tests/non262/expressions/trailing_comma_getter_setter.js88
-rw-r--r--js/src/tests/non262/expressions/trailing_comma_parameters.js165
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);