summaryrefslogtreecommitdiffstats
path: root/js/src/tests/shell
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/shell
parentInitial commit. (diff)
downloadthunderbird-upstream.tar.xz
thunderbird-upstream.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--js/src/tests/shell.js588
-rw-r--r--js/src/tests/shell/README1
-rw-r--r--js/src/tests/shell/futex-apis.js125
-rw-r--r--js/src/tests/shell/futex.js117
-rw-r--r--js/src/tests/shell/gcstats.js60
-rw-r--r--js/src/tests/shell/mailbox.js104
-rw-r--r--js/src/tests/shell/os.js39
-rw-r--r--js/src/tests/shell/script-file-name-utf8.js235
-rw-r--r--js/src/tests/shell/shell.js4
-rw-r--r--js/src/tests/shell/warning.js33
10 files changed, 1306 insertions, 0 deletions
diff --git a/js/src/tests/shell.js b/js/src/tests/shell.js
new file mode 100644
index 0000000000..4fd0942b8d
--- /dev/null
+++ b/js/src/tests/shell.js
@@ -0,0 +1,588 @@
+/* -*- 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/. */
+
+// NOTE: If you're adding new test harness functionality -- first, should you
+// at all? Most stuff is better in specific tests, or in nested shell.js
+// or browser.js. Second, supposing you should, please add it to this
+// IIFE for better modularity/resilience against tests that must do
+// particularly bizarre things that might break the harness.
+
+(function(global) {
+ "use strict";
+
+ /**********************************************************************
+ * CACHED PRIMORDIAL FUNCTIONALITY (before a test might overwrite it) *
+ **********************************************************************/
+
+ var undefined; // sigh
+
+ var Error = global.Error;
+ var Function = global.Function;
+ var Number = global.Number;
+ var RegExp = global.RegExp;
+ var String = global.String;
+ var Symbol = global.Symbol;
+ var TypeError = global.TypeError;
+
+ var ArrayIsArray = global.Array.isArray;
+ var MathAbs = global.Math.abs;
+ var ObjectCreate = global.Object.create;
+ var ObjectDefineProperty = global.Object.defineProperty;
+ var ReflectApply = global.Reflect.apply;
+ var RegExpPrototypeExec = global.RegExp.prototype.exec;
+ var StringPrototypeCharCodeAt = global.String.prototype.charCodeAt;
+ var StringPrototypeIndexOf = global.String.prototype.indexOf;
+ var StringPrototypeSubstring = global.String.prototype.substring;
+
+ var runningInBrowser = typeof global.window !== "undefined";
+ if (runningInBrowser) {
+ // Certain cached functionality only exists (and is only needed) when
+ // running in the browser. Segregate that caching here.
+
+ var SpecialPowersSetGCZeal =
+ global.SpecialPowers ? global.SpecialPowers.setGCZeal : undefined;
+ }
+
+ var evaluate = global.evaluate;
+ var options = global.options;
+
+ /****************************
+ * GENERAL HELPER FUNCTIONS *
+ ****************************/
+
+ // We *cannot* use Array.prototype.push for this, because that function sets
+ // the new trailing element, which could invoke a setter (left by a test) on
+ // Array.prototype or Object.prototype.
+ function ArrayPush(arr, val) {
+ assertEq(ArrayIsArray(arr), true,
+ "ArrayPush must only be used on actual arrays");
+
+ var desc = ObjectCreate(null);
+ desc.value = val;
+ desc.enumerable = true;
+ desc.configurable = true;
+ desc.writable = true;
+ ObjectDefineProperty(arr, arr.length, desc);
+ }
+
+ function StringCharCodeAt(str, index) {
+ return ReflectApply(StringPrototypeCharCodeAt, str, [index]);
+ }
+
+ function StringSplit(str, delimiter) {
+ assertEq(typeof str === "string" && typeof delimiter === "string", true,
+ "StringSplit must be called with two string arguments");
+ assertEq(delimiter.length > 0, true,
+ "StringSplit doesn't support an empty delimiter string");
+
+ var parts = [];
+ var last = 0;
+ while (true) {
+ var i = ReflectApply(StringPrototypeIndexOf, str, [delimiter, last]);
+ if (i < 0) {
+ if (last < str.length)
+ ArrayPush(parts, ReflectApply(StringPrototypeSubstring, str, [last]));
+ return parts;
+ }
+
+ ArrayPush(parts, ReflectApply(StringPrototypeSubstring, str, [last, i]));
+ last = i + delimiter.length;
+ }
+ }
+
+ function shellOptionsClear() {
+ assertEq(runningInBrowser, false, "Only called when running in the shell.");
+
+ // Return early if no options are set.
+ var currentOptions = options ? options() : "";
+ if (currentOptions === "")
+ return;
+
+ // Turn off current settings.
+ var optionNames = StringSplit(currentOptions, ",");
+ for (var i = 0; i < optionNames.length; i++) {
+ options(optionNames[i]);
+ }
+ }
+
+ /****************************
+ * TESTING FUNCTION EXPORTS *
+ ****************************/
+
+ function SameValue(v1, v2) {
+ // We could |return Object.is(v1, v2);|, but that's less portable.
+ if (v1 === 0 && v2 === 0)
+ return 1 / v1 === 1 / v2;
+ if (v1 !== v1 && v2 !== v2)
+ return true;
+ return v1 === v2;
+ }
+
+ var assertEq = global.assertEq;
+ if (typeof assertEq !== "function") {
+ assertEq = function assertEq(actual, expected, message) {
+ if (!SameValue(actual, expected)) {
+ throw new TypeError(`Assertion failed: got "${actual}", expected "${expected}"` +
+ (message ? ": " + message : ""));
+ }
+ };
+ global.assertEq = assertEq;
+ }
+
+ function assertEqArray(actual, expected) {
+ var len = actual.length;
+ assertEq(len, expected.length, "mismatching array lengths");
+
+ var i = 0;
+ try {
+ for (; i < len; i++)
+ assertEq(actual[i], expected[i], "mismatch at element " + i);
+ } catch (e) {
+ throw new Error(`Exception thrown at index ${i}: ${e}`);
+ }
+ }
+ global.assertEqArray = assertEqArray;
+
+ function assertThrows(f) {
+ if (arguments.length != 1) {
+ throw new Error("Too many arguments to assertThrows; maybe you meant assertThrowsInstanceOf?");
+ }
+ var ok = false;
+ try {
+ f();
+ } catch (exc) {
+ ok = true;
+ }
+ if (!ok)
+ throw new Error(`Assertion failed: ${f} did not throw as expected`);
+ }
+ global.assertThrows = assertThrows;
+
+ function assertThrowsInstanceOf(f, ctor, msg) {
+ var fullmsg;
+ try {
+ f();
+ } catch (exc) {
+ if (exc instanceof ctor)
+ return;
+ fullmsg = `Assertion failed: expected exception ${ctor.name}, got ${exc}`;
+ }
+
+ if (fullmsg === undefined)
+ fullmsg = `Assertion failed: expected exception ${ctor.name}, no exception thrown`;
+ if (msg !== undefined)
+ fullmsg += " - " + msg;
+
+ throw new Error(fullmsg);
+ }
+ global.assertThrowsInstanceOf = assertThrowsInstanceOf;
+
+ /****************************
+ * UTILITY FUNCTION EXPORTS *
+ ****************************/
+
+ var dump = global.dump;
+ if (typeof global.dump === "function") {
+ // A presumptively-functional |dump| exists, so no need to do anything.
+ } else {
+ // We don't have |dump|. Try to simulate the desired effect another way.
+ if (runningInBrowser) {
+ // We can't actually print to the console: |global.print| invokes browser
+ // printing functionality here (it's overwritten just below), and
+ // |global.dump| isn't a function that'll dump to the console (presumably
+ // because the preference to enable |dump| wasn't set). Just make it a
+ // no-op.
+ dump = function() {};
+ } else {
+ // |print| prints to stdout: make |dump| do likewise.
+ dump = global.print;
+ }
+ global.dump = dump;
+ }
+
+ var print;
+ if (runningInBrowser) {
+ // We're executing in a browser. Using |global.print| would invoke browser
+ // printing functionality: not what tests want! Instead, use a print
+ // function that syncs up with the test harness and console.
+ print = function print() {
+ var s = "TEST-INFO | ";
+ for (var i = 0; i < arguments.length; i++)
+ s += String(arguments[i]) + " ";
+
+ // Dump the string to the console for developers and the harness.
+ dump(s + "\n");
+
+ // AddPrintOutput doesn't require HTML special characters be escaped.
+ global.AddPrintOutput(s);
+ };
+
+ global.print = print;
+ } else {
+ // We're executing in a shell, and |global.print| is the desired function.
+ print = global.print;
+ }
+
+ var gczeal = global.gczeal;
+ if (typeof gczeal !== "function") {
+ if (typeof SpecialPowersSetGCZeal === "function") {
+ gczeal = function gczeal(z) {
+ SpecialPowersSetGCZeal(z);
+ };
+ } else {
+ gczeal = function() {}; // no-op if not available
+ }
+
+ global.gczeal = gczeal;
+ }
+
+ // Evaluates the given source code as global script code. browser.js provides
+ // a different implementation for this function.
+ var evaluateScript = global.evaluateScript;
+ if (typeof evaluate === "function" && typeof evaluateScript !== "function") {
+ evaluateScript = function evaluateScript(code) {
+ evaluate(String(code));
+ };
+
+ global.evaluateScript = evaluateScript;
+ }
+
+ function toPrinted(value) {
+ value = String(value);
+
+ var digits = "0123456789ABCDEF";
+ var result = "";
+ for (var i = 0; i < value.length; i++) {
+ var ch = StringCharCodeAt(value, i);
+ if (ch === 0x5C && i + 1 < value.length) {
+ var d = value[i + 1];
+ if (d === "n") {
+ result += "NL";
+ i++;
+ } else if (d === "r") {
+ result += "CR";
+ i++;
+ } else {
+ result += "\\";
+ }
+ } else if (ch === 0x0A) {
+ result += "NL";
+ } else if (ch < 0x20 || ch > 0x7E) {
+ var a = digits[ch & 0xf];
+ ch >>= 4;
+ var b = digits[ch & 0xf];
+ ch >>= 4;
+
+ if (ch) {
+ var c = digits[ch & 0xf];
+ ch >>= 4;
+ var d = digits[ch & 0xf];
+
+ result += "\\u" + d + c + b + a;
+ } else {
+ result += "\\x" + b + a;
+ }
+ } else {
+ result += value[i];
+ }
+ }
+
+ return result;
+ }
+
+ /*
+ * An xorshift pseudo-random number generator see:
+ * https://en.wikipedia.org/wiki/Xorshift#xorshift.2A
+ * This generator will always produce a value, n, where
+ * 0 <= n <= 255
+ */
+ function *XorShiftGenerator(seed, size) {
+ let x = seed;
+ for (let i = 0; i < size; i++) {
+ x ^= x >> 12;
+ x ^= x << 25;
+ x ^= x >> 27;
+ yield x % 256;
+ }
+ }
+ global.XorShiftGenerator = XorShiftGenerator;
+
+ /*************************************************************************
+ * HARNESS-CENTRIC EXPORTS (we should generally work to eliminate these) *
+ *************************************************************************/
+
+ var PASSED = " PASSED! ";
+ var FAILED = " FAILED! ";
+
+ /*
+ * Same as `new TestCase(description, expect, actual)`, except it doesn't
+ * return the newly created test case object.
+ */
+ function AddTestCase(description, expect, actual) {
+ new TestCase(description, expect, actual);
+ }
+ global.AddTestCase = AddTestCase;
+
+ var testCasesArray = [];
+
+ function TestCase(d, e, a, r) {
+ this.description = d;
+ this.expect = e;
+ this.actual = a;
+ this.passed = getTestCaseResult(e, a);
+ this.reason = typeof r !== 'undefined' ? String(r) : '';
+
+ ArrayPush(testCasesArray, this);
+ }
+ global.TestCase = TestCase;
+
+ TestCase.prototype = ObjectCreate(null);
+ TestCase.prototype.testPassed = (function TestCase_testPassed() { return this.passed; });
+ TestCase.prototype.testFailed = (function TestCase_testFailed() { return !this.passed; });
+ TestCase.prototype.testDescription = (function TestCase_testDescription() { return this.description + ' ' + this.reason; });
+
+ function getTestCaseResult(expected, actual) {
+ if (typeof expected !== typeof actual)
+ return false;
+ if (typeof expected !== 'number')
+ // Note that many tests depend on the use of '==' here, not '==='.
+ return actual == expected;
+
+ // Distinguish NaN from other values. Using x !== x comparisons here
+ // works even if tests redefine isNaN.
+ if (actual !== actual)
+ return expected !== expected;
+ if (expected !== expected)
+ return false;
+
+ // Tolerate a certain degree of error.
+ if (actual !== expected)
+ return MathAbs(actual - expected) <= 1E-10;
+
+ // Here would be a good place to distinguish 0 and -0, if we wanted
+ // to. However, doing so would introduce a number of failures in
+ // areas where they don't seem important. For example, the WeekDay
+ // function in ECMA-262 returns -0 for Sundays before the epoch, but
+ // the Date functions in SpiderMonkey specified in terms of WeekDay
+ // often don't. This seems unimportant.
+ return true;
+ }
+
+ function reportTestCaseResult(description, expected, actual, output) {
+ var testcase = new TestCase(description, expected, actual, output);
+
+ // if running under reftest, let it handle result reporting.
+ if (!runningInBrowser) {
+ if (testcase.passed) {
+ print(PASSED + description);
+ } else {
+ reportFailure(description + " : " + output);
+ }
+ }
+ }
+
+ function getTestCases() {
+ return testCasesArray;
+ }
+ global.getTestCases = getTestCases;
+
+ /*
+ * The test driver searches for such a phrase in the test output.
+ * If such phrase exists, it will set n as the expected exit code.
+ */
+ function expectExitCode(n) {
+ print('--- NOTE: IN THIS TESTCASE, WE EXPECT EXIT CODE ' + n + ' ---');
+ }
+ global.expectExitCode = expectExitCode;
+
+ /*
+ * Statuses current section of a test
+ */
+ function inSection(x) {
+ return "Section " + x + " of test - ";
+ }
+ global.inSection = inSection;
+
+ /*
+ * Report a failure in the 'accepted' manner
+ */
+ function reportFailure(msg) {
+ msg = String(msg);
+ var lines = StringSplit(msg, "\n");
+
+ for (var i = 0; i < lines.length; i++)
+ print(FAILED + " " + lines[i]);
+ }
+ global.reportFailure = reportFailure;
+
+ /*
+ * Print a non-failure message.
+ */
+ function printStatus(msg) {
+ msg = String(msg);
+ var lines = StringSplit(msg, "\n");
+
+ for (var i = 0; i < lines.length; i++)
+ print("STATUS: " + lines[i]);
+ }
+ global.printStatus = printStatus;
+
+ /*
+ * Print a bugnumber message.
+ */
+ function printBugNumber(num) {
+ print('BUGNUMBER: ' + num);
+ }
+ global.printBugNumber = printBugNumber;
+
+ /*
+ * Compare expected result to actual result, if they differ (in value and/or
+ * type) report a failure. If description is provided, include it in the
+ * failure report.
+ */
+ function reportCompare(expected, actual, description) {
+ var expected_t = typeof expected;
+ var actual_t = typeof actual;
+ var output = "";
+
+ if (typeof description === "undefined")
+ description = "";
+
+ if (expected_t !== actual_t)
+ output += `Type mismatch, expected type ${expected_t}, actual type ${actual_t} `;
+
+ if (expected != actual)
+ output += `Expected value '${toPrinted(expected)}', Actual value '${toPrinted(actual)}' `;
+
+ reportTestCaseResult(description, expected, actual, output);
+ }
+ global.reportCompare = reportCompare;
+
+ /*
+ * Attempt to match a regular expression describing the result to
+ * the actual result, if they differ (in value and/or
+ * type) report a failure. If description is provided, include it in the
+ * failure report.
+ */
+ function reportMatch(expectedRegExp, actual, description) {
+ var expected_t = "string";
+ var actual_t = typeof actual;
+ var output = "";
+
+ if (typeof description === "undefined")
+ description = "";
+
+ if (expected_t !== actual_t)
+ output += `Type mismatch, expected type ${expected_t}, actual type ${actual_t} `;
+
+ var matches = ReflectApply(RegExpPrototypeExec, expectedRegExp, [actual]) !== null;
+ if (!matches) {
+ output +=
+ `Expected match to '${toPrinted(expectedRegExp)}', Actual value '${toPrinted(actual)}' `;
+ }
+
+ reportTestCaseResult(description, true, matches, output);
+ }
+ global.reportMatch = reportMatch;
+
+ function compareSource(expect, actual, summary) {
+ // compare source
+ var expectP = String(expect);
+ var actualP = String(actual);
+
+ print('expect:\n' + expectP);
+ print('actual:\n' + actualP);
+
+ reportCompare(expectP, actualP, summary);
+
+ // actual must be compilable if expect is?
+ try {
+ var expectCompile = 'No Error';
+ var actualCompile;
+
+ Function(expect);
+ try {
+ Function(actual);
+ actualCompile = 'No Error';
+ } catch(ex1) {
+ actualCompile = ex1 + '';
+ }
+ reportCompare(expectCompile, actualCompile,
+ summary + ': compile actual');
+ } catch(ex) {
+ }
+ }
+ global.compareSource = compareSource;
+
+ function test() {
+ var testCases = getTestCases();
+ for (var i = 0; i < testCases.length; i++) {
+ var testCase = testCases[i];
+ testCase.reason += testCase.passed ? "" : "wrong value ";
+
+ // if running under reftest, let it handle result reporting.
+ if (!runningInBrowser) {
+ var message = `${testCase.description} = ${testCase.actual} expected: ${testCase.expect}`;
+ print((testCase.passed ? PASSED : FAILED) + message);
+ }
+ }
+ }
+ global.test = test;
+
+ // This function uses the shell's print function. When running tests in the
+ // browser, browser.js overrides this function to write to the page.
+ function writeHeaderToLog(string) {
+ print(string);
+ }
+ global.writeHeaderToLog = writeHeaderToLog;
+
+ /************************************
+ * PROMISE TESTING FUNCTION EXPORTS *
+ ************************************/
+
+ function getPromiseResult(promise) {
+ var result, error, caught = false;
+ promise.then(r => { result = r; },
+ e => { caught = true; error = e; });
+ drainJobQueue();
+ if (caught)
+ throw error;
+ return result;
+ }
+ global.getPromiseResult = getPromiseResult;
+
+ function assertEventuallyEq(promise, expected) {
+ assertEq(getPromiseResult(promise), expected);
+ }
+ global.assertEventuallyEq = assertEventuallyEq;
+
+ function assertEventuallyThrows(promise, expectedErrorType) {
+ assertThrowsInstanceOf(() => getPromiseResult(promise), expectedErrorType);
+ };
+ global.assertEventuallyThrows = assertEventuallyThrows;
+
+ function assertEventuallyDeepEq(promise, expected) {
+ assertDeepEq(getPromiseResult(promise), expected);
+ };
+ global.assertEventuallyDeepEq = assertEventuallyDeepEq;
+
+ /*******************************************
+ * RUN ONCE CODE TO SETUP ADDITIONAL STATE *
+ *******************************************/
+
+ // Clear all options before running any tests. browser.js performs this
+ // set-up as part of its jsTestDriverBrowserInit function.
+ if (!runningInBrowser) {
+ shellOptionsClear();
+ }
+
+ if (!runningInBrowser) {
+ // Set the minimum heap size for parallel marking to zero for testing
+ // purposes. We don't have access to gcparam in the browser.
+ gcparam('parallelMarkingThresholdKB', 0);
+ }
+})(this);
+
+var DESCRIPTION;
diff --git a/js/src/tests/shell/README b/js/src/tests/shell/README
new file mode 100644
index 0000000000..0c7a3d1498
--- /dev/null
+++ b/js/src/tests/shell/README
@@ -0,0 +1 @@
+Tests for JS shell-only functions
diff --git a/js/src/tests/shell/futex-apis.js b/js/src/tests/shell/futex-apis.js
new file mode 100644
index 0000000000..dd38a1c98b
--- /dev/null
+++ b/js/src/tests/shell/futex-apis.js
@@ -0,0 +1,125 @@
+// |reftest| skip-if(!xulRuntime.shell)
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+if (this.SharedArrayBuffer && this.Atomics) {
+
+// Checks for parameter validation of wait/wake API. All of these test
+// cases should throw exceptions during parameter validation, before
+// we check whether any waiting should be done.
+
+let ab = new ArrayBuffer(16);
+let sab = new SharedArrayBuffer(16);
+
+//////////////////////////////////////////////////////////////////////
+//
+// The view must be an Int32Array on a SharedArrayBuffer.
+
+// Check against non-TypedArray cases.
+
+{
+ let values = [null,
+ undefined,
+ true,
+ false,
+ new Boolean(true),
+ 10,
+ 3.14,
+ new Number(4),
+ "Hi there",
+ new Date,
+ /a*utomaton/g,
+ { password: "qumquat" },
+ new DataView(new ArrayBuffer(10)),
+ new ArrayBuffer(128),
+ new SharedArrayBuffer(128),
+ new Error("Ouch"),
+ [1,1,2,3,5,8],
+ ((x) => -x),
+ new Map(),
+ new Set(),
+ new WeakMap(),
+ new WeakSet(),
+ new Promise(() => "done"),
+ Symbol("halleluja"),
+ // TODO: Proxy?
+ Object,
+ Int32Array,
+ Date,
+ Math,
+ Atomics ];
+
+ for ( let i=0 ; i < values.length ; i++ ) {
+ let view = values[i];
+ assertThrowsInstanceOf(() => Atomics.wait(view, 0, 0), TypeError);
+ assertThrowsInstanceOf(() => Atomics.wake(view, 0), TypeError);
+ }
+}
+
+// Check against TypedArray on non-shared memory and wrong view types cases.
+
+{
+ let views = [Int8Array, Uint8Array, Int16Array, Uint16Array, Uint32Array,
+ Uint8ClampedArray, Float32Array, Float64Array];
+
+ for ( let View of views ) {
+ let view = new View(ab);
+
+ assertThrowsInstanceOf(() => Atomics.wait(view, 0, 0), TypeError);
+ assertThrowsInstanceOf(() => Atomics.wake(view, 0), TypeError);
+ }
+}
+
+// Check against TypedArray on non-shared memory and correct view types cases.
+
+{
+ let views = [Int32Array];
+
+ for ( let View of views ) {
+ let view = new View(ab);
+
+ assertThrowsInstanceOf(() => Atomics.wait(view, 0, 0), TypeError);
+ assertEq(Atomics.wake(view, 0, 0), 0);
+ }
+}
+
+// Check against TypedArray on shared memory, but wrong view type
+
+{
+ let views = [Int8Array, Uint8Array, Int16Array, Uint16Array, Uint32Array,
+ Uint8ClampedArray, Float32Array, Float64Array];
+
+ for ( let View of views ) {
+ let view = new View(sab);
+
+ assertThrowsInstanceOf(() => Atomics.wait(view, 0, 0), TypeError);
+ assertThrowsInstanceOf(() => Atomics.wake(view, 0), TypeError);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////
+//
+// The indices must be in the range of the array
+
+{
+ let view = new Int32Array(sab);
+
+ let indices = [ (view) => -1,
+ (view) => view.length,
+ (view) => view.length*2,
+ (view) => '-3.5',
+ (view) => ({ valueOf: () => -8 }) ];
+
+ for ( let iidx=0 ; iidx < indices.length ; iidx++ ) {
+ let Idx = indices[iidx](view);
+ assertThrowsInstanceOf(() => Atomics.wait(view, Idx, 10), RangeError);
+ assertThrowsInstanceOf(() => Atomics.wake(view, Idx), RangeError);
+ }
+}
+
+} // if (this.SharedArrayBuffer && this.Atomics) { ... }
+
+reportCompare(true,true);
diff --git a/js/src/tests/shell/futex.js b/js/src/tests/shell/futex.js
new file mode 100644
index 0000000000..f3ad8c0a3e
--- /dev/null
+++ b/js/src/tests/shell/futex.js
@@ -0,0 +1,117 @@
+// |reftest| slow skip-if(!xulRuntime.shell)
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var DEBUG = false;
+
+function dprint(s) {
+ if (DEBUG) print(s);
+}
+
+var hasSharedArrayBuffer = !!(this.SharedArrayBuffer &&
+ this.getSharedObject &&
+ this.setSharedObject);
+
+// Futex test
+
+// Only run if helper threads are available.
+if (hasSharedArrayBuffer && helperThreadCount() !== 0) {
+
+var mem = new Int32Array(new SharedArrayBuffer(1024));
+
+////////////////////////////////////////////////////////////
+
+// wait() returns "not-equal" if the value is not the expected one.
+
+mem[0] = 42;
+
+assertEq(Atomics.wait(mem, 0, 33), "not-equal");
+
+// wait() returns "timed-out" if it times out
+
+assertEq(Atomics.wait(mem, 0, 42, 100), "timed-out");
+
+////////////////////////////////////////////////////////////
+
+// Main is sharing the buffer with the worker; the worker is clearing
+// the buffer.
+
+mem[0] = 42;
+mem[1] = 37;
+mem[2] = DEBUG;
+
+setSharedObject(mem.buffer);
+
+evalInWorker(`
+var mem = new Int32Array(getSharedObject());
+function dprint(s) {
+ if (mem[2]) print(s);
+}
+assertEq(mem[0], 42); // what was written in the main thread
+assertEq(mem[1], 37); // is read in the worker
+mem[1] = 1337;
+dprint("Sleeping for 2 seconds");
+sleep(2);
+dprint("Waking the main thread now");
+setSharedObject(null);
+assertEq(Atomics.notify(mem, 0, 1), 1); // Can fail spuriously but very unlikely
+`);
+
+var then = Date.now();
+assertEq(Atomics.wait(mem, 0, 42), "ok");
+dprint("Woke up as I should have in " + (Date.now() - then)/1000 + "s");
+assertEq(mem[1], 1337); // what was written in the worker is read in the main thread
+assertEq(getSharedObject(), null); // The worker's clearing of the mbx is visible
+
+////////////////////////////////////////////////////////////
+
+// Test the default argument to Atomics.notify()
+
+setSharedObject(mem.buffer);
+
+evalInWorker(`
+var mem = new Int32Array(getSharedObject());
+sleep(2); // Probably long enough to avoid a spurious error next
+assertEq(Atomics.notify(mem, 0), 1); // Last argument to notify should default to +Infinity
+`);
+
+var then = Date.now();
+dprint("Main thread waiting on wakeup (2s)");
+assertEq(Atomics.wait(mem, 0, 42), "ok");
+dprint("Woke up as I should have in " + (Date.now() - then)/1000 + "s");
+
+////////////////////////////////////////////////////////////
+
+// A tricky case: while in the wait there will be an interrupt, and in
+// the interrupt handler we will execute a wait. This is
+// explicitly prohibited (for now), so there should be a catchable exception.
+
+var exn = false;
+timeout(2, function () {
+ dprint("In the interrupt, starting inner wait with timeout 2s");
+ try {
+ Atomics.wait(mem, 0, 42); // Should throw
+ } catch (e) {
+ dprint("Got the interrupt exception!");
+ exn = true;
+ }
+ return true;
+});
+try {
+ dprint("Starting outer wait");
+ assertEq(Atomics.wait(mem, 0, 42, 5000), "timed-out");
+}
+finally {
+ timeout(-1);
+}
+assertEq(exn, true);
+
+////////////////////////////////////////////////////////////
+
+} // if (hasSharedArrayBuffer && helperThreadCount() !== 0) { ... }
+
+dprint("Done");
+reportCompare(true,true);
diff --git a/js/src/tests/shell/gcstats.js b/js/src/tests/shell/gcstats.js
new file mode 100644
index 0000000000..8923a1e489
--- /dev/null
+++ b/js/src/tests/shell/gcstats.js
@@ -0,0 +1,60 @@
+// |reftest| skip-if(!xulRuntime.shell)
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+function garbage() {
+ var x;
+ for (var i = 0; i < 100000; i++)
+ x = { 'i': i };
+}
+
+setGCCallback({
+ action: "majorGC",
+ depth: 1,
+ phases: "both"
+});
+
+gc();
+garbage();
+
+setGCCallback({
+ action: "majorGC",
+ depth: 2,
+ phases: "both"
+});
+
+gc();
+garbage();
+
+setGCCallback({
+ action: "majorGC",
+ depth: 8,
+ phases: "begin"
+});
+
+gc();
+garbage();
+
+setGCCallback({
+ action: "minorGC",
+ phases: "both"
+});
+
+gc();
+garbage();
+
+var caught = false;
+try {
+ setGCCallback({
+ action: "majorGC",
+ depth: 10000,
+ phases: "begin"
+ });
+} catch (e) {
+ caught = ((""+e).indexOf("Nesting depth too large") >= 0);
+}
+
+reportCompare(caught, true);
diff --git a/js/src/tests/shell/mailbox.js b/js/src/tests/shell/mailbox.js
new file mode 100644
index 0000000000..1ab2f662cd
--- /dev/null
+++ b/js/src/tests/shell/mailbox.js
@@ -0,0 +1,104 @@
+// |reftest| skip-if(!xulRuntime.shell)
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+// Tests the shared-object mailbox in the shell.
+
+var hasSharedMemory = !!(this.SharedArrayBuffer &&
+ this.getSharedObject &&
+ this.setSharedObject);
+
+if (!hasSharedMemory) {
+ reportCompare(true, true);
+ quit(0);
+}
+
+var sab = new SharedArrayBuffer(12);
+var mem = new Int32Array(sab);
+
+// SharedArrayBuffer mailbox tests
+
+assertEq(getSharedObject(), null); // Mbx starts empty
+
+assertEq(setSharedObject(mem.buffer), undefined); // Setter returns undefined
+assertEq(getSharedObject() == null, false); // And then the mbx is not empty
+
+var v = getSharedObject();
+assertEq(v.byteLength, mem.buffer.byteLength); // Looks like what we put in?
+var w = new Int32Array(v);
+mem[0] = 314159;
+assertEq(w[0], 314159); // Shares memory (locally) with what we put in?
+mem[0] = 0;
+
+setSharedObject(3.14); // Share numbers
+assertEq(getSharedObject(), 3.14);
+
+setSharedObject(null); // Setting to null clears to null
+assertEq(getSharedObject(), null);
+
+setSharedObject(mem.buffer);
+setSharedObject(undefined); // Setting to undefined clears to null
+assertEq(getSharedObject(), null);
+
+setSharedObject(mem.buffer);
+setSharedObject(); // Setting without arguments clears to null
+assertEq(getSharedObject(), null);
+
+// Non-shared objects cannot be stored in the mbx
+
+assertThrowsInstanceOf(() => setSharedObject({x:10, y:20}), Error);
+assertThrowsInstanceOf(() => setSharedObject([1,2]), Error);
+assertThrowsInstanceOf(() => setSharedObject(new ArrayBuffer(10)), Error);
+assertThrowsInstanceOf(() => setSharedObject(new Int32Array(10)), Error);
+assertThrowsInstanceOf(() => setSharedObject(false), Error);
+assertThrowsInstanceOf(() => setSharedObject(mem), Error);
+assertThrowsInstanceOf(() => setSharedObject("abracadabra"), Error);
+assertThrowsInstanceOf(() => setSharedObject(() => 37), Error);
+
+// We can store wasm shared memories, too
+
+if (!this.WebAssembly || !wasmThreadsEnabled()) {
+ reportCompare(true, true);
+ quit(0);
+}
+
+setSharedObject(null);
+
+var mem = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true});
+setSharedObject(mem);
+var ia1 = new Int32Array(mem.buffer);
+
+var mem2 = getSharedObject();
+assertEq(mem2.buffer instanceof SharedArrayBuffer, true);
+assertEq(mem2.buffer.byteLength, 65536);
+var ia2 = new Int32Array(mem2.buffer);
+
+ia2[37] = 0x12345678;
+assertEq(ia1[37], 0x12345678);
+
+// Can't store a non-shared memory
+
+assertThrowsInstanceOf(() => setSharedObject(new WebAssembly.Memory({initial: 1, maximum: 1})), Error);
+
+// We can store wasm modules
+
+var mod = new WebAssembly.Module(wasmTextToBinary(`(module
+ (import "m" "f" (func (param i32) (result i32)))
+ (func (export "hi") (result i32)
+ (i32.const 37))
+ )`));
+
+setSharedObject(mod);
+
+var mod2 = getSharedObject();
+
+// This should fail because we're not providing the correct import object
+assertThrowsInstanceOf(() => new WebAssembly.Instance(mod2, {m:{}}), WebAssembly.LinkError);
+
+// But this should work
+new WebAssembly.Instance(mod2, {m:{f:(x) => x}});
+
+reportCompare(true,true);
diff --git a/js/src/tests/shell/os.js b/js/src/tests/shell/os.js
new file mode 100644
index 0000000000..06e08f683b
--- /dev/null
+++ b/js/src/tests/shell/os.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!xulRuntime.shell||xulRuntime.OS=="WINNT")
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var pid = os.getpid();
+assertEq(pid > 0, true);
+
+var PATH = os.getenv("PATH");
+assertEq(PATH.indexOf("bin") > 0, true);
+assertEq(os.getenv("SQUAMMISH_HILLBILLY_GOAT_SQUEEZERS"), undefined);
+
+assertEq(os.system("true"), 0, "/bin/true should exit 0");
+assertEq(os.system("false") != 0, true, "/bin/false should exit nonzero");
+
+var kidpid = os.spawn("sleep 60");
+assertEq(kidpid > 0, true, "spawning sleep");
+var info = os.waitpid(kidpid, true);
+assertEq(info.hasOwnProperty("pid"), false);
+assertEq(info.hasOwnProperty("exitStatus"), false);
+
+os.kill(kidpid);
+
+info = os.waitpid(kidpid);
+assertEq(info.hasOwnProperty("pid"), true, "waiting on dead process should return pid");
+assertEq(info.pid, kidpid);
+assertEq(info.hasOwnProperty("exitStatus"), false, "killed process should not have exitStatus");
+
+kidpid = os.spawn("false");
+assertEq(kidpid > 0, true, "spawning /bin/false");
+info = os.waitpid(kidpid);
+assertEq(info.hasOwnProperty("pid"), true, "waiting on dead process should return pid");
+assertEq(info.pid, kidpid);
+assertEq(info.hasOwnProperty("exitStatus"), true, "process should have exitStatus");
+assertEq(info.exitStatus, 1, "/bin/false should exit 1");
+
+reportCompare(true, true);
diff --git a/js/src/tests/shell/script-file-name-utf8.js b/js/src/tests/shell/script-file-name-utf8.js
new file mode 100644
index 0000000000..99fb318ae2
--- /dev/null
+++ b/js/src/tests/shell/script-file-name-utf8.js
@@ -0,0 +1,235 @@
+// |reftest| skip-if(!xulRuntime.shell)
+
+// Retrieve the script file name through various functions and ensure it's
+// always correctly decoded from UTF-8.
+
+// Special value when filename cannot be retrieved.
+const NOT_SUPPORTED = "*not supported*";
+
+// Return the file name from the Error#fileName property.
+function errorFileName(fileName) {
+ return evaluate("new Error().fileName", {fileName});
+}
+
+// Return the file name from the Parser by a SyntaxError.
+function errorFileNameParser(fileName) {
+ try {
+ evaluate("###", {fileName});
+ } catch (e) {
+ return e.fileName;
+ }
+}
+
+// Retrieve the file name through DescriptedCaller (1).
+function descriptedCallerViaThisFileName(fileName) {
+ return evaluate("thisFilename()", {fileName});
+}
+
+// Retrieve the file name through DescriptedCaller (2).
+function descriptedCallerViaEvalInContext(fileName) {
+ return evaluate("evalcx('new Error().fileName')", {fileName});
+}
+
+// Retrieve the file name through DescriptedCaller (3).
+function descriptedCallerViaEval(fileName) {
+ var pattern = / line 1 > eval$/;
+ return evaluate("eval('new Error().fileName')", {fileName}).replace(pattern, "");
+}
+
+// Retrieve the file name through DescriptedCaller (4).
+function descriptedCallerViaFunction(fileName) {
+ var pattern = / line 1 > Function$/;
+ return evaluate("Function('return new Error().fileName')()", {fileName}).replace(pattern, "");
+}
+
+// Retrieve the file name through DescriptedCaller (5).
+function descriptedCallerViaEvalReturningScope(fileName) {
+ return evaluate("evalReturningScope('var a = new Error().fileName')", {fileName}).a;
+}
+
+// Retrieve the file name through DescriptedCaller (7).
+var wasmModuleConstructorTemp;
+function descriptedCallerViaWasm(fileName) {
+ if (!wasmIsSupported()) {
+ return fileName;
+ }
+
+ wasmModuleConstructorTemp = null;
+ evaluate(`
+ function wasmEvalText(str, imports) {
+ let binary = wasmTextToBinary(str);
+ assertEq(WebAssembly.validate(binary), true);
+ let m = new WebAssembly.Module(binary);
+ return new WebAssembly.Instance(m, imports);
+ }
+ wasmEvalText('(module (import "" "a" (func)) (func (call 0)) (export "bar" (func 1)))',
+ {
+ "": {
+ a() {
+ wasmModuleConstructorTemp = new Error().stack;
+ return 0;
+ }
+ }
+ }).exports.bar();
+ `, {fileName});
+ var pattern = /^@(.*) line \d+ >.*$/;
+ var index = 1; // Direct caller is the wasm function.
+ return wasmModuleConstructorTemp.split("\n")[index].replace(pattern, "$1");
+}
+
+// Return the file name from Reflect.parse().
+function reflectParseSource(fileName) {
+ return Reflect.parse("", {source: fileName}).loc.source;
+}
+
+// Return the file name using the Error#stack property.
+function fromErrorStack(fileName) {
+ var pattern = /^@(.*):\d+:\d+$/;
+ return evaluate("new Error().stack", {fileName}).split("\n")[0].replace(pattern, "$1");
+}
+
+// Return the file name using the Error#stack property from an asm.js function.
+function fromErrorStackAsmJS(fileName) {
+ var asm = evaluate(`(function asm(stdlib, foreign) {
+ "use asm";
+ var f = foreign.f;
+ function g() {
+ return f() | 0;
+ }
+ return {g: g};
+ })`, {fileName});
+
+ var stackFileName;
+ var foreign = {
+ f() {
+ var pattern = /^g@(.*):\d+:\d+$/;
+ var index = 1; // Direct caller is the asm.js function.
+ var stack = new Error().stack;
+ stackFileName = stack.split("\n")[index].replace(pattern, "$1");
+ return 0;
+ }
+ };
+
+ asm(this, foreign).g();
+
+ return stackFileName;
+}
+
+// Return the file name using the Error#stacl property when a streaming compiled WASM function.
+function fromErrorStackStreamingWasm(fileName) {
+ if (!wasmIsSupported() || helperThreadCount() == 0) {
+ return fileName;
+ }
+
+ var source = new Uint8Array(wasmTextToBinary(`
+ (module (import "" "a" (func)) (func (call 0)) (export "bar" (func 1)))
+ `));
+ source.url = fileName;
+
+ var stackFileName;
+ var imports = {
+ "": {
+ a() {
+ var pattern = /^@(.*):wasm-function.*$/;
+ var index = 1; // Direct caller is the asm.js function.
+ var stack = new Error().stack;
+ stackFileName = stack.split("\n")[index].replace(pattern, "$1");
+ return 0;
+ }
+ }
+ };
+
+ var result;
+ WebAssembly.instantiateStreaming(source, imports).then(r => result = r);
+
+ drainJobQueue();
+
+ result.instance.exports.bar();
+
+ return stackFileName;
+}
+
+// Return the file name using the embedded info in getBacktrace().
+function getBacktraceScriptName(fileName) {
+ var pattern = /^\d+ <TOP LEVEL> \["(.*)":\d+:\d\]$/;
+ return evaluate("getBacktrace()", {fileName}).split("\n")[0].replace(pattern, "$1");
+}
+
+// Return the file name from the coverage report.
+function getLcovInfoScriptName(fileName) {
+ var g = newGlobal();
+ var scriptFiles = g.evaluate("getLcovInfo()", {fileName})
+ .split("\n")
+ .filter(x => x.startsWith("SF:"));
+ assertEq(scriptFiles.length, 1);
+ return scriptFiles[0].substring(3);
+}
+
+// Return the file name from the error during module import.
+function moduleResolutionError(fileName) {
+ const a = parseModule(`import { x } from "b";`, fileName);
+ const ma = registerModule("a", a);
+ const b = parseModule(`export var y = 10;`);
+ const mb = registerModule("b", b);
+
+ try {
+ moduleLink(ma);
+ } catch (e) {
+ return e.fileName;
+ }
+}
+
+// Return the file name from the profiler stack.
+function geckoInterpProfilingStack(fileName) {
+ enableGeckoProfilingWithSlowAssertions();
+ const stack = evaluate(`readGeckoInterpProfilingStack();`, { fileName });
+ if (stack.length === 0) {
+ return NOT_SUPPORTED;
+ }
+ const result = stack[0].dynamicString;
+ disableGeckoProfiling();
+ return result;
+}
+
+const testFunctions = [
+ errorFileName,
+ errorFileNameParser,
+ descriptedCallerViaThisFileName,
+ descriptedCallerViaEvalInContext,
+ descriptedCallerViaEval,
+ descriptedCallerViaFunction,
+ descriptedCallerViaEvalReturningScope,
+ descriptedCallerViaWasm,
+ reflectParseSource,
+ fromErrorStack,
+ fromErrorStackAsmJS,
+ fromErrorStackStreamingWasm,
+ getBacktraceScriptName,
+ moduleResolutionError,
+];
+
+if (isLcovEnabled()) {
+ testFunctions.push(getLcovInfoScriptName);
+}
+
+const fileNames = [
+ "",
+ "test",
+ "Ðëßþ",
+ "тест",
+ "テスト",
+ "\u{1F9EA}",
+];
+
+for (const fn of testFunctions) {
+ for (const fileName of fileNames) {
+ const result = fn(fileName);
+ if (result === NOT_SUPPORTED) {
+ continue;
+ }
+ assertEq(result, fileName, `Caller '${fn.name}'`);
+ }
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/shell/shell.js b/js/src/tests/shell/shell.js
new file mode 100644
index 0000000000..7584133dad
--- /dev/null
+++ b/js/src/tests/shell/shell.js
@@ -0,0 +1,4 @@
+/* -*- 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/. */
diff --git a/js/src/tests/shell/warning.js b/js/src/tests/shell/warning.js
new file mode 100644
index 0000000000..3a390b0864
--- /dev/null
+++ b/js/src/tests/shell/warning.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!xulRuntime.shell)
+
+var BUGNUMBER = 1170716;
+var summary = 'Add js shell functions to get last warning';
+
+print(BUGNUMBER + ": " + summary);
+
+// Warning with JSEXN_SYNTAXERR.
+
+enableLastWarning();
+eval(`function f() { if (false) { "use asm"; } }`);
+
+warning = getLastWarning();
+assertEq(warning !== null, true);
+assertEq(warning.name, "SyntaxError");
+assertEq(warning.message.includes("Directive Prologue"), true);
+assertEq(warning.lineNumber, 1);
+assertEq(warning.columnNumber, 28);
+
+// Disabled.
+
+disableLastWarning();
+
+eval(`function f() { if (false) { "use asm"; } }`);
+
+enableLastWarning();
+warning = getLastWarning();
+assertEq(warning, null);
+
+disableLastWarning();
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);