[Temporal] Avoid double overflow in AddDuration

Add a version of BalanceDuration which take two TimeDurationRecord
and add them internally after converting to BigInt as nanoseconds so it will not overflow the double.

Use "std::isinf()" instead of "!std::isfinite()"

Inspired by https://github.com/tc39/proposal-temporal/issues/2380#issuecomment-1219194995

Bug: v8:11544
Change-Id: I29e06fa857ff43f2668e1e4ffd07735ff6efee42
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3837852
Commit-Queue: Frank Tang <ftang@chromium.org>
Reviewed-by: Adam Klein <adamk@chromium.org>
Cr-Commit-Position: refs/heads/main@{#82576}
This commit is contained in:
Frank Tang 2022-08-18 14:17:32 -07:00 committed by V8 LUCI CQ
parent ff8d67c884
commit a8cb3cef03
2 changed files with 42 additions and 31 deletions

View File

@ -310,6 +310,11 @@ V8_WARN_UNUSED_RESULT Maybe<TimeDurationRecord> BalanceDuration(
V8_WARN_UNUSED_RESULT Maybe<TimeDurationRecord> BalanceDuration(
Isolate* isolate, Unit largest_unit, Handle<BigInt> nanoseconds,
const char* method_name);
// A special version of BalanceDuration which add two TimeDurationRecord
// internally as BigInt to avoid overflow double.
V8_WARN_UNUSED_RESULT Maybe<TimeDurationRecord> BalanceDuration(
Isolate* isolate, Unit largest_unit, const TimeDurationRecord& dur1,
const TimeDurationRecord& dur2, const char* method_name);
// sec-temporal-balancepossiblyinfiniteduration
enum BalanceOverflow {
@ -5104,6 +5109,18 @@ Maybe<TimeDurationRecord> BalanceDuration(Isolate* isolate, Unit largest_unit,
}
}
Maybe<TimeDurationRecord> BalanceDuration(Isolate* isolate, Unit largest_unit,
const TimeDurationRecord& dur1,
const TimeDurationRecord& dur2,
const char* method_name) {
// Add the two TimeDurationRecord as BigInt in nanoseconds.
Handle<BigInt> nanoseconds =
BigInt::Add(isolate, TotalDurationNanoseconds(isolate, dur1, 0),
TotalDurationNanoseconds(isolate, dur2, 0))
.ToHandleChecked();
return BalanceDuration(isolate, largest_unit, nanoseconds, method_name);
}
// #sec-temporal-balanceduration
Maybe<TimeDurationRecord> BalanceDuration(Isolate* isolate, Unit largest_unit,
Handle<Object> relative_to_obj,
@ -5347,9 +5364,10 @@ Maybe<BalancePossiblyInfiniteDurationResult> BalancePossiblyInfiniteDuration(
double milliseconds_value = BigInt::ToNumber(isolate, milliseconds)->Number();
double microseconds_value = BigInt::ToNumber(isolate, microseconds)->Number();
double nanoseconds_value = BigInt::ToNumber(isolate, nanoseconds)->Number();
if (!std::isfinite(hours_value) || !std::isfinite(minutes_value) ||
!std::isfinite(seconds_value) || !std::isfinite(milliseconds_value) ||
!std::isfinite(microseconds_value) || !std::isfinite(nanoseconds_value)) {
if (std::isinf(days) || std::isinf(hours_value) ||
std::isinf(minutes_value) || std::isinf(seconds_value) ||
std::isinf(milliseconds_value) || std::isinf(microseconds_value) ||
std::isinf(nanoseconds_value)) {
return Just(BalancePossiblyInfiniteDurationResult(
{{0, 0, 0, 0, 0, 0, 0},
sign == 1 ? BalanceOverflow::kPositive : BalanceOverflow::kNegative}));
@ -8415,20 +8433,16 @@ Maybe<DurationRecord> AddDuration(Isolate* isolate, const DurationRecord& dur1,
NEW_TEMPORAL_INVALID_ARG_RANGE_ERROR(),
Nothing<DurationRecord>());
}
// b. Let result be ! BalanceDuration(d1 + d2, h1 + h2, min1 + min2, s1 +
// b. Let result be ? BalanceDuration(d1 + d2, h1 + h2, min1 + min2, s1 +
// s2, ms1 + ms2, mus1 + mus2, ns1 + ns2, largestUnit).
result.time_duration =
BalanceDuration(
isolate, largest_unit,
{dur1.time_duration.days + dur2.time_duration.days,
dur1.time_duration.hours + dur2.time_duration.hours,
dur1.time_duration.minutes + dur2.time_duration.minutes,
dur1.time_duration.seconds + dur2.time_duration.seconds,
dur1.time_duration.milliseconds + dur2.time_duration.milliseconds,
dur1.time_duration.microseconds + dur2.time_duration.microseconds,
dur1.time_duration.nanoseconds + dur2.time_duration.nanoseconds},
method_name)
.ToChecked();
// Note: We call a special version of BalanceDuration which add two duration
// internally to avoid overflow the double.
MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, result.time_duration,
BalanceDuration(isolate, largest_unit, dur1.time_duration,
dur2.time_duration, method_name),
Nothing<DurationRecord>());
// c. Return ! CreateDurationRecord(0, 0, 0, result.[[Days]],
// result.[[Hours]], result.[[Minutes]], result.[[Seconds]],
// result.[[Milliseconds]], result.[[Microseconds]],
@ -8504,20 +8518,19 @@ Maybe<DurationRecord> AddDuration(Isolate* isolate, const DurationRecord& dur1,
CalendarDateUntil(isolate, calendar, relative_to, end,
difference_options),
Nothing<DurationRecord>());
// n. Let result be ! BalanceDuration(dateDifference.[[Days]], h1 + h2, min1
// n. Let result be ? BalanceDuration(dateDifference.[[Days]], h1 + h2, min1
// + min2, s1 + s2, ms1 + ms2, mus1 + mus2, ns1 + ns2, largestUnit).
result.time_duration =
BalanceDuration(
isolate, largest_unit,
{date_difference->days().Number(),
dur1.time_duration.hours + dur2.time_duration.hours,
dur1.time_duration.minutes + dur2.time_duration.minutes,
dur1.time_duration.seconds + dur2.time_duration.seconds,
dur1.time_duration.milliseconds + dur2.time_duration.milliseconds,
dur1.time_duration.microseconds + dur2.time_duration.microseconds,
dur1.time_duration.nanoseconds + dur2.time_duration.nanoseconds},
method_name)
.ToChecked();
// Note: We call a special version of BalanceDuration which add two duration
// internally to avoid overflow the double.
TimeDurationRecord time_dur1 = dur1.time_duration;
time_dur1.days = date_difference->days().Number();
TimeDurationRecord time_dur2 = dur2.time_duration;
time_dur2.days = 0;
MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, result.time_duration,
BalanceDuration(isolate, largest_unit, time_dur1, time_dur2,
method_name),
Nothing<DurationRecord>());
// l. Return ! CreateDurationRecord(dateDifference.[[Years]],
// dateDifference.[[Months]], dateDifference.[[Weeks]], result.[[Days]],
// result.[[Hours]], result.[[Minutes]], result.[[Seconds]],

View File

@ -703,11 +703,9 @@
'intl402/Temporal/Calendar/prototype/era/argument-calendar-datefromfields-called-with-null-prototype-fields': [FAIL],
'intl402/Temporal/Calendar/prototype/eraYear/argument-calendar-datefromfields-called-with-null-prototype-fields': [FAIL],
'built-ins/Temporal/PlainTime/prototype/equals/argument-string-no-implicit-midnight': [FAIL],
'built-ins/Temporal/Duration/prototype/add/days-is-number-max-value': [FAIL],
'built-ins/Temporal/Duration/prototype/add/nanoseconds-is-number-max-value-1': [FAIL],
'built-ins/Temporal/Duration/prototype/round/throws-in-balance-duration-when-sign-mismatched-with-zoned-date-time': [FAIL],
'built-ins/Temporal/Duration/prototype/round/total-duration-nanoseconds-too-large-with-zoned-datetime': [PASS, FAIL],
'built-ins/Temporal/Duration/prototype/subtract/days-is-number-max-value': [FAIL],
'built-ins/Temporal/Duration/prototype/subtract/nanoseconds-is-number-max-value-1': [FAIL],
'built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days-different-sign': [FAIL],
'built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days': [FAIL],