Bläddra i källkod

LibJS: Add the ToTemporalTime AO and stub the ParseTemporalTimeString AO

This AO is required for a bunch of PlainTime related methods.

As part of this change the `TemporalTime` record was renamed to
`UnregulatedTemporalTime` and a new `TemporalTime` record that matches
the other Temporal parse result records was added. This also has the
added benefit of getting rid of a would be round-trip cast from integer
to double and back in `ParseTemporalTimeString`.
Idan Horowitz 3 år sedan
förälder
incheckning
f6370fe3f7

+ 20 - 0
Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp

@@ -588,6 +588,26 @@ Optional<TemporalDuration> parse_temporal_duration_string(GlobalObject& global_o
     TODO();
 }
 
+// 13.43 ParseTemporalTimeString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaltimestring
+Optional<TemporalTime> parse_temporal_time_string(GlobalObject& global_object, [[maybe_unused]] String const& iso_string)
+{
+    auto& vm = global_object.vm();
+
+    // 1. Assert: Type(isoString) is String.
+
+    // 2. If isoString does not satisfy the syntax of a TemporalTimeString (see 13.33), then
+    // a. Throw a RangeError exception.
+    // TODO
+
+    // 3. Let result be ? ParseISODateTime(isoString).
+    auto result = parse_iso_date_time(global_object, iso_string);
+    if (vm.exception())
+        return {};
+
+    // 4. Return the Record { [[Hour]]: result.[[Hour]], [[Minute]]: result.[[Minute]], [[Second]]: result.[[Second]], [[Millisecond]]: result.[[Millisecond]], [[Microsecond]]: result.[[Microsecond]], [[Nanosecond]]: result.[[Nanosecond]], [[Calendar]]: result.[[Calendar]] }.
+    return TemporalTime { .hour = result->hour, .minute = result->minute, .second = result->second, .millisecond = result->millisecond, .microsecond = result->microsecond, .nanosecond = result->nanosecond, .calendar = move(result->calendar) };
+}
+
 // 13.44 ParseTemporalTimeZoneString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaltimezonestring
 Optional<TemporalTimeZone> parse_temporal_time_zone_string(GlobalObject& global_object, [[maybe_unused]] String const& iso_string)
 {

+ 11 - 0
Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h

@@ -53,6 +53,16 @@ struct TemporalDate {
     Optional<String> calendar;
 };
 
+struct TemporalTime {
+    u8 hour;
+    u8 minute;
+    u8 second;
+    u16 millisecond;
+    u16 microsecond;
+    u16 nanosecond;
+    Optional<String> calendar = {};
+};
+
 struct TemporalTimeZone {
     bool z;
     Optional<String> offset;
@@ -75,6 +85,7 @@ Optional<String> parse_temporal_calendar_string(GlobalObject&, String const& iso
 Optional<TemporalDate> parse_temporal_date_string(GlobalObject&, String const& iso_string);
 Optional<ISODateTime> parse_temporal_date_time_string(GlobalObject&, String const& iso_string);
 Optional<TemporalDuration> parse_temporal_duration_string(GlobalObject&, String const& iso_string);
+Optional<TemporalTime> parse_temporal_time_string(GlobalObject&, String const& iso_string);
 Optional<TemporalTimeZone> parse_temporal_time_zone_string(GlobalObject&, String const& iso_string);
 double to_positive_integer_or_infinity(GlobalObject&, Value argument);
 Object* prepare_temporal_fields(GlobalObject&, Object& fields, Vector<String> const& field_names, Vector<StringView> const& required_fields);

+ 8 - 10
Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp

@@ -103,10 +103,8 @@ Optional<ISODateTime> interpret_temporal_date_time_fields(GlobalObject& global_o
 {
     auto& vm = global_object.vm();
 
-    Optional<TemporalTime> time_result;
-
     // 1. Let timeResult be ? ToTemporalTimeRecord(fields).
-    time_result = to_temporal_time_record(global_object, fields);
+    auto unregulated_time_result = to_temporal_time_record(global_object, fields);
     if (vm.exception())
         return {};
 
@@ -121,7 +119,7 @@ Optional<ISODateTime> interpret_temporal_date_time_fields(GlobalObject& global_o
         return {};
 
     // 4. Let timeResult be ? RegulateTime(timeResult.[[Hour]], timeResult.[[Minute]], timeResult.[[Second]], timeResult.[[Millisecond]], timeResult.[[Microsecond]], timeResult.[[Nanosecond]], overflow).
-    time_result = regulate_time(global_object, time_result->hour, time_result->minute, time_result->second, time_result->millisecond, time_result->microsecond, time_result->nanosecond, *overflow);
+    auto time_result = regulate_time(global_object, unregulated_time_result->hour, unregulated_time_result->minute, unregulated_time_result->second, unregulated_time_result->millisecond, unregulated_time_result->microsecond, unregulated_time_result->nanosecond, *overflow);
     if (vm.exception())
         return {};
 
@@ -130,12 +128,12 @@ Optional<ISODateTime> interpret_temporal_date_time_fields(GlobalObject& global_o
         .year = temporal_date->iso_year(),
         .month = temporal_date->iso_month(),
         .day = temporal_date->iso_day(),
-        .hour = static_cast<u8>(time_result->hour),
-        .minute = static_cast<u8>(time_result->minute),
-        .second = static_cast<u8>(time_result->second),
-        .millisecond = static_cast<u16>(time_result->millisecond),
-        .microsecond = static_cast<u16>(time_result->microsecond),
-        .nanosecond = static_cast<u16>(time_result->nanosecond),
+        .hour = time_result->hour,
+        .minute = time_result->minute,
+        .second = time_result->second,
+        .millisecond = time_result->millisecond,
+        .microsecond = time_result->microsecond,
+        .nanosecond = time_result->nanosecond,
     };
 }
 

+ 105 - 5
Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp

@@ -10,8 +10,12 @@
 #include <LibJS/Runtime/Object.h>
 #include <LibJS/Runtime/Temporal/AbstractOperations.h>
 #include <LibJS/Runtime/Temporal/Calendar.h>
+#include <LibJS/Runtime/Temporal/Instant.h>
+#include <LibJS/Runtime/Temporal/PlainDateTime.h>
 #include <LibJS/Runtime/Temporal/PlainTime.h>
 #include <LibJS/Runtime/Temporal/PlainTimeConstructor.h>
+#include <LibJS/Runtime/Temporal/TimeZone.h>
+#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
 
 namespace JS::Temporal {
 
@@ -34,6 +38,102 @@ void PlainTime::visit_edges(Visitor& visitor)
     visitor.visit(&m_calendar);
 }
 
+// 4.5.2 ToTemporalTime ( item [ , overflow ] ), https://tc39.es/proposal-temporal/#sec-temporal-totemporaltime
+PlainTime* to_temporal_time(GlobalObject& global_object, Value item, Optional<StringView> overflow)
+{
+    auto& vm = global_object.vm();
+
+    // 1. If overflow is not present, set it to "constrain".
+    if (!overflow.has_value())
+        overflow = "constrain"sv;
+
+    // 2. Assert: overflow is either "constrain" or "reject".
+    VERIFY(overflow == "constrain"sv || overflow == "reject"sv);
+
+    Optional<TemporalTime> result;
+
+    // 3. If Type(item) is Object, then
+    if (item.is_object()) {
+        auto& item_object = item.as_object();
+
+        // a. If item has an [[InitializedTemporalTime]] internal slot, then
+        if (is<PlainTime>(item_object)) {
+            // i. Return item.
+            return &static_cast<PlainTime&>(item_object);
+        }
+
+        // b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then
+        if (is<ZonedDateTime>(item_object)) {
+            auto& zoned_date_time = static_cast<ZonedDateTime&>(item_object);
+            // i. Let instant be ! CreateTemporalInstant(item.[[Nanoseconds]]).
+            auto* instant = create_temporal_instant(global_object, zoned_date_time.nanoseconds());
+            // ii. Set plainDateTime to ? BuiltinTimeZoneGetPlainDateTimeFor(item.[[TimeZone]], instant, item.[[Calendar]]).
+            auto* plain_date_time = builtin_time_zone_get_plain_date_time_for(global_object, &zoned_date_time.time_zone(), *instant, zoned_date_time.calendar());
+            if (vm.exception())
+                return {};
+            // iii. Return ! CreateTemporalTime(plainDateTime.[[ISOHour]], plainDateTime.[[ISOMinute]], plainDateTime.[[ISOSecond]], plainDateTime.[[ISOMillisecond]], plainDateTime.[[ISOMicrosecond]], plainDateTime.[[ISONanosecond]]).
+            return create_temporal_time(global_object, plain_date_time->iso_hour(), plain_date_time->iso_minute(), plain_date_time->iso_second(), plain_date_time->iso_millisecond(), plain_date_time->iso_microsecond(), plain_date_time->iso_nanosecond());
+        }
+
+        // c. If item has an [[InitializedTemporalDateTime]] internal slot, then
+        if (is<PlainDateTime>(item_object)) {
+            auto& plain_date_time = static_cast<PlainDateTime&>(item_object);
+            // i. Return ! CreateTemporalTime(item.[[ISOHour]], item.[[ISOMinute]], item.[[ISOSecond]], item.[[ISOMillisecond]], item.[[ISOMicrosecond]], item.[[ISONanosecond]]).
+            return create_temporal_time(global_object, plain_date_time.iso_hour(), plain_date_time.iso_minute(), plain_date_time.iso_second(), plain_date_time.iso_millisecond(), plain_date_time.iso_microsecond(), plain_date_time.iso_nanosecond());
+        }
+
+        // d. Let calendar be ? GetTemporalCalendarWithISODefault(item).
+        auto* calendar = get_temporal_calendar_with_iso_default(global_object, item_object);
+        if (vm.exception())
+            return {};
+
+        // e. If ? ToString(calendar) is not "iso8601", then
+        auto calendar_identifier = Value(calendar).to_string(global_object);
+        if (vm.exception())
+            return {};
+        if (calendar_identifier != "iso8601"sv) {
+            // i. Throw a RangeError exception.
+            vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidCalendarIdentifier, calendar_identifier);
+            return {};
+        }
+
+        // f. Let result be ? ToTemporalTimeRecord(item).
+        auto unregulated_result = to_temporal_time_record(global_object, item_object);
+        if (vm.exception())
+            return {};
+
+        // g. Set result to ? RegulateTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]], overflow).
+        result = regulate_time(global_object, unregulated_result->hour, unregulated_result->minute, unregulated_result->second, unregulated_result->millisecond, unregulated_result->microsecond, unregulated_result->nanosecond, *overflow);
+        if (vm.exception())
+            return {};
+    }
+    // 4. Else,
+    else {
+        // a. Let string be ? ToString(item).
+        auto string = item.to_string(global_object);
+        if (vm.exception())
+            return {};
+
+        // b. Let result be ? ParseTemporalTimeString(string).
+        result = parse_temporal_time_string(global_object, string);
+        if (vm.exception())
+            return {};
+
+        // c. Assert: ! IsValidTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]) is true.
+        VERIFY(is_valid_time(result->hour, result->minute, result->second, result->millisecond, result->microsecond, result->nanosecond));
+
+        // d. If result.[[Calendar]] is not one of undefined or "iso8601", then
+        if (result->calendar.has_value() && *result->calendar != "iso8601"sv) {
+            // i. Throw a RangeError exception.
+            vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidCalendarIdentifier, *result->calendar);
+            return {};
+        }
+    }
+
+    // 5. 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.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)
 {
@@ -61,7 +161,7 @@ Optional<TemporalTime> regulate_time(GlobalObject& global_object, double hour, d
         }
 
         // b. Return the Record { [[Hour]]: hour, [[Minute]]: minute, [[Second]]: second, [[Millisecond]]: millisecond, [[Microsecond]]: microsecond, [[Nanosecond]]: nanosecond }.
-        return TemporalTime { .hour = hour, .minute = minute, .second = second, .millisecond = millisecond, .microsecond = microsecond, .nanosecond = nanosecond };
+        return TemporalTime { .hour = static_cast<u8>(hour), .minute = static_cast<u8>(minute), .second = static_cast<u8>(second), .millisecond = static_cast<u16>(millisecond), .microsecond = static_cast<u16>(microsecond), .nanosecond = static_cast<u16>(nanosecond) };
     }
 
     VERIFY_NOT_REACHED();
@@ -189,7 +289,7 @@ TemporalTime constrain_time(double hour, double minute, double second, double mi
     nanosecond = constrain_to_range(nanosecond, 0, 999);
 
     // 8. Return the Record { [[Hour]]: hour, [[Minute]]: minute, [[Second]]: second, [[Millisecond]]: millisecond, [[Microsecond]]: microsecond, [[Nanosecond]]: nanosecond }.
-    return TemporalTime { .hour = hour, .minute = minute, .second = second, .millisecond = millisecond, .microsecond = microsecond, .nanosecond = nanosecond };
+    return TemporalTime { .hour = static_cast<u8>(hour), .minute = static_cast<u8>(minute), .second = static_cast<u8>(second), .millisecond = static_cast<u16>(millisecond), .microsecond = static_cast<u16>(microsecond), .nanosecond = static_cast<u16>(nanosecond) };
 }
 
 // 4.5.8 CreateTemporalTime ( hour, minute, second, millisecond, microsecond, nanosecond [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporaltime
@@ -226,17 +326,17 @@ PlainTime* create_temporal_time(GlobalObject& global_object, u8 hour, u8 minute,
 }
 
 // 4.5.9 ToTemporalTimeRecord ( temporalTimeLike ), https://tc39.es/proposal-temporal/#sec-temporal-totemporaltimerecord
-Optional<TemporalTime> to_temporal_time_record(GlobalObject& global_object, Object& temporal_time_like)
+Optional<UnregulatedTemporalTime> to_temporal_time_record(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 = TemporalTime {};
+    auto result = UnregulatedTemporalTime {};
 
     // 3. For each row of Table 3, except the header row, in table order, do
-    for (auto& [internal_slot, property] : temporal_time_like_properties<TemporalTime, double>(vm)) {
+    for (auto& [internal_slot, property] : temporal_time_like_properties<UnregulatedTemporalTime, double>(vm)) {
         // a. Let property be the Property value of the current row.
 
         // b. Let value be ? Get(temporalTimeLike, property).

+ 3 - 2
Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.h

@@ -51,7 +51,7 @@ struct DaysAndTime {
     u16 nanosecond;
 };
 
-struct TemporalTime {
+struct UnregulatedTemporalTime {
     double hour;
     double minute;
     double second;
@@ -81,12 +81,13 @@ auto temporal_time_like_properties = [](VM& vm) {
     };
 };
 
+PlainTime* to_temporal_time(GlobalObject&, Value item, Optional<StringView> overflow = {});
 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);
 TemporalTime constrain_time(double hour, double minute, double second, double millisecond, double microsecond, double nanosecond);
 PlainTime* create_temporal_time(GlobalObject&, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, FunctionObject* new_target = nullptr);
-Optional<TemporalTime> to_temporal_time_record(GlobalObject&, Object& temporal_time_like);
+Optional<UnregulatedTemporalTime> to_temporal_time_record(GlobalObject&, Object& temporal_time_like);
 i8 compare_temporal_time(u8 hour1, u8 minute1, u8 second1, u16 millisecond1, u16 microsecond1, u16 nanosecond1, u8 hour2, u8 minute2, u8 second2, u16 millisecond2, u16 microsecond2, u16 nanosecond2);
 
 }