tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 37099145c6fbabf605808a45ce1cec792d7b25c9
parent b2239a2304b0cb1ce21718cd19095b5a5971fa6f
Author: André Bargull <andre.bargull@gmail.com>
Date:   Fri, 12 Dec 2025 10:54:20 +0000

Bug 2005482: Constrain day before calling CreateDateFromCodes with overflow=reject for Hebrew calendar. r=spidermonkey-reviewers,dminor

Constrain the input `day` before calling `CreateDateFromCodes` when
`overflow=reject` to avoid incorrectly rejecting dates. When the day was
constrained, handle `overflow=reject` through an explicit check using
`CalendarDaysInMonth`.

This case is specific to the Hebrew calendar and can't happen for Chinese/Dangi
calendars, because all Chinese/Dangi months have the same number of minimum resp.
maximum days per month.

Differential Revision: https://phabricator.services.mozilla.com/D275957

Diffstat:
Mjs/src/builtin/temporal/Calendar.cpp | 29++++++++++++++++++++++++++++-
Ajs/src/tests/non262/Temporal/PlainDate/hebrew-from-ordinal-month.js | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 97 insertions(+), 1 deletion(-)

diff --git a/js/src/builtin/temporal/Calendar.cpp b/js/src/builtin/temporal/Calendar.cpp @@ -1338,10 +1338,25 @@ static UniqueICU4XDate CreateDateFrom(JSContext* cx, CalendarId calendarId, MOZ_ASSERT(1 <= month && month <= 13); + // Constrain |day| when overflow is "reject" to avoid rejecting too large + // day values in CreateDateFromCodes. + // + // For example when month = 10 and day = 30 and the input year is a leap + // year. We first try month code "M10", but since "M10" can have at most + // 29 days, we need to constrain the days value before calling + // CreateDateFromCodes. + int32_t constrainedDay = day; + if (overflow == TemporalOverflow::Reject) { + constexpr auto daysInMonth = CalendarDaysInMonth(CalendarId::Hebrew); + if (day > daysInMonth.first && day <= daysInMonth.second) { + constrainedDay = daysInMonth.first; + } + } + // Create date with month number replaced by month-code. auto monthCode = MonthCode{std::min(month, 12)}; auto date = CreateDateFromCodes(cx, calendarId, calendar, eraYear, - monthCode, day, overflow); + monthCode, constrainedDay, overflow); if (!date) { return nullptr; } @@ -1350,6 +1365,18 @@ static UniqueICU4XDate CreateDateFrom(JSContext* cx, CalendarId calendarId, // changes are necessary and we can directly return |date|. int32_t ordinal = OrdinalMonth(date.get()); if (ordinal == month) { + // If |day| was constrained, check if the actual input days value + // exceeds the number of days in the resolved month. + if (constrainedDay < day) { + MOZ_ASSERT(overflow == TemporalOverflow::Reject); + + if (day > CalendarDaysInMonth(calendarId, monthCode).second) { + ReportCalendarFieldOverflow(cx, "day", day); + return nullptr; + } + return CreateDateFromCodes(cx, calendarId, calendar, eraYear, + monthCode, day, overflow); + } return date; } diff --git a/js/src/tests/non262/Temporal/PlainDate/hebrew-from-ordinal-month.js b/js/src/tests/non262/Temporal/PlainDate/hebrew-from-ordinal-month.js @@ -0,0 +1,69 @@ +// |reftest| skip-if(!this.hasOwnProperty("Temporal")) + +const calendar = "hebrew"; + +function assertDate(actual, {year, month, monthCode, day}) { + assertEq(actual.year, year); + assertEq(actual.month, month); + assertEq(actual.monthCode, monthCode); + assertEq(actual.day, day); +} + +// 5783 is a common year. +const commonYear = 5783; + +// 5784 is a leap year. +const leapYear = 5784; + +// Test common and leap years. +for (let year of [commonYear, leapYear]) { + let firstDayOfYear = Temporal.PlainDate.from({calendar, year, monthCode: "M01", day: 1}); + assertEq(firstDayOfYear.inLeapYear, (year === leapYear)); + + let monthsInYear = firstDayOfYear.monthsInYear; + assertEq(monthsInYear, 12 + firstDayOfYear.inLeapYear); + + // Test for each month in the year. + for (let month = 1; month <= monthsInYear; ++month) { + let firstDayOfMonthFromOrdinalMonth = Temporal.PlainDate.from({calendar, year, month, day: 1}); + let monthCode = firstDayOfMonthFromOrdinalMonth.monthCode; + assertDate(firstDayOfMonthFromOrdinalMonth, {year, month, monthCode, day: 1}); + + let firstDayOfMonthFromMonthCode = Temporal.PlainDate.from({calendar, year, monthCode, day: 1}); + assertDate(firstDayOfMonthFromMonthCode, {year, month, monthCode, day: 1}); + + // 29-30 days for each month. + let daysInMonth = firstDayOfMonthFromOrdinalMonth.daysInMonth; + assertEq(29 <= daysInMonth && daysInMonth <= 30 , true); + + // Test for each day of the month. + for (let day = 2; day <= daysInMonth; ++day) { + for (let overflow of ["constrain", "reject"]) { + let fromOrdinalMonth = Temporal.PlainDate.from({calendar, year, month, day}, {overflow}); + assertDate(fromOrdinalMonth, {year, month, monthCode, day}); + + let fromMonthCode = Temporal.PlainDate.from({calendar, year, monthCode, day}, {overflow}); + assertDate(fromMonthCode, {year, month, monthCode, day}); + } + } + + // Test too large day values. + for (let day = daysInMonth + 1; day <= daysInMonth + 4; ++day) { + let fromOrdinalMonth = Temporal.PlainDate.from({calendar, year, month, day}, {overflow: "constrain"}); + assertDate(fromOrdinalMonth, {year, month, monthCode, day: daysInMonth}); + + let fromMonthCode = Temporal.PlainDate.from({calendar, year, monthCode, day}, {overflow: "constrain"}); + assertDate(fromMonthCode, {year, month, monthCode, day: daysInMonth}); + + assertThrowsInstanceOf(() => { + Temporal.PlainDate.from({calendar, year, month, day}, {overflow: "reject"}); + }, RangeError); + assertThrowsInstanceOf(() => { + Temporal.PlainDate.from({calendar, year, monthCode, day}, {overflow: "reject"}); + }, RangeError); + } + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true);