Source code
Revision control
Copy as Markdown
Other Tools
// GENERATED, DO NOT EDIT
// file: asyncHelpers.js
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: |
A collection of assertion and wrapper functions for testing asynchronous built-ins.
defines: [asyncTest, assert.throwsAsync]
---*/
/**
* Defines the **sole** asynchronous test of a file.
* @see {@link ../docs/rfcs/async-helpers.md} for background.
*
* @param {Function} testFunc a callback whose returned promise indicates test results
* (fulfillment for success, rejection for failure)
* @returns {void}
*/
function asyncTest(testFunc) {
if (!Object.hasOwn(globalThis, "$DONE")) {
throw new Test262Error("asyncTest called without async flag");
}
if (typeof testFunc !== "function") {
$DONE(new Test262Error("asyncTest called with non-function argument"));
return;
}
try {
testFunc().then(
function () {
$DONE();
},
function (error) {
$DONE(error);
}
);
} catch (syncError) {
$DONE(syncError);
}
}
/**
* Asserts that a callback asynchronously throws an instance of a particular
* error (i.e., returns a promise whose rejection value is an object referencing
* the constructor).
*
* @param {Function} expectedErrorConstructor the expected constructor of the
* rejection value
* @param {Function} func the callback
* @param {string} [message] the prefix to use for failure messages
* @returns {Promise<void>} fulfills if the expected error is thrown,
* otherwise rejects
*/
assert.throwsAsync = function (expectedErrorConstructor, func, message) {
return new Promise(function (resolve) {
var fail = function (detail) {
if (message === undefined) {
throw new Test262Error(detail);
}
throw new Test262Error(message + " " + detail);
};
if (typeof expectedErrorConstructor !== "function") {
fail("assert.throwsAsync called with an argument that is not an error constructor");
}
if (typeof func !== "function") {
fail("assert.throwsAsync called with an argument that is not a function");
}
var expectedName = expectedErrorConstructor.name;
var expectation = "Expected a " + expectedName + " to be thrown asynchronously";
var res;
try {
res = func();
} catch (thrown) {
fail(expectation + " but the function threw synchronously");
}
if (res === null || typeof res !== "object" || typeof res.then !== "function") {
fail(expectation + " but result was not a thenable");
}
var onResFulfilled, onResRejected;
var resSettlementP = new Promise(function (onFulfilled, onRejected) {
onResFulfilled = onFulfilled;
onResRejected = onRejected;
});
try {
res.then(onResFulfilled, onResRejected)
} catch (thrown) {
fail(expectation + " but .then threw synchronously");
}
resolve(resSettlementP.then(
function () {
fail(expectation + " but no exception was thrown at all");
},
function (thrown) {
var actualName;
if (thrown === null || typeof thrown !== "object") {
fail(expectation + " but thrown value was not an object");
} else if (thrown.constructor !== expectedErrorConstructor) {
actualName = thrown.constructor.name;
if (expectedName === actualName) {
fail(expectation +
" but got a different error constructor with the same name");
}
fail(expectation + " but got a " + actualName);
}
}
));
});
};
// file: atomicsHelper.js
// Copyright (C) 2017 Mozilla Corporation. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: >
Collection of functions used to interact with Atomics.* operations across agent boundaries.
defines:
- $262.agent.getReportAsync
- $262.agent.getReport
- $262.agent.safeBroadcastAsync
- $262.agent.safeBroadcast
- $262.agent.setTimeout
- $262.agent.tryYield
- $262.agent.trySleep
---*/
/**
* @return {String} A report sent from an agent.
*/
{
// This is only necessary because the original
// $262.agent.getReport API was insufficient.
//
// All runtimes currently have their own
// $262.agent.getReport which is wrong, so we
// will pave over it with a corrected version.
//
// Binding $262.agent is necessary to prevent
// breaking SpiderMonkey's $262.agent.getReport
let getReport = $262.agent.getReport.bind($262.agent);
$262.agent.getReport = function() {
var r;
while ((r = getReport()) == null) {
$262.agent.sleep(1);
}
return r;
};
if (this.setTimeout === undefined) {
(function(that) {
that.setTimeout = function(callback, delay) {
let p = Promise.resolve();
let start = Date.now();
let end = start + delay;
function check() {
if ((end - Date.now()) > 0) {
p.then(check);
}
else {
callback();
}
}
p.then(check);
}
})(this);
}
$262.agent.setTimeout = setTimeout;
$262.agent.getReportAsync = function() {
return new Promise(function(resolve) {
(function loop() {
let result = getReport();
if (!result) {
setTimeout(loop, 1000);
} else {
resolve(result);
}
})();
});
};
}
/**
*
* Share a given Int32Array or BigInt64Array to all running agents. Ensure that the
* provided TypedArray is a "shared typed array".
*
* NOTE: Migrating all tests to this API is necessary to prevent tests from hanging
* indefinitely when a SAB is sent to a worker but the code in the worker attempts to
* create a non-sharable TypedArray (something that is not Int32Array or BigInt64Array).
* When that scenario occurs, an exception is thrown and the agent worker can no
* longer communicate with any other threads that control the SAB. If the main
* thread happens to be spinning in the $262.agent.waitUntil() while loop, it will never
* meet its termination condition and the test will hang indefinitely.
*
* Because we've defined $262.agent.broadcast(SAB) in
* that assume compatibility, which must be maintained.
*
*
* $262.agent.safeBroadcast(TA) should not be included in
*
*
* @param {(Int32Array|BigInt64Array)} typedArray An Int32Array or BigInt64Array with a SharedArrayBuffer
*/
$262.agent.safeBroadcast = function(typedArray) {
let Constructor = Object.getPrototypeOf(typedArray).constructor;
let temp = new Constructor(
new SharedArrayBuffer(Constructor.BYTES_PER_ELEMENT)
);
try {
// This will never actually wait, but that's fine because we only
// want to ensure that this typedArray CAN be waited on and is shareable.
Atomics.wait(temp, 0, Constructor === Int32Array ? 1 : BigInt(1));
} catch (error) {
throw new Test262Error(`${Constructor.name} cannot be used as a shared typed array. (${error})`);
}
$262.agent.broadcast(typedArray.buffer);
};
$262.agent.safeBroadcastAsync = async function(ta, index, expected) {
await $262.agent.broadcast(ta.buffer);
await $262.agent.waitUntil(ta, index, expected);
await $262.agent.tryYield();
return await Atomics.load(ta, index);
};
/**
* With a given Int32Array or BigInt64Array, wait until the expected number of agents have
* reported themselves by calling:
*
* Atomics.add(typedArray, index, 1);
*
* @param {(Int32Array|BigInt64Array)} typedArray An Int32Array or BigInt64Array with a SharedArrayBuffer
* @param {number} index The index of which all agents will report.
* @param {number} expected The number of agents that are expected to report as active.
*/
$262.agent.waitUntil = function(typedArray, index, expected) {
var agents = 0;
while ((agents = Atomics.load(typedArray, index)) !== expected) {
/* nothing */
}
assert.sameValue(agents, expected, "Reporting number of 'agents' equals the value of 'expected'");
};
/**
* Timeout values used throughout the Atomics tests. All timeouts are specified in milliseconds.
*
* @property {number} yield Used for `$262.agent.tryYield`. Must not be used in other functions.
* @property {number} small Used when agents will always timeout and `Atomics.wake` is not part
* of the test semantics. Must be larger than `$262.agent.timeouts.yield`.
* @property {number} long Used when some agents may timeout and `Atomics.wake` is called on some
* agents. The agents are required to wait and this needs to be observable
* by the main thread.
* @property {number} huge Used when `Atomics.wake` is called on all waiting agents. The waiting
* must not timeout. The agents are required to wait and this needs to be
* observable by the main thread. All waiting agents must be woken by the
* main thread.
*
* Usage for `$262.agent.timeouts.small`:
* const WAIT_INDEX = 0;
* const RUNNING = 1;
* const TIMEOUT = $262.agent.timeouts.small;
* const i32a = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 2));
*
* $262.agent.start(`
* $262.agent.receiveBroadcast(function(sab) {
* const i32a = new Int32Array(sab);
* Atomics.add(i32a, ${RUNNING}, 1);
*
* $262.agent.report(Atomics.wait(i32a, ${WAIT_INDEX}, 0, ${TIMEOUT}));
*
* $262.agent.leaving();
* });
* `);
* $262.agent.safeBroadcast(i32a.buffer);
*
* // Wait until the agent was started and then try to yield control to increase
* // the likelihood the agent has called `Atomics.wait` and is now waiting.
* $262.agent.waitUntil(i32a, RUNNING, 1);
* $262.agent.tryYield();
*
* // The agent is expected to time out.
* assert.sameValue($262.agent.getReport(), "timed-out");
*
*
* Usage for `$262.agent.timeouts.long`:
* const WAIT_INDEX = 0;
* const RUNNING = 1;
* const NUMAGENT = 2;
* const TIMEOUT = $262.agent.timeouts.long;
* const i32a = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 2));
*
* for (let i = 0; i < NUMAGENT; i++) {
* $262.agent.start(`
* $262.agent.receiveBroadcast(function(sab) {
* const i32a = new Int32Array(sab);
* Atomics.add(i32a, ${RUNNING}, 1);
*
* $262.agent.report(Atomics.wait(i32a, ${WAIT_INDEX}, 0, ${TIMEOUT}));
*
* $262.agent.leaving();
* });
* `);
* }
* $262.agent.safeBroadcast(i32a.buffer);
*
* // Wait until the agents were started and then try to yield control to increase
* // the likelihood the agents have called `Atomics.wait` and are now waiting.
* $262.agent.waitUntil(i32a, RUNNING, NUMAGENT);
* $262.agent.tryYield();
*
* // Wake exactly one agent.
* assert.sameValue(Atomics.wake(i32a, WAIT_INDEX, 1), 1);
*
* // When it doesn't matter how many agents were woken at once, a while loop
* // can be used to make the test more resilient against intermittent failures
* // in case even though `tryYield` was called, the agents haven't started to
* // wait.
* //
* // // Repeat until exactly one agent was woken.
* // var woken = 0;
* // while ((woken = Atomics.wake(i32a, WAIT_INDEX, 1)) !== 0) ;
* // assert.sameValue(woken, 1);
*
* // One agent was woken and the other one timed out.
* const reports = [$262.agent.getReport(), $262.agent.getReport()];
* assert(reports.includes("ok"));
* assert(reports.includes("timed-out"));
*
*
* Usage for `$262.agent.timeouts.huge`:
* const WAIT_INDEX = 0;
* const RUNNING = 1;
* const NUMAGENT = 2;
* const TIMEOUT = $262.agent.timeouts.huge;
* const i32a = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 2));
*
* for (let i = 0; i < NUMAGENT; i++) {
* $262.agent.start(`
* $262.agent.receiveBroadcast(function(sab) {
* const i32a = new Int32Array(sab);
* Atomics.add(i32a, ${RUNNING}, 1);
*
* $262.agent.report(Atomics.wait(i32a, ${WAIT_INDEX}, 0, ${TIMEOUT}));
*
* $262.agent.leaving();
* });
* `);
* }
* $262.agent.safeBroadcast(i32a.buffer);
*
* // Wait until the agents were started and then try to yield control to increase
* // the likelihood the agents have called `Atomics.wait` and are now waiting.
* $262.agent.waitUntil(i32a, RUNNING, NUMAGENT);
* $262.agent.tryYield();
*
* // Wake all agents.
* assert.sameValue(Atomics.wake(i32a, WAIT_INDEX), NUMAGENT);
*
* // When it doesn't matter how many agents were woken at once, a while loop
* // can be used to make the test more resilient against intermittent failures
* // in case even though `tryYield` was called, the agents haven't started to
* // wait.
* //
* // // Repeat until all agents were woken.
* // for (var wokenCount = 0; wokenCount < NUMAGENT; ) {
* // var woken = 0;
* // while ((woken = Atomics.wake(i32a, WAIT_INDEX)) !== 0) ;
* // // Maybe perform an action on the woken agents here.
* // wokenCount += woken;
* // }
*
* // All agents were woken and none timeout.
* for (var i = 0; i < NUMAGENT; i++) {
* assert($262.agent.getReport(), "ok");
* }
*/
$262.agent.timeouts = {
yield: 100,
small: 200,
long: 1000,
huge: 10000,
};
/**
* Try to yield control to the agent threads.
*
* Usage:
* const VALUE = 0;
* const RUNNING = 1;
* const i32a = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 2));
*
* $262.agent.start(`
* $262.agent.receiveBroadcast(function(sab) {
* const i32a = new Int32Array(sab);
* Atomics.add(i32a, ${RUNNING}, 1);
*
* Atomics.store(i32a, ${VALUE}, 1);
*
* $262.agent.leaving();
* });
* `);
* $262.agent.safeBroadcast(i32a.buffer);
*
* // Wait until agent was started and then try to yield control.
* $262.agent.waitUntil(i32a, RUNNING, 1);
* $262.agent.tryYield();
*
* // Note: This result is not guaranteed, but should hold in practice most of the time.
* assert.sameValue(Atomics.load(i32a, VALUE), 1);
*
* The default implementation simply waits for `$262.agent.timeouts.yield` milliseconds.
*/
$262.agent.tryYield = function() {
$262.agent.sleep($262.agent.timeouts.yield);
};
/**
* Try to sleep the current agent for the given amount of milliseconds. It is acceptable,
* but not encouraged, to ignore this sleep request and directly continue execution.
*
* The default implementation calls `$262.agent.sleep(ms)`.
*
* @param {number} ms Time to sleep in milliseconds.
*/
$262.agent.trySleep = function(ms) {
$262.agent.sleep(ms);
};
// file: detachArrayBuffer.js
// Copyright (C) 2016 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: |
A function used in the process of asserting correctness of TypedArray objects.
$262.detachArrayBuffer is defined by a host.
defines: [$DETACHBUFFER]
---*/
function $DETACHBUFFER(buffer) {
if (!$262 || typeof $262.detachArrayBuffer !== "function") {
throw new Test262Error("No method available to detach an ArrayBuffer");
}
$262.detachArrayBuffer(buffer);
}