summaryrefslogtreecommitdiffstats
path: root/js/src/tests/shell
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /js/src/tests/shell
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/tests/shell')
-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
9 files changed, 718 insertions, 0 deletions
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);