summaryrefslogtreecommitdiffstats
path: root/js/src/tests/ecma_2017
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/tests/ecma_2017')
-rw-r--r--js/src/tests/ecma_2017/AsyncFunctions/construct-newtarget.js79
-rw-r--r--js/src/tests/ecma_2017/AsyncFunctions/subclass.js31
-rw-r--r--js/src/tests/ecma_2017/lastIndex-exec.js80
-rw-r--r--js/src/tests/ecma_2017/lastIndex-match-or-replace.js122
-rw-r--r--js/src/tests/ecma_2017/lastIndex-search.js118
5 files changed, 430 insertions, 0 deletions
diff --git a/js/src/tests/ecma_2017/AsyncFunctions/construct-newtarget.js b/js/src/tests/ecma_2017/AsyncFunctions/construct-newtarget.js
new file mode 100644
index 000000000..7d75d0c93
--- /dev/null
+++ b/js/src/tests/ecma_2017/AsyncFunctions/construct-newtarget.js
@@ -0,0 +1,79 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const AsyncFunction = async function(){}.constructor;
+
+
+// Test subclassing %AsyncFunction% works correctly.
+class MyAsync extends AsyncFunction {}
+
+var fn = new MyAsync();
+assertEq(fn instanceof MyAsync, true);
+assertEq(fn instanceof AsyncFunction, true);
+assertEq(Object.getPrototypeOf(fn), MyAsync.prototype);
+
+fn = Reflect.construct(MyAsync, []);
+assertEq(fn instanceof MyAsync, true);
+assertEq(fn instanceof AsyncFunction, true);
+assertEq(Object.getPrototypeOf(fn), MyAsync.prototype);
+
+fn = Reflect.construct(MyAsync, [], MyAsync);
+assertEq(fn instanceof MyAsync, true);
+assertEq(fn instanceof AsyncFunction, true);
+assertEq(Object.getPrototypeOf(fn), MyAsync.prototype);
+
+fn = Reflect.construct(MyAsync, [], AsyncFunction);
+assertEq(fn instanceof MyAsync, false);
+assertEq(fn instanceof AsyncFunction, true);
+assertEq(Object.getPrototypeOf(fn), AsyncFunction.prototype);
+
+
+// Set a different constructor as NewTarget.
+fn = Reflect.construct(MyAsync, [], Array);
+assertEq(fn instanceof MyAsync, false);
+assertEq(fn instanceof AsyncFunction, false);
+assertEq(Object.getPrototypeOf(fn), Array.prototype);
+
+fn = Reflect.construct(AsyncFunction, [], Array);
+assertEq(fn instanceof AsyncFunction, false);
+assertEq(Object.getPrototypeOf(fn), Array.prototype);
+
+
+// The prototype defaults to %AsyncFunctionPrototype% if null.
+function NewTargetNullPrototype() {}
+NewTargetNullPrototype.prototype = null;
+
+fn = Reflect.construct(AsyncFunction, [], NewTargetNullPrototype);
+assertEq(fn instanceof AsyncFunction, true);
+assertEq(Object.getPrototypeOf(fn), AsyncFunction.prototype);
+
+fn = Reflect.construct(MyAsync, [], NewTargetNullPrototype);
+assertEq(fn instanceof MyAsync, false);
+assertEq(fn instanceof AsyncFunction, true);
+assertEq(Object.getPrototypeOf(fn), AsyncFunction.prototype);
+
+
+// "prototype" property is retrieved exactly once.
+var trapLog = [], getLog = [];
+var ProxiedConstructor = new Proxy(AsyncFunction, new Proxy({
+ get(target, propertyKey, receiver) {
+ getLog.push(propertyKey);
+ return Reflect.get(target, propertyKey, receiver);
+ }
+}, {
+ get(target, propertyKey, receiver) {
+ trapLog.push(propertyKey);
+ return Reflect.get(target, propertyKey, receiver);
+ }
+}));
+
+fn = Reflect.construct(AsyncFunction, [], ProxiedConstructor);
+assertEqArray(trapLog, ["get"]);
+assertEqArray(getLog, ["prototype"]);
+assertEq(fn instanceof AsyncFunction, true);
+assertEq(Object.getPrototypeOf(fn), AsyncFunction.prototype);
+
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0);
diff --git a/js/src/tests/ecma_2017/AsyncFunctions/subclass.js b/js/src/tests/ecma_2017/AsyncFunctions/subclass.js
new file mode 100644
index 000000000..da20ab19b
--- /dev/null
+++ b/js/src/tests/ecma_2017/AsyncFunctions/subclass.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!xulRuntime.shell) -- needs drainJobQueue
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const AsyncFunction = async function(){}.constructor;
+
+class MyAsync extends AsyncFunction {}
+
+// MyGen inherits from %AsyncFunction%.
+assertEq(Object.getPrototypeOf(MyAsync), AsyncFunction);
+
+// MyGen.prototype inherits from %AsyncFunctionPrototype%.
+assertEq(Object.getPrototypeOf(MyAsync.prototype), AsyncFunction.prototype);
+
+var fn = new MyAsync("return await 'ok';");
+
+// fn inherits from MyAsync.prototype.
+assertEq(Object.getPrototypeOf(fn), MyAsync.prototype);
+
+// Ensure the new async function can be executed.
+var promise = fn();
+
+// promise inherits from %Promise.prototype%.
+assertEq(Object.getPrototypeOf(promise), Promise.prototype);
+
+// Computes the expected result.
+assertEventuallyEq(promise, "ok");
+
+if (typeof reportCompare === "function")
+ reportCompare(0, 0);
diff --git a/js/src/tests/ecma_2017/lastIndex-exec.js b/js/src/tests/ecma_2017/lastIndex-exec.js
new file mode 100644
index 000000000..f42facbe3
--- /dev/null
+++ b/js/src/tests/ecma_2017/lastIndex-exec.js
@@ -0,0 +1,80 @@
+// RegExp.prototype.exec: Test lastIndex changes for ES2017.
+
+// Test various combinations of:
+// - Pattern matches or doesn't match
+// - Global and/or sticky flag is set.
+// - lastIndex exceeds the input string length
+// - lastIndex is +-0
+const testCases = [
+ { regExp: /a/, lastIndex: 0, input: "a", result: 0 },
+ { regExp: /a/g, lastIndex: 0, input: "a", result: 1 },
+ { regExp: /a/y, lastIndex: 0, input: "a", result: 1 },
+
+ { regExp: /a/, lastIndex: 0, input: "b", result: 0 },
+ { regExp: /a/g, lastIndex: 0, input: "b", result: 0 },
+ { regExp: /a/y, lastIndex: 0, input: "b", result: 0 },
+
+ { regExp: /a/, lastIndex: -0, input: "a", result: -0 },
+ { regExp: /a/g, lastIndex: -0, input: "a", result: 1 },
+ { regExp: /a/y, lastIndex: -0, input: "a", result: 1 },
+
+ { regExp: /a/, lastIndex: -0, input: "b", result: -0 },
+ { regExp: /a/g, lastIndex: -0, input: "b", result: 0 },
+ { regExp: /a/y, lastIndex: -0, input: "b", result: 0 },
+
+ { regExp: /a/, lastIndex: -1, input: "a", result: -1 },
+ { regExp: /a/g, lastIndex: -1, input: "a", result: 1 },
+ { regExp: /a/y, lastIndex: -1, input: "a", result: 1 },
+
+ { regExp: /a/, lastIndex: -1, input: "b", result: -1 },
+ { regExp: /a/g, lastIndex: -1, input: "b", result: 0 },
+ { regExp: /a/y, lastIndex: -1, input: "b", result: 0 },
+
+ { regExp: /a/, lastIndex: 100, input: "a", result: 100 },
+ { regExp: /a/g, lastIndex: 100, input: "a", result: 0 },
+ { regExp: /a/y, lastIndex: 100, input: "a", result: 0 },
+];
+
+// Basic test.
+for (let {regExp, lastIndex, input, result} of testCases) {
+ let re = new RegExp(regExp);
+ re.lastIndex = lastIndex;
+ re.exec(input);
+ assertEq(re.lastIndex, result);
+}
+
+// Test when lastIndex is non-writable.
+for (let {regExp, lastIndex, input} of testCases) {
+ let re = new RegExp(regExp);
+ Object.defineProperty(re, "lastIndex", { value: lastIndex, writable: false });
+ if (re.global || re.sticky) {
+ assertThrowsInstanceOf(() => re.exec(input), TypeError);
+ } else {
+ re.exec(input);
+ }
+ assertEq(re.lastIndex, lastIndex);
+}
+
+// Test when lastIndex is changed to non-writable as a side-effect.
+for (let {regExp, lastIndex, input} of testCases) {
+ let re = new RegExp(regExp);
+ let called = false;
+ re.lastIndex = {
+ valueOf() {
+ assertEq(called, false);
+ called = true;
+ Object.defineProperty(re, "lastIndex", { value: 9000, writable: false });
+ return lastIndex;
+ }
+ };
+ if (re.global || re.sticky) {
+ assertThrowsInstanceOf(() => re.exec(input), TypeError);
+ } else {
+ re.exec(input);
+ }
+ assertEq(re.lastIndex, 9000);
+ assertEq(called, true);
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/ecma_2017/lastIndex-match-or-replace.js b/js/src/tests/ecma_2017/lastIndex-match-or-replace.js
new file mode 100644
index 000000000..b0a8b537c
--- /dev/null
+++ b/js/src/tests/ecma_2017/lastIndex-match-or-replace.js
@@ -0,0 +1,122 @@
+// RegExp.prototype[Symbol.match, Symbol.replace]: Test lastIndex changes for ES2017.
+
+// RegExp-like class to test the RegExp method slow paths.
+class DuckRegExp extends RegExp {
+ constructor(pattern, flags) {
+ return Object.create(DuckRegExp.prototype, {
+ regExp: {
+ value: new RegExp(pattern, flags)
+ },
+ lastIndex: {
+ value: 0, writable: true, enumerable: false, configurable: false
+ }
+ });
+ }
+
+ exec(...args) {
+ this.regExp.lastIndex = this.lastIndex;
+ try {
+ return this.regExp.exec(...args);
+ } finally {
+ if (this.global || this.sticky)
+ this.lastIndex = this.regExp.lastIndex;
+ }
+ }
+
+ get source() { return this.regExp.source; }
+
+ get global() { return this.regExp.global; }
+ get ignoreCase() { return this.regExp.ignoreCase; }
+ get multiline() { return this.regExp.multiline; }
+ get sticky() { return this.regExp.sticky; }
+ get unicode() { return this.regExp.unicode; }
+}
+
+// Test various combinations of:
+// - Pattern matches or doesn't match
+// - Global and/or sticky flag is set.
+// - lastIndex exceeds the input string length
+// - lastIndex is +-0
+const testCases = [
+ { regExp: /a/, lastIndex: 0, input: "a", result: 0 },
+ { regExp: /a/g, lastIndex: 0, input: "a", result: 0 },
+ { regExp: /a/y, lastIndex: 0, input: "a", result: 1 },
+
+ { regExp: /a/, lastIndex: 0, input: "b", result: 0 },
+ { regExp: /a/g, lastIndex: 0, input: "b", result: 0 },
+ { regExp: /a/y, lastIndex: 0, input: "b", result: 0 },
+
+ { regExp: /a/, lastIndex: -0, input: "a", result: -0 },
+ { regExp: /a/g, lastIndex: -0, input: "a", result: 0 },
+ { regExp: /a/y, lastIndex: -0, input: "a", result: 1 },
+
+ { regExp: /a/, lastIndex: -0, input: "b", result: -0 },
+ { regExp: /a/g, lastIndex: -0, input: "b", result: 0 },
+ { regExp: /a/y, lastIndex: -0, input: "b", result: 0 },
+
+ { regExp: /a/, lastIndex: -1, input: "a", result: -1 },
+ { regExp: /a/g, lastIndex: -1, input: "a", result: 0 },
+ { regExp: /a/y, lastIndex: -1, input: "a", result: 1 },
+
+ { regExp: /a/, lastIndex: -1, input: "b", result: -1 },
+ { regExp: /a/g, lastIndex: -1, input: "b", result: 0 },
+ { regExp: /a/y, lastIndex: -1, input: "b", result: 0 },
+
+ { regExp: /a/, lastIndex: 100, input: "a", result: 100 },
+ { regExp: /a/g, lastIndex: 100, input: "a", result: 0 },
+ { regExp: /a/y, lastIndex: 100, input: "a", result: 0 },
+];
+
+for (let method of [RegExp.prototype[Symbol.match], RegExp.prototype[Symbol.replace]]) {
+ for (let Constructor of [RegExp, DuckRegExp]) {
+ // Basic test.
+ for (let {regExp, lastIndex, input, result} of testCases) {
+ let re = new Constructor(regExp);
+ re.lastIndex = lastIndex;
+ Reflect.apply(method, re, [input]);
+ assertEq(re.lastIndex, result);
+ }
+
+ // Test when lastIndex is non-writable.
+ for (let {regExp, lastIndex, input} of testCases) {
+ let re = new Constructor(regExp);
+ Object.defineProperty(re, "lastIndex", { value: lastIndex, writable: false });
+ if (re.global || re.sticky) {
+ assertThrowsInstanceOf(() => Reflect.apply(method, re, [input]), TypeError);
+ } else {
+ Reflect.apply(method, re, [input]);
+ }
+ assertEq(re.lastIndex, lastIndex);
+ }
+
+ // Test when lastIndex is changed to non-writable as a side-effect.
+ for (let {regExp, lastIndex, input, result} of testCases) {
+ let re = new Constructor(regExp);
+ let called = false;
+ re.lastIndex = {
+ valueOf() {
+ assertEq(called, false);
+ called = true;
+ Object.defineProperty(re, "lastIndex", { value: 9000, writable: false });
+ return lastIndex;
+ }
+ };
+ if (re.sticky) {
+ assertThrowsInstanceOf(() => Reflect.apply(method, re, [input]), TypeError);
+ assertEq(called, true);
+ assertEq(re.lastIndex, 9000);
+ } else if (re.global) {
+ Reflect.apply(method, re, [input]);
+ assertEq(called, false);
+ assertEq(re.lastIndex, result);
+ } else {
+ Reflect.apply(method, re, [input]);
+ assertEq(called, true);
+ assertEq(re.lastIndex, 9000);
+ }
+ }
+ }
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/ecma_2017/lastIndex-search.js b/js/src/tests/ecma_2017/lastIndex-search.js
new file mode 100644
index 000000000..5953b3a88
--- /dev/null
+++ b/js/src/tests/ecma_2017/lastIndex-search.js
@@ -0,0 +1,118 @@
+// RegExp.prototype[Symbol.search]: Test lastIndex changes for ES2017.
+
+// RegExp-like class to test the RegExp method slow paths.
+class DuckRegExp extends RegExp {
+ constructor(pattern, flags) {
+ return Object.create(DuckRegExp.prototype, {
+ regExp: {
+ value: new RegExp(pattern, flags)
+ },
+ lastIndex: {
+ value: 0, writable: true, enumerable: false, configurable: false
+ }
+ });
+ }
+
+ exec(...args) {
+ this.regExp.lastIndex = this.lastIndex;
+ try {
+ return this.regExp.exec(...args);
+ } finally {
+ if (this.global || this.sticky)
+ this.lastIndex = this.regExp.lastIndex;
+ }
+ }
+
+ get source() { return this.regExp.source; }
+
+ get global() { return this.regExp.global; }
+ get ignoreCase() { return this.regExp.ignoreCase; }
+ get multiline() { return this.regExp.multiline; }
+ get sticky() { return this.regExp.sticky; }
+ get unicode() { return this.regExp.unicode; }
+}
+
+// Test various combinations of:
+// - Pattern matches or doesn't match
+// - Global and/or sticky flag is set.
+// - lastIndex exceeds the input string length
+// - lastIndex is +-0
+const testCasesNotPositiveZero = [
+ { regExp: /a/, lastIndex: -1, input: "a" },
+ { regExp: /a/g, lastIndex: -1, input: "a" },
+ { regExp: /a/y, lastIndex: -1, input: "a" },
+
+ { regExp: /a/, lastIndex: 100, input: "a" },
+ { regExp: /a/g, lastIndex: 100, input: "a" },
+ { regExp: /a/y, lastIndex: 100, input: "a" },
+
+ { regExp: /a/, lastIndex: -1, input: "b" },
+ { regExp: /a/g, lastIndex: -1, input: "b" },
+ { regExp: /a/y, lastIndex: -1, input: "b" },
+
+ { regExp: /a/, lastIndex: -0, input: "a" },
+ { regExp: /a/g, lastIndex: -0, input: "a" },
+ { regExp: /a/y, lastIndex: -0, input: "a" },
+
+ { regExp: /a/, lastIndex: -0, input: "b" },
+ { regExp: /a/g, lastIndex: -0, input: "b" },
+ { regExp: /a/y, lastIndex: -0, input: "b" },
+];
+
+const testCasesPositiveZero = [
+ { regExp: /a/, lastIndex: 0, input: "a" },
+ { regExp: /a/g, lastIndex: 0, input: "a" },
+ { regExp: /a/y, lastIndex: 0, input: "a" },
+
+ { regExp: /a/, lastIndex: 0, input: "b" },
+ { regExp: /a/g, lastIndex: 0, input: "b" },
+ { regExp: /a/y, lastIndex: 0, input: "b" },
+];
+
+const testCases = [...testCasesNotPositiveZero, ...testCasesPositiveZero];
+
+for (let Constructor of [RegExp, DuckRegExp]) {
+ // Basic test.
+ for (let {regExp, lastIndex, input} of testCases) {
+ let re = new Constructor(regExp);
+ re.lastIndex = lastIndex;
+ re[Symbol.search](input);
+ assertEq(re.lastIndex, lastIndex);
+ }
+
+ // Test when lastIndex is non-writable and not positive zero.
+ for (let {regExp, lastIndex, input} of testCasesNotPositiveZero) {
+ let re = new Constructor(regExp);
+ Object.defineProperty(re, "lastIndex", { value: lastIndex, writable: false });
+ assertThrowsInstanceOf(() => re[Symbol.search](input), TypeError);
+ assertEq(re.lastIndex, lastIndex);
+ }
+
+ // Test when lastIndex is non-writable and positive zero.
+ for (let {regExp, lastIndex, input} of testCasesPositiveZero) {
+ let re = new Constructor(regExp);
+ Object.defineProperty(re, "lastIndex", { value: lastIndex, writable: false });
+ if (re.global || re.sticky) {
+ assertThrowsInstanceOf(() => re[Symbol.search](input), TypeError);
+ } else {
+ re[Symbol.search](input);
+ }
+ assertEq(re.lastIndex, lastIndex);
+ }
+
+ // Test lastIndex isn't converted to a number.
+ for (let {regExp, lastIndex, input} of testCases) {
+ let re = new RegExp(regExp);
+ let badIndex = {
+ valueOf() {
+ assertEq(false, true);
+ }
+ };
+ re.lastIndex = badIndex;
+ re[Symbol.search](input);
+ assertEq(re.lastIndex, badIndex);
+ }
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);