commit bfe0ba28059f68ea14978aec8444ca51d26d6843
parent 86a9e3ee8607b9435a0924fa72e4c030648366bf
Author: André Bargull <andre.bargull@gmail.com>
Date: Thu, 27 Nov 2025 10:03:36 +0000
Bug 1955545 - Part 3: Remove support for "islamic" and "islamic-rgsa" calendars in Intl. r=spidermonkey-reviewers,dminor
Implements `Intl` related changes from:
<https://github.com/tc39/proposal-intl-era-monthcode/pull/46>
Differential Revision: https://phabricator.services.mozilla.com/D272029
Diffstat:
12 files changed, 147 insertions(+), 39 deletions(-)
diff --git a/js/public/friend/ErrorNumbers.msg b/js/public/friend/ErrorNumbers.msg
@@ -652,6 +652,7 @@ MSG_DEF(JSMSG_INTL_DURATION_INVALID_DISPLAY_OPTION, 1, JSEXN_RANGEERR, "{0} can'
MSG_DEF(JSMSG_INTL_DURATION_INVALID_DISPLAY_OPTION_DEFAULT_STYLE, 1, JSEXN_RANGEERR, "{0} can't use \"always\" display when style defaults to \"numeric\"")
MSG_DEF(JSMSG_INTL_DURATION_INVALID_DISPLAY_OPTION_DEFAULT_DISPLAY, 1, JSEXN_RANGEERR, "{0} can't use \"numeric\" style when display defaults to \"always\"")
MSG_DEF(JSMSG_INVALID_FORMAT_OPTIONS, 1, JSEXN_TYPEERR, "can't format {0} using the requested options")
+MSG_DEF(JSMSG_DEPRECATED_CALENDAR, 1, JSEXN_WARN, "\"{0}\" is deprecated, use \"islamic-tbla\" instead")
// RegExp
MSG_DEF(JSMSG_BAD_CLASS_RANGE, 0, JSEXN_SYNTAXERR, "invalid range in character class")
diff --git a/js/src/builtin/intl/DateTimeFormat.js b/js/src/builtin/intl/DateTimeFormat.js
@@ -70,6 +70,16 @@ function resolveDateTimeFormatInternals(lazyDateTimeFormatData) {
localeData
);
+ // Changes from "Intl era and monthCode" proposal.
+ //
+ // https://tc39.es/proposal-intl-era-monthcode/#sec-createdatetimeformat
+ if (r.ca === "islamic" || r.ca === "islamic-rgsa") {
+ ReportWarning(JSMSG_DEPRECATED_CALENDAR, r.ca);
+
+ // Fallback to "islamic-tbla" calendar.
+ r.ca = "islamic-tbla";
+ }
+
// Steps 19-22.
internalProps.locale = r.locale;
internalProps.calendar = r.ca;
diff --git a/js/src/builtin/intl/DisplayNames.js b/js/src/builtin/intl/DisplayNames.js
@@ -76,6 +76,16 @@ function resolveDisplayNamesInternals(lazyDisplayNamesData) {
}
if (mozExtensions) {
+ // Changes from "Intl era and monthCode" proposal.
+ //
+ // https://tc39.es/proposal-intl-era-monthcode/#sec-createdatetimeformat
+ if (r.ca === "islamic" || r.ca === "islamic-rgsa") {
+ ReportWarning(JSMSG_DEPRECATED_CALENDAR, r.ca);
+
+ // Fallback to "islamic-tbla" calendar.
+ r.ca = "islamic-tbla";
+ }
+
internalProps.calendar = r.ca;
}
diff --git a/js/src/builtin/intl/IntlObject.cpp b/js/src/builtin/intl/IntlObject.cpp
@@ -556,8 +556,10 @@ static bool EnumerationIntoList(JSContext* cx, Enumeration values,
* |Intl.supportedValuesOf()|.
*/
static constexpr auto UnsupportedCalendars() {
- // No calendar values are currently unsupported.
- return std::array<const char*, 0>{};
+ return std::array{
+ "islamic",
+ "islamic-rgsa",
+ };
}
// Defined outside of the function to workaround bugs in GCC<9.
diff --git a/js/src/tests/jstests.list b/js/src/tests/jstests.list
@@ -1062,9 +1062,6 @@ skip-if(release_or_beta) script test262/staging/Intl402/Temporal/old/non-iso-cal
skip-if(release_or_beta) script test262/staging/Intl402/Temporal/old/islamic-calendars-islamic-umalqura.js
skip-if(release_or_beta) script test262/intl402/Temporal/PlainMonthDay/from/constrain-to-leap-day.js
-# Try re-enabling when Bug 1954138 or Bug 1955545 are fixed.
-skip script test262/intl402/DateTimeFormat/constructor-options-calendar-islamic-fallback.js
-
# https://github.com/tc39/proposal-intl-era-monthcode/issues/23. ICU4X 2.0 doesn't have inverse type
skip script test262/staging/Intl402/Temporal/old/non-iso-calendars-coptic.js
diff --git a/js/src/tests/non262/Intl/DateTimeFormat/calendar-option.js b/js/src/tests/non262/Intl/DateTimeFormat/calendar-option.js
@@ -53,6 +53,8 @@ const calendars = [
const canonical = {
"islamicc": "islamic-civil",
"ethiopic-amete-alem": "ethioaa",
+ "islamic": "islamic-tbla",
+ "islamic-rgsa": "islamic-tbla",
};
for (let calendar of calendars) {
diff --git a/js/src/tests/non262/Intl/DateTimeFormat/islamic.js b/js/src/tests/non262/Intl/DateTimeFormat/islamic.js
@@ -10,11 +10,6 @@ function tabularDate(options, date) {
return new Intl.DateTimeFormat("ar-SA-u-ca-islamic-tbla-nu-latn", opts).format(date);
}
-function sightingDate(options, date) {
- var opts = Object.assign({timeZone: "Asia/Riyadh"}, options);
- return new Intl.DateTimeFormat("ar-SA-u-ca-islamic-rgsa-nu-latn", opts).format(date);
-}
-
function ummAlQuraDate(options, date) {
var opts = Object.assign({timeZone: "Asia/Riyadh"}, options);
return new Intl.DateTimeFormat("ar-SA-u-ca-islamic-umalqura-nu-latn", opts).format(date);
@@ -34,17 +29,6 @@ function testIslamicTbla() {
assertEq(Number(civilDate(day, date)) - Number(tabularDate(day, date)), -1);
}
-// Test islamic-rgsa (Saudi Arabia sighting).
-// Sighting of the hilal (crescent moon) in Saudi Arabia.
-function testIslamicRgsa() {
- var date1 = new Date(Date.UTC(1975, 5 - 1, 6));
- var date2 = new Date(Date.UTC(2015, 1 - 1, 1));
- var dayMonthYear = {year: "numeric", month: "numeric", day: "numeric"};
-
- assertEq(sightingDate(dayMonthYear, date1), tabularDate(dayMonthYear, date1));
- assertEq(sightingDate(dayMonthYear, date2), civilDate(dayMonthYear, date2));
-}
-
// Test islamic-umalqura (Umm al-Qura).
function testIslamicUmalqura() {
var year = {year: "numeric"};
@@ -82,7 +66,6 @@ function testIslamicUmalqura() {
}
testIslamicTbla();
-testIslamicRgsa();
testIslamicUmalqura();
if (typeof reportCompare === "function")
diff --git a/js/src/tests/non262/Intl/DateTimeFormat/related-year.js b/js/src/tests/non262/Intl/DateTimeFormat/related-year.js
@@ -139,7 +139,7 @@ const tests = [
{
date: new Date("2019-01-01T00:00:00Z"),
options: {},
- calendar: "islamic",
+ calendar: "islamic-umalqura",
locales: {
"en": [Month("4"), Literal("/"), Day("25"), Literal("/"), Year("1440"), Literal(" "), Era("AH")],
"ar-EG": [Day("٢٥"), Literal("\u200F/"), Month("٤"), Literal("\u200F/"), Year("١٤٤٠"), Literal(" "), Era("هـ")],
diff --git a/js/src/tests/non262/Intl/DateTimeFormat/report-warning-for-deprecated-islamic.js b/js/src/tests/non262/Intl/DateTimeFormat/report-warning-for-deprecated-islamic.js
@@ -0,0 +1,77 @@
+// |reftest| skip-if(!this.hasOwnProperty("Intl")||!xulRuntime.shell)
+
+const locales = [
+ "en", "ar", "ar-SA", "ar-EG",
+];
+
+const deprecated = [
+ "islamic",
+ "islamic-rgsa"
+];
+
+const supportedCalendars = Intl.supportedValuesOf("calendar");
+
+function test(calendar, locale, options = undefined) {
+ // Enable warning reporter.
+ enableLastWarning();
+
+ // Create new DateTimeFormat.
+ let dtf = new Intl.DateTimeFormat(locale, options);
+
+ // Options are lazily resolved...
+ let resolved = dtf.resolvedOptions();
+
+ // Inspect last warning.
+ if (deprecated.includes(calendar)) {
+ let warning = getLastWarning();
+ assertEq(warning !== null, true, `missing warning for ${locale}`);
+ assertEq(
+ warning.message.includes(calendar),
+ true,
+ `warning "${warning}" doesn't include calendar "${calendar}"`
+ );
+
+ assertEq(
+ resolved.calendar,
+ "islamic-tbla",
+ `bad resolved fallback calendar for ${locale}`
+ );
+ } else {
+ let warning = getLastWarning();
+ assertEq(warning === null, true, `unexpected warning for ${locale}`);
+
+ assertEq(
+ resolved.calendar,
+ calendar,
+ `bad resolved calendar for ${locale}`
+ );
+ }
+
+ // Disable warning reporter.
+ disableLastWarning();
+}
+
+for (let calendar of [...deprecated, ...supportedCalendars]) {
+ if (deprecated.includes(calendar)) {
+ assertEq(
+ supportedCalendars.includes(calendar),
+ false,
+ `${calendar} is deprecated`
+ );
+ }
+
+ for (let locale of locales) {
+ // Test as option.
+ test(calendar, locale, {calendar});
+
+ // Test as Unicode local extension.
+ test(calendar, locale + "-u-ca-" + calendar);
+
+ // Also test with non-canonical case.
+ test(calendar, locale, {calendar: calendar.toUpperCase()});
+ test(calendar, locale + "-u-ca-" + calendar.toUpperCase());
+ }
+}
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);
diff --git a/js/src/tests/non262/Temporal/Intl/defaults.js b/js/src/tests/non262/Temporal/Intl/defaults.js
@@ -25,11 +25,6 @@ let plainTime = zonedDateTime.toPlainTime();
for (let locale of locales) {
for (let calendar of Intl.supportedValuesOf("calendar")) {
- // Invalid calendar identifiers for Temporal.
- if (calendar === "islamic" || calendar === "islamic-rgsa") {
- continue;
- }
-
// Calendar must match for YearMonth and MonthDay.
//
// https://github.com/js-temporal/proposal-temporal-v2/issues/29
diff --git a/js/src/tests/non262/Temporal/Intl/start-of-primary-calendar-era.js b/js/src/tests/non262/Temporal/Intl/start-of-primary-calendar-era.js
@@ -11,9 +11,7 @@ const calendars = {
gregory: "0001-01-01",
hebrew: "-003760-09-07",
indian: "0079-03-22",
- islamic: "0622-07-19",
"islamic-civil": "0622-07-19",
- "islamic-rgsa": "0622-07-19",
"islamic-tbla": "0622-07-18",
"islamic-umalqura": "0622-07-19",
japanese: "0001-01-01",
@@ -28,8 +26,6 @@ assertEqArray(
// See bug 1950425.
const calendarsNotEnabledInRelease = [
- "islamic",
- "islamic-rgsa",
"islamic-umalqura",
];
assertEq(calendarsNotEnabledInRelease.every(c => c in calendars), true);
diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp
@@ -97,6 +97,7 @@
#include "vm/ToSource.h" // js::ValueToSource
#include "vm/TypedArrayObject.h"
#include "vm/Uint8Clamped.h"
+#include "vm/Warnings.h"
#include "vm/WrapperObject.h"
#include "gc/WeakMap-inl.h"
@@ -310,25 +311,25 @@ static bool intrinsic_SubstringKernel(JSContext* cx, unsigned argc, Value* vp) {
return true;
}
-static void ThrowErrorWithType(JSContext* cx, JSExnType type,
- const CallArgs& args) {
- MOZ_RELEASE_ASSERT(args[0].isInt32());
+static bool PrepareErrorArguments(JSContext* cx, JSExnType type,
+ const CallArgs& args,
+ UniqueChars (&errorArgs)[3]) {
+#ifdef DEBUG
+ MOZ_ASSERT(args[0].isInt32());
uint32_t errorNumber = args[0].toInt32();
-#ifdef DEBUG
const JSErrorFormatString* efs = GetErrorMessage(nullptr, errorNumber);
MOZ_ASSERT(efs->argCount == args.length() - 1);
MOZ_ASSERT(efs->exnType == type,
"error-throwing intrinsic and error number are inconsistent");
#endif
- UniqueChars errorArgs[3];
for (unsigned i = 1; i < 4 && i < args.length(); i++) {
HandleValue val = args[i];
if (val.isInt32() || val.isString()) {
JSString* str = ToString<CanGC>(cx, val);
if (!str) {
- return;
+ return false;
}
errorArgs[i - 1] = QuoteString(cx, str);
} else {
@@ -336,9 +337,21 @@ static void ThrowErrorWithType(JSContext* cx, JSExnType type,
DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, val, nullptr);
}
if (!errorArgs[i - 1]) {
- return;
+ return false;
}
}
+ return true;
+}
+
+static void ThrowErrorWithType(JSContext* cx, JSExnType type,
+ const CallArgs& args) {
+ MOZ_RELEASE_ASSERT(args[0].isInt32());
+ uint32_t errorNumber = args[0].toInt32();
+
+ UniqueChars errorArgs[3];
+ if (!PrepareErrorArguments(cx, type, args, errorArgs)) {
+ return;
+ }
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber,
errorArgs[0].get(), errorArgs[1].get(),
@@ -397,6 +410,27 @@ static bool intrinsic_CreateSuppressedError(JSContext* cx, unsigned argc,
}
#endif
+static bool intrinsic_ReportWarning(JSContext* cx, unsigned argc, Value* vp) {
+ CallArgs args = CallArgsFromVp(argc, vp);
+ MOZ_ASSERT(args.length() >= 1);
+
+ MOZ_RELEASE_ASSERT(args[0].isInt32());
+ uint32_t errorNumber = args[0].toInt32();
+
+ UniqueChars errorArgs[3];
+ if (!PrepareErrorArguments(cx, JSEXN_WARN, args, errorArgs)) {
+ return false;
+ }
+
+ if (!WarnNumberUTF8(cx, errorNumber, errorArgs[0].get(), errorArgs[1].get(),
+ errorArgs[2].get())) {
+ return false;
+ }
+
+ args.rval().setUndefined();
+ return true;
+}
+
/**
* Handles an assertion failure in self-hosted code just like an assertion
* failure in C++ code. Information about the failure can be provided in
@@ -1785,6 +1819,7 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_INLINABLE_FN("RegExpSearcher", RegExpSearcher, 3, 0, RegExpSearcher),
JS_INLINABLE_FN("RegExpSearcherLastLimit", RegExpSearcherLastLimit, 0, 0,
RegExpSearcherLastLimit),
+ JS_FN("ReportWarning", intrinsic_ReportWarning, 4, 0),
JS_INLINABLE_FN("SameValue", js::obj_is, 2, 0, ObjectIs),
JS_FN("SetCopy", SetObject::copy, 1, 0),
JS_FN("StringReplaceAllString", intrinsic_StringReplaceAllString, 3, 0),