/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
deps/v8/src/objects/js-temporal-helpers.cc
310 строк
13 KB
Michaël Zasso
deps: update V8 to 14.1.146.11
04 окт 2025, 19:47
Не верифицирован
04 окт 2025, 19:47
7772a2d
Код
Авторство
О чём код?
// Copyright 2021 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/objects/js-temporal-helpers.h" #include "src/objects/js-objects-inl.h" namespace v8::internal::temporal { namespace { // #sec-isintegralnumber bool IsIntegralNumber(Isolate* isolate, DirectHandle<Object> argument) { // 1. If Type(argument) is not Number, return false. if (!IsNumber(*argument)) return false; // 2. If argument is NaN, +∞𝔽, or -∞𝔽, return false. double number = Object::NumberValue(Cast<Number>(*argument)); if (!std::isfinite(number)) return false; // 3. If floor(abs(ℝ(argument))) ≠ abs(ℝ(argument)), return false. if (std::floor(std::abs(number)) != std::abs(number)) return false; // 4. Return true. return true; } // #sec-temporal-tointegerwithoutrounding Maybe<double> ToIntegerWithoutRounding(Isolate* isolate, DirectHandle<Object> argument) { // 1. Let number be ? ToNumber(argument). DirectHandle<Number> number; ASSIGN_RETURN_ON_EXCEPTION(isolate, number, Object::ToNumber(isolate, argument)); // 2. If number is NaN, +0𝔽, or −0𝔽 return 0. if (IsNaN(*number) || Object::NumberValue(*number) == 0) { return Just(static_cast<double>(0)); } // 3. If IsIntegralNumber(number) is false, throw a RangeError exception. if (!IsIntegralNumber(isolate, number)) { THROW_NEW_ERROR(isolate, NEW_TEMPORAL_RANGE_ERROR("Number not integral.")); } // 4. Return ℝ(number). return Just(Object::NumberValue(*number)); } // Helper function to loop through Table 8 Duration Record Fields // This function implement // "For each row of Table 8, except the header row, in table order, do" // loop. It is designed to be used to implement the common part of // ToPartialDuration, ToTemporalDurationRecord Maybe<bool> IterateDurationRecordFieldsTable( Isolate* isolate, DirectHandle<JSReceiver> temporal_duration_like, Maybe<bool> (*RowFunction)(Isolate*, DirectHandle<JSReceiver> temporal_duration_like, DirectHandle<String>, double*), DurationRecord* record) { Factory* factory = isolate->factory(); std::array<std::pair<DirectHandle<String>, double*>, 10> table8 = { {{factory->days_string(), &record->time_duration.days}, {factory->hours_string(), &record->time_duration.hours}, {factory->microseconds_string(), &record->time_duration.microseconds}, {factory->milliseconds_string(), &record->time_duration.milliseconds}, {factory->minutes_string(), &record->time_duration.minutes}, {factory->months_string(), &record->months}, {factory->nanoseconds_string(), &record->time_duration.nanoseconds}, {factory->seconds_string(), &record->time_duration.seconds}, {factory->weeks_string(), &record->weeks}, {factory->years_string(), &record->years}}}; // x. Let any be false. bool any = false; // x+1. For each row of Table 8, except the header row, in table order, do for (const auto& row : table8) { bool result; // row.first is prop: the Property Name value of the current row // row.second is the address of result's field whose name is the Field Name // value of the current row ASSIGN_RETURN_ON_EXCEPTION( isolate, result, RowFunction(isolate, temporal_duration_like, row.first, row.second)); any |= result; } return Just(any); } } // namespace // #sec-temporal-isvalidduration bool IsValidDuration(Isolate* isolate, const DurationRecord& dur) { // 1. Let sign be ! DurationSign(years, months, weeks, days, hours, minutes, // seconds, milliseconds, microseconds, nanoseconds). int32_t sign = DurationRecord::Sign(dur); // 2. For each value v of « years, months, weeks, days, hours, minutes, // seconds, milliseconds, microseconds, nanoseconds », do a. If v is not // finite, return false. b. If v < 0 and sign > 0, return false. c. If v > 0 // and sign < 0, return false. // 3. Return true. const TimeDurationRecord& time = dur.time_duration; if (!(std::isfinite(dur.years) && std::isfinite(dur.months) && std::isfinite(dur.weeks) && std::isfinite(time.days) && std::isfinite(time.hours) && std::isfinite(time.minutes) && std::isfinite(time.seconds) && std::isfinite(time.milliseconds) && std::isfinite(time.microseconds) && std::isfinite(time.nanoseconds))) { return false; } if ((sign > 0 && (dur.years < 0 || dur.months < 0 || dur.weeks < 0 || time.days < 0 || time.hours < 0 || time.minutes < 0 || time.seconds < 0 || time.milliseconds < 0 || time.microseconds < 0 || time.nanoseconds < 0)) || (sign < 0 && (dur.years > 0 || dur.months > 0 || dur.weeks > 0 || time.days > 0 || time.hours > 0 || time.minutes > 0 || time.seconds > 0 || time.milliseconds > 0 || time.microseconds > 0 || time.nanoseconds > 0))) { return false; } static const double kPower32Of2 = static_cast<double>(int64_t(1) << 32); static const int64_t kPower53Of2 = int64_t(1) << 53; // 3. If abs(years) ≥ 2**32, return false. if (std::abs(dur.years) >= kPower32Of2) { return false; } // 4. If abs(months) ≥ 2**32, return false. if (std::abs(dur.months) >= kPower32Of2) { return false; } // 5. If abs(weeks) ≥ 2**32, return false. if (std::abs(dur.weeks) >= kPower32Of2) { return false; } // 6. Let normalizedSeconds be days × 86,400 + hours × 3600 + minutes × 60 + // seconds + ℝ(𝔽(milliseconds)) × 10**-3 + ℝ(𝔽(microseconds)) × 10**-6 + // ℝ(𝔽(nanoseconds)) × 10**-9. // 7. NOTE: The above step cannot be implemented directly using floating-point // arithmetic. Multiplying by 10**-3, 10**-6, and 10**-9 respectively may be // imprecise when milliseconds, microseconds, or nanoseconds is an unsafe // integer. This multiplication can be implemented in C++ with an // implementation of std::remquo() with sufficient bits in the quotient. // String manipulation will also give an exact result, since the // multiplication is by a power of 10. // 8. If abs(normalizedSeconds) ≥ 2**53, return false. int64_t allowed = kPower53Of2; double in_seconds = std::abs(time.days * 86400.0 + time.hours * 3600.0 + time.minutes * 60.0 + time.seconds); if (in_seconds >= allowed) { return false; } allowed -= in_seconds; // Check the part > 1 seconds. in_seconds = std::floor(std::abs(time.milliseconds / 1e3)) + std::floor(std::abs(time.microseconds / 1e6)) + std::floor(std::abs(time.nanoseconds / 1e9)); if (in_seconds >= allowed) { return false; } allowed -= in_seconds; // Sum of the three remainings will surely < 3 if (allowed > 3) { return true; } allowed *= 1000000000; // convert to ns int64_t remainders = std::abs(fmod(time.milliseconds, 1e3)) * 1000000 + std::abs(fmod(time.microseconds, 1e6)) * 1000 + std::abs(fmod(time.nanoseconds, 1e9)); if (remainders >= allowed) { return false; } return true; } // #sec-temporal-durationsign int32_t DurationRecord::Sign(const DurationRecord& dur) { // 1. For each value v of « years, months, weeks, days, hours, minutes, // seconds, milliseconds, microseconds, nanoseconds », do a. If v < 0, return // −1. b. If v > 0, return 1. // 2. Return 0. if (dur.years < 0) return -1; if (dur.years > 0) return 1; if (dur.months < 0) return -1; if (dur.months > 0) return 1; if (dur.weeks < 0) return -1; if (dur.weeks > 0) return 1; const TimeDurationRecord& time = dur.time_duration; if (time.days < 0) return -1; if (time.days > 0) return 1; if (time.hours < 0) return -1; if (time.hours > 0) return 1; if (time.minutes < 0) return -1; if (time.minutes > 0) return 1; if (time.seconds < 0) return -1; if (time.seconds > 0) return 1; if (time.milliseconds < 0) return -1; if (time.milliseconds > 0) return 1; if (time.microseconds < 0) return -1; if (time.microseconds > 0) return 1; if (time.nanoseconds < 0) return -1; if (time.nanoseconds > 0) return 1; return 0; } // #sec-temporal-createtimedurationrecord Maybe<TimeDurationRecord> TimeDurationRecord::Create( Isolate* isolate, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds) { // 1. If ! IsValidDuration(0, 0, 0, days, hours, minutes, seconds, // milliseconds, microseconds, nanoseconds) is false, throw a RangeError // exception. TimeDurationRecord record = {days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds}; if (!IsValidDuration(isolate, {0, 0, 0, record})) { THROW_NEW_ERROR(isolate, NEW_TEMPORAL_RANGE_ERROR("Invalid duration.")); } // 2. Return the Record { [[Days]]: ℝ(𝔽(days)), [[Hours]]: ℝ(𝔽(hours)), // [[Minutes]]: ℝ(𝔽(minutes)), [[Seconds]]: ℝ(𝔽(seconds)), [[Milliseconds]]: // ℝ(𝔽(milliseconds)), [[Microseconds]]: ℝ(𝔽(microseconds)), [[Nanoseconds]]: // ℝ(𝔽(nanoseconds)) }. return Just(record); } // #sec-temporal-createdurationrecord Maybe<DurationRecord> DurationRecord::Create( Isolate* isolate, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds) { // 1. If ! IsValidDuration(years, months, weeks, days, hours, minutes, // seconds, milliseconds, microseconds, nanoseconds) is false, throw a // RangeError exception. DurationRecord record = { years, months, weeks, {days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds}}; if (!IsValidDuration(isolate, record)) { THROW_NEW_ERROR(isolate, NEW_TEMPORAL_RANGE_ERROR("Invalid duration.")); } // 2. Return the Record { [[Years]]: ℝ(𝔽(years)), [[Months]]: ℝ(𝔽(months)), // [[Weeks]]: ℝ(𝔽(weeks)), [[Days]]: ℝ(𝔽(days)), [[Hours]]: ℝ(𝔽(hours)), // [[Minutes]]: ℝ(𝔽(minutes)), [[Seconds]]: ℝ(𝔽(seconds)), [[Milliseconds]]: // ℝ(𝔽(milliseconds)), [[Microseconds]]: ℝ(𝔽(microseconds)), [[Nanoseconds]]: // ℝ(𝔽(nanoseconds)) }. return Just(record); } // https://tc39.es/proposal-temporal/#sec-todurationrecord-deleted Maybe<DurationRecord> ToDurationRecord( Isolate* isolate, DirectHandle<Object> temporal_duration_like_obj, const DurationRecord& input) { // 1. If Type(temporalDurationLike) is not Object, then if (!IsJSReceiver(*temporal_duration_like_obj)) { // a. Throw a TypeError exception. THROW_NEW_ERROR(isolate, NEW_TEMPORAL_TYPE_ERROR( "Duration argument must be an object.")); } DirectHandle<JSReceiver> temporal_duration_like = Cast<JSReceiver>(temporal_duration_like_obj); // 2. Let result be a new partial Duration Record with each field set to // undefined. DurationRecord result = input; // 3. Let any be false. bool any = false; // Table 8: Duration Record Fields // #table-temporal-duration-record-fields // 4. For each row of Table 8, except the header row, in table order, do ASSIGN_RETURN_ON_EXCEPTION( isolate, any, IterateDurationRecordFieldsTable( isolate, temporal_duration_like, [](Isolate* isolate, DirectHandle<JSReceiver> temporal_duration_like, DirectHandle<String> prop, double* field) -> Maybe<bool> { bool not_undefined = false; // a. Let prop be the Property value of the current row. DirectHandle<Object> val; // b. Let val be ? Get(temporalDurationLike, prop). ASSIGN_RETURN_ON_EXCEPTION( isolate, val, JSReceiver::GetProperty(isolate, temporal_duration_like, prop)); // c. If val is not undefined, then if (!IsUndefined(*val)) { // i. Set any to true. not_undefined = true; // ii. Let val be 𝔽(? ToIntegerWithoutRounding(val)). // iii. Set result's field whose name is the Field Name value of // the current row to val. ASSIGN_RETURN_ON_EXCEPTION( isolate, *field, ToIntegerWithoutRounding(isolate, val)); } return Just(not_undefined); }, &result)); // 5. If any is false, then if (!any) { // a. Throw a TypeError exception. THROW_NEW_ERROR( isolate, NEW_TEMPORAL_TYPE_ERROR( "Duration argument must contain at least one date/time field.")); } // 6. Return result. return Just(result); } } // namespace v8::internal::temporal