diff --git a/Libraries/LibJS/CMakeLists.txt b/Libraries/LibJS/CMakeLists.txt index d8df7e09d43..c4a5692cece 100644 --- a/Libraries/LibJS/CMakeLists.txt +++ b/Libraries/LibJS/CMakeLists.txt @@ -219,10 +219,12 @@ set(SOURCES Runtime/Temporal/PlainMonthDay.cpp Runtime/Temporal/PlainMonthDayConstructor.cpp Runtime/Temporal/PlainMonthDayPrototype.cpp + Runtime/Temporal/PlainTime.cpp + Runtime/Temporal/PlainTimeConstructor.cpp + Runtime/Temporal/PlainTimePrototype.cpp Runtime/Temporal/PlainYearMonth.cpp Runtime/Temporal/PlainYearMonthConstructor.cpp Runtime/Temporal/PlainYearMonthPrototype.cpp - Runtime/Temporal/PlainTime.cpp Runtime/Temporal/Temporal.cpp Runtime/Temporal/TimeZone.cpp Runtime/TypedArray.cpp diff --git a/Libraries/LibJS/Forward.h b/Libraries/LibJS/Forward.h index 6c71b28cede..ac9f0e9e827 100644 --- a/Libraries/LibJS/Forward.h +++ b/Libraries/LibJS/Forward.h @@ -91,6 +91,7 @@ __JS_ENUMERATE(Duration, duration, DurationPrototype, DurationConstructor) \ __JS_ENUMERATE(PlainDate, plain_date, PlainDatePrototype, PlainDateConstructor) \ __JS_ENUMERATE(PlainMonthDay, plain_month_day, PlainMonthDayPrototype, PlainMonthDayConstructor) \ + __JS_ENUMERATE(PlainTime, plain_time, PlainTimePrototype, PlainTimeConstructor) \ __JS_ENUMERATE(PlainYearMonth, plain_year_month, PlainYearMonthPrototype, PlainYearMonthConstructor) #define JS_ENUMERATE_BUILTIN_NAMESPACE_OBJECTS \ diff --git a/Libraries/LibJS/Print.cpp b/Libraries/LibJS/Print.cpp index 3ee982851b0..746fca19821 100644 --- a/Libraries/LibJS/Print.cpp +++ b/Libraries/LibJS/Print.cpp @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -856,6 +857,13 @@ ErrorOr print_temporal_plain_month_day(JS::PrintContext& print_context, JS return {}; } +ErrorOr print_temporal_plain_time(JS::PrintContext& print_context, JS::Temporal::PlainTime const& plain_time, HashTable&) +{ + TRY(print_type(print_context, "Temporal.PlainTime"sv)); + TRY(js_out(print_context, " \033[34;1m{:02}:{:02}:{:02}.{:03}{:03}{:03}\033[0m", plain_time.time().hour, plain_time.time().minute, plain_time.time().second, plain_time.time().millisecond, plain_time.time().microsecond, plain_time.time().nanosecond)); + return {}; +} + ErrorOr print_temporal_plain_year_month(JS::PrintContext& print_context, JS::Temporal::PlainYearMonth const& plain_year_month, HashTable& seen_objects) { TRY(print_type(print_context, "Temporal.PlainYearMonth"sv)); @@ -986,6 +994,8 @@ ErrorOr print_value(JS::PrintContext& print_context, JS::Value value, Hash return print_temporal_plain_date(print_context, static_cast(object), seen_objects); if (is(object)) return print_temporal_plain_month_day(print_context, static_cast(object), seen_objects); + if (is(object)) + return print_temporal_plain_time(print_context, static_cast(object), seen_objects); if (is(object)) return print_temporal_plain_year_month(print_context, static_cast(object), seen_objects); return print_object(print_context, object, seen_objects); diff --git a/Libraries/LibJS/Runtime/ErrorTypes.h b/Libraries/LibJS/Runtime/ErrorTypes.h index 59850358e2c..f73c2276fec 100644 --- a/Libraries/LibJS/Runtime/ErrorTypes.h +++ b/Libraries/LibJS/Runtime/ErrorTypes.h @@ -278,6 +278,7 @@ M(TemporalInvalidRelativeToStringUTCDesignatorWithoutBracketedTimeZone, "Invalid relativeTo string '{}': must not contain a UTC " \ "designator without bracketed time zone") \ M(TemporalInvalidTime, "Invalid time") \ + M(TemporalInvalidTimeLikeField, "Invalid value {} for time field '{}'") \ M(TemporalInvalidTimeString, "Invalid time string '{}'") \ M(TemporalInvalidTimeStringUTCDesignator, "Invalid time string '{}': must not contain a UTC designator") \ M(TemporalInvalidTimeZoneName, "Invalid time zone name '{}'") \ diff --git a/Libraries/LibJS/Runtime/Intrinsics.cpp b/Libraries/LibJS/Runtime/Intrinsics.cpp index 1ad8621efd9..9c2783aa36d 100644 --- a/Libraries/LibJS/Runtime/Intrinsics.cpp +++ b/Libraries/LibJS/Runtime/Intrinsics.cpp @@ -105,6 +105,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp b/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp index 0d5e34d85c5..6e1b5df7898 100644 --- a/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp +++ b/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp @@ -628,6 +628,8 @@ ThrowCompletionOr is_partial_temporal_object(VM& vm, Value value) return false; if (is(object)) return false; + if (is(object)) + return false; if (is(object)) return false; diff --git a/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp b/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp index 3dcf5e3c010..d586398c4ba 100644 --- a/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp +++ b/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp @@ -11,10 +11,20 @@ #include #include #include +#include #include namespace JS::Temporal { +GC_DEFINE_ALLOCATOR(PlainTime); + +// 4 Temporal.PlainTime Objects, https://tc39.es/proposal-temporal/#sec-temporal-plaintime-objects +PlainTime::PlainTime(Time const& time, Object& prototype) + : Object(ConstructWithPrototypeTag::Tag, prototype) + , m_time(time) +{ +} + // FIXME: We should add a generic floor() method to our BigInt classes. But for now, since we know we are only dividing // by powers of 10, we can implement a very situationally specific method to compute the floor of a division. static TimeDuration big_floor(TimeDuration const& numerator, Crypto::UnsignedBigInteger const& denominator) @@ -93,6 +103,83 @@ TimeDuration difference_time(Time const& time1, Time const& time2) return time_duration; } +// 4.5.6 ToTemporalTime ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal-totemporaltime +ThrowCompletionOr> to_temporal_time(VM& vm, Value item, Value options) +{ + // 1. If options is not present, set options to undefined. + + Time time; + + // 2. If item is an Object, then + if (item.is_object()) { + auto const& object = item.as_object(); + + // a. If item has an [[InitializedTemporalTime]] internal slot, then + if (is(object)) { + auto const& plain_time = static_cast(object); + + // i. Let resolvedOptions be ? GetOptionsObject(options). + auto resolved_options = TRY(get_options_object(vm, options)); + + // ii. Perform ? GetTemporalOverflowOption(resolvedOptions). + TRY(get_temporal_overflow_option(vm, resolved_options)); + + // iii. Return ! CreateTemporalTime(item.[[Time]]). + return MUST(create_temporal_time(vm, plain_time.time())); + } + + // FIXME: b. If item has an [[InitializedTemporalDateTime]] internal slot, then + // FIXME: i. Let resolvedOptions be ? GetOptionsObject(options). + // FIXME: ii. Perform ? GetTemporalOverflowOption(resolvedOptions). + // FIXME: iii. Return ! CreateTemporalTime(item.[[ISODateTime]].[[Time]]). + + // FIXME: c. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then + // FIXME: i. Let isoDateTime be GetISODateTimeFor(item.[[TimeZone]], item.[[EpochNanoseconds]]). + // FIXME: ii. Let resolvedOptions be ? GetOptionsObject(options). + // FIXME: iii. Perform ? GetTemporalOverflowOption(resolvedOptions). + // FIXME: iv. Return ! CreateTemporalTime(isoDateTime.[[Time]]). + + // d. Let result be ? ToTemporalTimeRecord(item). + auto result = TRY(to_temporal_time_record(vm, object)); + + // e. Let resolvedOptions be ? GetOptionsObject(options). + auto resolved_options = TRY(get_options_object(vm, options)); + + // f. Let overflow be ? GetTemporalOverflowOption(resolvedOptions). + auto overflow = TRY(get_temporal_overflow_option(vm, resolved_options)); + + // g. Set result to ? RegulateTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]], overflow). + time = TRY(regulate_time(vm, *result.hour, *result.minute, *result.second, *result.millisecond, *result.microsecond, *result.nanosecond, overflow)); + } + // 3. Else, + else { + // a. If item is not a String, throw a TypeError exception. + if (!item.is_string()) + return vm.throw_completion(ErrorType::TemporalInvalidPlainTime); + + // b. Let parseResult be ? ParseISODateTime(item, « TemporalTimeString »). + auto parse_result = TRY(parse_iso_date_time(vm, item.as_string().utf8_string_view(), { { Production::TemporalTimeString } })); + + // c. Assert: parseResult.[[Time]] is not START-OF-DAY. + VERIFY(!parse_result.time.has()); + + // d. Set result to parseResult.[[Time]]. + time = parse_result.time.get