Explorar o código

LibJS: Implement the ToTemporalDate Abstract Operation

This is required by most Temporal.PlainDate.prototype methods.
Idan Horowitz %!s(int64=4) %!d(string=hai) anos
pai
achega
cf78efaef5

+ 1 - 0
Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h

@@ -136,6 +136,7 @@ namespace JS {
     P(exec)                                  \
     P(exec)                                  \
     P(exp)                                   \
     P(exp)                                   \
     P(expm1)                                 \
     P(expm1)                                 \
+    P(fields)                                \
     P(fill)                                  \
     P(fill)                                  \
     P(filter)                                \
     P(filter)                                \
     P(finally)                               \
     P(finally)                               \

+ 79 - 11
Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp

@@ -7,6 +7,7 @@
 
 
 #include <AK/CharacterTypes.h>
 #include <AK/CharacterTypes.h>
 #include <AK/DateTimeLexer.h>
 #include <AK/DateTimeLexer.h>
+#include <LibJS/Runtime/IteratorOperations.h>
 #include <LibJS/Runtime/Temporal/AbstractOperations.h>
 #include <LibJS/Runtime/Temporal/AbstractOperations.h>
 #include <LibJS/Runtime/Temporal/Duration.h>
 #include <LibJS/Runtime/Temporal/Duration.h>
 #include <LibJS/Runtime/Temporal/PlainDate.h>
 #include <LibJS/Runtime/Temporal/PlainDate.h>
@@ -15,6 +16,64 @@
 
 
 namespace JS::Temporal {
 namespace JS::Temporal {
 
 
+static Optional<OptionType> to_option_type(Value value)
+{
+    if (value.is_boolean())
+        return OptionType::Boolean;
+    if (value.is_string())
+        return OptionType::String;
+    if (value.is_number())
+        return OptionType::Number;
+    return {};
+}
+
+// 13.1 IterableToListOfType ( items, elementTypes ), https://tc39.es/proposal-temporal/#sec-iterabletolistoftype
+MarkedValueList iterable_to_list_of_type(GlobalObject& global_object, Value items, Vector<OptionType> const& element_types)
+{
+    auto& vm = global_object.vm();
+    auto& heap = global_object.heap();
+
+    // 1. Let iteratorRecord be ? GetIterator(items, sync).
+    auto iterator_record = get_iterator(global_object, items, IteratorHint::Sync);
+    if (vm.exception())
+        return MarkedValueList { heap };
+
+    // 2. Let values be a new empty List.
+    MarkedValueList values(heap);
+
+    // 3. Let next be true.
+    auto next = true;
+    // 4. Repeat, while next is not false,
+    while (next) {
+        // a. Set next to ? IteratorStep(iteratorRecord).
+        auto* iterator_result = iterator_step(global_object, *iterator_record);
+        if (vm.exception())
+            return MarkedValueList { heap };
+        next = iterator_result;
+
+        // b. If next is not false, then
+        if (next) {
+            // i. Let nextValue be ? IteratorValue(next).
+            auto next_value = iterator_value(global_object, *iterator_result);
+            if (vm.exception())
+                return MarkedValueList { heap };
+            // ii. If Type(nextValue) is not an element of elementTypes, then
+            if (auto type = to_option_type(next_value); !type.has_value() || !element_types.contains_slow(*type)) {
+                // 1. Let completion be ThrowCompletion(a newly created TypeError object).
+                vm.throw_exception<TypeError>(global_object, ErrorType::FixmeAddAnErrorString);
+                // 2. Return ? IteratorClose(iteratorRecord, completion).
+                iterator_close(*iterator_record);
+                return MarkedValueList { heap };
+            }
+            // iii. Append nextValue to the end of the List values.
+            values.append(next_value);
+        }
+    }
+
+    // 5. Return values.
+    return values;
+}
+
 // 13.2 GetOptionsObject ( options ), https://tc39.es/proposal-temporal/#sec-getoptionsobject
 // 13.2 GetOptionsObject ( options ), https://tc39.es/proposal-temporal/#sec-getoptionsobject
 Object* get_options_object(GlobalObject& global_object, Value options)
 Object* get_options_object(GlobalObject& global_object, Value options)
 {
 {
@@ -37,17 +96,6 @@ Object* get_options_object(GlobalObject& global_object, Value options)
     return {};
     return {};
 }
 }
 
 
-static Optional<OptionType> to_option_type(Value value)
-{
-    if (value.is_boolean())
-        return OptionType::Boolean;
-    if (value.is_string())
-        return OptionType::String;
-    if (value.is_number())
-        return OptionType::Number;
-    return {};
-}
-
 // 13.3 GetOption ( options, property, types, values, fallback ), https://tc39.es/proposal-temporal/#sec-getoption
 // 13.3 GetOption ( options, property, types, values, fallback ), https://tc39.es/proposal-temporal/#sec-getoption
 Value get_option(GlobalObject& global_object, Object& options, String const& property, Vector<OptionType> const& types, Vector<StringView> const& values, Value fallback)
 Value get_option(GlobalObject& global_object, Object& options, String const& property, Vector<OptionType> const& types, Vector<StringView> const& values, Value fallback)
 {
 {
@@ -464,6 +512,26 @@ Optional<String> parse_temporal_calendar_string([[maybe_unused]] GlobalObject& g
     return id_part.value();
     return id_part.value();
 }
 }
 
 
+// 13.38 ParseTemporalDateString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaldatestring
+Optional<TemporalDate> parse_temporal_date_string(GlobalObject& global_object, 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 TemporalDateString (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 new Record { [[Year]]: result.[[Year]], [[Month]]: result.[[Month]], [[Day]]: result.[[Day]], [[Calendar]]: result.[[Calendar]] }.
+    return TemporalDate { .year = result->year, .month = result->month, .day = result->day, .calendar = move(result->calendar) };
+}
+
 // 13.40 ParseTemporalDurationString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaldurationstring
 // 13.40 ParseTemporalDurationString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaldurationstring
 Optional<TemporalDuration> parse_temporal_duration_string(GlobalObject& global_object, String const& iso_string)
 Optional<TemporalDuration> parse_temporal_duration_string(GlobalObject& global_object, String const& iso_string)
 {
 {

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

@@ -59,6 +59,7 @@ struct TemporalTimeZone {
     Optional<String> name;
     Optional<String> name;
 };
 };
 
 
+MarkedValueList iterable_to_list_of_type(GlobalObject&, Value items, Vector<OptionType> const& element_types);
 Object* get_options_object(GlobalObject&, Value options);
 Object* get_options_object(GlobalObject&, Value options);
 Value get_option(GlobalObject&, Object& options, String const& property, Vector<OptionType> const& types, Vector<StringView> const& values, Value fallback);
 Value get_option(GlobalObject&, Object& options, String const& property, Vector<OptionType> const& types, Vector<StringView> const& values, Value fallback);
 Optional<String> to_temporal_overflow(GlobalObject&, Object& normalized_options);
 Optional<String> to_temporal_overflow(GlobalObject&, Object& normalized_options);
@@ -70,6 +71,7 @@ BigInt* round_number_to_increment(GlobalObject&, BigInt const&, u64 increment, S
 Optional<ISODateTime> parse_iso_date_time(GlobalObject&, String const& iso_string);
 Optional<ISODateTime> parse_iso_date_time(GlobalObject&, String const& iso_string);
 Optional<TemporalInstant> parse_temporal_instant_string(GlobalObject&, String const& iso_string);
 Optional<TemporalInstant> parse_temporal_instant_string(GlobalObject&, String const& iso_string);
 Optional<String> parse_temporal_calendar_string(GlobalObject&, String const& iso_string);
 Optional<String> parse_temporal_calendar_string(GlobalObject&, String const& iso_string);
+Optional<TemporalDate> parse_temporal_date_string(GlobalObject&, String const& iso_string);
 Optional<TemporalDuration> parse_temporal_duration_string(GlobalObject&, String const& iso_string);
 Optional<TemporalDuration> parse_temporal_duration_string(GlobalObject&, String const& iso_string);
 Optional<TemporalTimeZone> parse_temporal_time_zone_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);
 double to_positive_integer_or_infinity(GlobalObject&, Value argument);

+ 83 - 0
Userland/Libraries/LibJS/Runtime/Temporal/Calendar.cpp

@@ -5,6 +5,7 @@
  */
  */
 
 
 #include <LibJS/Runtime/AbstractOperations.h>
 #include <LibJS/Runtime/AbstractOperations.h>
+#include <LibJS/Runtime/Array.h>
 #include <LibJS/Runtime/GlobalObject.h>
 #include <LibJS/Runtime/GlobalObject.h>
 #include <LibJS/Runtime/Temporal/AbstractOperations.h>
 #include <LibJS/Runtime/Temporal/AbstractOperations.h>
 #include <LibJS/Runtime/Temporal/Calendar.h>
 #include <LibJS/Runtime/Temporal/Calendar.h>
@@ -82,6 +83,41 @@ Calendar* get_iso8601_calendar(GlobalObject& global_object)
     return get_builtin_calendar(global_object, "iso8601");
     return get_builtin_calendar(global_object, "iso8601");
 }
 }
 
 
+// 12.1.5 CalendarFields ( calendar, fieldNames ), https://tc39.es/proposal-temporal/#sec-temporal-calendarfields
+Vector<String> calendar_fields(GlobalObject& global_object, Object& calendar, Vector<StringView> const& field_names)
+{
+    auto& vm = global_object.vm();
+
+    // 1. Let fields be ? GetMethod(calendar, "fields").
+    auto fields = Value(&calendar).get_method(global_object, vm.names.fields);
+    if (vm.exception())
+        return {};
+
+    // 2. Let fieldsArray be ! CreateArrayFromList(fieldNames).
+    Vector<Value> field_names_values;
+    for (auto& field_name : field_names)
+        field_names_values.append(js_string(vm, field_name));
+    Value fields_array = Array::create_from(global_object, field_names_values);
+
+    // 3. If fields is not undefined, then
+    if (fields) {
+        // a. Set fieldsArray to ? Call(fields, calendar, « fieldsArray »).
+        fields_array = vm.call(*fields, &calendar, fields_array);
+        if (vm.exception())
+            return {};
+    }
+
+    // 4. Return ? IterableToListOfType(fieldsArray, « String »).
+    auto list = iterable_to_list_of_type(global_object, fields_array, { OptionType::String });
+    if (vm.exception())
+        return {};
+
+    Vector<String> result;
+    for (auto& value : list)
+        result.append(value.as_string().string());
+    return result;
+}
+
 // 12.1.21 ToTemporalCalendar ( temporalCalendarLike ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalcalendar
 // 12.1.21 ToTemporalCalendar ( temporalCalendarLike ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalcalendar
 Object* to_temporal_calendar(GlobalObject& global_object, Value temporal_calendar_like)
 Object* to_temporal_calendar(GlobalObject& global_object, Value temporal_calendar_like)
 {
 {
@@ -148,6 +184,53 @@ Object* to_temporal_calendar_with_iso_default(GlobalObject& global_object, Value
     return to_temporal_calendar(global_object, temporal_calendar_like);
     return to_temporal_calendar(global_object, temporal_calendar_like);
 }
 }
 
 
+// 12.1.23 GetTemporalCalendarWithISODefault ( item ), https://tc39.es/proposal-temporal/#sec-temporal-gettemporalcalendarwithisodefault
+Object* get_temporal_calendar_with_iso_default(GlobalObject& global_object, Object& item)
+{
+    auto& vm = global_object.vm();
+
+    // 1. If item has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalTime]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
+    // TODO: The rest of the Temporal built-ins
+    if (is<PlainDate>(item)) {
+        // a. Return item.[[Calendar]].
+        return &static_cast<PlainDate&>(item).calendar();
+    }
+
+    // 2. Let calendar be ? Get(item, "calendar").
+    auto calendar = item.get(vm.names.calendar);
+    if (vm.exception())
+        return {};
+
+    // 3. Return ? ToTemporalCalendarWithISODefault(calendar).
+    return to_temporal_calendar_with_iso_default(global_object, calendar);
+}
+
+// 12.1.24 DateFromFields ( calendar, fields, options ), https://tc39.es/proposal-temporal/#sec-temporal-datefromfields
+PlainDate* date_from_fields(GlobalObject& global_object, Object& calendar, Object& fields, Object& options)
+{
+    auto& vm = global_object.vm();
+
+    // 1. Assert: Type(calendar) is Object.
+    // 2. Assert: Type(fields) is Object.
+
+    // 3. Let date be ? Invoke(calendar, "dateFromFields", « fields, options »).
+    auto date = calendar.invoke(vm.names.dateFromFields, &fields, &options);
+    if (vm.exception())
+        return {};
+
+    // 4. Perform ? RequireInternalSlot(date, [[InitializedTemporalDate]]).
+    auto* date_object = date.to_object(global_object);
+    if (!date_object)
+        return {};
+    if (!is<PlainDate>(date_object)) {
+        vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "Temporal.PlainDate");
+        return {};
+    }
+
+    // 5. Return date.
+    return static_cast<PlainDate*>(date_object);
+}
+
 // 12.1.30 IsISOLeapYear ( year ), https://tc39.es/proposal-temporal/#sec-temporal-isisoleapyear
 // 12.1.30 IsISOLeapYear ( year ), https://tc39.es/proposal-temporal/#sec-temporal-isisoleapyear
 bool is_iso_leap_year(i32 year)
 bool is_iso_leap_year(i32 year)
 {
 {

+ 3 - 0
Userland/Libraries/LibJS/Runtime/Temporal/Calendar.h

@@ -33,8 +33,11 @@ Calendar* create_temporal_calendar(GlobalObject&, String const& identifier, Func
 bool is_builtin_calendar(String const& identifier);
 bool is_builtin_calendar(String const& identifier);
 Calendar* get_builtin_calendar(GlobalObject&, String const& identifier);
 Calendar* get_builtin_calendar(GlobalObject&, String const& identifier);
 Calendar* get_iso8601_calendar(GlobalObject&);
 Calendar* get_iso8601_calendar(GlobalObject&);
+Vector<String> calendar_fields(GlobalObject&, Object& calendar, Vector<StringView> const& field_names);
 Object* to_temporal_calendar(GlobalObject&, Value);
 Object* to_temporal_calendar(GlobalObject&, Value);
 Object* to_temporal_calendar_with_iso_default(GlobalObject&, Value);
 Object* to_temporal_calendar_with_iso_default(GlobalObject&, Value);
+Object* get_temporal_calendar_with_iso_default(GlobalObject&, Object&);
+PlainDate* date_from_fields(GlobalObject&, Object& calendar, Object& fields, Object& options);
 bool is_iso_leap_year(i32 year);
 bool is_iso_leap_year(i32 year);
 i32 iso_days_in_month(i32 year, i32 month);
 i32 iso_days_in_month(i32 year, i32 month);
 String build_iso_month_code(i32 month);
 String build_iso_month_code(i32 month);

+ 76 - 0
Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp

@@ -66,6 +66,82 @@ PlainDate* create_temporal_date(GlobalObject& global_object, i32 iso_year, i32 i
     return object;
     return object;
 }
 }
 
 
+// 3.5.2 ToTemporalDate ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal-totemporaldate
+PlainDate* to_temporal_date(GlobalObject& global_object, Value item, Object* options)
+{
+    auto& vm = global_object.vm();
+
+    // 1. If options is not present, set options to ! OrdinaryObjectCreate(null).
+    if (!options)
+        options = Object::create(global_object, nullptr);
+
+    // 2. Assert: Type(options) is Object.
+
+    // 3. If Type(item) is Object, then
+    if (item.is_object()) {
+        auto& item_object = item.as_object();
+        // a. If item has an [[InitializedTemporalDate]] internal slot, then
+        if (is<PlainDate>(item_object)) {
+            // i. Return item.
+            return static_cast<PlainDate*>(&item_object);
+        }
+
+        // b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then
+        // i. Let instant be ! CreateTemporalInstant(item.[[Nanoseconds]]).
+        // ii. Let plainDateTime be ? BuiltinTimeZoneGetPlainDateTimeFor(item.[[TimeZone]], instant, item.[[Calendar]]).
+        // iii. Return ! CreateTemporalDate(plainDateTime.[[ISOYear]], plainDateTime.[[ISOMonth]], plainDateTime.[[ISODay]], plainDateTime.[[Calendar]]).
+        // TODO
+
+        // c. If item has an [[InitializedTemporalDateTime]] internal slot, then
+        // i. Return ! CreateTemporalDate(item.[[ISOYear]], item.[[ISOMonth]], item.[[ISODay]], item.[[Calendar]]).
+        // TODO
+
+        // d. Let calendar be ? GetTemporalCalendarWithISODefault(item).
+        auto* calendar = get_temporal_calendar_with_iso_default(global_object, item_object);
+        if (vm.exception())
+            return {};
+
+        // e. Let fieldNames be ? CalendarFields(calendar, « "day", "month", "monthCode", "year" »).
+        auto field_names = calendar_fields(global_object, *calendar, { "day"sv, "month"sv, "monthCode"sv, "year"sv });
+        if (vm.exception())
+            return {};
+
+        // f. Let fields be ? PrepareTemporalFields(item, fieldNames, «»).
+        auto* fields = prepare_temporal_fields(global_object, item_object, field_names, {});
+        if (vm.exception())
+            return {};
+
+        // g. Return ? DateFromFields(calendar, fields, options).
+        return date_from_fields(global_object, *calendar, *fields, *options);
+    }
+
+    // 4. Perform ? ToTemporalOverflow(options).
+    (void)to_temporal_overflow(global_object, *options);
+    if (vm.exception())
+        return {};
+
+    // 5. Let string be ? ToString(item).
+    auto string = item.to_string(global_object);
+    if (vm.exception())
+        return {};
+
+    // 6. Let result be ? ParseTemporalDateString(string).
+    auto result = parse_temporal_date_string(global_object, string);
+    if (vm.exception())
+        return {};
+
+    // 7. Assert: ! IsValidISODate(result.[[Year]], result.[[Month]], result.[[Day]]) is true.
+    VERIFY(is_valid_iso_date(result->year, result->month, result->day));
+
+    // 8. Let calendar be ? ToTemporalCalendarWithISODefault(result.[[Calendar]]).
+    auto calendar = to_temporal_calendar_with_iso_default(global_object, result->calendar.has_value() ? js_string(vm, *result->calendar) : js_undefined());
+    if (vm.exception())
+        return {};
+
+    // 9. Return ? CreateTemporalDate(result.[[Year]], result.[[Month]], result.[[Day]], calendar).
+    return create_temporal_date(global_object, result->year, result->month, result->day, *calendar);
+}
+
 // 3.5.4 RegulateISODate ( year, month, day, overflow ), https://tc39.es/proposal-temporal/#sec-temporal-regulateisodate
 // 3.5.4 RegulateISODate ( year, month, day, overflow ), https://tc39.es/proposal-temporal/#sec-temporal-regulateisodate
 Optional<TemporalDate> regulate_iso_date(GlobalObject& global_object, double year, double month, double day, String const& overflow)
 Optional<TemporalDate> regulate_iso_date(GlobalObject& global_object, double year, double month, double day, String const& overflow)
 {
 {

+ 1 - 0
Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.h

@@ -35,6 +35,7 @@ private:
 };
 };
 
 
 PlainDate* create_temporal_date(GlobalObject&, i32 iso_year, i32 iso_month, i32 iso_day, Object& calendar, FunctionObject* new_target = nullptr);
 PlainDate* create_temporal_date(GlobalObject&, i32 iso_year, i32 iso_month, i32 iso_day, Object& calendar, FunctionObject* new_target = nullptr);
+PlainDate* to_temporal_date(GlobalObject&, Value item, Object* options = nullptr);
 Optional<TemporalDate> regulate_iso_date(GlobalObject&, double year, double month, double day, String const& overflow);
 Optional<TemporalDate> regulate_iso_date(GlobalObject&, double year, double month, double day, String const& overflow);
 bool is_valid_iso_date(i32 year, i32 month, i32 day);
 bool is_valid_iso_date(i32 year, i32 month, i32 day);