Bug 1874684 - Part 10: Replace BigInt with Int128 in RoundNumberToIncrement. r=mgaudet

Use `Int128` instead of `BigInt` in RoundNumberToIncrement to avoid duplicated
code for the `BigInt` code path.

Differential Revision: https://phabricator.services.mozilla.com/D198544
This commit is contained in:
André Bargull 2024-04-15 18:27:20 +00:00
Родитель d32aed0104
Коммит 5636d9f394
7 изменённых файлов: 278 добавлений и 953 удалений

Просмотреть файл

@ -3045,17 +3045,21 @@ static bool CreateCalendarMethodsRecordFromRelativeTo(
return true;
}
struct RoundedNumber final {
Int128 rounded;
double total;
};
/**
* RoundNumberToIncrement ( x, increment, roundingMode )
*/
static void TruncateNumber(int64_t numerator, int64_t denominator,
double* quotient, double* total) {
static RoundedNumber TruncateNumber(int64_t numerator, int64_t denominator) {
// Computes the quotient and real number value of the rational number
// |numerator / denominator|.
// Int64 division truncates.
int64_t q = numerator / denominator;
int64_t r = numerator % denominator;
int64_t quot = numerator / denominator;
int64_t rem = numerator % denominator;
// The total value is stored as a mathematical number in the draft proposal,
// so we can't convert it to a double without loss of precision. We use two
@ -3074,49 +3078,29 @@ static void TruncateNumber(int64_t numerator, int64_t denominator,
//
// The best possible approximation is |4000.0000000000004547...𝔽|, which can
// be computed through |q + r / denominator|.
double total;
if (::IsSafeInteger(numerator) && ::IsSafeInteger(denominator)) {
*quotient = double(q);
*total = double(numerator) / double(denominator);
total = double(numerator) / double(denominator);
} else {
*quotient = double(q);
*total = double(q) + double(r) / double(denominator);
total = double(quot) + double(rem) / double(denominator);
}
return {Int128{quot}, total};
}
/**
* RoundNumberToIncrement ( x, increment, roundingMode )
*/
static bool TruncateNumber(JSContext* cx, Handle<BigInt*> numerator,
Handle<BigInt*> denominator, double* quotient,
double* total) {
MOZ_ASSERT(!denominator->isNegative());
MOZ_ASSERT(!denominator->isZero());
static RoundedNumber TruncateNumber(const Int128& numerator,
const Int128& denominator) {
MOZ_ASSERT(denominator > Int128{});
MOZ_ASSERT(numerator > Int128{INT64_MAX} || denominator > Int128{INT64_MAX},
"small values use the int64 overload");
// Dividing zero is always zero.
if (numerator->isZero()) {
*quotient = 0;
*total = 0;
return true;
}
// Int128 division truncates.
auto [quot, rem] = numerator.divrem(denominator);
int64_t num, denom;
if (BigInt::isInt64(numerator, &num) &&
BigInt::isInt64(denominator, &denom)) {
TruncateNumber(num, denom, quotient, total);
return true;
}
// BigInt division truncates.
Rooted<BigInt*> quot(cx);
Rooted<BigInt*> rem(cx);
if (!BigInt::divmod(cx, numerator, denominator, &quot, &rem)) {
return false;
}
double q = BigInt::numberValue(quot);
*quotient = q;
*total = q + BigInt::numberValue(rem) / BigInt::numberValue(denominator);
return true;
double total = double(quot) + double(rem) / double(denominator);
return {quot, total};
}
struct RoundedDuration final {
@ -3264,139 +3248,31 @@ static bool DaysIsNegative(int64_t days,
return totalDays < 0 || (totalDays == 0 && timeAndDays.time < 0);
}
struct RoundedNumber {
double rounded;
double total;
};
static RoundedNumber RoundNumberToIncrement(
int64_t durationAmount, int64_t amountPassed, int64_t durationDays,
int32_t daysToAdd, const NormalizedTimeAndDays& timeAndDays,
int32_t oneUnitDays, Increment increment, TemporalRoundingMode roundingMode,
ComputeRemainder computeRemainder) {
#ifdef DEBUG
// Valid duration days are smaller than ⌈(2**53) / (24 * 60 * 60)⌉.
static constexpr int64_t maxDurationDays =
(int64_t(1) << 53) / (24 * 60 * 60);
static bool RoundNumberToIncrementSlow(JSContext* cx, double durationAmount,
double amountPassed, double durationDays,
int32_t daysToAdd,
const NormalizedTimeAndDays& timeAndDays,
int32_t oneUnitDays, Increment increment,
TemporalRoundingMode roundingMode,
ComputeRemainder computeRemainder,
RoundedNumber* result) {
// Numbers of days between nsMinInstant and nsMaxInstant.
static constexpr int32_t epochDays = 200'000'000;
#endif
MOZ_ASSERT(std::abs(durationAmount) < (int64_t(1) << 32));
MOZ_ASSERT(std::abs(amountPassed) < (int64_t(1) << 32));
MOZ_ASSERT(std::abs(durationDays) <= maxDurationDays);
MOZ_ASSERT(std::abs(daysToAdd) <= epochDays * 2);
MOZ_ASSERT(timeAndDays.dayLength > 0);
MOZ_ASSERT(timeAndDays.dayLength < (int64_t(1) << 53));
MOZ_ASSERT(std::abs(timeAndDays.time) < timeAndDays.dayLength);
MOZ_ASSERT(std::abs(timeAndDays.days) <= maxDurationDays);
MOZ_ASSERT(oneUnitDays != 0);
Rooted<BigInt*> biAmount(cx, BigInt::createFromDouble(cx, durationAmount));
if (!biAmount) {
return false;
}
Rooted<BigInt*> biAmountPassed(cx,
BigInt::createFromDouble(cx, amountPassed));
if (!biAmountPassed) {
return false;
}
biAmount = BigInt::add(cx, biAmount, biAmountPassed);
if (!biAmount) {
return false;
}
Rooted<BigInt*> days(cx, BigInt::createFromDouble(cx, durationDays));
if (!days) {
return false;
}
Rooted<BigInt*> nanoDays(cx, BigInt::createFromInt64(cx, timeAndDays.days));
if (!nanoDays) {
return false;
}
Rooted<BigInt*> biDaysToAdd(cx, BigInt::createFromInt64(cx, daysToAdd));
if (!biDaysToAdd) {
return false;
}
days = BigInt::add(cx, days, nanoDays);
if (!days) {
return false;
}
days = BigInt::add(cx, days, biDaysToAdd);
if (!days) {
return false;
}
Rooted<BigInt*> nanoseconds(cx,
BigInt::createFromInt64(cx, timeAndDays.time));
if (!nanoseconds) {
return false;
}
Rooted<BigInt*> dayLength(cx,
BigInt::createFromInt64(cx, timeAndDays.dayLength));
if (!dayLength) {
return false;
}
Rooted<BigInt*> denominator(
cx, BigInt::createFromInt64(cx, std::abs(oneUnitDays)));
if (!denominator) {
return false;
}
denominator = BigInt::mul(cx, denominator, dayLength);
if (!denominator) {
return false;
}
Rooted<BigInt*> totalNanoseconds(cx, BigInt::mul(cx, days, dayLength));
if (!totalNanoseconds) {
return false;
}
totalNanoseconds = BigInt::add(cx, totalNanoseconds, nanoseconds);
if (!totalNanoseconds) {
return false;
}
Rooted<BigInt*> amountNanos(cx, BigInt::mul(cx, biAmount, denominator));
if (!amountNanos) {
return false;
}
totalNanoseconds = BigInt::add(cx, totalNanoseconds, amountNanos);
if (!totalNanoseconds) {
return false;
}
double rounded;
double total = 0;
if (computeRemainder == ComputeRemainder::No) {
if (!temporal::RoundNumberToIncrement(cx, totalNanoseconds, denominator,
increment, roundingMode, &rounded)) {
return false;
}
} else {
if (!::TruncateNumber(cx, totalNanoseconds, denominator, &rounded,
&total)) {
return false;
}
}
*result = {rounded, total};
return true;
}
static bool RoundNumberToIncrement(JSContext* cx, double durationAmount,
double amountPassed, double durationDays,
int32_t daysToAdd,
const NormalizedTimeAndDays& timeAndDays,
int32_t oneUnitDays, Increment increment,
TemporalRoundingMode roundingMode,
ComputeRemainder computeRemainder,
RoundedNumber* result) {
MOZ_ASSERT(timeAndDays.dayLength > 0);
MOZ_ASSERT(std::abs(timeAndDays.time) < timeAndDays.dayLength);
MOZ_ASSERT(oneUnitDays != 0);
MOZ_ASSERT(std::abs(oneUnitDays) <= 200'000'000);
// TODO(anba): Rename variables.
MOZ_ASSERT(std::abs(oneUnitDays) <= epochDays);
MOZ_ASSERT(increment <= Increment::max());
// clang-format off
//
@ -3430,89 +3306,117 @@ static bool RoundNumberToIncrement(JSContext* cx, double durationAmount,
break;
}
int64_t intDays;
if (!mozilla::NumberEqualsInt64(durationDays, &intDays)) {
break;
}
auto totalDays = mozilla::CheckedInt64(intDays);
auto totalDays = mozilla::CheckedInt64(durationDays);
totalDays += timeAndDays.days;
totalDays += daysToAdd;
if (!totalDays.isValid()) {
break;
}
MOZ_ASSERT(totalDays.isValid());
auto totalNanoseconds = dayLength * totalDays;
if (!totalNanoseconds.isValid()) {
break;
}
totalNanoseconds += timeAndDays.time;
if (!totalNanoseconds.isValid()) {
break;
}
int64_t intAmount;
if (!mozilla::NumberEqualsInt64(durationAmount, &intAmount)) {
break;
}
int64_t intAmountPassed;
if (!mozilla::NumberEqualsInt64(amountPassed, &intAmountPassed)) {
break;
}
auto totalAmount = mozilla::CheckedInt64(intAmount) + intAmountPassed;
if (!totalAmount.isValid()) {
break;
}
auto totalAmount = mozilla::CheckedInt64(durationAmount) + amountPassed;
MOZ_ASSERT(totalAmount.isValid());
auto amountNanos = denominator * totalAmount;
if (!amountNanos.isValid()) {
break;
}
auto totalNanoseconds = dayLength * totalDays;
totalNanoseconds += timeAndDays.time;
totalNanoseconds += amountNanos;
if (!totalNanoseconds.isValid()) {
break;
}
double rounded;
double total = 0;
if (computeRemainder == ComputeRemainder::No) {
if (!temporal::RoundNumberToIncrement(cx, totalNanoseconds.value(),
denominator.value(), increment,
roundingMode, &rounded)) {
return false;
}
} else {
TruncateNumber(totalNanoseconds.value(), denominator.value(), &rounded,
&total);
if (computeRemainder == ComputeRemainder::Yes) {
return TruncateNumber(totalNanoseconds.value(), denominator.value());
}
*result = {rounded, total};
return true;
auto rounded = RoundNumberToIncrement(
totalNanoseconds.value(), denominator.value(), increment, roundingMode);
constexpr double total = 0;
return {rounded, total};
} while (false);
return RoundNumberToIncrementSlow(
cx, durationAmount, amountPassed, durationDays, daysToAdd, timeAndDays,
oneUnitDays, increment, roundingMode, computeRemainder, result);
// Use int128 when values are too large for int64. Additionally assert all
// values fit into int128.
// `dayLength` < 2**53
auto dayLength = Int128{timeAndDays.dayLength};
MOZ_ASSERT(dayLength < Int128{1} << 53);
// `abs(oneUnitDays)` < 200'000'000, log2(200'000'000) = ~27.57.
auto denominator = dayLength * Int128{std::abs(oneUnitDays)};
MOZ_ASSERT(denominator < Int128{1} << (53 + 28));
// log2(24*60*60) = ~16.4 and log2(2 * 200'000'000) = ~28.57.
//
// `abs(maxDurationDays)` ≤ 2**(53 - 16).
// `abs(timeAndDays.days)` ≤ 2**(53 - 16).
// `abs(daysToAdd)` ≤ 2**29.
//
// 2**(53 - 16) + 2**(53 - 16) + 2**29
// = 2**37 + 2**37 + 2**29
// = 2**38 + 2**29
// ≤ 2**39
auto totalDays = Int128{durationDays};
totalDays += Int128{timeAndDays.days};
totalDays += Int128{daysToAdd};
MOZ_ASSERT(totalDays.abs() <= Uint128{1} << 39);
// `abs(durationAmount)` ≤ 2**32
// `abs(amountPassed)` ≤ 2**32
auto totalAmount = Int128{durationAmount} + Int128{amountPassed};
MOZ_ASSERT(totalAmount.abs() <= Uint128{1} << 33);
// `denominator` < 2**(53 + 28)
// `abs(totalAmount)` <= 2**33
//
// `denominator * totalAmount`
// ≤ 2**(53 + 28) * 2**33
// = 2**(53 + 28 + 33)
// = 2**114
auto amountNanos = denominator * totalAmount;
MOZ_ASSERT(amountNanos.abs() <= Uint128{1} << 114);
// `dayLength` < 2**53
// `totalDays` ≤ 2**39
// `timeAndDays.time` < `dayLength` < 2**53
// `amountNanos` ≤ 2**114
//
// `dayLength * totalDays`
// ≤ 2**(53 + 39) = 2**92
//
// `dayLength * totalDays + timeAndDays.time`
// ≤ 2**93
//
// `dayLength * totalDays + timeAndDays.time + amountNanos`
// ≤ 2**115
auto totalNanoseconds = dayLength * totalDays;
totalNanoseconds += Int128{timeAndDays.time};
totalNanoseconds += amountNanos;
MOZ_ASSERT(totalNanoseconds.abs() <= Uint128{1} << 115);
if (computeRemainder == ComputeRemainder::Yes) {
return TruncateNumber(totalNanoseconds, denominator);
}
auto rounded = RoundNumberToIncrement(totalNanoseconds, denominator,
increment, roundingMode);
constexpr double total = 0;
return {rounded, total};
}
static bool RoundNumberToIncrement(JSContext* cx, double durationDays,
const NormalizedTimeAndDays& timeAndDays,
Increment increment,
TemporalRoundingMode roundingMode,
ComputeRemainder computeRemainder,
RoundedNumber* result) {
constexpr double daysAmount = 0;
constexpr double daysPassed = 0;
static RoundedNumber RoundNumberToIncrement(
double durationDays, const NormalizedTimeAndDays& timeAndDays,
Increment increment, TemporalRoundingMode roundingMode,
ComputeRemainder computeRemainder) {
constexpr int64_t daysAmount = 0;
constexpr int64_t daysPassed = 0;
constexpr int32_t oneDayDays = 1;
constexpr int32_t daysToAdd = 0;
return RoundNumberToIncrement(cx, daysAmount, daysPassed, durationDays,
daysToAdd, timeAndDays, oneDayDays, increment,
roundingMode, computeRemainder, result);
return RoundNumberToIncrement(daysAmount, daysPassed, durationDays, daysToAdd,
timeAndDays, oneDayDays, increment,
roundingMode, computeRemainder);
}
static bool RoundDurationYear(JSContext* cx, const NormalizedDuration& duration,
@ -3587,13 +3491,13 @@ static bool RoundDurationYear(JSContext* cx, const NormalizedDuration& duration,
}
// Step 10.m.
double yearsPassed = timePassed.years;
int64_t yearsPassed = int64_t(timePassed.years);
// Step 10.n.
// Our implementation keeps |years| and |yearsPassed| separate.
// Step 10.o.
Duration yearsPassedDuration = {yearsPassed};
Duration yearsPassedDuration = {double(yearsPassed)};
// Steps 10.p-r.
int32_t daysPassed;
@ -3631,13 +3535,9 @@ static bool RoundDurationYear(JSContext* cx, const NormalizedDuration& duration,
}
// Steps 10.y-aa.
RoundedNumber rounded;
if (!RoundNumberToIncrement(cx, years, yearsPassed, days, daysToAdd,
timeAndDays, oneYearDays, increment, roundingMode,
computeRemainder, &rounded)) {
return false;
}
auto [numYears, total] = rounded;
auto [numYears, total] = RoundNumberToIncrement(
years, yearsPassed, days, daysToAdd, timeAndDays, oneYearDays, increment,
roundingMode, computeRemainder);
// Step 10.ab.
int64_t numMonths = 0;
@ -3647,8 +3547,8 @@ static bool RoundDurationYear(JSContext* cx, const NormalizedDuration& duration,
constexpr auto time = NormalizedTimeDuration{};
// Step 20.
if (std::abs(numYears) >= double(int64_t(1) << 32)) {
return ThrowInvalidDurationPart(cx, numYears, "years",
if (numYears.abs() >= (Uint128{1} << 32)) {
return ThrowInvalidDurationPart(cx, double(numYears), "years",
JSMSG_TEMPORAL_DURATION_INVALID_NON_FINITE);
}
@ -3734,13 +3634,13 @@ static bool RoundDurationMonth(JSContext* cx,
}
// Step 11.m.
double monthsPassed = timePassed.months;
int64_t monthsPassed = int64_t(timePassed.months);
// Step 11.n.
// Our implementation keeps |months| and |monthsPassed| separate.
// Step 11.o.
Duration monthsPassedDuration = {0, monthsPassed};
Duration monthsPassedDuration = {0, double(monthsPassed)};
// Steps 11.p-r.
int32_t daysPassed;
@ -3778,13 +3678,9 @@ static bool RoundDurationMonth(JSContext* cx,
}
// Steps 11.y-aa.
RoundedNumber rounded;
if (!RoundNumberToIncrement(cx, months, monthsPassed, days, daysToAdd,
timeAndDays, oneMonthDays, increment,
roundingMode, computeRemainder, &rounded)) {
return false;
}
auto [numMonths, total] = rounded;
auto [numMonths, total] = RoundNumberToIncrement(
months, monthsPassed, days, daysToAdd, timeAndDays, oneMonthDays,
increment, roundingMode, computeRemainder);
// Step 11.ab.
int64_t numWeeks = 0;
@ -3793,8 +3689,8 @@ static bool RoundDurationMonth(JSContext* cx,
constexpr auto time = NormalizedTimeDuration{};
// Step 21.
if (std::abs(numMonths) >= double(int64_t(1) << 32)) {
return ThrowInvalidDurationPart(cx, numMonths, "months",
if (numMonths.abs() >= (Uint128{1} << 32)) {
return ThrowInvalidDurationPart(cx, double(numMonths), "months",
JSMSG_TEMPORAL_DURATION_INVALID_NON_FINITE);
}
@ -3850,13 +3746,13 @@ static bool RoundDurationWeek(JSContext* cx, const NormalizedDuration& duration,
}
// Step 12.f.
double weeksPassed = timePassed.weeks;
int64_t weeksPassed = int64_t(timePassed.weeks);
// Step 12.g.
// Our implementation keeps |weeks| and |weeksPassed| separate.
// Step 12.h.
Duration weeksPassedDuration = {0, 0, weeksPassed};
Duration weeksPassedDuration = {0, 0, double(weeksPassed)};
// Steps 12.i-k.
Rooted<Wrapped<PlainDateObject*>> newRelativeTo(cx);
@ -3895,20 +3791,16 @@ static bool RoundDurationWeek(JSContext* cx, const NormalizedDuration& duration,
}
// Steps 12.r-t.
RoundedNumber rounded;
if (!RoundNumberToIncrement(cx, weeks, weeksPassed, days, daysToAdd,
timeAndDays, oneWeekDays, increment, roundingMode,
computeRemainder, &rounded)) {
return false;
}
auto [numWeeks, total] = rounded;
auto [numWeeks, total] = RoundNumberToIncrement(
weeks, weeksPassed, days, daysToAdd, timeAndDays, oneWeekDays, increment,
roundingMode, computeRemainder);
// Step 12.u.
constexpr auto time = NormalizedTimeDuration{};
// Step 20.
if (std::abs(numWeeks) >= double(int64_t(1) << 32)) {
return ThrowInvalidDurationPart(cx, numWeeks, "weeks",
if (numWeeks.abs() >= (Uint128{1} << 32)) {
return ThrowInvalidDurationPart(cx, double(numWeeks), "weeks",
JSMSG_TEMPORAL_DURATION_INVALID_NON_FINITE);
}
@ -3930,12 +3822,11 @@ static bool RoundDurationDay(JSContext* cx, const NormalizedDuration& duration,
auto [years, months, weeks, days] = duration.date;
// Steps 13.a-b.
RoundedNumber rounded;
if (!RoundNumberToIncrement(cx, days, timeAndDays, increment, roundingMode,
computeRemainder, &rounded)) {
return false;
}
auto [numDays, total] = rounded;
auto [numDays, total] = RoundNumberToIncrement(
days, timeAndDays, increment, roundingMode, computeRemainder);
MOZ_ASSERT(Int128{INT64_MIN} <= numDays && numDays <= Int128{INT64_MAX},
"rounded days fits in int64");
// Step 13.c.
constexpr auto time = NormalizedTimeDuration{};

Просмотреть файл

@ -622,22 +622,27 @@ NormalizedTimeDuration js::temporal::DifferenceInstant(
/**
* RoundNumberToIncrementAsIfPositive ( x, increment, roundingMode )
*/
static bool RoundNumberToIncrementAsIfPositive(
JSContext* cx, const Instant& x, int64_t increment,
TemporalRoundingMode roundingMode, Instant* result) {
static Instant RoundNumberToIncrementAsIfPositive(
const Instant& x, int64_t increment, TemporalRoundingMode roundingMode) {
MOZ_ASSERT(IsValidEpochInstant(x));
MOZ_ASSERT(increment > 0);
MOZ_ASSERT(increment <= ToNanoseconds(TemporalUnit::Day));
// This operation is equivalent to adjusting the rounding mode through
// |ToPositiveRoundingMode| and then calling |RoundNumberToIncrement|.
return RoundNumberToIncrement(cx, x, increment,
ToPositiveRoundingMode(roundingMode), result);
auto rounded =
RoundNumberToIncrement(x.toTotalNanoseconds(), Int128{increment},
ToPositiveRoundingMode(roundingMode));
return Instant::fromNanoseconds(rounded);
}
/**
* RoundTemporalInstant ( ns, increment, unit, roundingMode )
*/
bool js::temporal::RoundTemporalInstant(JSContext* cx, const Instant& ns,
Increment increment, TemporalUnit unit,
TemporalRoundingMode roundingMode,
Instant* result) {
Instant js::temporal::RoundTemporalInstant(const Instant& ns,
Increment increment,
TemporalUnit unit,
TemporalRoundingMode roundingMode) {
MOZ_ASSERT(IsValidEpochInstant(ns));
MOZ_ASSERT(increment >= Increment::min());
MOZ_ASSERT(uint64_t(increment.value()) <= ToNanoseconds(TemporalUnit::Day));
@ -651,7 +656,7 @@ bool js::temporal::RoundTemporalInstant(JSContext* cx, const Instant& ns,
// Step 7.
return RoundNumberToIncrementAsIfPositive(
cx, ns, increment.value() * toNanoseconds, roundingMode, result);
ns, increment.value() * toNanoseconds, roundingMode);
}
/**
@ -1226,11 +1231,8 @@ static bool Instant_round(JSContext* cx, const CallArgs& args) {
}
// Step 17.
Instant roundedNs;
if (!RoundTemporalInstant(cx, instant, roundingIncrement, smallestUnit,
roundingMode, &roundedNs)) {
return false;
}
auto roundedNs = RoundTemporalInstant(instant, roundingIncrement,
smallestUnit, roundingMode);
// Step 18.
auto* result = CreateTemporalInstant(cx, roundedNs);
@ -1338,11 +1340,8 @@ static bool Instant_toString(JSContext* cx, const CallArgs& args) {
}
// Step 12.
Instant ns;
if (!RoundTemporalInstant(cx, instant, precision.increment, precision.unit,
roundingMode, &ns)) {
return false;
}
auto ns = RoundTemporalInstant(instant, precision.increment, precision.unit,
roundingMode);
// Step 13.
Rooted<InstantObject*> roundedInstant(cx, CreateTemporalInstant(cx, ns));

Просмотреть файл

@ -127,9 +127,9 @@ Instant GetUTCEpochNanoseconds(const PlainDateTime& dateTime,
/**
* RoundTemporalInstant ( ns, increment, unit, roundingMode )
*/
bool RoundTemporalInstant(JSContext* cx, const Instant& ns, Increment increment,
TemporalUnit unit, TemporalRoundingMode roundingMode,
Instant* result);
Instant RoundTemporalInstant(const Instant& ns, Increment increment,
TemporalUnit unit,
TemporalRoundingMode roundingMode);
/**
* AddNormalizedTimeDurationToEpochNanoseconds ( d, epochNs )

Просмотреть файл

@ -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"
@ -744,59 +742,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));
@ -887,11 +832,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) {
@ -924,28 +873,27 @@ RoundedTime js::temporal::RoundTime(const PlainTime& time, Increment increment,
// Step 2.
int64_t quantity = TimeToNanos(time);
MOZ_ASSERT(quantity < ToNanoseconds(TemporalUnit::Day));
MOZ_ASSERT(0 <= quantity && 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);
}
//
// 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|.
int64_t divisor = int64_t(std::min(dayLengthNs.toTotalNanoseconds(),
Int128{ToNanoseconds(TemporalUnit::Day)}));
MOZ_ASSERT(divisor > 0);
MOZ_ASSERT(increment == Increment{1}, "Rounding increment for 'day' is 1");
int64_t result =
::RoundNumberToIncrement(quantity, divisor, increment, roundingMode);
auto result =
RoundNumberToIncrement(quantity, divisor, increment, roundingMode);
MOZ_ASSERT(result == Int128{int64_t(result)});
// Step 10.
return {result, {0, 0, 0, 0, 0, 0}};
return {int64_t(result), {0, 0, 0, 0, 0, 0}};
}
/**

Просмотреть файл

@ -48,7 +48,6 @@
#include "js/RootingAPI.h"
#include "js/String.h"
#include "js/Value.h"
#include "vm/BigIntType.h"
#include "vm/BytecodeUtil.h"
#include "vm/GlobalObject.h"
#include "vm/JSAtomState.h"
@ -440,558 +439,6 @@ bool js::temporal::ToTemporalRoundingMode(JSContext* cx,
return true;
}
static BigInt* Divide(JSContext* cx, Handle<BigInt*> dividend, int64_t divisor,
TemporalRoundingMode roundingMode) {
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, &quotient, &remainder)) {
return nullptr;
}
// No rounding needed when the remainder is zero.
if (remainder->isZero()) {
return quotient;
}
switch (roundingMode) {
case TemporalRoundingMode::Ceil: {
if (!remainder->isNegative()) {
return BigInt::inc(cx, quotient);
}
return quotient;
}
case TemporalRoundingMode::Floor: {
if (remainder->isNegative()) {
return BigInt::dec(cx, quotient);
}
return quotient;
}
case TemporalRoundingMode::Trunc:
// BigInt division truncates.
return quotient;
case TemporalRoundingMode::Expand: {
if (!remainder->isNegative()) {
return BigInt::inc(cx, quotient);
}
return BigInt::dec(cx, quotient);
}
case TemporalRoundingMode::HalfCeil: {
int64_t rem;
MOZ_ALWAYS_TRUE(BigInt::isInt64(remainder, &rem));
if (!remainder->isNegative()) {
if (uint64_t(std::abs(rem)) * 2 >= uint64_t(divisor)) {
return BigInt::inc(cx, quotient);
}
} else {
if (uint64_t(std::abs(rem)) * 2 > uint64_t(divisor)) {
return BigInt::dec(cx, quotient);
}
}
return quotient;
}
case TemporalRoundingMode::HalfFloor: {
int64_t rem;
MOZ_ALWAYS_TRUE(BigInt::isInt64(remainder, &rem));
if (remainder->isNegative()) {
if (uint64_t(std::abs(rem)) * 2 >= uint64_t(divisor)) {
return BigInt::dec(cx, quotient);
}
} else {
if (uint64_t(std::abs(rem)) * 2 > uint64_t(divisor)) {
return BigInt::inc(cx, quotient);
}
}
return quotient;
}
case TemporalRoundingMode::HalfExpand: {
int64_t rem;
MOZ_ALWAYS_TRUE(BigInt::isInt64(remainder, &rem));
if (uint64_t(std::abs(rem)) * 2 >= uint64_t(divisor)) {
if (!dividend->isNegative()) {
return BigInt::inc(cx, quotient);
}
return BigInt::dec(cx, quotient);
}
return quotient;
}
case TemporalRoundingMode::HalfTrunc: {
int64_t rem;
MOZ_ALWAYS_TRUE(BigInt::isInt64(remainder, &rem));
if (uint64_t(std::abs(rem)) * 2 > uint64_t(divisor)) {
if (!dividend->isNegative()) {
return BigInt::inc(cx, quotient);
}
return BigInt::dec(cx, quotient);
}
return quotient;
}
case TemporalRoundingMode::HalfEven: {
int64_t rem;
MOZ_ALWAYS_TRUE(BigInt::isInt64(remainder, &rem));
if (uint64_t(std::abs(rem)) * 2 == uint64_t(divisor)) {
bool isOdd = !quotient->isZero() && (quotient->digit(0) & 1) == 1;
if (isOdd) {
if (!dividend->isNegative()) {
return BigInt::inc(cx, quotient);
}
return BigInt::dec(cx, quotient);
}
}
if (uint64_t(std::abs(rem)) * 2 > uint64_t(divisor)) {
if (!dividend->isNegative()) {
return BigInt::inc(cx, quotient);
}
return BigInt::dec(cx, quotient);
}
return quotient;
}
}
MOZ_CRASH("invalid rounding mode");
}
static BigInt* Divide(JSContext* cx, Handle<BigInt*> dividend,
Handle<BigInt*> divisor,
TemporalRoundingMode roundingMode) {
MOZ_ASSERT(!divisor->isNegative());
MOZ_ASSERT(!divisor->isZero());
Rooted<BigInt*> quotient(cx);
Rooted<BigInt*> remainder(cx);
if (!BigInt::divmod(cx, dividend, divisor, &quotient, &remainder)) {
return nullptr;
}
// No rounding needed when the remainder is zero.
if (remainder->isZero()) {
return quotient;
}
switch (roundingMode) {
case TemporalRoundingMode::Ceil: {
if (!remainder->isNegative()) {
return BigInt::inc(cx, quotient);
}
return quotient;
}
case TemporalRoundingMode::Floor: {
if (remainder->isNegative()) {
return BigInt::dec(cx, quotient);
}
return quotient;
}
case TemporalRoundingMode::Trunc:
// BigInt division truncates.
return quotient;
case TemporalRoundingMode::Expand: {
if (!remainder->isNegative()) {
return BigInt::inc(cx, quotient);
}
return BigInt::dec(cx, quotient);
}
case TemporalRoundingMode::HalfCeil: {
BigInt* rem = BigInt::add(cx, remainder, remainder);
if (!rem) {
return nullptr;
}
if (!remainder->isNegative()) {
if (BigInt::absoluteCompare(rem, divisor) >= 0) {
return BigInt::inc(cx, quotient);
}
} else {
if (BigInt::absoluteCompare(rem, divisor) > 0) {
return BigInt::dec(cx, quotient);
}
}
return quotient;
}
case TemporalRoundingMode::HalfFloor: {
BigInt* rem = BigInt::add(cx, remainder, remainder);
if (!rem) {
return nullptr;
}
if (remainder->isNegative()) {
if (BigInt::absoluteCompare(rem, divisor) >= 0) {
return BigInt::dec(cx, quotient);
}
} else {
if (BigInt::absoluteCompare(rem, divisor) > 0) {
return BigInt::inc(cx, quotient);
}
}
return quotient;
}
case TemporalRoundingMode::HalfExpand: {
BigInt* rem = BigInt::add(cx, remainder, remainder);
if (!rem) {
return nullptr;
}
if (BigInt::absoluteCompare(rem, divisor) >= 0) {
if (!dividend->isNegative()) {
return BigInt::inc(cx, quotient);
}
return BigInt::dec(cx, quotient);
}
return quotient;
}
case TemporalRoundingMode::HalfTrunc: {
BigInt* rem = BigInt::add(cx, remainder, remainder);
if (!rem) {
return nullptr;
}
if (BigInt::absoluteCompare(rem, divisor) > 0) {
if (!dividend->isNegative()) {
return BigInt::inc(cx, quotient);
}
return BigInt::dec(cx, quotient);
}
return quotient;
}
case TemporalRoundingMode::HalfEven: {
BigInt* rem = BigInt::add(cx, remainder, remainder);
if (!rem) {
return nullptr;
}
if (BigInt::absoluteCompare(rem, divisor) == 0) {
bool isOdd = !quotient->isZero() && (quotient->digit(0) & 1) == 1;
if (isOdd) {
if (!dividend->isNegative()) {
return BigInt::inc(cx, quotient);
}
return BigInt::dec(cx, quotient);
}
}
if (BigInt::absoluteCompare(rem, divisor) > 0) {
if (!dividend->isNegative()) {
return BigInt::inc(cx, quotient);
}
return BigInt::dec(cx, quotient);
}
return quotient;
}
}
MOZ_CRASH("invalid rounding mode");
}
static BigInt* RoundNumberToIncrementSlow(JSContext* cx, Handle<BigInt*> x,
int64_t divisor, int64_t increment,
TemporalRoundingMode roundingMode) {
// Steps 1-8.
Rooted<BigInt*> rounded(cx, Divide(cx, x, divisor, roundingMode));
if (!rounded) {
return nullptr;
}
// We can skip the next step when |increment=1|.
if (increment == 1) {
return rounded;
}
// Step 9.
Rooted<BigInt*> inc(cx, BigInt::createFromInt64(cx, increment));
if (!inc) {
return nullptr;
}
return BigInt::mul(cx, rounded, inc);
}
static BigInt* RoundNumberToIncrementSlow(JSContext* cx, Handle<BigInt*> x,
int64_t increment,
TemporalRoundingMode roundingMode) {
return RoundNumberToIncrementSlow(cx, x, increment, increment, roundingMode);
}
/**
* RoundNumberToIncrement ( x, increment, roundingMode )
*/
bool js::temporal::RoundNumberToIncrement(JSContext* cx, const Instant& x,
int64_t increment,
TemporalRoundingMode roundingMode,
Instant* result) {
MOZ_ASSERT(IsValidEpochInstant(x));
MOZ_ASSERT(increment > 0);
MOZ_ASSERT(increment <= ToNanoseconds(TemporalUnit::Day));
// Fast path for the default case.
if (increment == 1) {
*result = x;
return true;
}
// Dividing zero is always zero.
if (x == Instant{}) {
*result = x;
return true;
}
// Fast-path when we can perform the whole computation with int64 values.
if (auto num = x.toNanoseconds(); MOZ_LIKELY(num.isValid())) {
// Steps 1-8.
int64_t rounded = Divide(num.value(), increment, roundingMode);
// Step 9.
mozilla::CheckedInt64 checked = rounded;
checked *= increment;
if (MOZ_LIKELY(checked.isValid())) {
*result = Instant::fromNanoseconds(checked.value());
return true;
}
}
Rooted<BigInt*> bi(cx, ToEpochNanoseconds(cx, x));
if (!bi) {
return false;
}
auto* rounded = RoundNumberToIncrementSlow(cx, bi, increment, roundingMode);
if (!rounded) {
return false;
}
*result = ToInstant(rounded);
return true;
}
/**
* RoundNumberToIncrement ( x, increment, roundingMode )
*/
bool js::temporal::RoundNumberToIncrement(JSContext* cx, int64_t numerator,
TemporalUnit unit,
Increment increment,
TemporalRoundingMode roundingMode,
double* result) {
MOZ_ASSERT(unit >= TemporalUnit::Day);
MOZ_ASSERT(Increment::min() <= increment && increment <= Increment::max());
// Take the slow path when the increment is too large.
if (MOZ_UNLIKELY(increment > Increment{100'000})) {
Rooted<BigInt*> bi(cx, BigInt::createFromInt64(cx, numerator));
if (!bi) {
return false;
}
Rooted<BigInt*> denominator(
cx, BigInt::createFromInt64(cx, ToNanoseconds(unit)));
if (!denominator) {
return false;
}
return RoundNumberToIncrement(cx, bi, denominator, increment, roundingMode,
result);
}
int64_t divisor = ToNanoseconds(unit) * increment.value();
MOZ_ASSERT(divisor > 0);
MOZ_ASSERT(divisor <= 8'640'000'000'000'000'000);
// Division by one has no remainder.
if (divisor == 1) {
MOZ_ASSERT(increment == Increment{1});
*result = double(numerator);
return true;
}
// Steps 1-8.
int64_t rounded = Divide(numerator, divisor, roundingMode);
// Step 9.
mozilla::CheckedInt64 checked = rounded;
checked *= increment.value();
if (checked.isValid()) {
*result = double(checked.value());
return true;
}
Rooted<BigInt*> bi(cx, BigInt::createFromInt64(cx, numerator));
if (!bi) {
return false;
}
return RoundNumberToIncrement(cx, bi, unit, increment, roundingMode, result);
}
/**
* RoundNumberToIncrement ( x, increment, roundingMode )
*/
bool js::temporal::RoundNumberToIncrement(
JSContext* cx, Handle<BigInt*> numerator, TemporalUnit unit,
Increment increment, TemporalRoundingMode roundingMode, double* result) {
MOZ_ASSERT(unit >= TemporalUnit::Day);
MOZ_ASSERT(Increment::min() <= increment && increment <= Increment::max());
// Take the slow path when the increment is too large.
if (MOZ_UNLIKELY(increment > Increment{100'000})) {
Rooted<BigInt*> denominator(
cx, BigInt::createFromInt64(cx, ToNanoseconds(unit)));
if (!denominator) {
return false;
}
return RoundNumberToIncrement(cx, numerator, denominator, increment,
roundingMode, result);
}
int64_t divisor = ToNanoseconds(unit) * increment.value();
MOZ_ASSERT(divisor > 0);
MOZ_ASSERT(divisor <= 8'640'000'000'000'000'000);
// Division by one has no remainder.
if (divisor == 1) {
MOZ_ASSERT(increment == Increment{1});
*result = BigInt::numberValue(numerator);
return true;
}
// Dividing zero is always zero.
if (numerator->isZero()) {
*result = 0;
return true;
}
// All callers are already in the slow path, so we don't need to fast-path the
// case when |x| can be represented by an int64 value.
// Steps 1-9.
auto* rounded = RoundNumberToIncrementSlow(cx, numerator, divisor,
increment.value(), roundingMode);
if (!rounded) {
return false;
}
*result = BigInt::numberValue(rounded);
return true;
}
/**
* RoundNumberToIncrement ( x, increment, roundingMode )
*/
bool js::temporal::RoundNumberToIncrement(JSContext* cx, int64_t numerator,
int64_t denominator,
Increment increment,
TemporalRoundingMode roundingMode,
double* result) {
MOZ_ASSERT(denominator > 0);
MOZ_ASSERT(Increment::min() <= increment && increment <= Increment::max());
// Dividing zero is always zero.
if (numerator == 0) {
*result = 0;
return true;
}
// We don't have to adjust the divisor when |increment=1|.
if (increment == Increment{1}) {
int64_t divisor = denominator;
int64_t rounded = Divide(numerator, divisor, roundingMode);
*result = double(rounded);
return true;
}
auto divisor = mozilla::CheckedInt64(denominator) * increment.value();
if (MOZ_LIKELY(divisor.isValid())) {
MOZ_ASSERT(divisor.value() > 0);
// Steps 1-8.
int64_t rounded = Divide(numerator, divisor.value(), roundingMode);
// Step 9.
auto adjusted = mozilla::CheckedInt64(rounded) * increment.value();
if (MOZ_LIKELY(adjusted.isValid())) {
*result = double(adjusted.value());
return true;
}
}
// Slow path on overflow.
Rooted<BigInt*> bi(cx, BigInt::createFromInt64(cx, numerator));
if (!bi) {
return false;
}
Rooted<BigInt*> denom(cx, BigInt::createFromInt64(cx, denominator));
if (!denom) {
return false;
}
return RoundNumberToIncrement(cx, bi, denom, increment, roundingMode, result);
}
/**
* RoundNumberToIncrement ( x, increment, roundingMode )
*/
bool js::temporal::RoundNumberToIncrement(
JSContext* cx, Handle<BigInt*> numerator, Handle<BigInt*> denominator,
Increment increment, TemporalRoundingMode roundingMode, double* result) {
MOZ_ASSERT(!denominator->isNegative());
MOZ_ASSERT(!denominator->isZero());
MOZ_ASSERT(Increment::min() <= increment && increment <= Increment::max());
// Dividing zero is always zero.
if (numerator->isZero()) {
*result = 0;
return true;
}
// We don't have to adjust the divisor when |increment=1|.
if (increment == Increment{1}) {
auto divisor = denominator;
auto* rounded = Divide(cx, numerator, divisor, roundingMode);
if (!rounded) {
return false;
}
*result = BigInt::numberValue(rounded);
return true;
}
Rooted<BigInt*> inc(cx, BigInt::createFromUint64(cx, increment.value()));
if (!inc) {
return false;
}
Rooted<BigInt*> divisor(cx, BigInt::mul(cx, denominator, inc));
if (!divisor) {
return false;
}
MOZ_ASSERT(!divisor->isNegative());
MOZ_ASSERT(!divisor->isZero());
// Steps 1-8.
Rooted<BigInt*> rounded(cx, Divide(cx, numerator, divisor, roundingMode));
if (!rounded) {
return false;
}
// Step 9.
auto* adjusted = BigInt::mul(cx, rounded, inc);
if (!adjusted) {
return false;
}
*result = BigInt::numberValue(adjusted);
return true;
}
#ifdef DEBUG
// Copied from mozilla::CheckedInt.
static bool IsValidMul(const Int128& x, const Int128& y) {
@ -1008,14 +455,81 @@ static bool IsValidMul(const Int128& x, const Int128& y) {
}
#endif
/**
* RoundNumberToIncrement ( x, increment, roundingMode )
*/
Int128 js::temporal::RoundNumberToIncrement(int64_t numerator,
int64_t denominator,
Increment increment,
TemporalRoundingMode roundingMode) {
MOZ_ASSERT(denominator > 0);
MOZ_ASSERT(Increment::min() <= increment && increment <= Increment::max());
// Dividing zero is always zero.
if (numerator == 0) {
return Int128{0};
}
// We don't have to adjust the divisor when |increment=1|.
if (increment == Increment{1}) {
// Steps 1-8 and implicit step 9.
return Int128{Divide(numerator, denominator, roundingMode)};
}
// Fast-path when we can perform the whole computation with int64 values.
auto divisor = mozilla::CheckedInt64(denominator) * increment.value();
if (MOZ_LIKELY(divisor.isValid())) {
MOZ_ASSERT(divisor.value() > 0);
// Steps 1-8.
int64_t rounded = Divide(numerator, divisor.value(), roundingMode);
// Step 9.
auto result = mozilla::CheckedInt64(rounded) * increment.value();
if (MOZ_LIKELY(result.isValid())) {
return Int128{result.value()};
}
}
// Int128 path on overflow.
return RoundNumberToIncrement(Int128{numerator}, Int128{denominator},
increment, roundingMode);
}
/**
* RoundNumberToIncrement ( x, increment, roundingMode )
*/
Int128 js::temporal::RoundNumberToIncrement(const Int128& numerator,
const Int128& denominator,
Increment increment,
TemporalRoundingMode roundingMode) {
MOZ_ASSERT(denominator > Int128{0});
MOZ_ASSERT(Increment::min() <= increment && increment <= Increment::max());
auto inc = Int128{increment.value()};
MOZ_ASSERT(IsValidMul(denominator, inc), "unsupported overflow");
auto divisor = denominator * inc;
MOZ_ASSERT(divisor > Int128{0});
// Steps 1-8.
auto rounded = Divide(numerator, divisor, roundingMode);
// Step 9.
MOZ_ASSERT(IsValidMul(rounded, inc), "unsupported overflow");
return rounded * inc;
}
/**
* RoundNumberToIncrement ( x, increment, roundingMode )
*/
Int128 js::temporal::RoundNumberToIncrement(const Int128& x,
const Int128& increment,
TemporalRoundingMode roundingMode) {
MOZ_ASSERT(increment > Int128{0});
// Steps 1-8.
auto rounded = Divide(numerator, increment, roundingMode);
auto rounded = Divide(x, increment, roundingMode);
// Step 9.
MOZ_ASSERT(IsValidMul(rounded, increment), "unsupported overflow");

Просмотреть файл

@ -13,6 +13,7 @@
#include "jstypes.h"
#include "builtin/temporal/Int128.h"
#include "builtin/temporal/TemporalRoundingMode.h"
#include "builtin/temporal/TemporalUnit.h"
#include "js/RootingAPI.h"
@ -35,10 +36,6 @@ class TemporalObject : public NativeObject {
static const ClassSpec classSpec_;
};
struct Instant;
struct PlainTime;
class Int128;
/**
* Rounding increment, which is an integer in the range [1, 1'000'000'000].
*
@ -172,43 +169,22 @@ bool ToTemporalRoundingMode(JSContext* cx, JS::Handle<JSObject*> options,
/**
* RoundNumberToIncrement ( x, increment, roundingMode )
*/
Int128 RoundNumberToIncrement(const Int128& x, const Int128& increment,
Int128 RoundNumberToIncrement(int64_t numerator, int64_t denominator,
Increment increment,
TemporalRoundingMode roundingMode);
/**
* RoundNumberToIncrement ( x, increment, roundingMode )
*/
bool RoundNumberToIncrement(JSContext* cx, const Instant& x, int64_t increment,
TemporalRoundingMode roundingMode, Instant* result);
Int128 RoundNumberToIncrement(const Int128& numerator,
const Int128& denominator, Increment increment,
TemporalRoundingMode roundingMode);
/**
* RoundNumberToIncrement ( x, increment, roundingMode )
*/
bool RoundNumberToIncrement(JSContext* cx, int64_t numerator, TemporalUnit unit,
Increment increment,
TemporalRoundingMode roundingMode, double* result);
/**
* RoundNumberToIncrement ( x, increment, roundingMode )
*/
bool RoundNumberToIncrement(JSContext* cx, JS::Handle<JS::BigInt*> numerator,
TemporalUnit unit, Increment increment,
TemporalRoundingMode roundingMode, double* result);
/**
* RoundNumberToIncrement ( x, increment, roundingMode )
*/
bool RoundNumberToIncrement(JSContext* cx, int64_t numerator,
int64_t denominator, Increment increment,
TemporalRoundingMode roundingMode, double* result);
/**
* RoundNumberToIncrement ( x, increment, roundingMode )
*/
bool RoundNumberToIncrement(JSContext* cx, JS::Handle<JS::BigInt*> numerator,
JS::Handle<JS::BigInt*> denominator,
Increment increment,
TemporalRoundingMode roundingMode, double* result);
Int128 RoundNumberToIncrement(const Int128& x, const Int128& increment,
TemporalRoundingMode roundingMode);
enum class CalendarOption { Auto, Always, Never, Critical };

Просмотреть файл

@ -636,11 +636,8 @@ JSString* js::temporal::TemporalZonedDateTimeToString(
// Steps 1-3. (Not applicable in our implementation.)
// Step 4.
Instant ns;
if (!RoundTemporalInstant(cx, zonedDateTime.instant(), increment, unit,
roundingMode, &ns)) {
return nullptr;
}
auto ns = RoundTemporalInstant(zonedDateTime.instant(), increment, unit,
roundingMode);
// Step 5.
auto timeZone = zonedDateTime.timeZone();