tor-browser

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

commit 94e008929aede14ae1b4c6dadc2f6da4a0152701
parent 0624561f4306c2efb34f28da4cd41473b7c78459
Author: André Bargull <andre.bargull@gmail.com>
Date:   Thu, 27 Nov 2025 10:03:33 +0000

Bug 1998672 - Part 2: Validate unit-valued options after all options are read. r=spidermonkey-reviewers,dminor

Implements the changes from:
- https://github.com/tc39/proposal-temporal/pull/3130
- https://github.com/tc39/proposal-temporal/pull/3135

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

Diffstat:
Mjs/src/builtin/temporal/Duration.cpp | 223++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mjs/src/builtin/temporal/Instant.cpp | 69++++++++++++++++++++++++++++++++++++++++++++-------------------------
Mjs/src/builtin/temporal/PlainDateTime.cpp | 50++++++++++++++++++++++++++++++++------------------
Mjs/src/builtin/temporal/PlainTime.cpp | 51++++++++++++++++++++++++++++++++++-----------------
Mjs/src/builtin/temporal/Temporal.cpp | 137+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Mjs/src/builtin/temporal/Temporal.h | 17+++++++++--------
Mjs/src/builtin/temporal/ZonedDateTime.cpp | 91++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Mjs/src/tests/jstests.list | 12------------
8 files changed, 370 insertions(+), 280 deletions(-)

diff --git a/js/src/builtin/temporal/Duration.cpp b/js/src/builtin/temporal/Duration.cpp @@ -3414,12 +3414,12 @@ static bool Duration_subtract(JSContext* cx, unsigned argc, Value* vp) { static bool Duration_round(JSContext* cx, const CallArgs& args) { auto duration = ToDuration(&args.thisv().toObject().as<DurationObject>()); - // Step 17. (Reordered) + // Step 18. (Reordered) auto existingLargestUnit = DefaultTemporalLargestUnit(duration); - // Steps 3-25. - auto smallestUnit = TemporalUnit::Auto; - TemporalUnit largestUnit; + // Steps 3-26. + auto smallestUnit = TemporalUnit::Unset; + auto largestUnit = TemporalUnit::Unset; auto roundingMode = TemporalRoundingMode::HalfExpand; auto roundingIncrement = Increment{1}; Rooted<PlainDate> plainRelativeTo(cx); @@ -3432,26 +3432,31 @@ static bool Duration_round(JSContext* cx, const CallArgs& args) { // Step 15. Rooted<JSString*> paramString(cx, args[0].toString()); if (!GetTemporalUnitValuedOption( - cx, paramString, TemporalUnitKey::SmallestUnit, - TemporalUnitGroup::DateTime, &smallestUnit)) { + cx, paramString, TemporalUnitKey::SmallestUnit, &smallestUnit)) { return false; } - // Step 16. (Not applicable) + // Step 16. + if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit, + smallestUnit, TemporalUnitGroup::DateTime)) { + return false; + } - // Step 17. (Moved above) + // Step 17. (Not applicable) - // Step 18. + // Step 18. (Moved above) + + // Step 19. auto defaultLargestUnit = std::min(existingLargestUnit, smallestUnit); - // Step 19. (Not applicable) + // Step 20. (Not applicable) - // Step 19.a. (Not applicable) + // Step 20.a. (Not applicable) - // Step 19.b. + // Step 20.b. largestUnit = defaultLargestUnit; - // Steps 20-25. (Not applicable) + // Steps 21-26. (Not applicable) } else { // Steps 3 and 5. Rooted<JSObject*> options( @@ -3467,29 +3472,11 @@ static bool Duration_round(JSContext* cx, const CallArgs& args) { bool largestUnitPresent = true; // Steps 8-9. - // - // Inlined GetTemporalUnitValuedOption and GetOption so we can more easily - // detect an absent "largestUnit" value. - Rooted<Value> largestUnitValue(cx); - if (!GetProperty(cx, options, options, cx->names().largestUnit, - &largestUnitValue)) { + if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::LargestUnit, + &largestUnit)) { return false; } - if (!largestUnitValue.isUndefined()) { - Rooted<JSString*> largestUnitStr(cx, JS::ToString(cx, largestUnitValue)); - if (!largestUnitStr) { - return false; - } - - largestUnit = TemporalUnit::Auto; - if (!GetTemporalUnitValuedOption( - cx, largestUnitStr, TemporalUnitKey::LargestUnit, - TemporalUnitGroup::DateTime, &largestUnit)) { - return false; - } - } - // Steps 10-12. if (!GetTemporalRelativeToOption(cx, options, &plainRelativeTo, &zonedRelativeTo)) { @@ -3509,64 +3496,69 @@ static bool Duration_round(JSContext* cx, const CallArgs& args) { // Step 15. if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit, - TemporalUnitGroup::DateTime, &smallestUnit)) { return false; } // Step 16. - if (smallestUnit == TemporalUnit::Auto) { - // Step 16.a. + if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit, + smallestUnit, TemporalUnitGroup::DateTime)) { + return false; + } + + // Step 17. + if (smallestUnit == TemporalUnit::Unset) { + // Step 17.a. smallestUnitPresent = false; - // Step 16.b. + // Step 17.b. smallestUnit = TemporalUnit::Nanosecond; } - // Step 17. (Moved above) + // Step 18. (Moved above) - // Step 18. + // Step 19. auto defaultLargestUnit = std::min(existingLargestUnit, smallestUnit); - // Steps 19-20. - if (largestUnitValue.isUndefined()) { - // Step 19.a. + // Steps 20-21. + if (largestUnit == TemporalUnit::Unset) { + // Step 20.a. largestUnitPresent = false; - // Step 19.b. + // Step 20.b. largestUnit = defaultLargestUnit; } else if (largestUnit == TemporalUnit::Auto) { - // Step 20.a + // Step 21.a largestUnit = defaultLargestUnit; } - // Step 21. + // Step 22. if (!smallestUnitPresent && !largestUnitPresent) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TEMPORAL_DURATION_MISSING_UNIT_SPECIFIER); return false; } - // Step 22. + // Step 23. if (largestUnit > smallestUnit) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TEMPORAL_INVALID_UNIT_RANGE); return false; } - // Steps 23-24. + // Steps 24-25. if (smallestUnit > TemporalUnit::Day) { - // Step 23. + // Step 24. auto maximum = MaximumTemporalDurationRoundingIncrement(smallestUnit); - // Step 24. + // Step 25. if (!ValidateTemporalRoundingIncrement(cx, roundingIncrement, maximum, false)) { return false; } } - // Step 25. + // Step 26. if (roundingIncrement > Increment{1} && largestUnit != smallestUnit && smallestUnit <= TemporalUnit::Day) { Int32ToCStringBuf cbuf; @@ -3580,21 +3572,21 @@ static bool Duration_round(JSContext* cx, const CallArgs& args) { } } - // Step 26. + // Step 27. if (zonedRelativeTo) { - // Step 26.a. + // Step 27.a. auto internalDuration = ToInternalDurationRecord(duration); - // Steps 26.b-d. (Not applicable in our implementation.) + // Steps 27.b-d. (Not applicable in our implementation.) - // Step 26.e. + // Step 27.e. EpochNanoseconds targetEpochNs; if (!AddZonedDateTime(cx, zonedRelativeTo, internalDuration, &targetEpochNs)) { return false; } - // Step 26.f. + // Step 27.f. if (!DifferenceZonedDateTimeWithRounding(cx, zonedRelativeTo, targetEpochNs, { smallestUnit, @@ -3606,10 +3598,10 @@ static bool Duration_round(JSContext* cx, const CallArgs& args) { return false; } - // Step 26.g. + // Step 27.g. largestUnit = std::max(largestUnit, TemporalUnit::Hour); - // Step 26.h + // Step 27.h Duration result; if (!TemporalDurationFromInternal(cx, internalDuration, largestUnit, &result)) { @@ -3625,18 +3617,18 @@ static bool Duration_round(JSContext* cx, const CallArgs& args) { return true; } - // Step 27. + // Step 28. if (plainRelativeTo) { - // Step 27.a. + // Step 28.a. auto internalDuration = ToInternalDurationRecordWith24HourDays(duration); - // Step 27.b. + // Step 28.b. auto targetTime = AddTime(Time{}, internalDuration.time); - // Step 27.c. + // Step 28.c. auto calendar = plainRelativeTo.calendar(); - // Step 27.d. + // Step 28.d. auto dateDuration = DateDuration{ internalDuration.date.years, internalDuration.date.months, @@ -3645,17 +3637,17 @@ static bool Duration_round(JSContext* cx, const CallArgs& args) { }; MOZ_ASSERT(IsValidDuration(dateDuration)); - // 27.e. + // Step 28.e. ISODate targetDate; if (!CalendarDateAdd(cx, calendar, plainRelativeTo, dateDuration, TemporalOverflow::Constrain, &targetDate)) { return false; } - // Step 27.f. + // Step 28.f. auto isoDateTime = ISODateTime{plainRelativeTo, {}}; - // Step 27.g. + // Step 28.g. auto targetDateTime = ISODateTime{targetDate, targetTime.time}; // DifferencePlainDateTimeWithRounding, step 2. @@ -3666,7 +3658,7 @@ static bool Duration_round(JSContext* cx, const CallArgs& args) { return false; } - // Step 27.h. + // Step 28.h. if (!DifferencePlainDateTimeWithRounding(cx, isoDateTime, targetDateTime, calendar, { @@ -3679,7 +3671,7 @@ static bool Duration_round(JSContext* cx, const CallArgs& args) { return false; } - // Step 27.i + // Step 28.i Duration result; if (!TemporalDurationFromInternal(cx, internalDuration, largestUnit, &result)) { @@ -3695,7 +3687,7 @@ static bool Duration_round(JSContext* cx, const CallArgs& args) { return true; } - // Step 28. + // Step 29. if (existingLargestUnit < TemporalUnit::Day || largestUnit < TemporalUnit::Day) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, @@ -3704,16 +3696,16 @@ static bool Duration_round(JSContext* cx, const CallArgs& args) { return false; } - // Step 29. + // Step 30. MOZ_ASSERT(smallestUnit >= TemporalUnit::Day); - // Step 30. + // Step 31. auto internalDuration = ToInternalDurationRecordWith24HourDays(duration); MOZ_ASSERT(internalDuration.date == DateDuration{}); - // Steps 31-32. + // Steps 32-33. if (smallestUnit == TemporalUnit::Day) { - // Steps 31.a-b. + // Steps 32.a-b. constexpr auto nsPerDay = ToNanoseconds(TemporalUnit::Day); auto rounded = RoundNumberToIncrement(internalDuration.time.toNanoseconds(), nsPerDay, @@ -3722,7 +3714,7 @@ static bool Duration_round(JSContext* cx, const CallArgs& args) { "rounded days fits in int64"); auto days = static_cast<int64_t>(rounded); - // Step 31.c. (Inlined CreateDateDurationRecord) + // Step 32.c. (Inlined CreateDateDurationRecord) if (std::abs(days) > TimeDuration::max().toDays()) { JS_ReportErrorNumberASCII( cx, GetErrorMessage, nullptr, @@ -3732,21 +3724,21 @@ static bool Duration_round(JSContext* cx, const CallArgs& args) { auto dateDuration = DateDuration{0, 0, 0, days}; MOZ_ASSERT(IsValidDuration(dateDuration)); - // Step 31.d. + // Step 32.d. internalDuration = {dateDuration, {}}; } else { - // Step 32.a. + // Step 33.a. TimeDuration timeDuration; if (!RoundTimeDuration(cx, internalDuration.time, roundingIncrement, smallestUnit, roundingMode, &timeDuration)) { return false; } - // Step 32.b. + // Step 33.b. internalDuration = {{}, timeDuration}; } - // Step 32. + // Step 34. Duration result; if (!TemporalDurationFromInternal(cx, internalDuration, largestUnit, &result)) { @@ -3781,7 +3773,7 @@ static bool Duration_total(JSContext* cx, const CallArgs& args) { // Steps 3-10. Rooted<PlainDate> plainRelativeTo(cx); Rooted<ZonedDateTime> zonedRelativeTo(cx); - auto unit = TemporalUnit::Auto; + auto unit = TemporalUnit::Unset; if (args.get(0).isString()) { // Step 4. (Not applicable in our implementation.) @@ -3791,7 +3783,7 @@ static bool Duration_total(JSContext* cx, const CallArgs& args) { // Step 10. Rooted<JSString*> paramString(cx, args[0].toString()); if (!GetTemporalUnitValuedOption(cx, paramString, TemporalUnitKey::Unit, - TemporalUnitGroup::DateTime, &unit)) { + &unit)) { return false; } } else { @@ -3811,48 +3803,54 @@ static bool Duration_total(JSContext* cx, const CallArgs& args) { // Step 10. if (!GetTemporalUnitValuedOption(cx, totalOf, TemporalUnitKey::Unit, - TemporalUnitGroup::DateTime, &unit)) { + &unit)) { return false; } - if (unit == TemporalUnit::Auto) { + if (unit == TemporalUnit::Unset) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TEMPORAL_MISSING_OPTION, "unit"); return false; } } - // Steps 11-13. + // Step 11. + if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::Unit, unit, + TemporalUnitGroup::DateTime)) { + return false; + } + + // Steps 12-14. double total; if (zonedRelativeTo) { - // Step 11.a. + // Step 12.a. auto internalDuration = ToInternalDurationRecord(duration); - // Steps 11.b-d. (Not applicable in our implementation.) + // Steps 12.b-d. (Not applicable in our implementation.) - // Step 11.e. + // Step 12.e. EpochNanoseconds targetEpochNs; if (!AddZonedDateTime(cx, zonedRelativeTo, internalDuration, &targetEpochNs)) { return false; } - // Step 11.f. + // Step 12.f. if (!DifferenceZonedDateTimeWithTotal(cx, zonedRelativeTo, targetEpochNs, unit, &total)) { return false; } } else if (plainRelativeTo) { - // Step 12.a. + // Step 13.a. auto internalDuration = ToInternalDurationRecordWith24HourDays(duration); - // Step 12.b. + // Step 13.b. auto targetTime = AddTime(Time{}, internalDuration.time); - // Step 12.c. + // Step 13.c. auto calendar = plainRelativeTo.calendar(); - // Step 12.d. + // Step 13.d. auto dateDuration = DateDuration{ internalDuration.date.years, internalDuration.date.months, @@ -3861,17 +3859,17 @@ static bool Duration_total(JSContext* cx, const CallArgs& args) { }; MOZ_ASSERT(IsValidDuration(dateDuration)); - // Step 12.e. + // Step 13.e. ISODate targetDate; if (!CalendarDateAdd(cx, calendar, plainRelativeTo, dateDuration, TemporalOverflow::Constrain, &targetDate)) { return false; } - // Step 12.f. + // Step 13.f. auto isoDateTime = ISODateTime{plainRelativeTo, {}}; - // Step 12.g. + // Step 13.g. auto targetDateTime = ISODateTime{targetDate, targetTime.time}; // DifferencePlainDateTimeWithTotal, step 2. @@ -3882,13 +3880,13 @@ static bool Duration_total(JSContext* cx, const CallArgs& args) { return false; } - // Step 12.h. + // Step 13.h. if (!DifferencePlainDateTimeWithTotal(cx, isoDateTime, targetDateTime, calendar, unit, &total)) { return false; } } else { - // Steps 13.a-b. + // Steps 14.a-b. if (duration.years != 0 || duration.months != 0 || duration.weeks != 0 || unit < TemporalUnit::Day) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, @@ -3897,14 +3895,14 @@ static bool Duration_total(JSContext* cx, const CallArgs& args) { return false; } - // Step 13.c. + // Step 14.c. auto internalDuration = ToInternalDurationRecordWith24HourDays(duration); - // Step 13.d. + // Step 14.d. total = TotalTimeDuration(internalDuration.time, unit); } - // Step 14. + // Step 15. args.rval().setNumber(total); return true; } @@ -3924,7 +3922,6 @@ static bool Duration_total(JSContext* cx, unsigned argc, Value* vp) { static bool Duration_toString(JSContext* cx, const CallArgs& args) { auto duration = ToDuration(&args.thisv().toObject().as<DurationObject>()); - // Steps 3-9. SecondsStringPrecision precision = {Precision::Auto(), TemporalUnit::Nanosecond, Increment{1}}; auto roundingMode = TemporalRoundingMode::Trunc; @@ -3948,13 +3945,19 @@ static bool Duration_toString(JSContext* cx, const CallArgs& args) { } // Step 7. - auto smallestUnit = TemporalUnit::Auto; + auto smallestUnit = TemporalUnit::Unset; if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit, - TemporalUnitGroup::Time, &smallestUnit)) { + &smallestUnit)) { return false; } // Step 8. + if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit, + smallestUnit, TemporalUnitGroup::Time)) { + return false; + } + + // Step 9. if (smallestUnit == TemporalUnit::Hour || smallestUnit == TemporalUnit::Minute) { const char* smallestUnitStr = @@ -3965,35 +3968,35 @@ static bool Duration_toString(JSContext* cx, const CallArgs& args) { return false; } - // Step 9. + // Step 10. precision = ToSecondsStringPrecision(smallestUnit, digits); } MOZ_ASSERT(precision.unit >= TemporalUnit::Minute); - // Steps 10-16. + // Steps 11-17. auto roundedDuration = duration; if (precision.unit != TemporalUnit::Nanosecond || precision.increment != Increment{1}) { - // Step 11. + // Step 12. auto largestUnit = DefaultTemporalLargestUnit(duration); - // Step 12. + // Step 13. auto internalDuration = ToInternalDurationRecord(duration); - // Step 13. + // Step 14. TimeDuration timeDuration; if (!RoundTimeDuration(cx, internalDuration.time, precision.increment, precision.unit, roundingMode, &timeDuration)) { return false; } - // Step 14. + // Step 15. internalDuration = {internalDuration.date, timeDuration}; - // Step 15. + // Step 16. auto roundedLargestUnit = std::min(largestUnit, TemporalUnit::Second); - // Step 16. + // Step 17. if (!TemporalDurationFromInternal(cx, internalDuration, roundedLargestUnit, &roundedDuration)) { return false; @@ -4001,7 +4004,7 @@ static bool Duration_toString(JSContext* cx, const CallArgs& args) { MOZ_ASSERT(IsValidDuration(roundedDuration)); } - // Steps 10.a. and 17. + // Steps 11.a. and 18. JSString* str = TemporalDurationToString(cx, roundedDuration, precision.precision); if (!str) { diff --git a/js/src/builtin/temporal/Instant.cpp b/js/src/builtin/temporal/Instant.cpp @@ -951,8 +951,8 @@ static bool Instant_since(JSContext* cx, unsigned argc, Value* vp) { static bool Instant_round(JSContext* cx, const CallArgs& args) { auto epochNs = args.thisv().toObject().as<InstantObject>().epochNanoseconds(); - // Steps 3-16. - auto smallestUnit = TemporalUnit::Auto; + // Steps 3-17. + auto smallestUnit = TemporalUnit::Unset; auto roundingMode = TemporalRoundingMode::HalfExpand; auto roundingIncrement = Increment{1}; if (args.get(0).isString()) { @@ -960,13 +960,18 @@ static bool Instant_round(JSContext* cx, const CallArgs& args) { // Step 9. Rooted<JSString*> paramString(cx, args[0].toString()); - if (!GetTemporalUnitValuedOption(cx, paramString, - TemporalUnitKey::SmallestUnit, - TemporalUnitGroup::Time, &smallestUnit)) { + if (!GetTemporalUnitValuedOption( + cx, paramString, TemporalUnitKey::SmallestUnit, &smallestUnit)) { return false; } - // Steps 10-16. (Not applicable in our implementation.) + // Step 10. + if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit, + smallestUnit, TemporalUnitGroup::Time)) { + return false; + } + + // Steps 11-17. (Not applicable in our implementation.) } else { // Steps 3 and 5. Rooted<JSObject*> options( @@ -987,30 +992,37 @@ static bool Instant_round(JSContext* cx, const CallArgs& args) { // Step 9. if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit, - TemporalUnitGroup::Time, &smallestUnit)) { + &smallestUnit)) { return false; } - if (smallestUnit == TemporalUnit::Auto) { + + if (smallestUnit == TemporalUnit::Unset) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TEMPORAL_MISSING_OPTION, "smallestUnit"); return false; } - // Steps 10-15. + // Step 10. + if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit, + smallestUnit, TemporalUnitGroup::Time)) { + return false; + } + + // Steps 11-16. int64_t maximum = UnitsPerDay(smallestUnit); - // Step 16. + // Step 17. if (!ValidateTemporalRoundingIncrement(cx, roundingIncrement, maximum, true)) { return false; } } - // Step 17. + // Step 18. auto roundedNs = RoundTemporalInstant(epochNs, roundingIncrement, smallestUnit, roundingMode); - // Step 18. + // Step 19. auto* result = CreateTemporalInstant(cx, roundedNs); if (!result) { return false; @@ -1084,42 +1096,49 @@ static bool Instant_toString(JSContext* cx, const CallArgs& args) { } // Step 7. - auto smallestUnit = TemporalUnit::Auto; + auto smallestUnit = TemporalUnit::Unset; if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit, - TemporalUnitGroup::Time, &smallestUnit)) { + &smallestUnit)) { return false; } // Step 8. - if (smallestUnit == TemporalUnit::Hour) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_TEMPORAL_INVALID_UNIT_OPTION, "hour", - "smallestUnit"); + Rooted<Value> timeZoneValue(cx); + if (!GetProperty(cx, options, options, cx->names().timeZone, + &timeZoneValue)) { return false; } // Step 9. - Rooted<Value> value(cx); - if (!GetProperty(cx, options, options, cx->names().timeZone, &value)) { + if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit, + smallestUnit, TemporalUnitGroup::Time)) { return false; } // Step 10. - if (!value.isUndefined()) { - if (!ToTemporalTimeZone(cx, value, &timeZone)) { + if (smallestUnit == TemporalUnit::Hour) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_TEMPORAL_INVALID_UNIT_OPTION, "hour", + "smallestUnit"); + return false; + } + + // Step 11. + if (!timeZoneValue.isUndefined()) { + if (!ToTemporalTimeZone(cx, timeZoneValue, &timeZone)) { return false; } } - // Step 11. + // Step 12. precision = ToSecondsStringPrecision(smallestUnit, digits); } - // Steps 12-13. + // Steps 13-14. auto roundedNs = RoundTemporalInstant(epochNs, precision.increment, precision.unit, roundingMode); - // Step 14. + // Step 15. JSString* str = TemporalInstantToString(cx, roundedNs, timeZone, precision.precision); if (!str) { diff --git a/js/src/builtin/temporal/PlainDateTime.cpp b/js/src/builtin/temporal/PlainDateTime.cpp @@ -1725,8 +1725,8 @@ static bool PlainDateTime_round(JSContext* cx, const CallArgs& args) { auto dateTime = temporalDateTime->dateTime(); Rooted<CalendarValue> calendar(cx, temporalDateTime->calendar()); - // Steps 3-12. - auto smallestUnit = TemporalUnit::Auto; + // Steps 3-13. + auto smallestUnit = TemporalUnit::Unset; auto roundingMode = TemporalRoundingMode::HalfExpand; auto roundingIncrement = Increment{1}; if (args.get(0).isString()) { @@ -1735,15 +1735,19 @@ static bool PlainDateTime_round(JSContext* cx, const CallArgs& args) { // Step 9. Rooted<JSString*> paramString(cx, args[0].toString()); if (!GetTemporalUnitValuedOption( - cx, paramString, TemporalUnitKey::SmallestUnit, - TemporalUnitGroup::DayTime, &smallestUnit)) { + cx, paramString, TemporalUnitKey::SmallestUnit, &smallestUnit)) { return false; } + // Step 10. + if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit, + smallestUnit, TemporalUnitGroup::DayTime)) { + return false; + } MOZ_ASSERT(TemporalUnit::Day <= smallestUnit && smallestUnit <= TemporalUnit::Nanosecond); - // Steps 6-8 and 10-12. (Implicit) + // Steps 6-8 and 11-13. (Implicit) } else { // Steps 3 and 5. Rooted<JSObject*> roundTo( @@ -1764,21 +1768,25 @@ static bool PlainDateTime_round(JSContext* cx, const CallArgs& args) { // Step 9. if (!GetTemporalUnitValuedOption(cx, roundTo, TemporalUnitKey::SmallestUnit, - TemporalUnitGroup::DayTime, &smallestUnit)) { return false; } - if (smallestUnit == TemporalUnit::Auto) { + if (smallestUnit == TemporalUnit::Unset) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TEMPORAL_MISSING_OPTION, "smallestUnit"); return false; } + // Step 10. + if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit, + smallestUnit, TemporalUnitGroup::DayTime)) { + return false; + } MOZ_ASSERT(TemporalUnit::Day <= smallestUnit && smallestUnit <= TemporalUnit::Nanosecond); - // Steps 10-11. + // Steps 11-12. auto maximum = Increment{1}; bool inclusive = true; if (smallestUnit > TemporalUnit::Day) { @@ -1786,14 +1794,14 @@ static bool PlainDateTime_round(JSContext* cx, const CallArgs& args) { inclusive = false; } - // Step 12. + // Step 13. if (!ValidateTemporalRoundingIncrement(cx, roundingIncrement, maximum, inclusive)) { return false; } } - // Step 13. + // Step 14. if (smallestUnit == TemporalUnit::Nanosecond && roundingIncrement == Increment{1}) { auto* obj = CreateTemporalDateTime(cx, dateTime, calendar); @@ -1805,11 +1813,11 @@ static bool PlainDateTime_round(JSContext* cx, const CallArgs& args) { return true; } - // Step 14. + // Step 15. auto result = RoundISODateTime(dateTime, roundingIncrement, smallestUnit, roundingMode); - // Step 15. + // Step 16. auto* obj = CreateTemporalDateTime(cx, result, calendar); if (!obj) { return false; @@ -1896,13 +1904,19 @@ static bool PlainDateTime_toString(JSContext* cx, const CallArgs& args) { } // Step 8. - auto smallestUnit = TemporalUnit::Auto; + auto smallestUnit = TemporalUnit::Unset; if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit, - TemporalUnitGroup::Time, &smallestUnit)) { + &smallestUnit)) { return false; } // Step 9. + if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit, + smallestUnit, TemporalUnitGroup::Time)) { + return false; + } + + // Step 10. if (smallestUnit == TemporalUnit::Hour) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TEMPORAL_INVALID_UNIT_OPTION, "hour", @@ -1910,22 +1924,22 @@ static bool PlainDateTime_toString(JSContext* cx, const CallArgs& args) { return false; } - // Step 10. + // Step 11. precision = ToSecondsStringPrecision(smallestUnit, digits); } - // Step 11. + // Step 12. auto result = RoundISODateTime(dt, precision.increment, precision.unit, roundingMode); - // Step 12. + // Step 13. if (!ISODateTimeWithinLimits(result)) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TEMPORAL_PLAIN_DATE_TIME_INVALID); return false; } - // Step 13. + // Step 14. JSString* str = ISODateTimeToString(cx, result, calendar, precision.precision, showCalendar); if (!str) { diff --git a/js/src/builtin/temporal/PlainTime.cpp b/js/src/builtin/temporal/PlainTime.cpp @@ -1347,8 +1347,8 @@ static bool PlainTime_round(JSContext* cx, const CallArgs& args) { auto* temporalTime = &args.thisv().toObject().as<PlainTimeObject>(); auto time = temporalTime->time(); - // Steps 3-12. - auto smallestUnit = TemporalUnit::Auto; + // Steps 3-13. + auto smallestUnit = TemporalUnit::Unset; auto roundingMode = TemporalRoundingMode::HalfExpand; auto roundingIncrement = Increment{1}; if (args.get(0).isString()) { @@ -1356,13 +1356,18 @@ static bool PlainTime_round(JSContext* cx, const CallArgs& args) { // Step 9. Rooted<JSString*> paramString(cx, args[0].toString()); - if (!GetTemporalUnitValuedOption(cx, paramString, - TemporalUnitKey::SmallestUnit, - TemporalUnitGroup::Time, &smallestUnit)) { + if (!GetTemporalUnitValuedOption( + cx, paramString, TemporalUnitKey::SmallestUnit, &smallestUnit)) { return false; } - // Steps 6-8 and 10-12. (Implicit) + // Step 10. + if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit, + smallestUnit, TemporalUnitGroup::Time)) { + return false; + } + + // Steps 6-8 and 11-13. (Implicit) } else { // Steps 3 and 5. Rooted<JSObject*> options( @@ -1383,30 +1388,36 @@ static bool PlainTime_round(JSContext* cx, const CallArgs& args) { // Step 9. if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit, - TemporalUnitGroup::Time, &smallestUnit)) { + &smallestUnit)) { return false; } - if (smallestUnit == TemporalUnit::Auto) { + if (smallestUnit == TemporalUnit::Unset) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TEMPORAL_MISSING_OPTION, "smallestUnit"); return false; } - // Steps 10-11. + // Step 10. + if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit, + smallestUnit, TemporalUnitGroup::Time)) { + return false; + } + + // Steps 11-12. auto maximum = MaximumTemporalDurationRoundingIncrement(smallestUnit); - // Step 12. + // Step 13. if (!ValidateTemporalRoundingIncrement(cx, roundingIncrement, maximum, false)) { return false; } } - // Step 13. + // Step 14. auto result = RoundTime(time, roundingIncrement, smallestUnit, roundingMode); - // Step 14. + // Step 15. auto* obj = CreateTemporalTime(cx, result.time); if (!obj) { return false; @@ -1481,13 +1492,19 @@ static bool PlainTime_toString(JSContext* cx, const CallArgs& args) { } // Step 7. - auto smallestUnit = TemporalUnit::Auto; + auto smallestUnit = TemporalUnit::Unset; if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit, - TemporalUnitGroup::Time, &smallestUnit)) { + &smallestUnit)) { return false; } // Step 8. + if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit, + smallestUnit, TemporalUnitGroup::Time)) { + return false; + } + + // Step 9. if (smallestUnit == TemporalUnit::Hour) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TEMPORAL_INVALID_UNIT_OPTION, "hour", @@ -1495,15 +1512,15 @@ static bool PlainTime_toString(JSContext* cx, const CallArgs& args) { return false; } - // Step 9. + // Step 10. precision = ToSecondsStringPrecision(smallestUnit, digits); } - // Step 10. + // Step 11. auto roundedTime = RoundTime(time, precision.increment, precision.unit, roundingMode); - // Step 11. + // Step 12. JSString* str = TimeRecordToString(cx, roundedTime.time, precision.precision); if (!str) { return false; diff --git a/js/src/builtin/temporal/Temporal.cpp b/js/src/builtin/temporal/Temporal.cpp @@ -201,6 +201,7 @@ static bool ToTemporalUnit(JSContext* cx, JSLinearString* str, }; static constexpr UnitMap mapping[] = { + {"auto", TemporalUnit::Auto}, {"year", TemporalUnit::Year}, {"years", TemporalUnit::Year}, {"month", TemporalUnit::Month}, @@ -272,71 +273,79 @@ static std::pair<TemporalUnit, TemporalUnit> AllowedValues( } /** - * GetTemporalUnitValuedOption ( normalizedOptions, key, unitGroup, default [ , - * extraValues ] ) + * GetTemporalUnitValuedOption ( options, key, default ) */ bool js::temporal::GetTemporalUnitValuedOption(JSContext* cx, Handle<JSObject*> options, TemporalUnitKey key, - TemporalUnitGroup unitGroup, TemporalUnit* unit) { - // Steps 1-8. (Not applicable in our implementation.) + // Steps 1-5. (Not applicable in our implementation.) - // Step 9. + // Step 6. Rooted<JSString*> value(cx); if (!GetStringOption(cx, options, ToPropertyName(cx, key), &value)) { return false; } + // Step 7. + // // Caller should fill in the fallback. if (!value) { return true; } - return GetTemporalUnitValuedOption(cx, value, key, unitGroup, unit); + // Steps 8-9. + auto* linear = value->ensureLinear(cx); + if (!linear) { + return false; + } + return ToTemporalUnit(cx, linear, key, unit); } /** - * GetTemporalUnitValuedOption ( normalizedOptions, key, unitGroup, default [ , - * extraValues ] ) + * GetTemporalUnitValuedOption ( options, key, default ) */ bool js::temporal::GetTemporalUnitValuedOption(JSContext* cx, Handle<JSString*> value, TemporalUnitKey key, - TemporalUnitGroup unitGroup, TemporalUnit* unit) { - // Steps 1-9. (Not applicable in our implementation.) - - // Step 10. (Handled in caller.) + // Steps 1-7. (Not applicable in our implementation.) - Rooted<JSLinearString*> linear(cx, value->ensureLinear(cx)); + // Steps 8-9. + auto* linear = value->ensureLinear(cx); if (!linear) { return false; } + return ToTemporalUnit(cx, linear, key, unit); +} - // Caller should fill in the fallback. - if (key == TemporalUnitKey::LargestUnit) { - if (StringEqualsLiteral(linear, "auto")) { - return true; - } +/** + * ValidateTemporalUnitValue ( value, unitGroup [ , extraValues ] ) + */ +bool js::temporal::ValidateTemporalUnitValue(JSContext* cx, TemporalUnitKey key, + TemporalUnit unit, + TemporalUnitGroup unitGroup) { + // Step 1. + if (unit == TemporalUnit::Unset) { + return true; } - // Step 11. - if (!ToTemporalUnit(cx, linear, key, unit)) { - return false; + // Step 2. (Partial) + if (key == TemporalUnitKey::LargestUnit && unit == TemporalUnit::Auto) { + return true; } + // Steps 2-5. auto allowedValues = AllowedValues(unitGroup); - if (*unit < allowedValues.first || *unit > allowedValues.second) { - if (auto chars = QuoteString(cx, linear, '"')) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_INVALID_OPTION_VALUE, ToCString(key), - chars.get()); - } - return false; + if (allowedValues.first <= unit && unit <= allowedValues.second) { + return true; } - return true; + // Step 6. + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_INVALID_OPTION_VALUE, ToCString(key), + TemporalUnitToString(unit)); + return false; } /** @@ -821,7 +830,7 @@ bool js::temporal::GetTemporalFractionalSecondDigitsOption( */ SecondsStringPrecision js::temporal::ToSecondsStringPrecision( TemporalUnit smallestUnit, Precision fractionalDigitCount) { - MOZ_ASSERT(smallestUnit == TemporalUnit::Auto || + MOZ_ASSERT(smallestUnit == TemporalUnit::Unset || smallestUnit >= TemporalUnit::Minute); MOZ_ASSERT(fractionalDigitCount == Precision::Auto() || fractionalDigitCount.value() <= 9); @@ -848,9 +857,10 @@ SecondsStringPrecision js::temporal::ToSecondsStringPrecision( case TemporalUnit::Nanosecond: return {Precision{9}, TemporalUnit::Nanosecond, Increment{1}}; - case TemporalUnit::Auto: + case TemporalUnit::Unset: break; + case TemporalUnit::Auto: case TemporalUnit::Year: case TemporalUnit::Month: case TemporalUnit::Week: @@ -1260,46 +1270,69 @@ bool js::temporal::GetDifferenceSettings( TemporalUnitGroup unitGroup, TemporalUnit smallestAllowedUnit, TemporalUnit fallbackSmallestUnit, TemporalUnit smallestLargestDefaultUnit, DifferenceSettings* result) { + MOZ_ASSERT(TemporalUnit::Auto < fallbackSmallestUnit && + fallbackSmallestUnit <= smallestAllowedUnit); + MOZ_ASSERT(TemporalUnit::Auto < smallestLargestDefaultUnit && + smallestLargestDefaultUnit <= smallestAllowedUnit); + // Steps 1-2. auto largestUnit = TemporalUnit::Auto; if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::LargestUnit, - unitGroup, &largestUnit)) { + &largestUnit)) { return false; } // Step 3. - if (largestUnit > smallestAllowedUnit) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_TEMPORAL_INVALID_UNIT_OPTION, - TemporalUnitToString(largestUnit), "largestUnit"); + auto roundingIncrement = Increment{1}; + if (!GetRoundingIncrementOption(cx, options, &roundingIncrement)) { return false; } // Step 4. - auto roundingIncrement = Increment{1}; - if (!GetRoundingIncrementOption(cx, options, &roundingIncrement)) { + auto roundingMode = TemporalRoundingMode::Trunc; + if (!GetRoundingModeOption(cx, options, &roundingMode)) { return false; } // Step 5. - auto roundingMode = TemporalRoundingMode::Trunc; - if (!GetRoundingModeOption(cx, options, &roundingMode)) { + auto smallestUnit = fallbackSmallestUnit; + if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit, + &smallestUnit)) { return false; } // Step 6. + if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::LargestUnit, largestUnit, + unitGroup)) { + return false; + } + + // Step 7. (Not applicable in our implementation.) + MOZ_ASSERT(largestUnit != TemporalUnit::Unset); + + // Step 8. + if (largestUnit > smallestAllowedUnit) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_TEMPORAL_INVALID_UNIT_OPTION, + TemporalUnitToString(largestUnit), "largestUnit"); + return false; + } + + // Step 9. if (operation == TemporalDifference::Since) { roundingMode = NegateRoundingMode(roundingMode); } - // Step 7. - auto smallestUnit = fallbackSmallestUnit; - if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit, - unitGroup, &smallestUnit)) { + // Step 10. + if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit, + smallestUnit, unitGroup)) { return false; } - // Step 8. + // Step 11. (Not applicable in our implementation.) + MOZ_ASSERT(smallestUnit != TemporalUnit::Unset); + + // Step 12. if (smallestUnit > smallestAllowedUnit) { JS_ReportErrorNumberASCII( cx, GetErrorMessage, nullptr, JSMSG_TEMPORAL_INVALID_UNIT_OPTION, @@ -1307,34 +1340,34 @@ bool js::temporal::GetDifferenceSettings( return false; } - // Step 9. (Inlined call to LargerOfTwoTemporalUnits) + // Step 13. (Inlined call to LargerOfTwoTemporalUnits) auto defaultLargestUnit = std::min(smallestLargestDefaultUnit, smallestUnit); - // Step 10. + // Step 14. if (largestUnit == TemporalUnit::Auto) { largestUnit = defaultLargestUnit; } - // Step 11. + // Step 15. if (largestUnit > smallestUnit) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TEMPORAL_INVALID_UNIT_RANGE); return false; } - // Steps 12-13. + // Steps 16-17. if (smallestUnit > TemporalUnit::Day) { - // Step 12. + // Step 16. auto maximum = MaximumTemporalDurationRoundingIncrement(smallestUnit); - // Step 13. + // Step 17. if (!ValidateTemporalRoundingIncrement(cx, roundingIncrement, maximum, false)) { return false; } } - // Step 14. + // Step 18. *result = {smallestUnit, largestUnit, roundingMode, roundingIncrement}; return true; } diff --git a/js/src/builtin/temporal/Temporal.h b/js/src/builtin/temporal/Temporal.h @@ -141,22 +141,23 @@ enum class TemporalUnitKey { }; /** - * GetTemporalUnitValuedOption ( normalizedOptions, key, unitGroup, default [ , - * extraValues ] ) + * GetTemporalUnitValuedOption ( options, key, default ) */ bool GetTemporalUnitValuedOption(JSContext* cx, JS::Handle<JSObject*> options, - TemporalUnitKey key, - TemporalUnitGroup unitGroup, - TemporalUnit* unit); + TemporalUnitKey key, TemporalUnit* unit); /** * GetTemporalUnitValuedOption ( normalizedOptions, key, unitGroup, default [ , * extraValues ] ) */ bool GetTemporalUnitValuedOption(JSContext* cx, JS::Handle<JSString*> value, - TemporalUnitKey key, - TemporalUnitGroup unitGroup, - TemporalUnit* unit); + TemporalUnitKey key, TemporalUnit* unit); + +/** + * ValidateTemporalUnitValue ( value, unitGroup [ , extraValues ] ) + */ +bool ValidateTemporalUnitValue(JSContext* cx, TemporalUnitKey key, + TemporalUnit unit, TemporalUnitGroup unitGroup); /** * GetRoundingModeOption ( normalizedOptions, fallback ) diff --git a/js/src/builtin/temporal/ZonedDateTime.cpp b/js/src/builtin/temporal/ZonedDateTime.cpp @@ -2449,8 +2449,8 @@ static bool ZonedDateTime_round(JSContext* cx, const CallArgs& args) { Rooted<ZonedDateTime> zonedDateTime( cx, ZonedDateTime{&args.thisv().toObject().as<ZonedDateTimeObject>()}); - // Steps 3-12. - auto smallestUnit = TemporalUnit::Auto; + // Steps 3-13. + auto smallestUnit = TemporalUnit::Unset; auto roundingMode = TemporalRoundingMode::HalfExpand; auto roundingIncrement = Increment{1}; if (args.get(0).isString()) { @@ -2459,12 +2459,17 @@ static bool ZonedDateTime_round(JSContext* cx, const CallArgs& args) { // Step 9. Rooted<JSString*> paramString(cx, args[0].toString()); if (!GetTemporalUnitValuedOption( - cx, paramString, TemporalUnitKey::SmallestUnit, - TemporalUnitGroup::DayTime, &smallestUnit)) { + cx, paramString, TemporalUnitKey::SmallestUnit, &smallestUnit)) { return false; } - // Steps 6-8 and 10-12. (Implicit) + // Step 10. + if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit, + smallestUnit, TemporalUnitGroup::DayTime)) { + return false; + } + + // Steps 6-8 and 11-13. (Implicit) } else { // Steps 3 and 5.a Rooted<JSObject*> roundTo( @@ -2485,21 +2490,25 @@ static bool ZonedDateTime_round(JSContext* cx, const CallArgs& args) { // Step 9. if (!GetTemporalUnitValuedOption(cx, roundTo, TemporalUnitKey::SmallestUnit, - TemporalUnitGroup::DayTime, &smallestUnit)) { return false; } - if (smallestUnit == TemporalUnit::Auto) { + if (smallestUnit == TemporalUnit::Unset) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TEMPORAL_MISSING_OPTION, "smallestUnit"); return false; } + // Step 10. + if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit, + smallestUnit, TemporalUnitGroup::DayTime)) { + return false; + } MOZ_ASSERT(TemporalUnit::Day <= smallestUnit && smallestUnit <= TemporalUnit::Nanosecond); - // Steps 10-11. + // Steps 11-12. auto maximum = Increment{1}; bool inclusive = true; if (smallestUnit > TemporalUnit::Day) { @@ -2507,17 +2516,17 @@ static bool ZonedDateTime_round(JSContext* cx, const CallArgs& args) { inclusive = false; } - // Step 12. + // Step 13. if (!ValidateTemporalRoundingIncrement(cx, roundingIncrement, maximum, inclusive)) { return false; } } - // Step 13. + // Step 14. if (smallestUnit == TemporalUnit::Nanosecond && roundingIncrement == Increment{1}) { - // Step 13.a. + // Step 14.a. auto* result = CreateTemporalZonedDateTime( cx, zonedDateTime.epochNanoseconds(), zonedDateTime.timeZone(), zonedDateTime.calendar()); @@ -2529,28 +2538,28 @@ static bool ZonedDateTime_round(JSContext* cx, const CallArgs& args) { return true; } - // Step 14. + // Step 15. auto thisNs = zonedDateTime.epochNanoseconds(); - // Step 15. + // Step 16. auto timeZone = zonedDateTime.timeZone(); - // Step 16. + // Step 17. auto calendar = zonedDateTime.calendar(); - // Step 17. + // Step 18. ISODateTime isoDateTime; if (!GetISODateTimeFor(cx, timeZone, thisNs, &isoDateTime)) { return false; } - // Steps 18-19. + // Steps 19-20. EpochNanoseconds epochNanoseconds; if (smallestUnit == TemporalUnit::Day) { - // Step 18.a. + // Step 19.a. const auto& dateStart = isoDateTime.date; - // Step 18.b. + // Step 19.b. auto dateEnd = BalanceISODate(dateStart, 1); if (!ISODateWithinLimits(dateEnd)) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, @@ -2558,30 +2567,30 @@ static bool ZonedDateTime_round(JSContext* cx, const CallArgs& args) { return false; } - // Step 18.c. + // Step 19.c. EpochNanoseconds startNs; if (!GetStartOfDay(cx, timeZone, dateStart, &startNs)) { return false; } - // Step 18.d. + // Step 19.d. MOZ_ASSERT(thisNs >= startNs); - // Step 18.e. + // Step 19.e. EpochNanoseconds endNs; if (!GetStartOfDay(cx, timeZone, dateEnd, &endNs)) { return false; } - // Step 18.f. + // Step 19.f. MOZ_ASSERT(thisNs < endNs); - // Step 18.g. + // Step 19.g. auto dayLengthNs = endNs - startNs; MOZ_ASSERT(IsValidEpochDuration(dayLengthNs)); MOZ_ASSERT(dayLengthNs > EpochDuration{}, "dayLengthNs is positive"); - // Step 18.h. (Inlined TimeDurationFromEpochNanosecondsDifference) + // Step 19.h. (Inlined TimeDurationFromEpochNanosecondsDifference) auto dayProgressNs = thisNs - startNs; MOZ_ASSERT(IsValidEpochDuration(dayProgressNs)); MOZ_ASSERT(dayProgressNs >= EpochDuration{}, @@ -2592,7 +2601,7 @@ static bool ZonedDateTime_round(JSContext* cx, const CallArgs& args) { MOZ_ASSERT(dayLengthNs <= EpochDuration::fromDays(2), "maximum day length for repeated days"); - // Step 18.i. (Inlined RoundTimeDurationToIncrement) + // Step 19.i. (Inlined RoundTimeDurationToIncrement) auto rounded = RoundNumberToIncrement( static_cast<int64_t>(dayProgressNs.toNanoseconds()), static_cast<int64_t>(dayLengthNs.toNanoseconds()), roundingMode); @@ -2601,22 +2610,22 @@ static bool ZonedDateTime_round(JSContext* cx, const CallArgs& args) { roundedDaysNs == dayLengthNs); MOZ_ASSERT(IsValidEpochDuration(roundedDaysNs)); - // Step 18.j. (Inlined AddTimeDurationToEpochNanoseconds) + // Step 19.j. (Inlined AddTimeDurationToEpochNanoseconds) epochNanoseconds = startNs + roundedDaysNs; MOZ_ASSERT(epochNanoseconds == startNs || epochNanoseconds == endNs); } else { - // Step 19.a. + // Step 20.a. auto roundResult = RoundISODateTime(isoDateTime, roundingIncrement, smallestUnit, roundingMode); - // Step 19.b. + // Step 20.b. int64_t offsetNanoseconds; if (!GetOffsetNanosecondsFor(cx, timeZone, thisNs, &offsetNanoseconds)) { return false; } MOZ_ASSERT(std::abs(offsetNanoseconds) < ToNanoseconds(TemporalUnit::Day)); - // Step 19.c. + // Step 20.c. if (!InterpretISODateTimeOffset( cx, roundResult, OffsetBehaviour::Option, offsetNanoseconds, timeZone, TemporalDisambiguation::Compatible, @@ -2627,7 +2636,7 @@ static bool ZonedDateTime_round(JSContext* cx, const CallArgs& args) { } MOZ_ASSERT(IsValidEpochNanoseconds(epochNanoseconds)); - // Step 20. + // Step 21. auto* result = CreateTemporalZonedDateTime(cx, epochNanoseconds, timeZone, calendar); if (!result) { @@ -2721,30 +2730,36 @@ static bool ZonedDateTime_toString(JSContext* cx, const CallArgs& args) { } // Step 9. - auto smallestUnit = TemporalUnit::Auto; + auto smallestUnit = TemporalUnit::Unset; if (!GetTemporalUnitValuedOption(cx, options, TemporalUnitKey::SmallestUnit, - TemporalUnitGroup::Time, &smallestUnit)) { + &smallestUnit)) { return false; } // Step 10. - if (smallestUnit == TemporalUnit::Hour) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_TEMPORAL_INVALID_UNIT_OPTION, "hour", - "smallestUnit"); + if (!GetTemporalShowTimeZoneNameOption(cx, options, &showTimeZone)) { return false; } // Step 11. - if (!GetTemporalShowTimeZoneNameOption(cx, options, &showTimeZone)) { + if (!ValidateTemporalUnitValue(cx, TemporalUnitKey::SmallestUnit, + smallestUnit, TemporalUnitGroup::Time)) { return false; } // Step 12. + if (smallestUnit == TemporalUnit::Hour) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_TEMPORAL_INVALID_UNIT_OPTION, "hour", + "smallestUnit"); + return false; + } + + // Step 13. precision = ToSecondsStringPrecision(smallestUnit, digits); } - // Step 13. + // Step 14. JSString* str = TemporalZonedDateTimeToString( cx, zonedDateTime, precision.precision, showCalendar, showTimeZone, showOffset, precision.increment, precision.unit, roundingMode); diff --git a/js/src/tests/jstests.list b/js/src/tests/jstests.list @@ -1099,15 +1099,3 @@ skip script test262/intl402/Temporal/PlainDateTime/from/calendar-not-supporting- skip script test262/built-ins/Temporal/PlainDateTime/prototype/until/bubble-time-unit.js skip script test262/built-ins/Temporal/PlainDateTime/prototype/since/bubble-time-unit.js skip script test262/built-ins/Temporal/Duration/prototype/round/bubble-time-unit.js - -# https://bugzilla.mozilla.org/show_bug.cgi?id=1998672 -skip script test262/built-ins/Temporal/ZonedDateTime/prototype/toString/options-read-before-algorithmic-validation.js -skip script test262/built-ins/Temporal/PlainDate/prototype/since/options-read-before-algorithmic-validation.js -skip script test262/built-ins/Temporal/PlainDate/prototype/until/options-read-before-algorithmic-validation.js -skip script test262/built-ins/Temporal/PlainTime/prototype/since/options-read-before-algorithmic-validation.js -skip script test262/built-ins/Temporal/PlainTime/prototype/until/options-read-before-algorithmic-validation.js -skip script test262/built-ins/Temporal/Instant/prototype/since/options-read-before-algorithmic-validation.js -skip script test262/built-ins/Temporal/Instant/prototype/until/options-read-before-algorithmic-validation.js -skip script test262/built-ins/Temporal/Instant/prototype/toString/options-read-before-algorithmic-validation.js -skip script test262/built-ins/Temporal/PlainYearMonth/prototype/since/options-read-before-algorithmic-validation.js -skip script test262/built-ins/Temporal/PlainYearMonth/prototype/until/options-read-before-algorithmic-validation.js