Merge autoland to mozilla-central. a=merge

This commit is contained in:
Iulian Moraru 2022-07-28 00:43:09 +03:00
commit 2bbb0c0a90
149 changed files with 2842 additions and 1051 deletions

View File

@ -171,7 +171,8 @@ static const uint64_t kCacheInitialized = ((uint64_t)0x1) << 63;
return mRole == roles::GROUPING || mRole == roles::RADIO_GROUP ||
mRole == roles::FIGURE || mRole == roles::GRAPHIC ||
mRole == roles::DOCUMENT || mRole == roles::OUTLINE ||
mRole == roles::ARTICLE;
mRole == roles::ARTICLE || mRole == roles::ENTRY ||
mRole == roles::SPINBUTTON;
}
- (mozilla::a11y::Accessible*)geckoAccessible {

View File

@ -89,3 +89,23 @@ addAccessibleTask(
ok(!article.getAttributeValue("AXTitle"));
}
);
/**
* Test text and number inputs supply only labels not titles
*/
addAccessibleTask(
`<label for="input">Your favorite number?</label><input type="text" name="input" value="11" id="input" aria-label="The best number you know of">`,
async (browser, accDoc) => {
let input = getNativeInterface(accDoc, "input");
is(input.getAttributeValue("AXDescription"), "The best number you know of");
ok(!input.getAttributeValue("AXTitle"));
let evt = waitForEvent(EVENT_SHOW, "input");
await SpecialPowers.spawn(browser, [], () => {
content.document.getElementById("input").setAttribute("type", "number");
});
await evt;
input = getNativeInterface(accDoc, "input");
is(input.getAttributeValue("AXDescription"), "The best number you know of");
ok(!input.getAttributeValue("AXTitle"));
}
);

View File

@ -776,7 +776,7 @@ function setCertErrorDetails(event) {
desc,
"cert-error-symantec-distrust-description",
{
HOST_NAME,
hostname: HOST_NAME,
}
);

View File

@ -31,8 +31,10 @@ __webpack_require__.r(__webpack_exports__);
/* 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/. */
// If we're in a subdialog, then this is a spotlight modal
const page = document.querySelector(":root[dialogroot=true]") ? "spotlight" : "about:welcome";
// If we're in a subdialog, then this is a spotlight modal.
// Otherwise, this is about:welcome or a Feature Callout
// in another "about" page and we should return the current page.
const page = document.querySelector(":root[dialogroot=true]") ? "spotlight" : document.location.href;
const AboutWelcomeUtils = {
handleUserAction(action) {
window.AWSendToParent("SPECIAL_ACTION", action);

View File

@ -2,10 +2,12 @@
* 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/. */
// If we're in a subdialog, then this is a spotlight modal
// If we're in a subdialog, then this is a spotlight modal.
// Otherwise, this is about:welcome or a Feature Callout
// in another "about" page and we should return the current page.
const page = document.querySelector(":root[dialogroot=true]")
? "spotlight"
: "about:welcome";
: document.location.href;
export const AboutWelcomeUtils = {
handleUserAction(action) {

View File

@ -29,6 +29,7 @@ XPCOMUtils.defineLazyModuleGetters(lazy, {
"resource://activity-stream/lib/ASRouterTriggerListeners.jsm",
KintoHttpClient: "resource://services-common/kinto-http-client.js",
Downloader: "resource://services-settings/Attachments.jsm",
RemoteImages: "resource://activity-stream/lib/RemoteImages.jsm",
RemoteL10n: "resource://activity-stream/lib/RemoteL10n.jsm",
ExperimentAPI: "resource://nimbus/ExperimentAPI.jsm",
NimbusFeatures: "resource://nimbus/ExperimentAPI.jsm",
@ -97,6 +98,14 @@ const TOPIC_EXPERIMENT_FORCE_ENROLLED = "nimbus:force-enroll";
const USE_REMOTE_L10N_PREF =
"browser.newtabpage.activity-stream.asrouter.useRemoteL10n";
const MESSAGING_EXPERIMENTS_DEFAULT_FEATURES = [
"cfr",
"infobar",
"moments-page",
"pbNewtab",
"spotlight",
];
// Experiment groups that need to report the reach event in Messaging-Experiments.
// If you're adding new groups to it, make sure they're also added in the
// `messaging_experiments.reach.objects` defined in "toolkit/components/telemetry/Events.yaml"
@ -325,15 +334,27 @@ const MessageLoaderUtils = {
return RemoteSettings(bucket).get();
},
/**
* Return messages from active Nimbus experiments.
*
* @param {object} provider A messaging experiments provider.
* @param {string[]?} provider.featureIds
* An optional array of Nimbus feature IDs to check for
* messaging experiments. If not provided, we will fall
* back to the set of default features. Otherwise, if
* provided and empty, we will not ingest messages from any
* features.
*
* @return {object[]} The list of messages from active experiments, as well as
* the messages defined in unenrolled branches so that they
* reach events can be recorded (if we record reach events
* for that feature).
*/
async _experimentsAPILoader(provider) {
// Allow tests to override the set of featureIds
const featureIds = provider.featureIds ?? [
"cfr",
"infobar",
"moments-page",
"pbNewtab",
"spotlight",
];
const featureIds = Array.isArray(provider.featureIds)
? provider.featureIds
: MESSAGING_EXPERIMENTS_DEFAULT_FEATURES;
let experiments = [];
for (const featureId of featureIds) {
let featureAPI = lazy.NimbusFeatures[featureId];
@ -552,6 +573,7 @@ class _ASRouter {
errors: [],
localeInUse: Services.locale.appLocaleAsBCP47,
};
this._experimentChangedListeners = new Map();
this._triggerHandler = this._triggerHandler.bind(this);
this._localProviders = localProviders;
this.blockMessageById = this.blockMessageById.bind(this);
@ -636,6 +658,13 @@ class _ASRouter {
);
provider.url = Services.urlFormatter.formatURL(provider.url);
}
if (provider.id === "messaging-experiments") {
// By default, the messaging-experiments provider lacks a featureIds
// property, so fall back to the list of default features.
if (!provider.featureIds) {
provider.featureIds = MESSAGING_EXPERIMENTS_DEFAULT_FEATURES;
}
}
// Reset provider update timestamp to force message refresh
provider.lastUpdated = undefined;
return provider;
@ -652,6 +681,21 @@ class _ASRouter {
}
}
{
// If the feature IDs of the messaging-experiments provider has changed,
// then we need to update which features for which we are listening to
// changes.
const prevExpts = previousProviders.find(
p => p.id === "messaging-experiments"
);
const expts = providers.find(p => p.id === "messaging-experiments");
this._onFeatureListChanged(
prevExpts?.enabled ? prevExpts.featureIds : [],
expts?.enabled ? expts.featureIds : []
);
}
return this.setState(prevState => ({
providers,
// Clear any messages from removed providers
@ -1848,7 +1892,7 @@ class _ASRouter {
await lazy.ToolbarPanelHub._hideToolbarButton(win);
}
async _onExperimentForceEnrolled(subject, topic, data) {
async _onExperimentForceEnrolled(subject, topic, slug) {
const experimentProvider = this.state.providers.find(
p => p.id === "messaging-experiments"
);
@ -1856,8 +1900,89 @@ class _ASRouter {
return;
}
const branch = lazy.ExperimentAPI.getActiveBranch({ slug });
const features = branch.features ?? [branch.feature];
const featureIds = features.map(feature => feature.featureId);
this._onFeaturesUpdated(...featureIds);
await this.loadMessagesFromAllProviders([experimentProvider]);
}
/**
* Handle a change to the list of featureIds that the messaging-experiments
* provider is watching.
*
* This normally occurs when ASRouter update message providers, which happens
* every startup and when the messaging-experiment provider pref changes.
*
* On startup, |oldFeatures| will be an empty array and we will subscribe to
* everything in |newFeatures|.
*
* When the pref changes, we unsubscribe from |oldFeatures - newFeatures| and
* subscribe to |newFeatures - oldFeatures|. Features that are listed in both
* sets do not have their subscription status changed. Pref changes are mostly
* during unit tests.
*
* @param {string[]} oldFeatures The list of feature IDs we were previously
* listening to for new experiments.
* @param {string[]} newFeatures The list of feature IDs we are now listening
* to for new experiments.
*/
_onFeatureListChanged(oldFeatures, newFeatures) {
for (const featureId of oldFeatures) {
if (!newFeatures.includes(featureId)) {
const listener = this._experimentChangedListeners.get(featureId);
this._experimentChangedListeners.delete(featureId);
lazy.NimbusFeatures[featureId].off(listener);
}
}
const newlySubscribed = [];
for (const featureId of newFeatures) {
if (!oldFeatures.includes(featureId)) {
const listener = () => this._onFeaturesUpdated(featureId);
this._experimentChangedListeners.set(featureId, listener);
lazy.NimbusFeatures[featureId].onUpdate(listener);
newlySubscribed.push(featureId);
}
}
// Check for any messages present in the newly subscribed to Nimbus features
// so we can prefetch their remote images (if any).
this._onFeaturesUpdated(...newlySubscribed);
}
/**
* Handle updated experiment features.
*
* If there are messages for the feature, RemoteImages will prefetch any
* images.
*
* @param {string[]} featureIds The feature IDs that have been updated.
*/
_onFeaturesUpdated(...featureIds) {
const messages = [];
for (const featureId of featureIds) {
const featureAPI = lazy.NimbusFeatures[featureId];
// If there is no active experiment for the feature, this will return
// null.
if (lazy.ExperimentAPI.getExperimentMetaData({ featureId })) {
// Otherwise, getAllVariables() will return the JSON blob for the
// message.
messages.push(featureAPI.getAllVariables());
}
}
// We are not awaiting this because we want these images to load in the
// background.
if (messages.length) {
lazy.RemoteImages.prefetchImagesFor(messages);
}
}
}
/**

View File

@ -45,11 +45,35 @@ const REMOTE_IMAGES_DB_PATH = PathUtils.join(REMOTE_IMAGES_PATH, "db.json");
const IMAGE_EXPIRY_DURATION = 30 * 24 * 60 * 60; // 30 days in seconds.
const PREFETCH_FINISHED_TOPIC = "remote-images:prefetch-finished";
/**
* Inspectors for FxMS messages.
*
* Each member is the name of a FxMS template (spotlight, infobar, etc.) and
* corresponds to a function that accepts a message and returns all record IDs
* for remote images.
*/
const MessageInspectors = {
spotlight(message) {
if (
message.content.template === "logo-and-content" &&
message.content.logo?.imageId
) {
return [message.content.logo.imageId];
}
return [];
},
};
class _RemoteImages {
#dbPromise;
#fetching;
constructor() {
this.#dbPromise = null;
this.#fetching = new Map();
RemoteSettings(RS_COLLECTION).on("sync", () => this.#onSync());
@ -193,6 +217,12 @@ class _RemoteImages {
async #loadImpl(db, imageId) {
const recordId = this.#getRecordId(imageId);
// If we are pre-fetching an image, we can piggy-back on that request.
if (this.#fetching.has(imageId)) {
const { record, arrayBuffer } = await this.#fetching.get(imageId);
return new Blob([arrayBuffer], { type: record.data.attachment.mimetype });
}
let blob;
if (db.data.images[recordId]) {
// We have previously fetched this image, we can load it from disk.
@ -231,6 +261,9 @@ class _RemoteImages {
}
#onSync() {
// This is OK to run while pre-fetches are ocurring. Pre-fetches don't check
// if there is a new version available, so there will be no race between
// syncing an updated image and pre-fetching
return this.withDb(async db => {
await this.#cleanup(db);
@ -245,7 +278,7 @@ class _RemoteImages {
.filter(
entry => recordsById[entry.recordId]?.attachment.hash !== entry.hash
)
.map(entry => this.#download(db, entry.recordId, { refresh: true }))
.map(entry => this.#download(db, entry.recordId, { fetchOnly: true }))
);
});
}
@ -261,6 +294,8 @@ class _RemoteImages {
* finished.
*/
async #cleanup(db) {
// This may run while background fetches are happening. However, that
// doesn't matter because those images will definitely not be expired.
const now = Date.now();
await Promise.all(
Object.values(db.data.images)
@ -330,39 +365,19 @@ class _RemoteImages {
* @param {JSONFile} db The RemoteImages database.
* @param {string} recordId The record ID of the image.
* @param {object} options Options for downloading the image.
* @param {boolean} options.refresh Whether or not the image is being
* downloaded because it is out of sync with
* Remote Settings. If true, the blob will be
* written to disk and not returned.
* Additionally, the |lastLoaded| field will
* not be updated in its db entry.
* @param {boolean} options.fetchOnly Whether or not to only fetch the image.
*
* @returns A promise that resolves with a Blob of the image data or rejects
* with an Error.
* @returns If |fetchOnly| is true, a promise that resolves to undefined.
* If |fetchOnly| is false, a promise that resolves to a Blob of the
* image data.
*/
async #download(db, recordId, { refresh = false } = {}) {
const client = new lazy.KintoHttpClient(lazy.Utils.SERVER_URL);
const record = await client
.bucket(RS_MAIN_BUCKET)
.collection(RS_COLLECTION)
.getRecord(recordId);
const downloader = new lazy.Downloader(RS_MAIN_BUCKET, RS_COLLECTION);
const arrayBuffer = await downloader.downloadAsBytes(record.data, {
retries: RS_DOWNLOAD_MAX_RETRIES,
});
// Cache to disk.
const path = PathUtils.join(REMOTE_IMAGES_PATH, recordId);
// We do not await this promise because any other attempt to interact with
// the file via IOUtils will have to synchronize via the IOUtils event queue
// anyway.
IOUtils.write(path, new Uint8Array(arrayBuffer));
async #download(db, recordId, { fetchOnly = false } = {}) {
// It is safe to call #unsafeDownload here because we hold the db while the
// entire download runs.
const { record, arrayBuffer } = await this.#unsafeDownload(recordId);
const { mimetype, hash } = record.data.attachment;
if (refresh) {
if (fetchOnly) {
Object.assign(db.data.images[recordId], { mimetype, hash });
} else {
db.data.images[recordId] = {
@ -375,11 +390,177 @@ class _RemoteImages {
db.saveSoon();
if (!refresh) {
return new Blob([arrayBuffer], { type: record.data.attachment.mimetype });
if (fetchOnly) {
return undefined;
}
return undefined;
return new Blob([arrayBuffer], { type: record.data.attachment.mimetype });
}
/**
* Download an image *without* holding a handle to the database.
*
* @param {string} recordId The record ID of the image to download
*
* @returns A promise that resolves to the RemoteSettings record and the
* downloaded ArrayBuffer.
*/
async #unsafeDownload(recordId) {
const client = new lazy.KintoHttpClient(lazy.Utils.SERVER_URL);
const record = await client
.bucket(RS_MAIN_BUCKET)
.collection(RS_COLLECTION)
.getRecord(recordId);
const downloader = new lazy.Downloader(RS_MAIN_BUCKET, RS_COLLECTION);
const arrayBuffer = await downloader.downloadAsBytes(record.data, {
retries: RS_DOWNLOAD_MAX_RETRIES,
});
const path = PathUtils.join(REMOTE_IMAGES_PATH, recordId);
// Cache to disk.
//
// We do not await this promise because any other attempt to interact with
// the file via IOUtils will have to synchronize via the IOUtils event queue
// anyway.
//
// This is OK to do without holding the db because cleanup will not touch
// this image.
IOUtils.write(path, new Uint8Array(arrayBuffer));
return { record, arrayBuffer };
}
/**
* Prefetch images for the given messages.
*
* This will only acquire the db handle when we need to handle internal state
* so that other consumers can interact with RemoteImages while pre-fetches
* are happening.
*
* NB: This function is not intended to be awaited so that it can run the
* fetches in the background.
*
* @param {object[]} messages The FxMS messages to prefetch images for.
*/
async prefetchImagesFor(messages) {
// Collect the list of record IDs from the message, if we have an inspector
// for it.
const recordIds = messages
.filter(
message =>
message.template && Object.hasOwn(MessageInspectors, message.template)
)
.flatMap(message => MessageInspectors[message.template](message))
.map(imageId => this.#getRecordId(imageId));
// If we find some messages, grab the db lock and queue the downloads of
// each.
if (recordIds.length) {
const promises = await this.withDb(
db =>
new Map(
recordIds.reduce((entries, recordId) => {
const promise = this.#beginPrefetch(db, recordId);
// If we already have the image, #beginPrefetching will return
// null instead of a promise.
if (promise !== null) {
this.#fetching.set(recordId, promise);
entries.push([recordId, promise]);
}
return entries;
}, [])
)
);
// We have dropped db lock and the fetches will continue in the background.
// If we do not drop the lock here, nothing can interact with RemoteImages
// while we are pre-fetching.
//
// As each prefetch request finishes, they will individually grab the db
// lock (inside #finishPrefetch or #handleFailedPrefetch) to update
// internal state.
const prefetchesFinished = Array.from(promises.entries()).map(
([recordId, promise]) =>
promise.then(
result => this.#finishPrefetch(result),
() => this.#handleFailedPrefetch(recordId)
)
);
// Wait for all prefetches to finish before we send our notification.
await Promise.all(prefetchesFinished);
Services.obs.notifyObservers(null, PREFETCH_FINISHED_TOPIC);
}
}
/**
* Ensure the image for the given record ID has a database entry.
* Begin pre-fetching the requested image if we do not already have it locally.
*
* @param {JSONFile} db The database.
* @param {string} recordId The record ID of the image.
*
* @returns If the image is already cached locally, null is returned.
* Otherwise, a promise that resolves to an object including the
* recordId, the Remote Settings record, and the ArrayBuffer of the
* downloaded file.
*/
#beginPrefetch(db, recordId) {
if (!Object.hasOwn(db.data.images, recordId)) {
// We kick off the download while we hold the db (so we can record the
// promise in #fetches), but we do not ensure that the download completes
// while we hold it.
//
// It is safe to call #unsafeDownload here and let the promises resolve
// outside this function because we record the recordId and promise in
// #fetching so any concurrent request to load the same image will re-use
// that promise and not trigger a second download (and therefore IO).
const promise = this.#unsafeDownload(recordId);
this.#fetching.set(recordId, promise);
return promise;
}
return null;
}
/**
* Finish prefetching an image.
*
* @param {object} options
* @param {object} options.record The Remote Settings record.
*/
#finishPrefetch({ record }) {
return this.withDb(db => {
const { id: recordId } = record.data;
const { mimetype, hash } = record.data.attachment;
this.#fetching.delete(recordId);
db.data.images[recordId] = {
recordId,
mimetype,
hash,
lastLoaded: Date.now(),
};
db.saveSoon();
});
}
/**
* Remove the prefetch entry for a fetch that failed.
*/
#handleFailedPrefetch(recordId) {
return this.withDb(db => {
this.#fetching.delete(recordId);
});
}
/**

View File

@ -3,12 +3,24 @@
"use strict";
const { ASRouter } = ChromeUtils.import(
"resource://activity-stream/lib/ASRouter.jsm"
);
const { BrowserUtils } = ChromeUtils.import(
"resource://gre/modules/BrowserUtils.jsm"
);
const { BrowserTestUtils } = ChromeUtils.import(
"resource://testing-common/BrowserTestUtils.jsm"
);
const { Downloader } = ChromeUtils.import(
"resource://services-settings/Attachments.jsm"
);
const { ExperimentFakes } = ChromeUtils.import(
"resource://testing-common/NimbusTestUtils.jsm"
);
const { PanelTestProvider } = ChromeUtils.import(
"resource://activity-stream/lib/PanelTestProvider.jsm"
);
const {
RemoteImages,
REMOTE_IMAGES_PATH,
@ -20,6 +32,11 @@ const { RemoteImagesTestUtils, RemoteSettingsServer } = ChromeUtils.import(
const { RemoteSettings } = ChromeUtils.import(
"resource://services-settings/remote-settings.js"
);
const { RemoteSettingsExperimentLoader } = ChromeUtils.import(
"resource://nimbus/lib/RemoteSettingsExperimentLoader.jsm"
);
const PREFETCH_FINISHED_TOPIC = "remote-images:prefetch-finished";
function dbWriteFinished(db) {
// RemoteImages calls JSONFile.saveSoon(), so make sure that the DeferredTask
@ -223,7 +240,7 @@ add_task(async function test_remoteImages_load_dedup() {
);
} finally {
await stop();
await RemoteImagesTestUtils.triggerCleanup();
await RemoteImagesTestUtils.wipeCache();
sandbox.restore();
}
});
@ -305,8 +322,109 @@ add_task(async function test_remoteImages_sync() {
});
} finally {
await server.stop();
await RemoteImagesTestUtils.triggerCleanup();
await RemoteImagesTestUtils.wipeCache();
sandbox.restore();
client.verifySignatures = true;
}
});
add_task(async function test_remoteImages_prefetch() {
const { AboutRobots } = RemoteImagesTestUtils.images;
const sandbox = sinon.createSandbox();
sandbox.stub(RemoteSettingsExperimentLoader, "setTimer");
sandbox.stub(RemoteSettingsExperimentLoader, "updateRecipes").resolves();
await SpecialPowers.pushPrefEnv({
set: [
["app.shield.optoutstudies.enabled", true],
["datareporting.healthreport.uploadEnabled", true],
[
"browser.newtabpage.activity-stream.asrouter.providers.messaging-experiments",
`{"id":"messaging-experiments","enabled":true,"type":"remote-experiments","updateCycleInMs":0}`,
],
],
});
const stop = await RemoteImagesTestUtils.serveRemoteImages(AboutRobots);
const message = await PanelTestProvider.getMessages().then(msgs =>
msgs.find(m => m.id === "SPOTLIGHT_MESSAGE_93")
);
message.content.logo = { imageId: AboutRobots.recordId };
const recipe = ExperimentFakes.recipe("spotlight-test", {
branches: [
{
slug: "snail",
ratio: 1,
features: [
{
featureId: "spotlight",
value: message,
},
],
},
],
bucketConfig: {
start: 0,
count: 100,
total: 100,
namespace: "mochitest",
randomizationUnit: "normandy_id",
},
});
Assert.ok(ASRouter.initialized, "ASRouter should be initialized");
const prefetchFinished = BrowserUtils.promiseObserved(
PREFETCH_FINISHED_TOPIC
);
const {
enrollmentPromise,
doExperimentCleanup,
} = ExperimentFakes.enrollmentHelper(recipe);
await Promise.all([enrollmentPromise, prefetchFinished]);
try {
await RemoteImages.withDb(async db => {
const entry = db.data.images[AboutRobots.recordId];
Assert.equal(
entry.recordId,
AboutRobots.recordId,
"Prefetched image DB entry should exist"
);
Assert.equal(
entry.mimetype,
AboutRobots.mimetype,
"Prefetched image should have correct mimetype"
);
Assert.equal(
entry.hash,
AboutRobots.hash,
"Prefetched image should have correct hash"
);
});
const path = PathUtils.join(REMOTE_IMAGES_PATH, AboutRobots.recordId);
await BrowserTestUtils.waitForCondition(
() => IOUtils.exists(path),
"Prefetched image should be written to disk"
);
Assert.equal(
await IOUtils.computeHexDigest(path, "sha256"),
AboutRobots.hash,
"AboutRobots image should have correct hash"
);
} finally {
await doExperimentCleanup();
await stop();
await SpecialPowers.popPrefEnv();
await ASRouter._updateMessageProviders();
await RemoteImagesTestUtils.wipeCache();
sandbox.reset();
}
});

View File

@ -13,8 +13,7 @@ from buildconfig import substs
"""
Scans the given directories for binaries referencing the AddressSanitizer
runtime library, copies it to the main directory and rewrites binaries to not
reference it with absolute paths but with @executable_path instead.
runtime library, copies it to the main directory.
"""
# This is the dylib name pattern
@ -83,10 +82,6 @@ def scan_directory(path):
dylibName = match.group(0)
absDylibPath = line.split()[0]
# Don't try to rewrite binaries twice
if absDylibPath.startswith("@executable_path/"):
continue
dylibsRequired.add(dylibName)
if dylibName not in dylibsCopied:
@ -100,16 +95,6 @@ def scan_directory(path):
# Copy the runtime once to the main directory, which is passed
# as the argument to this function.
shutil.copy(copyDylibPath, str(path))
# Now rewrite the library itself
subprocess.check_call(
[
substs["INSTALL_NAME_TOOL"],
"-id",
f"@executable_path/{dylibName}",
str(path / dylibName),
]
)
dylibsCopied.add(dylibName)
else:
print(
@ -117,20 +102,6 @@ def scan_directory(path):
file=sys.stderr,
)
# Now use install_name_tool to rewrite the path in our binary
if file.parent == path:
relpath = ""
else:
relpath = f"{os.path.relpath(str(path), str(file.parent))}/"
subprocess.check_call(
[
substs["INSTALL_NAME_TOOL"],
"-change",
absDylibPath,
f"@executable_path/{relpath}{dylibName}",
str(file),
]
)
break
dylibsMissing = dylibsRequired - dylibsCopied

View File

@ -58,7 +58,7 @@ impl MyObserver {
&self,
_subject: *const nsISupports,
_topic: *const c_char,
_data: *const i16,
_data: *const u16,
) -> nsresult {
self.ran.store(true, Ordering::SeqCst);
nserror::NS_OK

View File

@ -528,6 +528,11 @@ already_AddRefed<PerformanceMeasure> Performance::Measure(
JSContext* aCx, const nsAString& aName,
const StringOrPerformanceMeasureOptions& aStartOrMeasureOptions,
const Optional<nsAString>& aEndMark, ErrorResult& aRv) {
if (!GetParentObject()) {
aRv.ThrowInvalidStateError("Global object is unavailable");
return nullptr;
}
// When resisting fingerprinting, we don't add marks to the buffer. Since
// measure relies on relationships between marks in the buffer, this method
// will throw if we look for user-entered marks so we return a dummy measure

View File

@ -40,13 +40,17 @@
ok(true, 'new PerformanceMark() on dying global did not crash');
try {
dyingWindow.performance.mark('markMethod', {detail: 'markMethodDetail'});
dyingWindow.performance.mark('markMethod', {detail: 'markMethodDetail'});
} catch (e) {
is(e.code, e.INVALID_STATE_ERR, 'performance.mark on dying global threw expected exception');
is(e.code, e.INVALID_STATE_ERR, 'performance.mark on dying global threw expected exception');
}
ok(true, 'performance.mark on dying global did not crash');
dyingWindow.performance.measure('measureMethod');
try {
dyingWindow.performance.measure('measureMethod');
} catch (e) {
is(e.code, e.INVALID_STATE_ERR, 'performance.measure on dying global threw expected exception');
}
ok(true, 'performance.measure on dying global did not crash');
}
</script>

View File

@ -585,8 +585,6 @@ MSG_DEF(JSMSG_UNDEFINED_UNIT, 0, JSEXN_TYPEERR, "undefined unit in Numb
MSG_DEF(JSMSG_UNDEFINED_DATE, 2, JSEXN_TYPEERR, "undefined {0}-date in DateTimeFormat.{1}()")
MSG_DEF(JSMSG_UNDEFINED_NUMBER, 3, JSEXN_TYPEERR, "undefined {0} number in {1}.{2}()")
MSG_DEF(JSMSG_UNDEFINED_TYPE, 0, JSEXN_TYPEERR, "missing \"type\" option in DisplayNames()")
MSG_DEF(JSMSG_START_AFTER_END_DATE, 1, JSEXN_RANGEERR, "start-date after end-date in DateTimeFormat.{0}()")
MSG_DEF(JSMSG_START_AFTER_END_NUMBER, 2, JSEXN_RANGEERR, "start number after end number in {0}.{1}()")
MSG_DEF(JSMSG_EXPONENT_TOO_LARGE, 0, JSEXN_RANGEERR, "exponent is too large")
MSG_DEF(JSMSG_NAN_NUMBER_RANGE, 3, JSEXN_RANGEERR, "range can't {0} with NaN in {1}.{2}()")
MSG_DEF(JSMSG_INVALID_NUMBER_OPTION, 2, JSEXN_TYPEERR, "can't set option {0} when {1} is used")

View File

@ -1407,7 +1407,6 @@ static bool PartitionDateTimeRangePattern(
ClippedTime y, bool* equal) {
MOZ_ASSERT(x.isValid());
MOZ_ASSERT(y.isValid());
MOZ_ASSERT(x.toDouble() <= y.toDouble());
// We can't access the calendar used by UDateIntervalFormat to change it to a
// proleptic Gregorian calendar. Instead we need to call a different formatter
@ -1424,7 +1423,8 @@ static bool PartitionDateTimeRangePattern(
GregorianChangeDate + msPerDay;
mozilla::intl::ICUResult result = Ok();
if (x.toDouble() < GregorianChangeDatePlusOneDay) {
if (x.toDouble() < GregorianChangeDatePlusOneDay ||
y.toDouble() < GregorianChangeDatePlusOneDay) {
// Create calendar objects for the start and end date by cloning the date
// formatter calendar. The date formatter calendar already has the correct
// time zone set and was changed to use a proleptic Gregorian calendar.
@ -1565,10 +1565,6 @@ bool js::intl_FormatDateTimeRange(JSContext* cx, unsigned argc, Value* vp) {
return false;
}
// Self-hosted code should have checked this condition.
MOZ_ASSERT(x.toDouble() <= y.toDouble(),
"start date mustn't be after the end date");
mozilla::intl::DateTimeFormat* df =
GetOrCreateDateTimeFormat(cx, dateTimeFormat);
if (!df) {

View File

@ -667,33 +667,28 @@ function Intl_DateTimeFormat_formatRange(startDate, endDate) {
// Step 1.
var dtf = this;
// Steps 2-3.
// Step 2.
if (!IsObject(dtf) || (dtf = intl_GuardToDateTimeFormat(dtf)) === null) {
return callFunction(intl_CallDateTimeFormatMethodIfWrapped, this, startDate, endDate,
"Intl_DateTimeFormat_formatRange");
}
// Step 4.
// Step 3.
if (startDate === undefined || endDate === undefined) {
ThrowTypeError(JSMSG_UNDEFINED_DATE, startDate === undefined ? "start" : "end",
"formatRange");
}
// Step 5.
// Step 4.
var x = ToNumber(startDate);
// Step 6.
// Step 5.
var y = ToNumber(endDate);
// Step 7.
if (x > y) {
ThrowRangeError(JSMSG_START_AFTER_END_DATE, "formatRange");
}
// Ensure the DateTimeFormat internals are resolved.
getDateTimeFormatInternals(dtf);
// Step 8.
// Step 6.
return intl_FormatDateTimeRange(dtf, x, y, /* formatToParts = */ false);
}
@ -706,33 +701,28 @@ function Intl_DateTimeFormat_formatRangeToParts(startDate, endDate) {
// Step 1.
var dtf = this;
// Steps 2-3.
// Step 2.
if (!IsObject(dtf) || (dtf = intl_GuardToDateTimeFormat(dtf)) === null) {
return callFunction(intl_CallDateTimeFormatMethodIfWrapped, this, startDate, endDate,
"Intl_DateTimeFormat_formatRangeToParts");
}
// Step 4.
// Step 3.
if (startDate === undefined || endDate === undefined) {
ThrowTypeError(JSMSG_UNDEFINED_DATE, startDate === undefined ? "start" : "end",
"formatRangeToParts");
}
// Step 5.
// Step 4.
var x = ToNumber(startDate);
// Step 6.
// Step 5.
var y = ToNumber(endDate);
// Step 7.
if (x > y) {
ThrowRangeError(JSMSG_START_AFTER_END_DATE, "formatRangeToParts");
}
// Ensure the DateTimeFormat internals are resolved.
getDateTimeFormatInternals(dtf);
// Step 8.
// Step 6.
return intl_FormatDateTimeRange(dtf, x, y, /* formatToParts = */ true);
}

View File

@ -38,6 +38,9 @@ namespace js::intl {
* Note: Internally we leave the decimal point where it lies to avoid copying
* the string, but otherwise ignore it once we calculate the normalized
* exponent.
*
* TODO: Remove unused capabilities once there's a concrete PR for
* <https://github.com/tc39/proposal-intl-numberformat-v3/issues/98>.
*/
class MOZ_STACK_CLASS DecimalNumber final {
using Latin1String = mozilla::Span<const JS::Latin1Char>;

View File

@ -979,8 +979,7 @@ static bool IsNonDecimalNumber(JSLinearString* str) {
: IsNonDecimalNumber(str->twoByteRange(nogc));
}
static bool ToIntlMathematicalValue(JSContext* cx, MutableHandleValue value,
double* numberApproximation = nullptr) {
static bool ToIntlMathematicalValue(JSContext* cx, MutableHandleValue value) {
if (!ToPrimitive(cx, JSTYPE_NUMBER, value)) {
return false;
}
@ -1026,9 +1025,6 @@ static bool ToIntlMathematicalValue(JSContext* cx, MutableHandleValue value,
// Parse the string as a number.
double number = LinearStringToNumber(str);
if (numberApproximation) {
*numberApproximation = number;
}
bool exponentTooLarge = false;
if (mozilla::IsNaN(number)) {
@ -1256,207 +1252,6 @@ static JSLinearString* ToLinearString(JSContext* cx, HandleValue val) {
return str ? str->ensureLinear(cx) : nullptr;
};
static bool ValidateNumberRange(JSContext* cx, MutableHandleValue start,
double startApprox, MutableHandleValue end,
double endApprox, bool formatToParts) {
static auto isSpecificDouble = [](const Value& val, auto fn) {
return val.isDouble() && fn(val.toDouble());
};
static auto isNaN = [](const Value& val) {
return isSpecificDouble(val, mozilla::IsNaN<double>);
};
static auto isPositiveInfinity = [](const Value& val) {
return isSpecificDouble(
val, [](double num) { return num > 0 && mozilla::IsInfinite(num); });
};
static auto isNegativeInfinity = [](const Value& val) {
return isSpecificDouble(
val, [](double num) { return num < 0 && mozilla::IsInfinite(num); });
};
static auto isNegativeZero = [](const Value& val) {
return isSpecificDouble(val, mozilla::IsNegativeZero<double>);
};
static auto isMathematicalValue = [](const Value& val) {
// |ToIntlMathematicalValue()| normalizes non-finite values and negative
// zero to Double values, so any string is guaranteed to be a mathematical
// value at this point.
if (!val.isDouble()) {
return true;
}
double num = val.toDouble();
return mozilla::IsFinite(num) && !mozilla::IsNegativeZero(num);
};
static auto isPositiveOrZero = [](const Value& val, double approx) {
MOZ_ASSERT(isMathematicalValue(val));
if (val.isNumber()) {
return val.toNumber() >= 0;
}
if (val.isBigInt()) {
return !val.toBigInt()->isNegative();
}
return approx >= 0;
};
auto throwRangeError = [&]() {
JS_ReportErrorNumberASCII(
cx, GetErrorMessage, nullptr, JSMSG_START_AFTER_END_NUMBER,
"NumberFormat", formatToParts ? "formatRangeToParts" : "formatRange");
return false;
};
// PartitionNumberRangePattern, step 1.
if (isNaN(start)) {
JS_ReportErrorNumberASCII(
cx, GetErrorMessage, nullptr, JSMSG_NAN_NUMBER_RANGE, "start",
"NumberFormat", formatToParts ? "formatRangeToParts" : "formatRange");
return false;
}
if (isNaN(end)) {
JS_ReportErrorNumberASCII(
cx, GetErrorMessage, nullptr, JSMSG_NAN_NUMBER_RANGE, "end",
"NumberFormat", formatToParts ? "formatRangeToParts" : "formatRange");
return false;
}
// Make sure |start| and |end| can be correctly classified.
MOZ_ASSERT(isMathematicalValue(start) || isNegativeZero(start) ||
isNegativeInfinity(start) || isPositiveInfinity(start));
MOZ_ASSERT(isMathematicalValue(end) || isNegativeZero(end) ||
isNegativeInfinity(end) || isPositiveInfinity(end));
// PartitionNumberRangePattern, step 2.
if (isMathematicalValue(start)) {
// PartitionNumberRangePattern, step 2.a.
if (isMathematicalValue(end)) {
if (!start.isString() && !end.isString()) {
MOZ_ASSERT(start.isNumeric() && end.isNumeric());
bool isLessThan;
if (!LessThan(cx, end, start, &isLessThan)) {
return false;
}
if (isLessThan) {
return throwRangeError();
}
} else {
// |startApprox| and |endApprox| are only initially computed for string
// numbers.
if (start.isNumber()) {
startApprox = start.toNumber();
} else if (start.isBigInt()) {
startApprox = BigInt::numberValue(start.toBigInt());
}
if (end.isNumber()) {
endApprox = end.toNumber();
} else if (end.isBigInt()) {
endApprox = BigInt::numberValue(end.toBigInt());
}
// If the approximation is smaller, the actual value is definitely
// smaller, too.
if (endApprox < startApprox) {
return throwRangeError();
}
// If both approximations are equal to each other, we have to perform
// more work.
if (endApprox == startApprox) {
Rooted<JSLinearString*> strStart(cx, ToLinearString(cx, start));
if (!strStart) {
return false;
}
Rooted<JSLinearString*> strEnd(cx, ToLinearString(cx, end));
if (!strEnd) {
return false;
}
bool endLessThanStart;
{
JS::AutoCheckCannotGC nogc;
auto decStart = intl::DecimalNumber::from(strStart, nogc);
MOZ_ASSERT(decStart);
auto decEnd = intl::DecimalNumber::from(strEnd, nogc);
MOZ_ASSERT(decEnd);
endLessThanStart = decEnd->compareTo(*decStart) < 0;
}
if (endLessThanStart) {
return throwRangeError();
}
// If either value is a string, we end up passing both values as
// strings to the formatter. So let's save the string representation
// here, because then we don't have to recompute them later on.
start.setString(strStart);
end.setString(strEnd);
}
}
}
// PartitionNumberRangePattern, step 2.b.
else if (isNegativeInfinity(end)) {
return throwRangeError();
}
// PartitionNumberRangePattern, step 2.c.
else if (isNegativeZero(end)) {
if (isPositiveOrZero(start, startApprox)) {
return throwRangeError();
}
}
// No range restrictions when the end is positive infinity.
else {
MOZ_ASSERT(isPositiveInfinity(end));
}
}
// PartitionNumberRangePattern, step 3.
else if (isPositiveInfinity(start)) {
// PartitionNumberRangePattern, steps 3.a-c.
if (!isPositiveInfinity(end)) {
return throwRangeError();
}
}
// PartitionNumberRangePattern, step 4.
else if (isNegativeZero(start)) {
// PartitionNumberRangePattern, step 4.a.
if (isMathematicalValue(end)) {
if (!isPositiveOrZero(end, endApprox)) {
return throwRangeError();
}
}
// PartitionNumberRangePattern, step 4.b.
else if (isNegativeInfinity(end)) {
return throwRangeError();
}
// No range restrictions when the end is negative zero or positive infinity.
else {
MOZ_ASSERT(isNegativeZero(end) || isPositiveInfinity(end));
}
}
// No range restrictions when the start is negative infinity.
else {
MOZ_ASSERT(isNegativeInfinity(start));
}
return true;
}
bool js::intl_FormatNumberRange(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 4);
@ -1470,19 +1265,26 @@ bool js::intl_FormatNumberRange(JSContext* cx, unsigned argc, Value* vp) {
bool formatToParts = args[3].toBoolean();
RootedValue start(cx, args[1]);
double startApprox = mozilla::UnspecifiedNaN<double>();
if (!ToIntlMathematicalValue(cx, &start, &startApprox)) {
if (!ToIntlMathematicalValue(cx, &start)) {
return false;
}
RootedValue end(cx, args[2]);
double endApprox = mozilla::UnspecifiedNaN<double>();
if (!ToIntlMathematicalValue(cx, &end, &endApprox)) {
if (!ToIntlMathematicalValue(cx, &end)) {
return false;
}
if (!ValidateNumberRange(cx, &start, startApprox, &end, endApprox,
formatToParts)) {
// PartitionNumberRangePattern, step 1.
if (start.isDouble() && mozilla::IsNaN(start.toDouble())) {
JS_ReportErrorNumberASCII(
cx, GetErrorMessage, nullptr, JSMSG_NAN_NUMBER_RANGE, "start",
"NumberFormat", formatToParts ? "formatRangeToParts" : "formatRange");
return false;
}
if (end.isDouble() && mozilla::IsNaN(end.toDouble())) {
JS_ReportErrorNumberASCII(
cx, GetErrorMessage, nullptr, JSMSG_NAN_NUMBER_RANGE, "end",
"NumberFormat", formatToParts ? "formatRangeToParts" : "formatRange");
return false;
}

View File

@ -365,21 +365,13 @@ bool js::intl_SelectPluralRuleRange(JSContext* cx, unsigned argc, Value* vp) {
return false;
}
// Step 6.
if (x > y) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_START_AFTER_END_NUMBER, "PluralRules",
"selectRange");
return false;
}
using PluralRules = mozilla::intl::PluralRules;
PluralRules* pr = GetOrCreatePluralRules(cx, pluralRules);
if (!pr) {
return false;
}
// Steps 7-11.
// Steps 6-10.
auto keywordResult = pr->SelectRange(x, y);
if (keywordResult.isErr()) {
intl::ReportInternalError(cx, keywordResult.unwrapErr());

View File

@ -1038,12 +1038,7 @@ void MacroAssembler::assertStackAlignment(uint32_t alignment,
}
void MacroAssembler::storeCallBoolResult(Register reg) {
if (reg != ReturnReg) {
mov(ReturnReg, reg);
}
// C++ compilers like to only use the bottom byte for bools, but we
// need to maintain the entire register.
and32(Imm32(0xFF), reg);
convertBoolToInt32(ReturnReg, reg);
}
void MacroAssembler::storeCallInt32Result(Register reg) {

View File

@ -1,16 +1,23 @@
// |reftest| skip-if(!this.hasOwnProperty("Intl")||release_or_beta)
// String representation for Number.MAX_VALUE.
const en_Number_MAX_VALUE = "179,769,313,486,231,570" + ",000".repeat(97);
const de_Number_MAX_VALUE = en_Number_MAX_VALUE.replaceAll(",", ".");
const fr_Number_MAX_VALUE = en_Number_MAX_VALUE.replaceAll(",", "");
const tests = {
"en": {
options: {},
ranges: [
// Values around zero.
{start: 0, end: 0, result: "~0"},
{start: 0, end: -0, result: "0-0"},
{start: -0, end: 0, result: "-0 0"},
{start: -0, end: 0.1e-3, result: "-0 0"},
{start: -0, end: "0.1e-3", result: "-0 0"},
{start: "-0", end: 0.1e-3, result: "-0 0"},
{start: -0, end: -0, result: "~-0"},
{start: -0, end: -0.1, result: "-0 -0.1"},
// Values starting at negative infinity.
{start: -Infinity, end: -Infinity, result: "~-∞"},
@ -18,6 +25,19 @@ const tests = {
{start: -Infinity, end: +0, result: "-∞ 0"},
{start: -Infinity, end: +Infinity, result: "-∞ ∞"},
// Values ending at negative infinity.
{start: -Number.MAX_VALUE, end: -Infinity, result: "-" + en_Number_MAX_VALUE + " -∞"},
{start: -0, end: -Infinity, result: "-0 -∞"},
{start: 0, end: -Infinity, result: "0-∞"},
{start: Number.MAX_VALUE, end: -Infinity, result: en_Number_MAX_VALUE + "-∞"},
// Values starting at positive infinity.
{start: Infinity, end: Number.MAX_VALUE, result: "∞–" + en_Number_MAX_VALUE},
{start: Infinity, end: 0, result: "∞0"},
{start: Infinity, end: -0, result: "∞–-0"},
{start: Infinity, end: -Number.MAX_VALUE, result: "∞–-" + en_Number_MAX_VALUE},
{start: Infinity, end: -Infinity, result: "∞–-∞"},
// Values ending at positive infinity.
{start: Infinity, end: Infinity, result: "~∞"},
@ -37,6 +57,26 @@ const tests = {
end: "9007199254740993",
result: "9,007,199,254,740,9919,007,199,254,740,993",
},
// Start value is larger than end value.
{start: -0, end: -0.1, result: "-0 -0.1"},
{start: -0, end: -Number.MAX_VALUE, result: "-0 -" + en_Number_MAX_VALUE},
{start: 1, end: 0, result: "10"},
{start: 0, end: -1, result: "0-1"},
{start: 1, end: -1, result: "1-1"},
{start: -1, end: -2, result: "-1 -2"},
{start: "10e2", end: "1e-3", result: "1,0000.001"},
{start: "0x100", end: "1e1", result: "25610"},
{start: ".1e-999999", end: ".01e-999999", result: "~0"},
{start: ".1e99999", end: "0", result: "100" + ",000".repeat(33332) + "0"},
// Number.MAX_VALUE is 1.7976931348623157e+308.
{
start: "1.7976931348623158e+308",
end: Number.MAX_VALUE,
result: "179,769,313,486,231,580" + ",000".repeat(97) + "" + en_Number_MAX_VALUE,
},
// Number.MIN_VALUE is 5e-324.
{start: "6e-324", end: Number.MIN_VALUE, result: "~0"},
],
},
"de": {
@ -44,11 +84,13 @@ const tests = {
ranges: [
// Values around zero.
{start: 0, end: 0, result: "≈0,00 €"},
{start: 0, end: -0, result: "0,00  -0,00 €"},
{start: -0, end: 0, result: "-0,00  0,00 €"},
{start: -0, end: 0.1e-3, result: "-0,00  0,00 €"},
{start: -0, end: "0.1e-3", result: "-0,00  0,00 €"},
{start: "-0", end: 0.1e-3, result: "-0,00  0,00 €"},
{start: -0, end: -0, result: "≈-0,00 €"},
{start: -0, end: -0.1, result: "-0,000,10 €"},
// Values starting at negative infinity.
{start: -Infinity, end: -Infinity, result: "≈-∞ €"},
@ -56,6 +98,19 @@ const tests = {
{start: -Infinity, end: +0, result: "-∞ € 0,00 €"},
{start: -Infinity, end: +Infinity, result: "-∞ € ∞ €"},
// Values ending at negative infinity.
{start: -Number.MAX_VALUE, end: -Infinity, result: "-" + de_Number_MAX_VALUE + ",00 €"},
{start: -0, end: -Infinity, result: "-0,00 €"},
{start: 0, end: -Infinity, result: "0,00  -∞ €"},
{start: Number.MAX_VALUE, end: -Infinity, result: de_Number_MAX_VALUE + ",00  -∞ €"},
// Values starting at positive infinity.
{start: Infinity, end: Number.MAX_VALUE, result: "∞–" + de_Number_MAX_VALUE + ",00 €"},
{start: Infinity, end: 0, result: "∞0,00 €"},
{start: Infinity, end: -0, result: "∞ € -0,00 €"},
{start: Infinity, end: -Number.MAX_VALUE, result: "∞ € -" + de_Number_MAX_VALUE + ",00 €"},
{start: Infinity, end: -Infinity, result: "∞ € -∞ €"},
// Values ending at positive infinity.
{start: Infinity, end: Infinity, result: "≈∞ €"},
@ -75,6 +130,26 @@ const tests = {
end: "9007199254740993",
result: "9.007.199.254.740.991,009.007.199.254.740.993,00 €",
},
// Start value is larger than end value.
{start: -0, end: -0.1, result: "-0,000,10 €"},
{start: -0, end: -Number.MAX_VALUE, result: "-0,00" + de_Number_MAX_VALUE + ",00 €"},
{start: 1, end: 0, result: "1,000,00 €"},
{start: 0, end: -1, result: "0,00  -1,00 €"},
{start: 1, end: -1, result: "1,00  -1,00 €"},
{start: -1, end: -2, result: "-1,002,00 €"},
{start: "10e2", end: "1e-3", result: "1.000,000,00 €"},
{start: "0x100", end: "1e1", result: "256,0010,00 €"},
{start: ".1e-999999", end: ".01e-999999", result: "≈0,00 €"},
{start: ".1e99999", end: "0", result: "100" + ".000".repeat(33332) + ",000,00 €"},
// Number.MAX_VALUE is 1.7976931348623157e+308.
{
start: "1.7976931348623158e+308",
end: Number.MAX_VALUE,
result: "179.769.313.486.231.580" + ".000".repeat(97) + ",00" + de_Number_MAX_VALUE + ",00 €",
},
// Number.MIN_VALUE is 5e-324.
{start: "6e-324", end: Number.MIN_VALUE, result: "≈0,00 €"},
],
},
"fr": {
@ -83,10 +158,12 @@ const tests = {
// Values around zero.
{start: 0, end: 0, result: "≃0m"},
{start: -0, end: 0, result: "-0 0m"},
{start: -0, end: 0, result: "-0 0m"},
{start: -0, end: 0.1e-3, result: "-0 0m"},
{start: -0, end: "0.1e-3", result: "-0 0m"},
{start: "-0", end: 0.1e-3, result: "-0 0m"},
{start: -0, end: -0, result: "≃-0m"},
{start: -0, end: -0.1, result: "-0 -0,1m"},
// Values starting at negative infinity.
{start: -Infinity, end: -Infinity, result: "≃-∞m"},
@ -94,6 +171,19 @@ const tests = {
{start: -Infinity, end: +0, result: "-∞ 0m"},
{start: -Infinity, end: +Infinity, result: "-∞ m"},
// Values ending at negative infinity.
{start: -Number.MAX_VALUE, end: -Infinity, result: "-" + fr_Number_MAX_VALUE + " -∞m"},
{start: -0, end: -Infinity, result: "-0 -∞m"},
{start: 0, end: -Infinity, result: "0-∞m"},
{start: Number.MAX_VALUE, end: -Infinity, result: fr_Number_MAX_VALUE + "-∞m"},
// Values starting at positive infinity.
{start: Infinity, end: Number.MAX_VALUE, result: "∞–" + fr_Number_MAX_VALUE + "m"},
{start: Infinity, end: 0, result: "∞0m"},
{start: Infinity, end: -0, result: "∞–-0m"},
{start: Infinity, end: -Number.MAX_VALUE, result: "∞–-" + fr_Number_MAX_VALUE + "m"},
{start: Infinity, end: -Infinity, result: "∞–-∞m"},
// Values ending at positive infinity.
{start: Infinity, end: Infinity, result: "≃∞m"},
@ -113,6 +203,26 @@ const tests = {
end: "9007199254740993",
result: "90071992547409919007199254740993m",
},
// Start value is larger than end value.
{start: -0, end: -0.1, result: "-0 -0,1m"},
{start: -0, end: -Number.MAX_VALUE, result: "-0 -" + fr_Number_MAX_VALUE + "m"},
{start: 1, end: 0, result: "10m"},
{start: 0, end: -1, result: "0-1m"},
{start: 1, end: -1, result: "1-1m"},
{start: -1, end: -2, result: "-1 -2m"},
{start: "10e2", end: "1e-3", result: "10000,001m"},
{start: "0x100", end: "1e1", result: "25610m"},
{start: ".1e-999999", end: ".01e-999999", result: "≃0m"},
{start: ".1e99999", end: "0", result: "100" + "000".repeat(33332) + "0m"},
// Number.MAX_VALUE is 1.7976931348623157e+308.
{
start: "1.7976931348623158e+308",
end: Number.MAX_VALUE,
result: "179769313486231580" + "000".repeat(97) + "" + fr_Number_MAX_VALUE + "m",
},
// Number.MIN_VALUE is 5e-324.
{start: "6e-324", end: Number.MIN_VALUE, result: "≃0m"},
],
},
// Non-ASCII digits.
@ -165,51 +275,14 @@ for (let [locale, {options, ranges}] of Object.entries(tests)) {
}
}
// Throws an error if either value is NaN.
{
const errorTests = [
// Throws an error if either value is NaN.
{start: NaN, end: NaN},
{start: 0, end: NaN},
{start: NaN, end: 0},
{start: Infinity, end: NaN},
{start: NaN, end: Infinity},
// Positive infinity is larger than any other value.
{start: Infinity, end: Number.MAX_VALUE},
{start: Infinity, end: 0},
{start: Infinity, end: -0},
{start: Infinity, end: -Number.MAX_VALUE},
{start: Infinity, end: -Infinity},
// Negative infinity is smaller than any other value.
{start: -Number.MAX_VALUE, end: -Infinity},
{start: -0, end: -Infinity},
{start: 0, end: -Infinity},
{start: Number.MAX_VALUE, end: -Infinity},
// Negative zero is larger than any other negative value.
{start: -0, end: -0.1},
{start: -0, end: -Number.MAX_VALUE},
{start: -0, end: -Infinity},
// When values are mathematical values, |start| mustn't be larger than |end|.
{start: 1, end: 0},
{start: 0, end: -1},
{start: 1, end: -1},
{start: -1, end: -2},
// Positive zero is larger than negative zero.
{start: 0, end: -0},
// String cases require to parse and interpret the inputs.
{start: "10e2", end: "1e-3"},
{start: "0x100", end: "1e1"},
{start: ".1e-999999", end: ".01e-999999"},
{start: ".1e99999", end: "0"},
// Number.MAX_VALUE is 1.7976931348623157e+308.
{start: "1.7976931348623158e+308", end: Number.MAX_VALUE},
// Number.MIN_VALUE is 5e-324.
{start: "6e-324", end: Number.MIN_VALUE},
];
let nf = new Intl.NumberFormat("en");

View File

@ -2,7 +2,7 @@
// Any combination returns "other" for "en-US".
{
let numbers = [0, 0.5, 1.2, 1.5, 1.7, -1, 1, "1", 123456789.123456789];
let numbers = [0, 0.5, 1.2, 1.5, 1.7, -1, 1, "1", 123456789.123456789, Infinity, -Infinity];
const weirdCases = [
NaN,
@ -15,26 +15,10 @@
let pr = new Intl.PluralRules("en-US", {type});
for (let start of numbers) {
for (let end of numbers) {
if (start <= end) {
assertEq(pr.selectRange(start, end), "other");
} else {
assertThrowsInstanceOf(() => pr.selectRange(start, end), RangeError);
}
assertEq(pr.selectRange(start, end), "other");
}
}
{
assertThrowsInstanceOf(() => pr.selectRange(Infinity, 0), RangeError);
assertEq(pr.selectRange(0, Infinity), "other");
assertEq(pr.selectRange(Infinity, Infinity), "other");
}
{
assertEq(pr.selectRange(-Infinity, 0), "other");
assertThrowsInstanceOf(() => pr.selectRange(0, -Infinity), RangeError);
assertEq(pr.selectRange(-Infinity, -Infinity), "other");
}
for (let c of weirdCases) {
assertThrowsInstanceOf(() => pr.selectRange(c, 0), RangeError);
assertThrowsInstanceOf(() => pr.selectRange(0, c), RangeError);

View File

@ -1,9 +1,13 @@
commit a3040a5047694f37b793c4394fd9cfa591dd17c1
Author: André Bargull <andre.bargull@gmail.com>
Date: Tue Jul 5 12:48:26 2022 +0200
commit e623dd7a1183b04d862baea22a4b133b1635cb95
Author: Mathias Bynens <mathias@qiwi.be>
Date: Tue Jul 26 19:42:06 2022 +0200
Import SpiderMonkey Temporal tests
Add manually written tests for RegExp `v` flag proposal (#3614)
Temporal tests written for the SpiderMonkey implementation. Mostly
covers edge cases around mathematical operations and regression tests
for reported spec bugs.
https://github.com/tc39/proposal-regexp-v-flag
Issue: #3496, https://github.com/tc39/proposal-regexp-v-flag/issues/52
Add more tests for the new RegExp `v` flag
Add test for combination of `u` and `v` flag

View File

@ -10,13 +10,24 @@ features: [Symbol.asyncIterator, async-iteration]
---*/
async function* generator() {}
var AsyncIteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf(generator.prototype))
const AsyncIteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf(generator.prototype))
const getAsyncIterator = AsyncIteratorPrototype[Symbol.asyncIterator];
var thisValue = {};
const thisValues = [
{},
Symbol(),
4,
4n,
true,
undefined,
null,
];
assert.sameValue(
AsyncIteratorPrototype[Symbol.asyncIterator].call(thisValue),
thisValue
);
for (const thisValue of thisValues) {
assert.sameValue(
getAsyncIterator.call(thisValue),
thisValue
);
}
reportCompare(0, 0);

View File

@ -1,21 +1,34 @@
// Copyright (C) 2015 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
es6id: 25.1.2.1
description: Descriptor for `name` property
esid: sec-%iteratorprototype%-@@iterator
description: Return value of @@iterator on IteratorPrototype
info: |
%IteratorPrototype% [ @@iterator ] ( )
1. Return the this value.
features: [Symbol.iterator]
---*/
var IteratorPrototype = Object.getPrototypeOf(
const IteratorPrototype = Object.getPrototypeOf(
Object.getPrototypeOf([][Symbol.iterator]())
);
var thisValue = {};
const getIterator = IteratorPrototype[Symbol.iterator];
assert.sameValue(
IteratorPrototype[Symbol.iterator].call(thisValue),
thisValue
);
const thisValues = [
{},
Symbol(),
4,
4n,
true,
undefined,
null,
];
for (const thisValue of thisValues) {
assert.sameValue(
getIterator.call(thisValue),
thisValue
);
}
reportCompare(0, 0);

View File

@ -1,3 +1,4 @@
// |reftest| skip -- regexp-v-flag is not supported
// Copyright (C) 2017 Aleksey Shvayka. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
@ -17,19 +18,22 @@ info: |
11. If dotAll is true, append "s" as the last code unit of result.
12. Let unicode be ToBoolean(? Get(R, "unicode")).
13. If unicode is true, append "u" as the last code unit of result.
14. Let sticky be ToBoolean(? Get(R, "sticky")).
15. If sticky is true, append "y" as the last code unit of result.
16. Return result.
features: [regexp-dotall, regexp-match-indices]
14. Let unicodeSets be ! ToBoolean(? Get(R, "unicodeSets")).
15. If unicodeSets is true, append "v" as the last code unit of result.
16. Let sticky be ToBoolean(? Get(R, "sticky")).
17. If sticky is true, append "y" as the last code unit of result.
18. Return result.
features: [regexp-dotall, regexp-match-indices, regexp-v-flag]
---*/
assert.sameValue(/./.flags, '', 'no flags');
assert.sameValue(/./d.flags, 'd', 'hasIndices');
assert.sameValue(/./g.flags, 'g', 'global');
assert.sameValue(/./i.flags, 'i', 'ignoreCase');
assert.sameValue(/./m.flags, 'm', 'multiline');
assert.sameValue(/./s.flags, 's', 'dotAll');
assert.sameValue(/./u.flags, 'u', 'unicode');
assert.sameValue(/./v.flags, 'v', 'unicodeSets');
assert.sameValue(/./y.flags, 'y', 'sticky');
assert.sameValue(/./d.flags, 'd', 'hasIndices');
reportCompare(0, 0);

View File

@ -0,0 +1,32 @@
// |reftest| skip -- regexp-v-flag is not supported
// Copyright (C) 2022 Mathias Bynens, Ron Buckton, and the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-get-regexp.prototype.unicodeSets
description: RegExp#unicodeSets invoked on a cross-realm object
info: |
get RegExp.prototype.unicodeSets -> RegExpHasFlag
1. Let R be the this value.
2. If Type(R) is not Object, throw a TypeError exception.
3. If R does not have an [[OriginalFlags]] internal slot, then
a. If SameValue(R, %RegExpPrototype%) is true, return undefined.
b. Otherwise, throw a TypeError exception.
features: [regexp-v-flag, cross-realm]
---*/
var unicodeSets = Object.getOwnPropertyDescriptor(RegExp.prototype, 'unicodeSets').get;
var other = $262.createRealm().global;
var otherRegExpProto = other.RegExp.prototype;
var otherRegExpGetter = Object.getOwnPropertyDescriptor(otherRegExpProto, 'unicodeSets').get;
assert.throws(TypeError, function() {
unicodeSets.call(otherRegExpProto);
}, 'cross-realm RegExp.prototype');
assert.throws(other.TypeError, function() {
otherRegExpGetter.call(RegExp.prototype);
}, 'cross-realm RegExp.prototype getter method against primary realm RegExp.prototype');
reportCompare(0, 0);

View File

@ -0,0 +1,37 @@
// |reftest| skip -- regexp-v-flag is not supported
// Copyright (C) 2022 Mathias Bynens, Ron Buckton, and André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-get-regexp.prototype.unicodeSets
description: >
get RegExp.prototype.unicodeSets.length is 0.
info: |
get RegExp.prototype.unicodeSets
17 ECMAScript Standard Built-in Objects:
Every built-in Function object, including constructors, has a length
property whose value is an integer. Unless otherwise specified, this
value is equal to the largest number of named arguments shown in the
subclause headings for the function description, including optional
parameters. However, rest parameters shown using the form ...name
are not included in the default argument count.
Unless otherwise specified, the length property of a built-in Function
object has the attributes { [[Writable]]: false, [[Enumerable]]: false,
[[Configurable]]: true }.
includes: [propertyHelper.js]
features: [regexp-v-flag]
---*/
var desc = Object.getOwnPropertyDescriptor(RegExp.prototype, "unicodeSets");
assert.sameValue(desc.get.length, 0);
verifyProperty(desc.get, "length", {
enumerable: false,
writable: false,
configurable: true,
});
reportCompare(0, 0);

View File

@ -0,0 +1,30 @@
// |reftest| skip -- regexp-v-flag is not supported
// Copyright (C) 2022 Mathias Bynens, Ron Buckton, and the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-get-regexp.prototype.unicodeSets
description: >
RegExp.prototype.unicodeSets name
info: |
17 ECMAScript Standard Built-in Objects
Functions that are specified as get or set accessor functions of built-in
properties have "get " or "set " prepended to the property name string.
includes: [propertyHelper.js]
features: [regexp-v-flag]
---*/
var desc = Object.getOwnPropertyDescriptor(RegExp.prototype, "unicodeSets");
assert.sameValue(
desc.get.name,
"get unicodeSets"
);
verifyProperty(desc.get, "name", {
enumerable: false,
writable: false,
configurable: true,
});
reportCompare(0, 0);

View File

@ -0,0 +1,32 @@
// |reftest| skip -- regexp-v-flag is not supported
// Copyright (C) 2022 Mathias Bynens, Ron Buckton, and the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-get-regexp.prototype.unicodeSets
description: >
`pending` property descriptor
info: |
RegExp.prototype.unicodeSets is an accessor property whose set accessor
function is undefined.
17 ECMAScript Standard Built-in Objects
Every accessor property described in clauses 18 through 26 and in Annex B.2 has the attributes
{ [[Enumerable]]: false, [[Configurable]]: true } unless otherwise specified. If only a get
accessor function is described, the set accessor function is the default value, undefined. If
only a set accessor is described the get accessor is the default value, undefined.
includes: [propertyHelper.js]
features: [regexp-v-flag]
---*/
var desc = Object.getOwnPropertyDescriptor(RegExp.prototype, "unicodeSets");
assert.sameValue(desc.set, undefined);
assert.sameValue(typeof desc.get, "function");
verifyProperty(RegExp.prototype, "unicodeSets", {
enumerable: false,
configurable: true,
});
reportCompare(0, 0);

View File

@ -0,0 +1,37 @@
// |reftest| skip -- regexp-v-flag is not supported
// Copyright (C) 2022 Mathias Bynens, Ron Buckton, and the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-get-regexp.prototype.unicodeSets
description: Invoked on an object without an [[OriginalFlags]] internal slot
info: |
get RegExp.prototype.unicodeSets -> RegExpHasFlag
1. Let R be the this value.
2. If Type(R) is not Object, throw a TypeError exception.
3. If R does not have an [[OriginalFlags]] internal slot, then
a. If SameValue(R, %RegExpPrototype%) is true, return undefined.
b. Otherwise, throw a TypeError exception.
features: [regexp-v-flag]
---*/
var unicodeSets = Object.getOwnPropertyDescriptor(RegExp.prototype, 'unicodeSets').get;
assert.throws(TypeError, function() {
unicodeSets.call({});
}, 'ordinary object');
assert.throws(TypeError, function() {
unicodeSets.call([]);
}, 'array exotic object');
assert.throws(TypeError, function() {
unicodeSets.call(arguments);
}, 'arguments object');
assert.throws(TypeError, function() {
unicodeSets.call(() => {});
}, 'function object');
reportCompare(0, 0);

View File

@ -0,0 +1,47 @@
// |reftest| skip -- regexp-v-flag is not supported
// Copyright (C) 2022 Mathias Bynens, Ron Buckton, and the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-get-regexp.prototype.unicodeSets
description: >
`unicodeSets` accessor invoked on a non-object value
info: |
get RegExp.prototype.unicodeSets -> RegExpHasFlag
1. Let R be the this value.
2. If Type(R) is not Object, throw a TypeError exception.
features: [Symbol, regexp-v-flag]
---*/
var unicodeSets = Object.getOwnPropertyDescriptor(RegExp.prototype, "unicodeSets").get;
assert.throws(TypeError, function() {
unicodeSets.call(undefined);
}, "undefined");
assert.throws(TypeError, function() {
unicodeSets.call(null);
}, "null");
assert.throws(TypeError, function() {
unicodeSets.call(true);
}, "true");
assert.throws(TypeError, function() {
unicodeSets.call("string");
}, "string");
assert.throws(TypeError, function() {
unicodeSets.call(Symbol("s"));
}, "symbol");
assert.throws(TypeError, function() {
unicodeSets.call(4);
}, "number");
assert.throws(TypeError, function() {
unicodeSets.call(4n);
}, "bigint");
reportCompare(0, 0);

View File

@ -0,0 +1,24 @@
// |reftest| skip -- regexp-v-flag is not supported
// Copyright (C) 2022 Mathias Bynens, Ron Buckton, and the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-get-regexp.prototype.unicodeSets
description: >
Return value of `undefined` when the "this" value is the RegExp prototype
object
info: |
get RegExp.prototype.unicodeSets -> RegExpHasFlag
1. Let R be the this value.
2. If Type(R) is not Object, throw a TypeError exception.
3. If R does not have an [[OriginalFlags]] internal slot, then
a. If SameValue(R, %RegExpPrototype%) is true, return undefined.
features: [regexp-v-flag]
---*/
var get = Object.getOwnPropertyDescriptor(RegExp.prototype, "unicodeSets").get;
assert.sameValue(get.call(RegExp.prototype), undefined);
reportCompare(0, 0);

View File

@ -0,0 +1,54 @@
// |reftest| skip -- regexp-v-flag is not supported
// Copyright (C) 2022 Mathias Bynens, Ron Buckton, and the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-get-regexp.prototype.unicodeSets
description: >
`unicodeSets` accessor function invoked on a RegExp instance
info: |
get RegExp.prototype.unicodeSets -> RegExpHasFlag
4. Let flags be the value of Rs [[OriginalFlags]] internal slot.
5. If flags contains the code unit "s", return true.
6. Return false.
features: [regexp-v-flag]
---*/
assert.sameValue(/./.unicodeSets, false, "/./.unicodeSets");
assert.sameValue(/./d.unicodeSets, false, "/./d.unicodeSets");
assert.sameValue(/./g.unicodeSets, false, "/./g.unicodeSets");
assert.sameValue(/./i.unicodeSets, false, "/./i.unicodeSets");
assert.sameValue(/./m.unicodeSets, false, "/./m.unicodeSets");
assert.sameValue(/./s.unicodeSets, false, "/./s.unicodeSets");
assert.sameValue(/./u.unicodeSets, false, "/./u.unicodeSets");
assert.sameValue(/./y.unicodeSets, false, "/./y.unicodeSets");
assert.sameValue(/./v.unicodeSets, true, "/./v.unicodeSets");
assert.sameValue(/./vd.unicodeSets, true, "/./vd.unicodeSets");
assert.sameValue(/./vg.unicodeSets, true, "/./vg.unicodeSets");
assert.sameValue(/./vi.unicodeSets, true, "/./vi.unicodeSets");
assert.sameValue(/./vm.unicodeSets, true, "/./vm.unicodeSets");
assert.sameValue(/./vs.unicodeSets, true, "/./vs.unicodeSets");
// Note: `/vu` throws an early parse error and is tested separately.
assert.sameValue(/./vy.unicodeSets, true, "/./vy.unicodeSets");
assert.sameValue(new RegExp(".", "").unicodeSets, false, "new RegExp('.', '').unicodeSets");
assert.sameValue(new RegExp(".", "d").unicodeSets, false, "new RegExp('.', 'd').unicodeSets");
assert.sameValue(new RegExp(".", "g").unicodeSets, false, "new RegExp('.', 'g').unicodeSets");
assert.sameValue(new RegExp(".", "i").unicodeSets, false, "new RegExp('.', 'i').unicodeSets");
assert.sameValue(new RegExp(".", "m").unicodeSets, false, "new RegExp('.', 'm').unicodeSets");
assert.sameValue(new RegExp(".", "s").unicodeSets, false, "new RegExp('.', 's').unicodeSets");
assert.sameValue(new RegExp(".", "u").unicodeSets, false, "new RegExp('.', 'u').unicodeSets");
assert.sameValue(new RegExp(".", "y").unicodeSets, false, "new RegExp('.', 'y').unicodeSets");
assert.sameValue(new RegExp(".", "v").unicodeSets, true, "new RegExp('.', 'v').unicodeSets");
assert.sameValue(new RegExp(".", "vd").unicodeSets, true, "new RegExp('.', 'vd').unicodeSets");
assert.sameValue(new RegExp(".", "vg").unicodeSets, true, "new RegExp('.', 'vg').unicodeSets");
assert.sameValue(new RegExp(".", "vi").unicodeSets, true, "new RegExp('.', 'vi').unicodeSets");
assert.sameValue(new RegExp(".", "vm").unicodeSets, true, "new RegExp('.', 'vm').unicodeSets");
assert.sameValue(new RegExp(".", "vs").unicodeSets, true, "new RegExp('.', 'vs').unicodeSets");
// Note: `new RegExp(pattern, 'vu')` throws a runtime error and is tested separately.
assert.sameValue(new RegExp(".", "vy").unicodeSets, true, "new RegExp('.', 'vy').unicodeSets");
reportCompare(0, 0);

View File

@ -0,0 +1,17 @@
// |reftest| skip -- regexp-v-flag is not supported
// Copyright 2022 Mathias Bynens. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
author: Mathias Bynens
description: >
Setting the `u` and `v` flag at the same time produces an error.
esid: sec-parsepattern
features: [regexp-v-flag]
---*/
assert.throws(SyntaxError, function() {
new RegExp(".", "uv");
});
reportCompare(0, 0);

View File

@ -0,0 +1,18 @@
// |reftest| skip error:SyntaxError -- regexp-v-flag is not supported
// Copyright 2022 Mathias Bynens. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
author: Mathias Bynens
description: >
Setting the `u` and `v` flag at the same time produces an error.
esid: sec-parsepattern
negative:
phase: parse
type: SyntaxError
features: [regexp-v-flag]
---*/
$DONOTEVALUATE();
/./uv;

View File

@ -1,3 +1,4 @@
// |reftest| skip-if(!this.hasOwnProperty('SharedArrayBuffer')) -- SharedArrayBuffer is not enabled unconditionally
// Copyright (C) 2021 Leo Balter. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
@ -7,6 +8,7 @@ info: |
This property has the attributes { [[Writable]]: false, [[Enumerable]]:
false, [[Configurable]]: false }.
includes: [propertyHelper.js]
features: [SharedArrayBuffer]
---*/
verifyProperty(SharedArrayBuffer, 'prototype', {

View File

@ -0,0 +1,31 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.add
description: temporalDurationLike object must contain at least one correctly spelled property
features: [Temporal]
---*/
const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
assert.throws(
TypeError,
() => instance.add({}),
"Throws TypeError if no property is present"
);
assert.throws(
TypeError,
() => instance.add({ nonsense: true }),
"Throws TypeError if no recognized property is present"
);
assert.throws(
TypeError,
() => instance.add({ sign: 1 }),
"Sign property is not recognized"
);
reportCompare(0, 0);

View File

@ -0,0 +1,20 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.add
description: Positive and negative values in the temporalDurationLike argument are not acceptable
features: [Temporal]
---*/
const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
assert.throws(
RangeError,
() => instance.add({ hours: 1, minutes: -30 }),
`mixed positive and negative values always throw`
);
reportCompare(0, 0);

View File

@ -0,0 +1,22 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.add
description: Passing a primitive other than string to add() throws
features: [Symbol, Temporal]
---*/
const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
assert.throws(RangeError, () => instance.add(undefined), "undefined");
assert.throws(RangeError, () => instance.add(null), "null");
assert.throws(RangeError, () => instance.add(true), "boolean");
assert.throws(RangeError, () => instance.add(""), "empty string");
assert.throws(TypeError, () => instance.add(Symbol()), "Symbol");
assert.throws(RangeError, () => instance.add(7), "number");
assert.throws(RangeError, () => instance.add(7n), "bigint");
assert.throws(TypeError, () => instance.add([]), "array");
assert.throws(TypeError, () => instance.add(() => {}), "function");
reportCompare(0, 0);

View File

@ -1,16 +0,0 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.add
description: Mixed positive and negative values or missing properties always throw
features: [Temporal]
---*/
const duration = Temporal.Duration.from({ days: 1, minutes: 5 });
assert.throws(RangeError, () => duration.add({ hours: 1, minutes: -30 }), "mixed signs");
assert.throws(TypeError, () => duration.add({}), "no properties");
assert.throws(TypeError, () => duration.add({ month: 12 }), "only singular 'month' property");
reportCompare(0, 0);

View File

@ -0,0 +1,30 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.add
description: Singular properties in the property bag are always ignored
features: [Temporal]
---*/
const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
[
{ year: 1 },
{ month: 2 },
{ week: 3 },
{ day: 4 },
{ hour: 5 },
{ minute: 6 },
{ second: 7 },
{ millisecond: 8 },
{ microsecond: 9 },
{ nanosecond: 10 },
].forEach((badObject) => {
assert.throws(TypeError, () => instance.add(badObject),
"Throw TypeError if temporalDurationLike is not valid");
});
reportCompare(0, 0);

View File

@ -8,7 +8,7 @@ description: A non-integer value for any recognized property in the property bag
features: [Temporal]
---*/
const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
const fields = [
"years",
"months",

View File

@ -0,0 +1,31 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.subtract
description: temporalDurationLike object must contain at least one correctly spelled property
features: [Temporal]
---*/
const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
assert.throws(
TypeError,
() => instance.subtract({}),
"Throws TypeError if no property is present"
);
assert.throws(
TypeError,
() => instance.subtract({ nonsense: true }),
"Throws TypeError if no recognized property is present"
);
assert.throws(
TypeError,
() => instance.subtract({ sign: 1 }),
"Sign property is not recognized"
);
reportCompare(0, 0);

View File

@ -0,0 +1,19 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.subtract
description: Positive and negative values in the temporalDurationLike argument are not acceptable
features: [Temporal]
---*/
const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
assert.throws(
RangeError,
() => instance.subtract({ hours: 1, minutes: -30 }),
`mixed positive and negative values always throw`
);
reportCompare(0, 0);

View File

@ -0,0 +1,22 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.subtract
description: Passing a primitive other than string to subtract() throws
features: [Symbol, Temporal]
---*/
const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
assert.throws(RangeError, () => instance.subtract(undefined), "undefined");
assert.throws(RangeError, () => instance.subtract(null), "null");
assert.throws(RangeError, () => instance.subtract(true), "boolean");
assert.throws(RangeError, () => instance.subtract(""), "empty string");
assert.throws(TypeError, () => instance.subtract(Symbol()), "Symbol");
assert.throws(RangeError, () => instance.subtract(7), "number");
assert.throws(RangeError, () => instance.subtract(7n), "bigint");
assert.throws(TypeError, () => instance.subtract([]), "array");
assert.throws(TypeError, () => instance.subtract(() => {}), "function");
reportCompare(0, 0);

View File

@ -1,16 +0,0 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.subtract
description: Mixed positive and negative values or missing properties always throw
features: [Temporal]
---*/
const duration = Temporal.Duration.from({ days: 1, minutes: 5 });
assert.throws(RangeError, () => duration.subtract({ hours: 1, minutes: -30 }), "mixed signs");
assert.throws(TypeError, () => duration.subtract({}), "no properties");
assert.throws(TypeError, () => duration.subtract({ month: 12 }), "only singular 'month' property");
reportCompare(0, 0);

View File

@ -0,0 +1,30 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.subtract
description: Singular properties in the property bag are always ignored
features: [Temporal]
---*/
const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
[
{ year: 1 },
{ month: 2 },
{ week: 3 },
{ day: 4 },
{ hour: 5 },
{ minute: 6 },
{ second: 7 },
{ millisecond: 8 },
{ microsecond: 9 },
{ nanosecond: 10 },
].forEach((badObject) => {
assert.throws(TypeError, () => instance.subtract(badObject),
"Throw TypeError if temporalDurationLike is not valid");
});
reportCompare(0, 0);

View File

@ -8,7 +8,7 @@ description: A non-integer value for any recognized property in the property bag
features: [Temporal]
---*/
const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
const fields = [
"years",
"months",

View File

@ -1,16 +0,0 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.with
description: Singular properties are ignored.
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const d = Temporal.Duration.from({ years: 5, days: 1 });
const d2 = d.with({ year: 1, days: 0 });
TemporalHelpers.assertDuration(d2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0);
reportCompare(0, 0);

View File

@ -0,0 +1,31 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.with
description: temporalDurationLike object must contain at least one correctly spelled property
features: [Temporal]
---*/
const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
assert.throws(
TypeError,
() => instance.with({}),
"Throws TypeError if no property is present"
);
assert.throws(
TypeError,
() => instance.with({ nonsense: true }),
"Throws TypeError if no recognized property is present"
);
assert.throws(
TypeError,
() => instance.with({ sign: 1 }),
"Sign property is not recognized"
);
reportCompare(0, 0);

View File

@ -4,11 +4,16 @@
/*---
esid: sec-temporal.duration.prototype.with
description: The durationLike argument must not contain different signs.
description: Positive and negative values in the temporalDurationLike argument are not acceptable
features: [Temporal]
---*/
const d = new Temporal.Duration(1, 2, 3, 4, 5);
assert.throws(RangeError, () => d.with({ hours: 1, minutes: -1 }));
const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
assert.throws(
RangeError,
() => instance.with({ hours: 1, minutes: -30 }),
`mixed positive and negative values always throw`
);
reportCompare(0, 0);

View File

@ -0,0 +1,23 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.with
description: Passing a primitive other than string to with() throws
features: [Symbol, Temporal]
---*/
const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
assert.throws(TypeError, () => instance.with(undefined), "undefined");
assert.throws(TypeError, () => instance.with(null), "null");
assert.throws(TypeError, () => instance.with(true), "boolean");
assert.throws(TypeError, () => instance.with(""), "empty string");
assert.throws(TypeError, () => instance.with(Symbol()), "Symbol");
assert.throws(TypeError, () => instance.with(7), "number");
assert.throws(TypeError, () => instance.with(7n), "bigint");
assert.throws(TypeError, () => instance.with([]), "array");
assert.throws(TypeError, () => instance.with(() => {}), "function");
assert.throws(TypeError, () => instance.with("string"), "string");
reportCompare(0, 0);

View File

@ -1,17 +0,0 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.with
description: Passing a sign property is not supported.
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const d = Temporal.Duration.from({ years: 5, days: 1 });
assert.throws(TypeError, () => d.with({ sign: -1 }));
const d2 = d.with({ sign: -1, days: 0 });
TemporalHelpers.assertDuration(d2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0);
reportCompare(0, 0);

View File

@ -1,21 +1,17 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2021 the V8 project authors. All rights reserved.
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.with
description: >
The durationLike argument must contain at least one correctly spelled property
description: Singular properties in the property bag are always ignored
features: [Temporal]
---*/
let d = new Temporal.Duration(1, 2, 3, 4, 5);
const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
[
{},
[],
() => {},
// objects with only singular keys (plural is the correct spelling)
{ year: 1 },
{ month: 2 },
{ week: 3 },
@ -27,8 +23,9 @@ let d = new Temporal.Duration(1, 2, 3, 4, 5);
{ microsecond: 9 },
{ nanosecond: 10 },
].forEach((badObject) => {
assert.throws(TypeError, () => d.with(badObject),
assert.throws(TypeError, () => instance.with(badObject),
"Throw TypeError if temporalDurationLike is not valid");
});
reportCompare(0, 0);

View File

@ -1,30 +0,0 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2021 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.duration.prototype.with
description: Throw TypeError if the temporalDurationLike argument is the wrong type
features: [Temporal]
---*/
let d = new Temporal.Duration(1, 2, 3, 4, 5);
[
"string",
"P1YT1M",
true,
false,
NaN,
Infinity,
undefined,
null,
123,
Symbol(),
456n,
].forEach((badInput) => {
assert.throws(TypeError, () => d.with(badInput),
"Throw TypeError if temporalDurationLike is not valid");
});
reportCompare(0, 0);

View File

@ -8,7 +8,7 @@ description: A non-integer value for any recognized property in the property bag
features: [Temporal]
---*/
const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321);
const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321);
const fields = [
"years",
"months",

View File

@ -0,0 +1,31 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.instant.prototype.add
description: temporalDurationLike object must contain at least one correctly spelled property
features: [Temporal]
---*/
const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
assert.throws(
TypeError,
() => instance.add({}),
"Throws TypeError if no property is present"
);
assert.throws(
TypeError,
() => instance.add({ nonsense: true }),
"Throws TypeError if no recognized property is present"
);
assert.throws(
TypeError,
() => instance.add({ sign: 1 }),
"Sign property is not recognized"
);
reportCompare(0, 0);

View File

@ -0,0 +1,19 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.instant.prototype.add
description: Positive and negative values in the temporalDurationLike argument are not acceptable
features: [Temporal]
---*/
const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
assert.throws(
RangeError,
() => instance.add({ hours: 1, minutes: -30 }),
`mixed positive and negative values always throw`
);
reportCompare(0, 0);

View File

@ -0,0 +1,22 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.instant.prototype.add
description: Passing a primitive other than string to add() throws
features: [Symbol, Temporal]
---*/
const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
assert.throws(RangeError, () => instance.add(undefined), "undefined");
assert.throws(RangeError, () => instance.add(null), "null");
assert.throws(RangeError, () => instance.add(true), "boolean");
assert.throws(RangeError, () => instance.add(""), "empty string");
assert.throws(TypeError, () => instance.add(Symbol()), "Symbol");
assert.throws(RangeError, () => instance.add(7), "number");
assert.throws(RangeError, () => instance.add(7n), "bigint");
assert.throws(TypeError, () => instance.add([]), "array");
assert.throws(TypeError, () => instance.add(() => {}), "function");
reportCompare(0, 0);

View File

@ -0,0 +1,30 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.instant.prototype.add
description: Singular properties in the property bag are always ignored
features: [Temporal]
---*/
const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
[
{ year: 1 },
{ month: 2 },
{ week: 3 },
{ day: 4 },
{ hour: 5 },
{ minute: 6 },
{ second: 7 },
{ millisecond: 8 },
{ microsecond: 9 },
{ nanosecond: 10 },
].forEach((badObject) => {
assert.throws(TypeError, () => instance.add(badObject),
"Throw TypeError if temporalDurationLike is not valid");
});
reportCompare(0, 0);

View File

@ -0,0 +1,31 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.instant.prototype.subtract
description: temporalDurationLike object must contain at least one correctly spelled property
features: [Temporal]
---*/
const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
assert.throws(
TypeError,
() => instance.subtract({}),
"Throws TypeError if no property is present"
);
assert.throws(
TypeError,
() => instance.subtract({ nonsense: true }),
"Throws TypeError if no recognized property is present"
);
assert.throws(
TypeError,
() => instance.subtract({ sign: 1 }),
"Sign property is not recognized"
);
reportCompare(0, 0);

View File

@ -0,0 +1,19 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.instant.prototype.subtract
description: Positive and negative values in the temporalDurationLike argument are not acceptable
features: [Temporal]
---*/
const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
assert.throws(
RangeError,
() => instance.subtract({ hours: 1, minutes: -30 }),
`mixed positive and negative values always throw`
);
reportCompare(0, 0);

View File

@ -0,0 +1,22 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.instant.prototype.subtract
description: Passing a primitive other than string to subtract() throws
features: [Symbol, Temporal]
---*/
const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
assert.throws(RangeError, () => instance.subtract(undefined), "undefined");
assert.throws(RangeError, () => instance.subtract(null), "null");
assert.throws(RangeError, () => instance.subtract(true), "boolean");
assert.throws(RangeError, () => instance.subtract(""), "empty string");
assert.throws(TypeError, () => instance.subtract(Symbol()), "Symbol");
assert.throws(RangeError, () => instance.subtract(7), "number");
assert.throws(RangeError, () => instance.subtract(7n), "bigint");
assert.throws(TypeError, () => instance.subtract([]), "array");
assert.throws(TypeError, () => instance.subtract(() => {}), "function");
reportCompare(0, 0);

View File

@ -0,0 +1,30 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.instant.prototype.subtract
description: Singular properties in the property bag are always ignored
features: [Temporal]
---*/
const instance = new Temporal.Instant(1_000_000_000_000_000_000n);
[
{ year: 1 },
{ month: 2 },
{ week: 3 },
{ day: 4 },
{ hour: 5 },
{ minute: 6 },
{ second: 7 },
{ millisecond: 8 },
{ microsecond: 9 },
{ nanosecond: 10 },
].forEach((badObject) => {
assert.throws(TypeError, () => instance.subtract(badObject),
"Throw TypeError if temporalDurationLike is not valid");
});
reportCompare(0, 0);

View File

@ -1,19 +0,0 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindate.prototype.add
description: RangeError thrown when signs don't match in the duration
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const calendar = TemporalHelpers.calendarThrowEverything();
const date = new Temporal.PlainDate(2000, 5, 2, calendar);
const duration = { months: 1, days: -30 };
for (const overflow of ["constrain", "reject"]) {
assert.throws(RangeError, () => date.add(duration, { overflow }));
}
reportCompare(0, 0);

View File

@ -0,0 +1,31 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindate.prototype.add
description: temporalDurationLike object must contain at least one correctly spelled property
features: [Temporal]
---*/
const instance = new Temporal.PlainDate(2000, 5, 2);
assert.throws(
TypeError,
() => instance.add({}),
"Throws TypeError if no property is present"
);
assert.throws(
TypeError,
() => instance.add({ nonsense: true }),
"Throws TypeError if no recognized property is present"
);
assert.throws(
TypeError,
() => instance.add({ sign: 1 }),
"Sign property is not recognized"
);
reportCompare(0, 0);

View File

@ -1,21 +0,0 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindate.prototype.add
description: TypeError for missing properties
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const date = new Temporal.PlainDate(2000, 5, 2);
const options = {
get overflow() {
TemporalHelpers.assertUnreachable("should not get overflow");
}
};
assert.throws(TypeError, () => date.add({}, options), "empty object");
assert.throws(TypeError, () => date.add({ month: 12 }, options), "misspelled 'months'");
reportCompare(0, 0);

View File

@ -0,0 +1,21 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindate.prototype.add
description: Positive and negative values in the temporalDurationLike argument are not acceptable
features: [Temporal]
---*/
const instance = new Temporal.PlainDate(2000, 5, 2);
["constrain", "reject"].forEach((overflow) => {
assert.throws(
RangeError,
() => instance.add({ hours: 1, minutes: -30 }, { overflow }),
`mixed positive and negative values always throw (overflow = "${overflow}")`
);
});
reportCompare(0, 0);

View File

@ -8,7 +8,7 @@ description: Passing a primitive other than string to add() throws
features: [Symbol, Temporal]
---*/
const instance = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2 });
const instance = new Temporal.PlainDate(2000, 5, 2);
assert.throws(RangeError, () => instance.add(undefined), "undefined");
assert.throws(RangeError, () => instance.add(null), "null");
assert.throws(RangeError, () => instance.add(true), "boolean");
@ -16,5 +16,7 @@ assert.throws(RangeError, () => instance.add(""), "empty string");
assert.throws(TypeError, () => instance.add(Symbol()), "Symbol");
assert.throws(RangeError, () => instance.add(7), "number");
assert.throws(RangeError, () => instance.add(7n), "bigint");
assert.throws(TypeError, () => instance.add([]), "array");
assert.throws(TypeError, () => instance.add(() => {}), "function");
reportCompare(0, 0);

View File

@ -0,0 +1,30 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindate.prototype.add
description: Singular properties in the property bag are always ignored
features: [Temporal]
---*/
const instance = new Temporal.PlainDate(2000, 5, 2);
[
{ year: 1 },
{ month: 2 },
{ week: 3 },
{ day: 4 },
{ hour: 5 },
{ minute: 6 },
{ second: 7 },
{ millisecond: 8 },
{ microsecond: 9 },
{ nanosecond: 10 },
].forEach((badObject) => {
assert.throws(TypeError, () => instance.add(badObject),
"Throw TypeError if temporalDurationLike is not valid");
});
reportCompare(0, 0);

View File

@ -1,16 +0,0 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindate.prototype.add
description: Ignores singular properties
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const date = Temporal.PlainDate.from("2019-11-18");
TemporalHelpers.assertPlainDate(date.add({ month: 1, days: 1 }),
2019, 11, "M11", 19);
reportCompare(0, 0);

View File

@ -1,19 +0,0 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindate.prototype.subtract
description: RangeError thrown when signs don't match in the duration
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const calendar = TemporalHelpers.calendarThrowEverything();
const date = new Temporal.PlainDate(2000, 5, 2, calendar);
const duration = { months: 1, days: -30 };
for (const overflow of ["constrain", "reject"]) {
assert.throws(RangeError, () => date.subtract(duration, { overflow }));
}
reportCompare(0, 0);

View File

@ -0,0 +1,31 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindate.prototype.subtract
description: temporalDurationLike object must contain at least one correctly spelled property
features: [Temporal]
---*/
const instance = new Temporal.PlainDate(2000, 5, 2);
assert.throws(
TypeError,
() => instance.subtract({}),
"Throws TypeError if no property is present"
);
assert.throws(
TypeError,
() => instance.subtract({ nonsense: true }),
"Throws TypeError if no recognized property is present"
);
assert.throws(
TypeError,
() => instance.subtract({ sign: 1 }),
"Sign property is not recognized"
);
reportCompare(0, 0);

View File

@ -1,21 +0,0 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindate.prototype.subtract
description: TypeError for missing properties
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const date = new Temporal.PlainDate(2000, 5, 2);
const options = {
get overflow() {
TemporalHelpers.assertUnreachable("should not get overflow");
}
};
assert.throws(TypeError, () => date.subtract({}, options), "empty object");
assert.throws(TypeError, () => date.subtract({ month: 12 }, options), "misspelled 'months'");
reportCompare(0, 0);

View File

@ -0,0 +1,21 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindate.prototype.subtract
description: Positive and negative values in the temporalDurationLike argument are not acceptable
features: [Temporal]
---*/
const instance = new Temporal.PlainDate(2000, 5, 2);
["constrain", "reject"].forEach((overflow) => {
assert.throws(
RangeError,
() => instance.subtract({ hours: 1, minutes: -30 }, { overflow }),
`mixed positive and negative values always throw (overflow = "${overflow}")`
);
});
reportCompare(0, 0);

View File

@ -8,7 +8,7 @@ description: Passing a primitive other than string to subtract() throws
features: [Symbol, Temporal]
---*/
const instance = Temporal.PlainDate.from({ year: 2000, month: 5, day: 2 });
const instance = new Temporal.PlainDate(2000, 5, 2);
assert.throws(RangeError, () => instance.subtract(undefined), "undefined");
assert.throws(RangeError, () => instance.subtract(null), "null");
assert.throws(RangeError, () => instance.subtract(true), "boolean");
@ -16,5 +16,7 @@ assert.throws(RangeError, () => instance.subtract(""), "empty string");
assert.throws(TypeError, () => instance.subtract(Symbol()), "Symbol");
assert.throws(RangeError, () => instance.subtract(7), "number");
assert.throws(RangeError, () => instance.subtract(7n), "bigint");
assert.throws(TypeError, () => instance.subtract([]), "array");
assert.throws(TypeError, () => instance.subtract(() => {}), "function");
reportCompare(0, 0);

View File

@ -0,0 +1,30 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindate.prototype.subtract
description: Singular properties in the property bag are always ignored
features: [Temporal]
---*/
const instance = new Temporal.PlainDate(2000, 5, 2);
[
{ year: 1 },
{ month: 2 },
{ week: 3 },
{ day: 4 },
{ hour: 5 },
{ minute: 6 },
{ second: 7 },
{ millisecond: 8 },
{ microsecond: 9 },
{ nanosecond: 10 },
].forEach((badObject) => {
assert.throws(TypeError, () => instance.subtract(badObject),
"Throw TypeError if temporalDurationLike is not valid");
});
reportCompare(0, 0);

View File

@ -1,16 +0,0 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindate.prototype.subtract
description: Ignores singular properties
includes: [temporalHelpers.js]
features: [Temporal]
---*/
const date = Temporal.PlainDate.from("2019-11-18");
TemporalHelpers.assertPlainDate(date.subtract({ month: 1, days: 1 }),
2019, 11, "M11", 17);
reportCompare(0, 0);

View File

@ -0,0 +1,31 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindatetime.prototype.add
description: temporalDurationLike object must contain at least one correctly spelled property
features: [Temporal]
---*/
const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
assert.throws(
TypeError,
() => instance.add({}),
"Throws TypeError if no property is present"
);
assert.throws(
TypeError,
() => instance.add({ nonsense: true }),
"Throws TypeError if no recognized property is present"
);
assert.throws(
TypeError,
() => instance.add({ sign: 1 }),
"Sign property is not recognized"
);
reportCompare(0, 0);

View File

@ -8,12 +8,12 @@ description: Positive and negative values in the temporalDurationLike argument a
features: [Temporal]
---*/
const jan31 = new Temporal.PlainDateTime(2020, 1, 31, 15, 0);
const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
["constrain", "reject"].forEach((overflow) => {
assert.throws(
RangeError,
() => jan31.add({ hours: 1, minutes: -30 }, { overflow }),
() => instance.add({ hours: 1, minutes: -30 }, { overflow }),
`mixed positive and negative values always throw (overflow = "${overflow}")`
);
});

View File

@ -8,7 +8,7 @@ description: Passing a primitive other than string to add() throws
features: [Symbol, Temporal]
---*/
const instance = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
assert.throws(RangeError, () => instance.add(undefined), "undefined");
assert.throws(RangeError, () => instance.add(null), "null");
assert.throws(RangeError, () => instance.add(true), "boolean");
@ -16,5 +16,7 @@ assert.throws(RangeError, () => instance.add(""), "empty string");
assert.throws(TypeError, () => instance.add(Symbol()), "Symbol");
assert.throws(RangeError, () => instance.add(7), "number");
assert.throws(RangeError, () => instance.add(7n), "bigint");
assert.throws(TypeError, () => instance.add([]), "array");
assert.throws(TypeError, () => instance.add(() => {}), "function");
reportCompare(0, 0);

View File

@ -1,38 +0,0 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindatetime.prototype.add
description: At least one recognized property has to be present in argument
features: [Temporal]
includes: [temporalHelpers.js]
---*/
const jan31 = new Temporal.PlainDateTime(2020, 1, 31, 15, 0);
assert.throws(
TypeError,
() => jan31.add({}),
"empty object not acceptable"
);
assert.throws(
TypeError,
() => jan31.add({ month: 12 }), // should be "months"
"misspelled property in argument throws if no other properties are present"
);
assert.throws(
TypeError,
() => jan31.add({ nonsense: true }),
"unrecognized properties throw if no other recognized property is present"
);
TemporalHelpers.assertPlainDateTime(
jan31.add({ nonsense: 1, days: 1 }),
2020, 2, "M02", 1, 15, 0, 0, 0, 0, 0,
"unrecognized properties ignored provided at least one recognized property is present"
);
reportCompare(0, 0);

View File

@ -0,0 +1,30 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindatetime.prototype.add
description: Singular properties in the property bag are always ignored
features: [Temporal]
---*/
const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
[
{ year: 1 },
{ month: 2 },
{ week: 3 },
{ day: 4 },
{ hour: 5 },
{ minute: 6 },
{ second: 7 },
{ millisecond: 8 },
{ microsecond: 9 },
{ nanosecond: 10 },
].forEach((badObject) => {
assert.throws(TypeError, () => instance.add(badObject),
"Throw TypeError if temporalDurationLike is not valid");
});
reportCompare(0, 0);

View File

@ -0,0 +1,31 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindatetime.prototype.subtract
description: temporalDurationLike object must contain at least one correctly spelled property
features: [Temporal]
---*/
const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
assert.throws(
TypeError,
() => instance.subtract({}),
"Throws TypeError if no property is present"
);
assert.throws(
TypeError,
() => instance.subtract({ nonsense: true }),
"Throws TypeError if no recognized property is present"
);
assert.throws(
TypeError,
() => instance.subtract({ sign: 1 }),
"Sign property is not recognized"
);
reportCompare(0, 0);

View File

@ -8,12 +8,12 @@ description: Positive and negative values in the temporalDurationLike argument a
features: [Temporal]
---*/
const jan31 = new Temporal.PlainDateTime(2020, 1, 31, 15, 0);
const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
["constrain", "reject"].forEach((overflow) => {
assert.throws(
RangeError,
() => jan31.subtract({ hours: 1, minutes: -30 }, { overflow }),
() => instance.subtract({ hours: 1, minutes: -30 }, { overflow }),
`mixed positive and negative values always throw (overflow = "${overflow}")`
);
});

View File

@ -8,7 +8,7 @@ description: Passing a primitive other than string to subtract() throws
features: [Symbol, Temporal]
---*/
const instance = Temporal.PlainDateTime.from({ year: 2000, month: 5, day: 2, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
assert.throws(RangeError, () => instance.subtract(undefined), "undefined");
assert.throws(RangeError, () => instance.subtract(null), "null");
assert.throws(RangeError, () => instance.subtract(true), "boolean");
@ -16,5 +16,7 @@ assert.throws(RangeError, () => instance.subtract(""), "empty string");
assert.throws(TypeError, () => instance.subtract(Symbol()), "Symbol");
assert.throws(RangeError, () => instance.subtract(7), "number");
assert.throws(RangeError, () => instance.subtract(7n), "bigint");
assert.throws(TypeError, () => instance.subtract([]), "array");
assert.throws(TypeError, () => instance.subtract(() => {}), "function");
reportCompare(0, 0);

View File

@ -1,38 +0,0 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindatetime.prototype.subtract
description: At least one recognized property has to be present in argument
features: [Temporal]
includes: [temporalHelpers.js]
---*/
const jan31 = new Temporal.PlainDateTime(2020, 1, 31, 15, 0);
assert.throws(
TypeError,
() => jan31.subtract({}),
"empty object not acceptable"
);
assert.throws(
TypeError,
() => jan31.subtract({ month: 12 }), // should be "months"
"misspelled property in argument throws if no other properties are present"
);
assert.throws(
TypeError,
() => jan31.subtract({ nonsense: true }),
"unrecognized properties throw if no other recognized property is present"
);
TemporalHelpers.assertPlainDateTime(
jan31.subtract({ nonsense: 1, days: 1 }),
2020, 1, "M01", 30, 15, 0, 0, 0, 0, 0,
"unrecognized properties ignored provided at least one recognized property is present"
);
reportCompare(0, 0);

View File

@ -0,0 +1,30 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaindatetime.prototype.subtract
description: Singular properties in the property bag are always ignored
features: [Temporal]
---*/
const instance = new Temporal.PlainDateTime(2000, 5, 2, 15, 30, 45, 987, 654, 321);
[
{ year: 1 },
{ month: 2 },
{ week: 3 },
{ day: 4 },
{ hour: 5 },
{ minute: 6 },
{ second: 7 },
{ millisecond: 8 },
{ microsecond: 9 },
{ nanosecond: 10 },
].forEach((badObject) => {
assert.throws(TypeError, () => instance.subtract(badObject),
"Throw TypeError if temporalDurationLike is not valid");
});
reportCompare(0, 0);

View File

@ -0,0 +1,16 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plainmonthday
description: referenceISOYear argument, if given, can cause RangeError
features: [Temporal]
---*/
const calendar = new Temporal.Calendar("iso8601");
assert.throws(RangeError, () => new Temporal.PlainMonthDay(9, 14, calendar, 275760), "after the maximum ISO date");
assert.throws(RangeError, () => new Temporal.PlainMonthDay(4, 18, calendar, -271821), "before the minimum ISO date")
reportCompare(0, 0);

View File

@ -0,0 +1,31 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaintime.prototype.add
description: temporalDurationLike object must contain at least one correctly spelled property
features: [Temporal]
---*/
const instance = new Temporal.PlainTime(15, 30, 45, 987, 654, 321);
assert.throws(
TypeError,
() => instance.add({}),
"Throws TypeError if no property is present"
);
assert.throws(
TypeError,
() => instance.add({ nonsense: true }),
"Throws TypeError if no recognized property is present"
);
assert.throws(
TypeError,
() => instance.add({ sign: 1 }),
"Sign property is not recognized"
);
reportCompare(0, 0);

View File

@ -0,0 +1,19 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaintime.prototype.add
description: Positive and negative values in the temporalDurationLike argument are not acceptable
features: [Temporal]
---*/
const instance = new Temporal.PlainTime(15, 30, 45, 987, 654, 321);
assert.throws(
RangeError,
() => instance.add({ hours: 1, minutes: -30 }),
`mixed positive and negative values always throw`
);
reportCompare(0, 0);

View File

@ -8,7 +8,7 @@ description: Passing a primitive other than string to add() throws
features: [Symbol, Temporal]
---*/
const instance = Temporal.PlainTime.from({ hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
const instance = new Temporal.PlainTime(15, 30, 45, 987, 654, 321);
assert.throws(RangeError, () => instance.add(undefined), "undefined");
assert.throws(RangeError, () => instance.add(null), "null");
assert.throws(RangeError, () => instance.add(true), "boolean");
@ -16,5 +16,7 @@ assert.throws(RangeError, () => instance.add(""), "empty string");
assert.throws(TypeError, () => instance.add(Symbol()), "Symbol");
assert.throws(RangeError, () => instance.add(7), "number");
assert.throws(RangeError, () => instance.add(7n), "bigint");
assert.throws(TypeError, () => instance.add([]), "array");
assert.throws(TypeError, () => instance.add(() => {}), "function");
reportCompare(0, 0);

View File

@ -1,16 +0,0 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaintime.prototype.add
description: Mixed positive and negative values or missing properties always throw
features: [Temporal]
---*/
const plainTime = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
assert.throws(RangeError, () => plainTime.add({ hours: 1, minutes: -6 }), "mixed signs");
assert.throws(TypeError, () => plainTime.add({}), "no properties");
assert.throws(TypeError, () => plainTime.add({ hour: 12 }), "only singular 'hour' property");
reportCompare(0, 0);

View File

@ -0,0 +1,30 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaintime.prototype.add
description: Singular properties in the property bag are always ignored
features: [Temporal]
---*/
const instance = new Temporal.PlainTime(15, 30, 45, 987, 654, 321);
[
{ year: 1 },
{ month: 2 },
{ week: 3 },
{ day: 4 },
{ hour: 5 },
{ minute: 6 },
{ second: 7 },
{ millisecond: 8 },
{ microsecond: 9 },
{ nanosecond: 10 },
].forEach((badObject) => {
assert.throws(TypeError, () => instance.add(badObject),
"Throw TypeError if temporalDurationLike is not valid");
});
reportCompare(0, 0);

View File

@ -0,0 +1,31 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaintime.prototype.subtract
description: temporalDurationLike object must contain at least one correctly spelled property
features: [Temporal]
---*/
const instance = new Temporal.PlainTime(15, 30, 45, 987, 654, 321);
assert.throws(
TypeError,
() => instance.subtract({}),
"Throws TypeError if no property is present"
);
assert.throws(
TypeError,
() => instance.subtract({ nonsense: true }),
"Throws TypeError if no recognized property is present"
);
assert.throws(
TypeError,
() => instance.subtract({ sign: 1 }),
"Sign property is not recognized"
);
reportCompare(0, 0);

View File

@ -0,0 +1,19 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaintime.prototype.subtract
description: Positive and negative values in the temporalDurationLike argument are not acceptable
features: [Temporal]
---*/
const instance = new Temporal.PlainTime(15, 30, 45, 987, 654, 321);
assert.throws(
RangeError,
() => instance.subtract({ hours: 1, minutes: -30 }),
`mixed positive and negative values always throw`
);
reportCompare(0, 0);

View File

@ -8,7 +8,7 @@ description: Passing a primitive other than string to subtract() throws
features: [Symbol, Temporal]
---*/
const instance = Temporal.PlainTime.from({ hour: 12, minute: 34, second: 56, millisecond: 987, microsecond: 654, nanosecond: 321 });
const instance = new Temporal.PlainTime(15, 30, 45, 987, 654, 321);
assert.throws(RangeError, () => instance.subtract(undefined), "undefined");
assert.throws(RangeError, () => instance.subtract(null), "null");
assert.throws(RangeError, () => instance.subtract(true), "boolean");
@ -16,5 +16,7 @@ assert.throws(RangeError, () => instance.subtract(""), "empty string");
assert.throws(TypeError, () => instance.subtract(Symbol()), "Symbol");
assert.throws(RangeError, () => instance.subtract(7), "number");
assert.throws(RangeError, () => instance.subtract(7n), "bigint");
assert.throws(TypeError, () => instance.subtract([]), "array");
assert.throws(TypeError, () => instance.subtract(() => {}), "function");
reportCompare(0, 0);

View File

@ -1,16 +0,0 @@
// |reftest| skip -- Temporal is not supported
// Copyright (C) 2022 Igalia, S.L. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-temporal.plaintime.prototype.subtract
description: Mixed positive and negative values or missing properties always throw
features: [Temporal]
---*/
const plainTime = new Temporal.PlainTime(12, 34, 56, 123, 987, 500);
assert.throws(RangeError, () => plainTime.subtract({ hours: 1, minutes: -6 }), "mixed signs");
assert.throws(TypeError, () => plainTime.subtract({}), "no properties");
assert.throws(TypeError, () => plainTime.subtract({ hour: 12 }), "only singular 'hour' property");
reportCompare(0, 0);

Some files were not shown because too many files have changed in this diff Show More