From 1be337353b35f94aef39e78a2ba106b1501d7d7d Mon Sep 17 00:00:00 2001 From: davidot Date: Thu, 13 Oct 2022 23:39:37 +0200 Subject: [PATCH] LibJS: Make sure to exactly add nanoseconds in add_duration If the given nanoseconds values add to something above 2**53 - 1 we are potentially rounding the double to some near integer value. --- .../LibJS/Runtime/Temporal/Duration.cpp | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/Duration.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/Duration.cpp index e1b89a8a7f..1a3e8df37d 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/Duration.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/Duration.cpp @@ -988,10 +988,15 @@ ThrowCompletionOr add_duration(VM& vm, double years1, double mon } // b. Let result be ? BalanceDuration(d1 + d2, h1 + h2, min1 + min2, s1 + s2, ms1 + ms2, mus1 + mus2, ns1 + ns2, largestUnit). - VERIFY(trunc(nanoseconds1 + nanoseconds2) == nanoseconds1 + nanoseconds2); - if (!isfinite(nanoseconds1 + nanoseconds2)) - return vm.throw_completion(ErrorType::TemporalInvalidDuration); - auto result = TRY(balance_duration(vm, days1 + days2, hours1 + hours2, minutes1 + minutes2, seconds1 + seconds2, milliseconds1 + milliseconds2, microseconds1 + microseconds2, Crypto::SignedBigInteger { nanoseconds1 + nanoseconds2 }, largest_unit)); + // NOTE: Nanoseconds is the only one that can overflow the safe integer range of a double + // so we have to check for that case. + Crypto::SignedBigInteger sum_of_nano_seconds; + if (fabs(nanoseconds1 + nanoseconds2) >= MAX_ARRAY_LIKE_INDEX) + sum_of_nano_seconds = Crypto::SignedBigInteger { nanoseconds1 }.plus(Crypto::SignedBigInteger { nanoseconds2 }); + else + sum_of_nano_seconds = Crypto::SignedBigInteger { nanoseconds1 + nanoseconds2 }; + + auto result = TRY(balance_duration(vm, days1 + days2, hours1 + hours2, minutes1 + minutes2, seconds1 + seconds2, milliseconds1 + milliseconds2, microseconds1 + microseconds2, sum_of_nano_seconds, largest_unit)); // c. Return ! CreateDurationRecord(0, 0, 0, result.[[Days]], result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]). return MUST(create_duration_record(vm, 0, 0, 0, result.days, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds)); @@ -1032,10 +1037,15 @@ ThrowCompletionOr add_duration(VM& vm, double years1, double mon auto* date_difference = TRY(calendar_date_until(vm, calendar, &relative_to, end, *difference_options)); // k. Let result be ? BalanceDuration(dateDifference.[[Days]], h1 + h2, min1 + min2, s1 + s2, ms1 + ms2, mus1 + mus2, ns1 + ns2, largestUnit). - VERIFY(trunc(nanoseconds1 + nanoseconds2) == nanoseconds1 + nanoseconds2); - if (!isfinite(nanoseconds1 + nanoseconds2)) - return vm.throw_completion(ErrorType::TemporalInvalidDuration); - auto result = TRY(balance_duration(vm, date_difference->days(), hours1 + hours2, minutes1 + minutes2, seconds1 + seconds2, milliseconds1 + milliseconds2, microseconds1 + microseconds2, Crypto::SignedBigInteger { nanoseconds1 + nanoseconds2 }, largest_unit)); + // NOTE: Nanoseconds is the only one that can overflow the safe integer range of a double + // so we have to check for that case. + Crypto::SignedBigInteger sum_of_nano_seconds; + if (fabs(nanoseconds1 + nanoseconds2) >= MAX_ARRAY_LIKE_INDEX) + sum_of_nano_seconds = Crypto::SignedBigInteger { nanoseconds1 }.plus(Crypto::SignedBigInteger { nanoseconds2 }); + else + sum_of_nano_seconds = Crypto::SignedBigInteger { nanoseconds1 + nanoseconds2 }; + + auto result = TRY(balance_duration(vm, date_difference->days(), hours1 + hours2, minutes1 + minutes2, seconds1 + seconds2, milliseconds1 + milliseconds2, microseconds1 + microseconds2, sum_of_nano_seconds, largest_unit)); // l. Return ? CreateDurationRecord(dateDifference.[[Years]], dateDifference.[[Months]], dateDifference.[[Weeks]], result.[[Days]], result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]). return MUST(create_duration_record(vm, date_difference->years(), date_difference->months(), date_difference->weeks(), result.days, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds));