Переглянути джерело

LibJS: Throw on conversion from TimeZone to Calendar and vice versa

This is a normative change in the Temporal spec.

See: https://github.com/tc39/proposal-temporal/commit/2084e77
Linus Groh 2 роки тому
батько
коміт
b0e7d59b8b

+ 2 - 0
Userland/Libraries/LibJS/Runtime/ErrorTypes.h

@@ -282,6 +282,8 @@
     M(TemporalPropertyMustBeFinite, "Property must not be Infinity")                                                                    \
     M(TemporalPropertyMustBePositiveInteger, "Property must be a positive integer")                                                     \
     M(TemporalTimeZoneOffsetStringMismatch, "Time zone offset string mismatch: '{}' is not equal to '{}'")                              \
+    M(TemporalUnexpectedCalendarObject, "Got unexpected Calendar object in conversion to TimeZone")                                     \
+    M(TemporalUnexpectedTimeZoneObject, "Got unexpected TimeZone object in conversion to Calendar")                                     \
     M(TemporalUnknownCriticalAnnotation, "Unknown annotation key in critical annotation: '{}'")                                         \
     M(TemporalZonedDateTimeRoundZeroOrNegativeLengthDay, "Cannot round a ZonedDateTime in a calendar or time zone that has zero or "    \
                                                          "negative length days")                                                        \

+ 17 - 5
Userland/Libraries/LibJS/Runtime/Temporal/Calendar.cpp

@@ -20,6 +20,7 @@
 #include <LibJS/Runtime/Temporal/PlainMonthDay.h>
 #include <LibJS/Runtime/Temporal/PlainTime.h>
 #include <LibJS/Runtime/Temporal/PlainYearMonth.h>
+#include <LibJS/Runtime/Temporal/TimeZone.h>
 #include <LibJS/Runtime/Temporal/ZonedDateTime.h>
 #include <LibJS/Runtime/Value.h>
 
@@ -446,16 +447,27 @@ ThrowCompletionOr<Object*> to_temporal_calendar(VM& vm, Value temporal_calendar_
         if (is<ZonedDateTime>(temporal_calendar_like_object))
             return &static_cast<ZonedDateTime&>(temporal_calendar_like_object).calendar();
 
-        // c. If ? HasProperty(temporalCalendarLike, "calendar") is false, return temporalCalendarLike.
+        // c. If temporalCalendarLike has an [[InitializedTemporalTimeZone]] internal slot, throw a RangeError exception.
+        if (is<TimeZone>(temporal_calendar_like_object))
+            return vm.throw_completion<RangeError>(ErrorType::TemporalUnexpectedTimeZoneObject);
+
+        // d. If ? HasProperty(temporalCalendarLike, "calendar") is false, return temporalCalendarLike.
         if (!TRY(temporal_calendar_like_object.has_property(vm.names.calendar)))
             return &temporal_calendar_like_object;
 
-        // d. Set temporalCalendarLike to ? Get(temporalCalendarLike, "calendar").
+        // e. Set temporalCalendarLike to ? Get(temporalCalendarLike, "calendar").
         temporal_calendar_like = TRY(temporal_calendar_like_object.get(vm.names.calendar));
 
-        // e. If Type(temporalCalendarLike) is Object and ? HasProperty(temporalCalendarLike, "calendar") is false, return temporalCalendarLike.
-        if (temporal_calendar_like.is_object() && !TRY(temporal_calendar_like.as_object().has_property(vm.names.calendar)))
-            return &temporal_calendar_like.as_object();
+        // f. If Type(temporalCalendarLike) is Object, then
+        if (temporal_calendar_like.is_object()) {
+            // i. If temporalCalendarLike has an [[InitializedTemporalTimeZone]] internal slot, throw a RangeError exception.
+            if (is<TimeZone>(temporal_calendar_like.as_object()))
+                return vm.throw_completion<RangeError>(ErrorType::TemporalUnexpectedTimeZoneObject);
+
+            // ii. If ? HasProperty(temporalCalendarLike, "calendar") is false, return temporalCalendarLike.
+            if (!TRY(temporal_calendar_like.as_object().has_property(vm.names.calendar)))
+                return &temporal_calendar_like.as_object();
+        }
     }
 
     // 2. Let identifier be ? ToString(temporalCalendarLike).

+ 17 - 5
Userland/Libraries/LibJS/Runtime/Temporal/TimeZone.cpp

@@ -13,6 +13,7 @@
 #include <LibJS/Runtime/GlobalObject.h>
 #include <LibJS/Runtime/IteratorOperations.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/TimeZone.h>
@@ -307,16 +308,27 @@ ThrowCompletionOr<Object*> to_temporal_time_zone(VM& vm, Value temporal_time_zon
             return &zoned_date_time.time_zone();
         }
 
-        // c. If ? HasProperty(temporalTimeZoneLike, "timeZone") is false, return temporalTimeZoneLike.
+        // c. If temporalTimeZoneLike has an [[InitializedTemporalCalendar]] internal slot, throw a RangeError exception.
+        if (is<Calendar>(temporal_time_zone_like.as_object()))
+            return vm.throw_completion<RangeError>(ErrorType::TemporalUnexpectedCalendarObject);
+
+        // d. If ? HasProperty(temporalTimeZoneLike, "timeZone") is false, return temporalTimeZoneLike.
         if (!TRY(temporal_time_zone_like.as_object().has_property(vm.names.timeZone)))
             return &temporal_time_zone_like.as_object();
 
-        // d. Set temporalTimeZoneLike to ? Get(temporalTimeZoneLike, "timeZone").
+        // e. Set temporalTimeZoneLike to ? Get(temporalTimeZoneLike, "timeZone").
         temporal_time_zone_like = TRY(temporal_time_zone_like.as_object().get(vm.names.timeZone));
 
-        // e. If Type(temporalTimeZoneLike) is Object and ? HasProperty(temporalTimeZoneLike, "timeZone") is false, return temporalTimeZoneLike.
-        if (temporal_time_zone_like.is_object() && !TRY(temporal_time_zone_like.as_object().has_property(vm.names.timeZone)))
-            return &temporal_time_zone_like.as_object();
+        // f. If Type(temporalTimeZoneLike) is Object, then
+        if (temporal_time_zone_like.is_object()) {
+            // i. If temporalTimeZoneLike has an [[InitializedTemporalCalendar]] internal slot, throw a RangeError exception.
+            if (is<Calendar>(temporal_time_zone_like.as_object()))
+                return vm.throw_completion<RangeError>(ErrorType::TemporalUnexpectedCalendarObject);
+
+            // ii. If ? HasProperty(temporalTimeZoneLike, "timeZone") is false, return temporalTimeZoneLike.
+            if (!TRY(temporal_time_zone_like.as_object().has_property(vm.names.timeZone)))
+                return &temporal_time_zone_like.as_object();
+        }
     }
 
     // 2. Let identifier be ? ToString(temporalTimeZoneLike).

+ 12 - 0
Userland/Libraries/LibJS/Tests/builtins/Temporal/Calendar/Calendar.from.js

@@ -43,3 +43,15 @@ describe("normal behavior", () => {
         expect(madeObservableHasPropertyLookup).toBeFalse();
     });
 });
+
+describe("errors", () => {
+    test("Calendar from TimeZone", () => {
+        const timeZone = new Temporal.TimeZone("UTC");
+        expect(() => {
+            Temporal.Calendar.from(timeZone);
+        }).toThrowWithMessage(
+            RangeError,
+            "Got unexpected TimeZone object in conversion to Calendar"
+        );
+    });
+});

+ 12 - 0
Userland/Libraries/LibJS/Tests/builtins/Temporal/TimeZone/TimeZone.from.js

@@ -64,3 +64,15 @@ describe("normal behavior", () => {
         expect(madeObservableHasPropertyLookup).toBeFalse();
     });
 });
+
+describe("errors", () => {
+    test("TimeZone from Calendar", () => {
+        const calendar = new Temporal.Calendar("iso8601");
+        expect(() => {
+            Temporal.TimeZone.from(calendar);
+        }).toThrowWithMessage(
+            RangeError,
+            "Got unexpected Calendar object in conversion to TimeZone"
+        );
+    });
+});