1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 15:17:36 +00:00

LibJS: Implement Temporal.PlainTime.prototype.with

Ticks off one box in #8982 and fixes one test262 case.
This commit is contained in:
Luke Wilde 2021-09-08 18:59:09 +01:00 committed by Linus Groh
parent b8d683c5fb
commit d943b8f100
8 changed files with 399 additions and 0 deletions

View file

@ -7,6 +7,7 @@
#include <AK/CharacterTypes.h>
#include <AK/DateTimeLexer.h>
#include <AK/TypeCasts.h>
#include <AK/Variant.h>
#include <LibJS/Runtime/IteratorOperations.h>
#include <LibJS/Runtime/PropertyName.h>
@ -14,8 +15,10 @@
#include <LibJS/Runtime/Temporal/Calendar.h>
#include <LibJS/Runtime/Temporal/Duration.h>
#include <LibJS/Runtime/Temporal/PlainDate.h>
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
#include <LibJS/Runtime/Temporal/PlainTime.h>
#include <LibJS/Runtime/Temporal/TimeZone.h>
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
namespace JS::Temporal {
@ -597,6 +600,20 @@ Optional<u16> maximum_temporal_duration_rounding_increment(StringView unit)
return 1000;
}
// 13.26 RejectTemporalCalendarType ( object ), https://tc39.es/proposal-temporal/#sec-temporal-rejecttemporalcalendartype
void reject_temporal_calendar_type(GlobalObject& global_object, Object& object)
{
auto& vm = global_object.vm();
// 1. Assert: Type(object) is Object.
// 2. If object has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalTime]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
if (is<PlainDate>(object) || is<PlainDateTime>(object) || is<PlainMonthDay>(object) || is<PlainTime>(object) || is<PlainYearMonth>(object) || is<ZonedDateTime>(object)) {
// a. Throw a TypeError exception.
vm.throw_exception<TypeError>(global_object, ErrorType::TemporalPlainTimeWithArgumentMustNotHave, "calendar or timeZone");
}
}
// 13.27 FormatSecondsStringPart ( second, millisecond, microsecond, nanosecond, precision ), https://tc39.es/proposal-temporal/#sec-temporal-formatsecondsstringpart
String format_seconds_string_part(u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Variant<String, u8> const& precision)
{

View file

@ -91,6 +91,7 @@ Optional<String> to_smallest_temporal_unit(GlobalObject&, Object& normalized_opt
void validate_temporal_unit_range(GlobalObject&, String const& largest_unit, String const& smallest_unit);
String larger_of_two_temporal_units(StringView, StringView);
Optional<u16> maximum_temporal_duration_rounding_increment(StringView unit);
void reject_temporal_calendar_type(GlobalObject&, Object&);
String format_seconds_string_part(u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Variant<String, u8> const& precision);
double constrain_to_range(double x, double minimum, double maximum);
BigInt* round_number_to_increment(GlobalObject&, BigInt const&, u64 increment, StringView rounding_mode);

View file

@ -134,6 +134,54 @@ PlainTime* to_temporal_time(GlobalObject& global_object, Value item, Optional<St
return create_temporal_time(global_object, result->hour, result->minute, result->second, result->millisecond, result->microsecond, result->nanosecond);
}
// 4.5.3 ToPartialTime ( temporalTimeLike ), https://tc39.es/proposal-temporal/#sec-temporal-topartialtime
Optional<PartialUnregulatedTemporalTime> to_partial_time(GlobalObject& global_object, Object& temporal_time_like)
{
auto& vm = global_object.vm();
// 1. Assert: Type(temporalTimeLike) is Object.
// 2. Let result be the Record { [[Hour]]: undefined, [[Minute]]: undefined, [[Second]]: undefined, [[Millisecond]]: undefined, [[Microsecond]]: undefined, [[Nanosecond]]: undefined }.
auto result = PartialUnregulatedTemporalTime {};
// 3. Let any be false.
bool any = false;
// 4. For each row of Table 3, except the header row, in table order, do
for (auto& [internal_slot, property] : temporal_time_like_properties<PartialUnregulatedTemporalTime, Optional<double>>(vm)) {
// a. Let property be the Property value of the current row.
// b. Let value be ? Get(temporalTimeLike, property).
auto value = temporal_time_like.get(property);
if (vm.exception())
return {};
// c. If value is not undefined, then
if (!value.is_undefined()) {
// i. Set any to true.
any = true;
// ii. Set value to ? ToIntegerThrowOnInfinity(value).
auto value_number = to_integer_throw_on_infinity(global_object, value, ErrorType::TemporalPropertyMustBeFinite);
if (vm.exception())
return {};
// iii. Set result's internal slot whose name is the Internal Slot value of the current row to value.
result.*internal_slot = value_number;
}
}
// 5. If any is false, then
if (!any) {
// a. Throw a TypeError exception.
vm.throw_exception<TypeError>(global_object, ErrorType::TemporalInvalidPlainTimeLikeObject);
return {};
}
// 6. Return result.
return result;
}
// 4.5.4 RegulateTime ( hour, minute, second, millisecond, microsecond, nanosecond, overflow ), https://tc39.es/proposal-temporal/#sec-temporal-regulatetime
Optional<TemporalTime> regulate_time(GlobalObject& global_object, double hour, double minute, double second, double millisecond, double microsecond, double nanosecond, StringView overflow)
{

View file

@ -60,6 +60,15 @@ struct UnregulatedTemporalTime {
double nanosecond;
};
struct PartialUnregulatedTemporalTime {
Optional<double> hour;
Optional<double> minute;
Optional<double> second;
Optional<double> millisecond;
Optional<double> microsecond;
Optional<double> nanosecond;
};
// Table 3: Properties of a TemporalTimeLike, https://tc39.es/proposal-temporal/#table-temporal-temporaltimelike-properties
template<typename StructT, typename ValueT>
@ -82,6 +91,7 @@ auto temporal_time_like_properties = [](VM& vm) {
};
PlainTime* to_temporal_time(GlobalObject&, Value item, Optional<StringView> overflow = {});
Optional<PartialUnregulatedTemporalTime> to_partial_time(GlobalObject&, Object& temporal_time_like);
Optional<TemporalTime> regulate_time(GlobalObject&, double hour, double minute, double second, double millisecond, double microsecond, double nanosecond, StringView overflow);
bool is_valid_time(double hour, double minute, double second, double millisecond, double microsecond, double nanosecond);
DaysAndTime balance_time(i64 hour, i64 minute, i64 second, i64 millisecond, i64 microsecond, i64 nanosecond);

View file

@ -38,6 +38,7 @@ void PlainTimePrototype::initialize(GlobalObject& global_object)
define_native_accessor(vm.names.nanosecond, nanosecond_getter, {}, Attribute::Configurable);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(vm.names.with, with, 1, attr);
define_native_function(vm.names.equals, equals, 1, attr);
define_native_function(vm.names.toPlainDateTime, to_plain_date_time, 1, attr);
define_native_function(vm.names.getISOFields, get_iso_fields, 0, attr);
@ -148,6 +149,115 @@ JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::nanosecond_getter)
return Value(temporal_time->iso_nanosecond());
}
// 4.3.12 Temporal.PlainTime.prototype.with ( temporalTimeLike [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.with
JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::with)
{
// 1. Let temporalTime be the this value.
// 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).
auto* temporal_time = typed_this(global_object);
if (vm.exception())
return {};
auto temporal_time_like_argument = vm.argument(0);
// 3. If Type(temporalTimeLike) is not Object, then
if (!temporal_time_like_argument.is_object()) {
// a. Throw a TypeError exception.
vm.throw_exception<TypeError>(global_object, ErrorType::NotAnObject, temporal_time_like_argument.to_string_without_side_effects());
return {};
}
auto& temporal_time_like = temporal_time_like_argument.as_object();
// 4. Perform ? RejectTemporalCalendarType(temporalTimeLike).
reject_temporal_calendar_type(global_object, temporal_time_like);
if (vm.exception())
return {};
// 5. Let calendarProperty be ? Get(temporalTimeLike, "calendar").
auto calendar_property = temporal_time_like.get(vm.names.calendar);
if (vm.exception())
return {};
// 6. If calendarProperty is not undefined, then
if (!calendar_property.is_undefined()) {
// a. Throw a TypeError exception.
vm.throw_exception<TypeError>(global_object, ErrorType::TemporalPlainTimeWithArgumentMustNotHave, "calendar");
return {};
}
// 7. Let timeZoneProperty be ? Get(temporalTimeLike, "timeZone").
auto time_zone_property = temporal_time_like.get(vm.names.timeZone);
if (vm.exception())
return {};
// 8. If timeZoneProperty is not undefined, then
if (!time_zone_property.is_undefined()) {
// a. Throw a TypeError exception.
vm.throw_exception<TypeError>(global_object, ErrorType::TemporalPlainTimeWithArgumentMustNotHave, "timeZone");
return {};
}
// 9. Let partialTime be ? ToPartialTime(temporalTimeLike).
auto partial_time = to_partial_time(global_object, temporal_time_like);
if (vm.exception())
return {};
// 10. Set options to ? GetOptionsObject(options).
auto* options = get_options_object(global_object, vm.argument(1));
if (vm.exception())
return {};
// 11. Let overflow be ? ToTemporalOverflow(options).
auto overflow = to_temporal_overflow(global_object, *options);
if (vm.exception())
return {};
// 12. If partialTime.[[Hour]] is not undefined, then
// a. Let hour be partialTime.[[Hour]].
// 13. Else,
// a. Let hour be temporalTime.[[ISOHour]].
auto hour = partial_time->hour.value_or(temporal_time->iso_hour());
// 14. If partialTime.[[Minute]] is not undefined, then
// a. Let minute be partialTime.[[Minute]].
// 15. Else,
// a. Let minute be temporalTime.[[ISOMinute]].
auto minute = partial_time->minute.value_or(temporal_time->iso_minute());
// 16. If partialTime.[[Second]] is not undefined, then
// a. Let second be partialTime.[[Second]].
// 17. Else,
// a. Let second be temporalTime.[[ISOSecond]].
auto second = partial_time->second.value_or(temporal_time->iso_second());
// 18. If partialTime.[[Millisecond]] is not undefined, then
// a. Let millisecond be partialTime.[[Millisecond]].
// 19. Else,
// a. Let millisecond be temporalTime.[[ISOMillisecond]].
auto millisecond = partial_time->millisecond.value_or(temporal_time->iso_millisecond());
// 20. If partialTime.[[Microsecond]] is not undefined, then
// a. Let microsecond be partialTime.[[Microsecond]].
// 21. Else,
// a. Let microsecond be temporalTime.[[ISOMicrosecond]].
auto microsecond = partial_time->microsecond.value_or(temporal_time->iso_microsecond());
// 22. If partialTime.[[Nanosecond]] is not undefined, then
// a. Let nanosecond be partialTime.[[Nanosecond]].
// 23. Else,
// a. Let nanosecond be temporalTime.[[ISONanosecond]].
auto nanosecond = partial_time->nanosecond.value_or(temporal_time->iso_nanosecond());
// 24. Let result be ? RegulateTime(hour, minute, second, millisecond, microsecond, nanosecond, overflow).
auto result = regulate_time(global_object, hour, minute, second, millisecond, microsecond, nanosecond, *overflow);
if (vm.exception())
return {};
// 25. Return ? CreateTemporalTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]).
return create_temporal_time(global_object, result->hour, result->minute, result->second, result->millisecond, result->microsecond, result->nanosecond);
}
// 4.3.16 Temporal.PlainTime.prototype.equals ( other ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.equals
JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::equals)
{

View file

@ -26,6 +26,7 @@ private:
JS_DECLARE_NATIVE_FUNCTION(millisecond_getter);
JS_DECLARE_NATIVE_FUNCTION(microsecond_getter);
JS_DECLARE_NATIVE_FUNCTION(nanosecond_getter);
JS_DECLARE_NATIVE_FUNCTION(with);
JS_DECLARE_NATIVE_FUNCTION(equals);
JS_DECLARE_NATIVE_FUNCTION(to_plain_date_time);
JS_DECLARE_NATIVE_FUNCTION(get_iso_fields);