diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:35:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:35:29 +0000 |
commit | 59203c63bb777a3bacec32fb8830fba33540e809 (patch) | |
tree | 58298e711c0ff0575818c30485b44a2f21bf28a0 /js/src/builtin/temporal/PlainTime.cpp | |
parent | Adding upstream version 126.0.1. (diff) | |
download | firefox-59203c63bb777a3bacec32fb8830fba33540e809.tar.xz firefox-59203c63bb777a3bacec32fb8830fba33540e809.zip |
Adding upstream version 127.0.upstream/127.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/builtin/temporal/PlainTime.cpp')
-rw-r--r-- | js/src/builtin/temporal/PlainTime.cpp | 911 |
1 files changed, 74 insertions, 837 deletions
diff --git a/js/src/builtin/temporal/PlainTime.cpp b/js/src/builtin/temporal/PlainTime.cpp index bf35b9d93e..c928b06d46 100644 --- a/js/src/builtin/temporal/PlainTime.cpp +++ b/js/src/builtin/temporal/PlainTime.cpp @@ -7,7 +7,6 @@ #include "builtin/temporal/PlainTime.h" #include "mozilla/Assertions.h" -#include "mozilla/CheckedInt.h" #include "mozilla/FloatingPoint.h" #include "mozilla/Maybe.h" @@ -46,7 +45,6 @@ #include "js/PropertySpec.h" #include "js/RootingAPI.h" #include "js/Value.h" -#include "vm/BigIntType.h" #include "vm/BytecodeUtil.h" #include "vm/GlobalObject.h" #include "vm/JSAtomState.h" @@ -121,7 +119,8 @@ static bool IsValidTime(T hour, T minute, T second, T millisecond, * IsValidTime ( hour, minute, second, millisecond, microsecond, nanosecond ) */ bool js::temporal::IsValidTime(const PlainTime& time) { - auto& [hour, minute, second, millisecond, microsecond, nanosecond] = time; + const auto& [hour, minute, second, millisecond, microsecond, nanosecond] = + time; return ::IsValidTime(hour, minute, second, millisecond, microsecond, nanosecond); } @@ -217,7 +216,8 @@ static bool ThrowIfInvalidTime(JSContext* cx, T hour, T minute, T second, * IsValidTime ( hour, minute, second, millisecond, microsecond, nanosecond ) */ bool js::temporal::ThrowIfInvalidTime(JSContext* cx, const PlainTime& time) { - auto& [hour, minute, second, millisecond, microsecond, nanosecond] = time; + const auto& [hour, minute, second, millisecond, microsecond, nanosecond] = + time; return ::ThrowIfInvalidTime(cx, hour, minute, second, millisecond, microsecond, nanosecond); } @@ -261,9 +261,10 @@ static PlainTime ConstrainTime(double hour, double minute, double second, * RegulateTime ( hour, minute, second, millisecond, microsecond, nanosecond, * overflow ) */ -bool js::temporal::RegulateTime(JSContext* cx, const TimeRecord& time, +bool js::temporal::RegulateTime(JSContext* cx, const TemporalTimeLike& time, TemporalOverflow overflow, PlainTime* result) { - auto& [hour, minute, second, millisecond, microsecond, nanosecond] = time; + const auto& [hour, minute, second, millisecond, microsecond, nanosecond] = + time; // Step 1. MOZ_ASSERT(IsInteger(hour)); @@ -334,25 +335,28 @@ static PlainTimeObject* CreateTemporalTime(JSContext* cx, const CallArgs& args, } // Step 4. - object->setFixedSlot(PlainTimeObject::ISO_HOUR_SLOT, Int32Value(hour)); + object->setFixedSlot(PlainTimeObject::ISO_HOUR_SLOT, + Int32Value(int32_t(hour))); // Step 5. - object->setFixedSlot(PlainTimeObject::ISO_MINUTE_SLOT, Int32Value(minute)); + object->setFixedSlot(PlainTimeObject::ISO_MINUTE_SLOT, + Int32Value(int32_t(minute))); // Step 6. - object->setFixedSlot(PlainTimeObject::ISO_SECOND_SLOT, Int32Value(second)); + object->setFixedSlot(PlainTimeObject::ISO_SECOND_SLOT, + Int32Value(int32_t(second))); // Step 7. object->setFixedSlot(PlainTimeObject::ISO_MILLISECOND_SLOT, - Int32Value(millisecond)); + Int32Value(int32_t(millisecond))); // Step 8. object->setFixedSlot(PlainTimeObject::ISO_MICROSECOND_SLOT, - Int32Value(microsecond)); + Int32Value(int32_t(microsecond))); // Step 9. object->setFixedSlot(PlainTimeObject::ISO_NANOSECOND_SLOT, - Int32Value(nanosecond)); + Int32Value(int32_t(nanosecond))); // Step 10. return object; @@ -364,7 +368,8 @@ static PlainTimeObject* CreateTemporalTime(JSContext* cx, const CallArgs& args, */ PlainTimeObject* js::temporal::CreateTemporalTime(JSContext* cx, const PlainTime& time) { - auto& [hour, minute, second, millisecond, microsecond, nanosecond] = time; + const auto& [hour, minute, second, millisecond, microsecond, nanosecond] = + time; // Step 1. if (!ThrowIfInvalidTime(cx, time)) { @@ -403,63 +408,6 @@ PlainTimeObject* js::temporal::CreateTemporalTime(JSContext* cx, } /** - * CreateTimeDurationRecord ( days, hours, minutes, seconds, milliseconds, - * microseconds, nanoseconds ) - */ -static TimeDuration CreateTimeDurationRecord(double days, int32_t hours, - int32_t minutes, int32_t seconds, - int32_t milliseconds, - int32_t microseconds, - int32_t nanoseconds) { - // Step 1. - MOZ_ASSERT(IsValidDuration({0, 0, 0, days, double(hours), double(minutes), - double(seconds), double(microseconds), - double(nanoseconds)})); - - // Step 2. - return { - days, - double(hours), - double(minutes), - double(seconds), - double(milliseconds), - double(microseconds), - double(nanoseconds), - }; -} - -/** - * DurationSign ( years, months, weeks, days, hours, minutes, seconds, - * milliseconds, microseconds, nanoseconds ) - */ -static int32_t DurationSign(int32_t hours, int32_t minutes, int32_t seconds, - int32_t milliseconds, int32_t microseconds, - int32_t nanoseconds) { - // Step 1. (Loop unrolled) - if (hours) { - return hours > 0 ? 1 : -1; - } - if (minutes) { - return minutes > 0 ? 1 : -1; - } - if (seconds) { - return seconds > 0 ? 1 : -1; - } - if (milliseconds) { - return milliseconds > 0 ? 1 : -1; - } - if (microseconds) { - return microseconds > 0 ? 1 : -1; - } - if (nanoseconds) { - return nanoseconds > 0 ? 1 : -1; - } - - // Step 2. - return 0; -} - -/** * BalanceTime ( hour, minute, second, millisecond, microsecond, nanosecond ) */ template <typename IntT> @@ -531,7 +479,7 @@ static BalancedTime BalanceTime(int32_t hour, int32_t minute, int32_t second, BalancedTime js::temporal::BalanceTime(const PlainTime& time, int64_t nanoseconds) { MOZ_ASSERT(IsValidTime(time)); - MOZ_ASSERT(std::abs(nanoseconds) <= 2 * ToNanoseconds(TemporalUnit::Day)); + MOZ_ASSERT(std::abs(nanoseconds) <= ToNanoseconds(TemporalUnit::Day)); return ::BalanceTime<int64_t>(time.hour, time.minute, time.second, time.millisecond, time.microsecond, @@ -541,8 +489,8 @@ BalancedTime js::temporal::BalanceTime(const PlainTime& time, /** * DifferenceTime ( h1, min1, s1, ms1, mus1, ns1, h2, min2, s2, ms2, mus2, ns2 ) */ -TimeDuration js::temporal::DifferenceTime(const PlainTime& time1, - const PlainTime& time2) { +NormalizedTimeDuration js::temporal::DifferenceTime(const PlainTime& time1, + const PlainTime& time2) { MOZ_ASSERT(IsValidTime(time1)); MOZ_ASSERT(IsValidTime(time2)); @@ -565,22 +513,15 @@ TimeDuration js::temporal::DifferenceTime(const PlainTime& time1, int32_t nanoseconds = time2.nanosecond - time1.nanosecond; // Step 7. - int32_t sign = ::DurationSign(hours, minutes, seconds, milliseconds, - microseconds, nanoseconds); + auto result = NormalizeTimeDuration(hours, minutes, seconds, milliseconds, + microseconds, nanoseconds); // Step 8. - auto balanced = ::BalanceTime(hours * sign, minutes * sign, seconds * sign, - milliseconds * sign, microseconds * sign, - nanoseconds * sign); + MOZ_ASSERT(result.abs().toNanoseconds() < + Int128{ToNanoseconds(TemporalUnit::Day)}); // Step 9. - MOZ_ASSERT(balanced.days == 0); - - // Step 10. - return CreateTimeDurationRecord( - 0, balanced.time.hour * sign, balanced.time.minute * sign, - balanced.time.second * sign, balanced.time.millisecond * sign, - balanced.time.microsecond * sign, balanced.time.nanosecond * sign); + return result; } /** @@ -628,7 +569,7 @@ static bool ToTemporalTime(JSContext* cx, Handle<Value> item, } // Step 3.d. - TimeRecord timeResult; + TemporalTimeLike timeResult; if (!ToTemporalTimeRecord(cx, itemObj, &timeResult)) { return false; } @@ -684,218 +625,6 @@ bool js::temporal::ToTemporalTime(JSContext* cx, Handle<Value> item, } /** - * TotalDurationNanoseconds ( hours, minutes, seconds, milliseconds, - * microseconds, nanoseconds ) - */ -static int64_t TotalDurationNanoseconds(const Duration& duration) { - // This function is only called from BalanceTime. The difference between two - // plain times can't exceed the number of nanoseconds in a day. - MOZ_ASSERT(IsValidDuration(duration)); - MOZ_ASSERT(std::abs(duration.hours) <= 24); - MOZ_ASSERT(std::abs(duration.minutes) <= 60); - MOZ_ASSERT(std::abs(duration.seconds) <= 60); - MOZ_ASSERT(std::abs(duration.milliseconds) <= 1000); - MOZ_ASSERT(std::abs(duration.microseconds) <= 1000); - MOZ_ASSERT(std::abs(duration.nanoseconds) <= 1000); - - // Step 1. - auto minutes = int64_t(duration.minutes) + int64_t(duration.hours) * 60; - - // Step 2. - auto seconds = int64_t(duration.seconds) + minutes * 60; - - // Step 3. - auto milliseconds = int64_t(duration.milliseconds) + seconds * 1000; - - // Step 4. - auto microseconds = int64_t(duration.microseconds) + milliseconds * 1000; - - // Steps 5. - return int64_t(duration.nanoseconds) + microseconds * 1000; -} - -/** - * BalanceTimeDuration ( days, hours, minutes, seconds, milliseconds, - * microseconds, nanoseconds, largestUnit [ , relativeTo ] ) - */ -static Duration BalanceTimeDuration(const Duration& duration, - TemporalUnit largestUnit) { - MOZ_ASSERT(IsValidDuration(duration)); - MOZ_ASSERT(largestUnit > TemporalUnit::Day); - - // We only handle time components here. - MOZ_ASSERT(duration.years == 0); - MOZ_ASSERT(duration.months == 0); - MOZ_ASSERT(duration.weeks == 0); - MOZ_ASSERT(duration.days == 0); - - // Step 1. (Not applicable) - - // Step 2. - int64_t nanoseconds = TotalDurationNanoseconds(duration); - MOZ_ASSERT(std::abs(nanoseconds) <= ToNanoseconds(TemporalUnit::Day)); - - // Steps 3-4. (Not applicable) - - // Step 5. - int64_t hours = 0; - int64_t minutes = 0; - int64_t seconds = 0; - int64_t milliseconds = 0; - int64_t microseconds = 0; - - // Step 6. - int32_t sign = nanoseconds < 0 ? -1 : +1; - - // Step 7. - nanoseconds = std::abs(nanoseconds); - - // Steps 8-13. - switch (largestUnit) { - case TemporalUnit::Auto: - case TemporalUnit::Year: - case TemporalUnit::Month: - case TemporalUnit::Week: - case TemporalUnit::Day: - MOZ_CRASH("Unexpected temporal unit"); - - case TemporalUnit::Hour: { - // Step 8. - - // Step 8.a. - microseconds = nanoseconds / 1000; - - // Step 8.b. - nanoseconds = nanoseconds % 1000; - - // Step 8.c. - milliseconds = microseconds / 1000; - - // Step 8.d. - microseconds = microseconds % 1000; - - // Step 8.e. - seconds = milliseconds / 1000; - - // Step 8.f. - milliseconds = milliseconds % 1000; - - // Step 8.g. - minutes = seconds / 60; - - // Step 8.h. - seconds = seconds % 60; - - // Step 8.i. - hours = minutes / 60; - - // Step 8.j. - minutes = minutes % 60; - - break; - } - case TemporalUnit::Minute: { - // Step 9. - - // Step 9.a. - microseconds = nanoseconds / 1000; - - // Step 9.b. - nanoseconds = nanoseconds % 1000; - - // Step 9.c. - milliseconds = microseconds / 1000; - - // Step 9.d. - microseconds = microseconds % 1000; - - // Step 9.e. - seconds = milliseconds / 1000; - - // Step 9.f. - milliseconds = milliseconds % 1000; - - // Step 9.g. - minutes = seconds / 60; - - // Step 9.h. - seconds = seconds % 60; - - break; - } - case TemporalUnit::Second: { - // Step 10. - - // Step 10.a. - microseconds = nanoseconds / 1000; - - // Step 10.b. - nanoseconds = nanoseconds % 1000; - - // Step 10.c. - milliseconds = microseconds / 1000; - - // Step 10.d. - microseconds = microseconds % 1000; - - // Step 10.e. - seconds = milliseconds / 1000; - - // Step 10.f. - milliseconds = milliseconds % 1000; - - break; - } - case TemporalUnit::Millisecond: { - // Step 11. - - // Step 11.a. - microseconds = nanoseconds / 1000; - - // Step 11.b. - nanoseconds = nanoseconds % 1000; - - // Step 11.c. - milliseconds = microseconds / 1000; - - // Step 11.d. - microseconds = microseconds % 1000; - - break; - } - case TemporalUnit::Microsecond: { - // Step 12. - - // Step 12.a. - microseconds = nanoseconds / 1000; - - // Step 12.b. - nanoseconds = nanoseconds % 1000; - - break; - } - case TemporalUnit::Nanosecond: { - // Step 13. - break; - } - } - - // Step 14. - return { - 0, - 0, - 0, - 0, - double(hours * sign), - double(minutes * sign), - double(seconds * sign), - double(milliseconds * sign), - double(microseconds * sign), - double(nanoseconds * sign), - }; -} - -/** * CompareTemporalTime ( h1, min1, s1, ms1, mus1, ns1, h2, min2, s2, ms2, mus2, * ns2 ) */ @@ -940,7 +669,7 @@ int32_t js::temporal::CompareTemporalTime(const PlainTime& one, */ static bool ToTemporalTimeRecord(JSContext* cx, Handle<JSObject*> temporalTimeLike, - TimeRecord* result) { + TemporalTimeLike* result) { // Steps 1 and 3-4. (Not applicable in our implementation.) // Step 2. (Inlined call to PrepareTemporalFields.) @@ -1012,7 +741,7 @@ static bool ToTemporalTimeRecord(JSContext* cx, */ bool js::temporal::ToTemporalTimeRecord(JSContext* cx, Handle<JSObject*> temporalTimeLike, - TimeRecord* result) { + TemporalTimeLike* result) { // Step 3.a. (Set all fields to zero.) *result = {}; @@ -1020,59 +749,6 @@ bool js::temporal::ToTemporalTimeRecord(JSContext* cx, return ::ToTemporalTimeRecord(cx, temporalTimeLike, result); } -/** - * RoundNumberToIncrement ( x, increment, roundingMode ) - */ -static int64_t RoundNumberToIncrement(int64_t x, TemporalUnit unit, - Increment increment, - TemporalRoundingMode roundingMode) { - MOZ_ASSERT(x >= 0); - MOZ_ASSERT(x < ToNanoseconds(TemporalUnit::Day)); - - MOZ_ASSERT(unit >= TemporalUnit::Day); - MOZ_ASSERT_IF(unit == TemporalUnit::Day, increment == Increment{1}); - MOZ_ASSERT_IF(unit > TemporalUnit::Day, - increment <= MaximumTemporalDurationRoundingIncrement(unit)); - - int64_t divisor = ToNanoseconds(unit) * increment.value(); - MOZ_ASSERT(divisor > 0); - MOZ_ASSERT(divisor <= ToNanoseconds(TemporalUnit::Day)); - - // Division by one has no remainder. - if (divisor == 1) { - MOZ_ASSERT(increment == Increment{1}); - return x; - } - - // Steps 1-8. - int64_t rounded = Divide(x, divisor, roundingMode); - - // Step 9. - mozilla::CheckedInt64 result = rounded; - result *= increment.value(); - - MOZ_ASSERT(result.isValid(), "can't overflow when inputs are all in range"); - - return result.value(); -} - -/** - * RoundNumberToIncrement ( x, increment, roundingMode ) - */ -static int64_t RoundNumberToIncrement(int64_t x, int64_t divisor, - Increment increment, - TemporalRoundingMode roundingMode) { - MOZ_ASSERT(x >= 0); - MOZ_ASSERT(x < ToNanoseconds(TemporalUnit::Day)); - MOZ_ASSERT(divisor > 0); - MOZ_ASSERT(increment == Increment{1}, "Rounding increment for 'day' is 1"); - - // Steps 1-2. (Not applicable in our implementation) - - // Steps 3-8. - return Divide(x, divisor, roundingMode); -} - static int64_t TimeToNanos(const PlainTime& time) { // No overflow possible because the input is a valid time. MOZ_ASSERT(IsValidTime(time)); @@ -1090,7 +766,7 @@ static int64_t TimeToNanos(const PlainTime& time) { /** * RoundTime ( hour, minute, second, millisecond, microsecond, nanosecond, - * increment, unit, roundingMode [ , dayLengthNs ] ) + * increment, unit, roundingMode ) */ RoundedTime js::temporal::RoundTime(const PlainTime& time, Increment increment, TemporalUnit unit, @@ -1163,11 +839,15 @@ RoundedTime js::temporal::RoundTime(const PlainTime& time, Increment increment, } // Step 9. - int64_t r = ::RoundNumberToIncrement(TimeToNanos(quantity), unit, increment, - roundingMode); - MOZ_ASSERT(r == int64_t(int32_t(r)), - "no overflow possible due to limited range of arguments"); - *result = r; + int64_t nanos = TimeToNanos(quantity); + MOZ_ASSERT(0 <= nanos && nanos < ToNanoseconds(TemporalUnit::Day)); + + auto r = RoundNumberToIncrement(nanos, ToNanoseconds(unit), increment, + roundingMode); + MOZ_ASSERT(r == Int128{int32_t(r)}, + "can't overflow when inputs are all in range"); + + *result = int32_t(r); // Step 10. if (unit == TemporalUnit::Day) { @@ -1181,471 +861,31 @@ RoundedTime js::temporal::RoundTime(const PlainTime& time, Increment increment, } /** - * RoundTime ( hour, minute, second, millisecond, microsecond, nanosecond, - * increment, unit, roundingMode [ , dayLengthNs ] ) + * AddTime ( hour, minute, second, millisecond, microsecond, nanosecond, norm ) */ -RoundedTime js::temporal::RoundTime(const PlainTime& time, Increment increment, - TemporalUnit unit, - TemporalRoundingMode roundingMode, - const InstantSpan& dayLengthNs) { +AddedTime js::temporal::AddTime(const PlainTime& time, + const NormalizedTimeDuration& duration) { MOZ_ASSERT(IsValidTime(time)); - MOZ_ASSERT(IsValidInstantSpan(dayLengthNs)); - MOZ_ASSERT(dayLengthNs > (InstantSpan{})); + MOZ_ASSERT(IsValidNormalizedTimeDuration(duration)); - if (unit != TemporalUnit::Day) { - return RoundTime(time, increment, unit, roundingMode); + auto [seconds, nanoseconds] = duration; + if (seconds < 0 && nanoseconds > 0) { + seconds += 1; + nanoseconds -= 1'000'000'000; } - - // Step 1. (Not applicable) - - // Step 2. - int64_t quantity = TimeToNanos(time); - MOZ_ASSERT(quantity < ToNanoseconds(TemporalUnit::Day)); - - // Steps 3-8. (Not applicable) - - // Step 9. - int64_t divisor; - if (auto checkedDiv = dayLengthNs.toNanoseconds(); checkedDiv.isValid()) { - divisor = checkedDiv.value(); - } else { - // When the divisor is too large, the expression `quantity / divisor` is a - // value near zero. Substitute |divisor| with an equivalent expression. - // Choose |86'400'000'000'000| which will give a similar result because - // |quantity| is guaranteed to be lower than |86'400'000'000'000|. - divisor = ToNanoseconds(TemporalUnit::Day); - } - MOZ_ASSERT(divisor > 0); - - int64_t result = - ::RoundNumberToIncrement(quantity, divisor, increment, roundingMode); - - // Step 10. - return {result, {0, 0, 0, 0, 0, 0}}; -} - -/** - * AddTime ( hour, minute, second, millisecond, microsecond, nanosecond, hours, - * minutes, seconds, milliseconds, microseconds, nanoseconds ) - */ -static PlainTime AddTime(const PlainTime& time, const Duration& duration) { - MOZ_ASSERT(IsValidTime(time)); - MOZ_ASSERT(IsValidDuration(duration)); - - // Balance the duration so we don't have to worry about imprecise Number - // computations below. - - // Use either int64_t or int32_t below. Assert the total combined amount of - // the units can be expressed in either int64_t or int32_t. - static_assert(1 * UnitsPerDay(TemporalUnit::Nanosecond) > INT32_MAX, - "total combined nanoseconds per day"); - static_assert(2 * UnitsPerDay(TemporalUnit::Microsecond) > INT32_MAX, - "total combined microseconds per day"); - static_assert(3 * UnitsPerDay(TemporalUnit::Millisecond) <= INT32_MAX, - "total combined milliseconds per day"); - static_assert(4 * UnitsPerDay(TemporalUnit::Second) <= INT32_MAX, - "total combined seconds per day"); - static_assert(5 * UnitsPerDay(TemporalUnit::Minute) <= INT32_MAX, - "total combined minutes per day"); - static_assert(6 * UnitsPerDay(TemporalUnit::Hour) <= INT32_MAX, - "total combined hours per day"); - - // We ignore the days overflow in this function, therefore it's possible - // to restrict each unit to units-per-day. - int64_t nanoseconds = int64_t( - std::fmod(duration.nanoseconds, UnitsPerDay(TemporalUnit::Nanosecond))); - int64_t microseconds = int64_t( - std::fmod(duration.microseconds, UnitsPerDay(TemporalUnit::Microsecond))); - int32_t milliseconds = int32_t( - std::fmod(duration.milliseconds, UnitsPerDay(TemporalUnit::Millisecond))); - int32_t seconds = - int32_t(std::fmod(duration.seconds, UnitsPerDay(TemporalUnit::Second))); - int32_t minutes = - int32_t(std::fmod(duration.minutes, UnitsPerDay(TemporalUnit::Minute))); - int32_t hours = - int32_t(std::fmod(duration.hours, UnitsPerDay(TemporalUnit::Hour))); - - // Each unit is now less than the units-per-day. - MOZ_ASSERT(std::abs(nanoseconds) < UnitsPerDay(TemporalUnit::Nanosecond)); - MOZ_ASSERT(std::abs(microseconds) < UnitsPerDay(TemporalUnit::Microsecond)); - MOZ_ASSERT(std::abs(milliseconds) < UnitsPerDay(TemporalUnit::Millisecond)); - MOZ_ASSERT(std::abs(seconds) < UnitsPerDay(TemporalUnit::Second)); - MOZ_ASSERT(std::abs(minutes) < UnitsPerDay(TemporalUnit::Minute)); - MOZ_ASSERT(std::abs(hours) < UnitsPerDay(TemporalUnit::Hour)); - - microseconds += nanoseconds / 1000; - nanoseconds %= 1000; - MOZ_ASSERT(microseconds < 2 * UnitsPerDay(TemporalUnit::Microsecond)); - - milliseconds += microseconds / 1000; - microseconds %= 1000; - MOZ_ASSERT(milliseconds < 3 * UnitsPerDay(TemporalUnit::Millisecond)); - - seconds += milliseconds / 1000; - milliseconds %= 1000; - MOZ_ASSERT(seconds < 4 * UnitsPerDay(TemporalUnit::Second)); - - minutes += seconds / 60; - seconds %= 60; - MOZ_ASSERT(minutes < 5 * UnitsPerDay(TemporalUnit::Minute)); - - hours += minutes / 60; - minutes %= 60; - MOZ_ASSERT(hours < 6 * UnitsPerDay(TemporalUnit::Hour)); - - hours %= 24; - - MOZ_ASSERT(std::abs(hours) <= 23); - MOZ_ASSERT(std::abs(minutes) <= 59); - MOZ_ASSERT(std::abs(seconds) <= 59); - MOZ_ASSERT(std::abs(milliseconds) <= 999); - MOZ_ASSERT(std::abs(microseconds) <= 999); - MOZ_ASSERT(std::abs(nanoseconds) <= 999); + MOZ_ASSERT(std::abs(nanoseconds) <= 999'999'999); // Step 1. - int32_t hour = time.hour + hours; + int64_t second = time.second + seconds; // Step 2. - int32_t minute = time.minute + minutes; + int32_t nanosecond = time.nanosecond + nanoseconds; // Step 3. - int32_t second = time.second + seconds; - - // Step 4. - int32_t millisecond = time.millisecond + milliseconds; - - // Step 5. - int32_t microsecond = time.microsecond + int32_t(microseconds); - - // Step 6. - int32_t nanosecond = time.nanosecond + int32_t(nanoseconds); - - // Step 7. auto balanced = - ::BalanceTime(hour, minute, second, millisecond, microsecond, nanosecond); - return balanced.time; -} - -static BigInt* FloorDiv(JSContext* cx, Handle<BigInt*> dividend, - int32_t divisor) { - MOZ_ASSERT(divisor > 0); - - Rooted<BigInt*> div(cx, BigInt::createFromInt64(cx, divisor)); - if (!div) { - return nullptr; - } - - Rooted<BigInt*> quotient(cx); - Rooted<BigInt*> remainder(cx); - if (!BigInt::divmod(cx, dividend, div, "ient, &remainder)) { - return nullptr; - } - if (remainder->isNegative()) { - return BigInt::dec(cx, quotient); - } - return quotient; -} - -static bool AddTimeDaysSlow(JSContext* cx, const PlainTime& time, - const Duration& duration, double* result) { - MOZ_ASSERT(IsValidTime(time)); - MOZ_ASSERT(IsValidDuration(duration)); - - Rooted<BigInt*> days(cx, BigInt::createFromDouble(cx, duration.days)); - if (!days) { - return false; - } - - Rooted<BigInt*> hours(cx, BigInt::createFromDouble(cx, duration.hours)); - if (!hours) { - return false; - } - - Rooted<BigInt*> minutes(cx, BigInt::createFromDouble(cx, duration.minutes)); - if (!minutes) { - return false; - } - - Rooted<BigInt*> seconds(cx, BigInt::createFromDouble(cx, duration.seconds)); - if (!seconds) { - return false; - } - - Rooted<BigInt*> milliseconds( - cx, BigInt::createFromDouble(cx, duration.milliseconds)); - if (!milliseconds) { - return false; - } - - Rooted<BigInt*> microseconds( - cx, BigInt::createFromDouble(cx, duration.microseconds)); - if (!microseconds) { - return false; - } - - Rooted<BigInt*> nanoseconds( - cx, BigInt::createFromDouble(cx, duration.nanoseconds)); - if (!nanoseconds) { - return false; - } - - auto addWithInt32 = [cx](Handle<BigInt*> left, int32_t right) -> BigInt* { - Rooted<BigInt*> rightBigInt(cx, BigInt::createFromInt64(cx, right)); - if (!rightBigInt) { - return nullptr; - } - return BigInt::add(cx, left, rightBigInt); - }; - - // Step 1. - Rooted<BigInt*> hour(cx, addWithInt32(hours, time.hour)); - if (!hour) { - return false; - } - - // Step 2. - Rooted<BigInt*> minute(cx, addWithInt32(minutes, time.minute)); - if (!minute) { - return false; - } - - // Step 3. - Rooted<BigInt*> second(cx, addWithInt32(seconds, time.second)); - if (!second) { - return false; - } - - // Step 4. - Rooted<BigInt*> millisecond(cx, addWithInt32(milliseconds, time.millisecond)); - if (!millisecond) { - return false; - } - - // Step 5. - Rooted<BigInt*> microsecond(cx, addWithInt32(microseconds, time.microsecond)); - if (!microsecond) { - return false; - } - - // Step 6. - Rooted<BigInt*> nanosecond(cx, addWithInt32(nanoseconds, time.nanosecond)); - if (!nanosecond) { - return false; - } - - // Step 7. (Inlined BalanceTime) - - auto addFloorDiv = [cx](Handle<BigInt*> left, Handle<BigInt*> right, - int32_t divisor) -> BigInt* { - Rooted<BigInt*> quotient(cx, FloorDiv(cx, right, divisor)); - if (!quotient) { - return nullptr; - } - return BigInt::add(cx, left, quotient); - }; - - // BalanceTime, steps 1-2. - microsecond = addFloorDiv(microsecond, nanosecond, 1000); - if (!microsecond) { - return false; - } - - // BalanceTime, steps 3-4. - millisecond = addFloorDiv(millisecond, microsecond, 1000); - if (!millisecond) { - return false; - } - - // BalanceTime, steps 5-6. - second = addFloorDiv(second, millisecond, 1000); - if (!second) { - return false; - } - - // BalanceTime, steps 7-8. - minute = addFloorDiv(minute, second, 60); - if (!minute) { - return false; - } - - // BalanceTime, steps 9-10. - hour = addFloorDiv(hour, minute, 60); - if (!hour) { - return false; - } - - // BalanceTime, steps 11-13. - days = addFloorDiv(days, hour, 24); - if (!days) { - return false; - } - - // The days number is used as the input for a duration. Throw if the BigInt - // when converted to a Number can't be represented in a duration. - double daysNumber = BigInt::numberValue(days); - if (!ThrowIfInvalidDuration(cx, {0, 0, 0, daysNumber})) { - return false; - } - MOZ_ASSERT(IsInteger(daysNumber)); - - *result = daysNumber; - return true; -} - -static mozilla::Maybe<int64_t> AddTimeDays(const PlainTime& time, - const Duration& duration) { - MOZ_ASSERT(IsValidTime(time)); - MOZ_ASSERT(IsValidDuration(duration)); - - int64_t days; - if (!mozilla::NumberEqualsInt64(duration.days, &days)) { - return mozilla::Nothing(); - } - - int64_t hours; - if (!mozilla::NumberEqualsInt64(duration.hours, &hours)) { - return mozilla::Nothing(); - } - - int64_t minutes; - if (!mozilla::NumberEqualsInt64(duration.minutes, &minutes)) { - return mozilla::Nothing(); - } - - int64_t seconds; - if (!mozilla::NumberEqualsInt64(duration.seconds, &seconds)) { - return mozilla::Nothing(); - } - - int64_t milliseconds; - if (!mozilla::NumberEqualsInt64(duration.milliseconds, &milliseconds)) { - return mozilla::Nothing(); - } - - int64_t microseconds; - if (!mozilla::NumberEqualsInt64(duration.microseconds, µseconds)) { - return mozilla::Nothing(); - } - - int64_t nanoseconds; - if (!mozilla::NumberEqualsInt64(duration.nanoseconds, &nanoseconds)) { - return mozilla::Nothing(); - } - - // Step 1. - auto hour = mozilla::CheckedInt64(time.hour) + hours; - if (!hour.isValid()) { - return mozilla::Nothing(); - } - - // Step 2. - auto minute = mozilla::CheckedInt64(time.minute) + minutes; - if (!minute.isValid()) { - return mozilla::Nothing(); - } - - // Step 3. - auto second = mozilla::CheckedInt64(time.second) + seconds; - if (!second.isValid()) { - return mozilla::Nothing(); - } - - // Step 4. - auto millisecond = mozilla::CheckedInt64(time.millisecond) + milliseconds; - if (!millisecond.isValid()) { - return mozilla::Nothing(); - } - - // Step 5. - auto microsecond = mozilla::CheckedInt64(time.microsecond) + microseconds; - if (!microsecond.isValid()) { - return mozilla::Nothing(); - } - - // Step 6. - auto nanosecond = mozilla::CheckedInt64(time.nanosecond) + nanoseconds; - if (!nanosecond.isValid()) { - return mozilla::Nothing(); - } - - // Step 7. (Inlined BalanceTime) - - // BalanceTime, steps 1-2. - microsecond += FloorDiv(nanosecond.value(), 1000); - if (!microsecond.isValid()) { - return mozilla::Nothing(); - } - - // BalanceTime, steps 3-4. - millisecond += FloorDiv(microsecond.value(), 1000); - if (!millisecond.isValid()) { - return mozilla::Nothing(); - } - - // BalanceTime, steps 5-6. - second += FloorDiv(millisecond.value(), 1000); - if (!second.isValid()) { - return mozilla::Nothing(); - } - - // BalanceTime, steps 7-8. - minute += FloorDiv(second.value(), 60); - if (!minute.isValid()) { - return mozilla::Nothing(); - } - - // BalanceTime, steps 9-10. - hour += FloorDiv(minute.value(), 60); - if (!hour.isValid()) { - return mozilla::Nothing(); - } - - // BalanceTime, steps 11-13. - auto result = mozilla::CheckedInt64(days) + FloorDiv(hour.value(), 24); - if (!result.isValid()) { - return mozilla::Nothing(); - } - return mozilla::Some(result.value()); -} - -static bool AddTimeDays(JSContext* cx, const PlainTime& time, - const Duration& duration, double* result) { - // Fast-path when we can perform the whole computation with int64 values. - if (auto days = AddTimeDays(time, duration)) { - *result = *days; - return true; - } - return AddTimeDaysSlow(cx, time, duration, result); -} - -/** - * AddTime ( hour, minute, second, millisecond, microsecond, nanosecond, hours, - * minutes, seconds, milliseconds, microseconds, nanoseconds ) - */ -bool js::temporal::AddTime(JSContext* cx, const PlainTime& time, - const Duration& duration, PlainTime* result, - double* daysResult) { - MOZ_ASSERT(IsValidTime(time)); - MOZ_ASSERT(IsValidDuration(duration)); - - // Steps 1-7. - auto balanced = ::AddTime(time, duration); - - // Compute |days| separately to ensure no loss of precision occurs. - // - // The single caller of this |AddTime| function also needs to compute the - // addition of |duration.days| and the balanced days. Perform this addition - // here, so we don't need to pass around BigInt values for exact mathematical - // results. - double days; - if (!AddTimeDays(cx, time, duration, &days)) { - return false; - } - MOZ_ASSERT(IsInteger(days)); - - *result = balanced; - *daysResult = days; - return true; + ::BalanceTime<int64_t>(time.hour, time.minute, second, time.millisecond, + time.microsecond, nanosecond); + return {balanced.days, balanced.time}; } /** @@ -1699,30 +939,28 @@ static bool DifferenceTemporalPlainTime(JSContext* cx, // Step 5. auto diff = DifferenceTime(temporalTime, other); - MOZ_ASSERT(diff.days == 0); // Step 6. - auto roundedDuration = diff.toDuration(); if (settings.smallestUnit != TemporalUnit::Nanosecond || settings.roundingIncrement != Increment{1}) { // Steps 6.a-b. - if (!RoundDuration(cx, roundedDuration.time(), settings.roundingIncrement, - settings.smallestUnit, settings.roundingMode, - &roundedDuration)) { - return false; - } + diff = RoundDuration(diff, settings.roundingIncrement, + settings.smallestUnit, settings.roundingMode); } // Step 7. - auto balancedDuration = - BalanceTimeDuration(roundedDuration, settings.largestUnit); + TimeDuration balancedDuration; + if (!BalanceTimeDuration(cx, diff, settings.largestUnit, &balancedDuration)) { + return false; + } // Step 8. + auto duration = balancedDuration.toDuration(); if (operation == TemporalDifference::Since) { - balancedDuration = balancedDuration.negate(); + duration = duration.negate(); } - auto* result = CreateTemporalDuration(cx, balancedDuration); + auto* result = CreateTemporalDuration(cx, duration); if (!result) { return false; } @@ -1754,13 +992,14 @@ static bool AddDurationToOrSubtractDurationFromPlainTime( if (operation == PlainTimeDuration::Subtract) { duration = duration.negate(); } - auto result = AddTime(time, duration); + auto timeDuration = NormalizeTimeDuration(duration); // Step 4. - MOZ_ASSERT(IsValidTime(result)); + auto result = AddTime(time, timeDuration); + MOZ_ASSERT(IsValidTime(result.time)); // Step 5. - auto* obj = CreateTemporalTime(cx, result); + auto* obj = CreateTemporalTime(cx, result.time); if (!obj) { return false; } @@ -1864,7 +1103,7 @@ static bool PlainTime_from(JSContext* cx, unsigned argc, Value* vp) { } // Steps 4-5. - auto result = ToTemporalTime(cx, args.get(0), overflow); + auto* result = ToTemporalTime(cx, args.get(0), overflow); if (!result) { return false; } @@ -2059,29 +1298,27 @@ static bool PlainTime_with(JSContext* cx, const CallArgs& args) { if (!temporalTimeLike) { return false; } - - // Step 4. - if (!RejectTemporalLikeObject(cx, temporalTimeLike)) { + if (!ThrowIfTemporalLikeObject(cx, temporalTimeLike)) { return false; } auto overflow = TemporalOverflow::Constrain; if (args.hasDefined(1)) { - // Step 5. + // Step 4. Rooted<JSObject*> options(cx, RequireObjectArg(cx, "options", "with", args[1])); if (!options) { return false; } - // Step 6. + // Step 5. if (!ToTemporalOverflow(cx, options, &overflow)) { return false; } } - // Steps 7-19. - TimeRecord partialTime = { + // Steps 6-18. + TemporalTimeLike partialTime = { double(time.hour), double(time.minute), double(time.second), double(time.millisecond), double(time.microsecond), double(time.nanosecond), @@ -2090,13 +1327,13 @@ static bool PlainTime_with(JSContext* cx, const CallArgs& args) { return false; } - // Step 20. + // Step 19. PlainTime result; if (!RegulateTime(cx, partialTime, overflow, &result)) { return false; } - // Step 21. + // Step 20. auto* obj = CreateTemporalTime(cx, result); if (!obj) { return false; |