]> git.kaiwu.me - quickjs.git/commitdiff
added missing bound checks to dates in temporal objects and simplified constructor...
authorFabrice Bellard <fabrice@bellard.org>
Mon, 9 Nov 2026 16:36:13 +0000 (17:36 +0100)
committerFabrice Bellard <fabrice@bellard.org>
Mon, 9 Nov 2026 16:36:13 +0000 (17:36 +0100)
temporal/duration.c
temporal/instant.c
temporal/plaindate.c
temporal/plaindatetime.c
temporal/plainmonthday.c
temporal/plaintime.c
temporal/plainyearmonth.c
temporal/temporal.c
temporal/temporal_private.h
temporal/zoneddatetime.c

index 4322709cca199206a8e9d32d561f376cd478e8f5..1e0e96c251dfb87b80c60d8612249e0ad6d7ae69 100644 (file)
@@ -54,7 +54,7 @@ static TIUnit ti_larger_of_two_units(TIUnit a, TIUnit b)
     return a < b ? a : b;
 }
 
-JSValue js_create_duration(JSContext *ctx, const JSDurationData *d)
+static JSValue js_create_duration2(JSContext *ctx, JSValueConst new_target, const JSDurationData *d)
 {
     JSDurationData *s;
     JSValue obj;
@@ -64,7 +64,7 @@ JSValue js_create_duration(JSContext *ctx, const JSDurationData *d)
     *s = *d;
     /* Round fields to float64-representable integers to match spec behavior */
     duration_round_fields_to_float64(s);
-    obj = JS_NewObjectClass(ctx, (int)js_duration_class_id);
+    obj = JS_CreateFromConstructor(ctx, new_target, js_duration_class_id);
     if (JS_IsException(obj)) {
         js_free(ctx, s);
         return JS_EXCEPTION;
@@ -73,6 +73,11 @@ JSValue js_create_duration(JSContext *ctx, const JSDurationData *d)
     return obj;
 }
 
+JSValue js_create_duration(JSContext *ctx, const JSDurationData *d)
+{
+    return js_create_duration2(ctx, JS_UNDEFINED, d);
+}
+
 static int to_integer_if_integral(JSContext *ctx, JSValueConst val, int64_t *pres)
 {
     double d;
@@ -625,10 +630,6 @@ static JSValue js_duration_constructor(JSContext *ctx, JSValueConst new_target,
 {
     JSDurationData d = {0};
     js_int128_t tmp_microseconds = 0, tmp_nanoseconds = 0;
-    JSValue obj;
-    if (JS_IsUndefined(new_target)) {
-        return JS_ThrowTypeError(ctx, "Temporal.Duration constructor cannot be called as a function");
-    }
     if (argc > 0 && to_integer_if_integral(ctx, argv[0], &d.years) < 0)
         return JS_EXCEPTION;
     if (argc > 1 && to_integer_if_integral(ctx, argv[1], &d.months) < 0)
@@ -655,18 +656,7 @@ static JSValue js_duration_constructor(JSContext *ctx, JSValueConst new_target,
         return JS_ThrowRangeError(ctx, "duration out of range");
     if (duration_is_out_of_range(&d))
         return JS_ThrowRangeError(ctx, "duration out of range");
-    {
-        JSDurationData *s = js_mallocz(ctx, sizeof(*s));
-        if (!s)
-            return JS_EXCEPTION;
-        *s = d;
-        obj = js_create_temporal_object(ctx, new_target, js_duration_class_id, s);
-        if (JS_IsException(obj)) {
-            js_free(ctx, s);
-            return JS_EXCEPTION;
-        }
-        return obj;
-    }
+    return js_create_duration2(ctx, new_target, &d);
 }
 
 /* Temporal.Duration.from */
index c163710f438652f0adb99355bd574019ddb3c916..c97ae02012fec2aea32ca93098e6127856eada13 100644 (file)
@@ -25,15 +25,17 @@ static const JSClassDef js_instant_class = {
     .finalizer = js_instant_finalizer,
 };
 
-JSValue js_create_instant(JSContext *ctx, const JSTimeSpec *ts)
+static JSValue js_create_instant2(JSContext *ctx, JSValueConst new_target, const JSTimeSpec *ts)
 {
     JSInstantData *s;
     JSValue obj;
+    if (!is_valid_epoch_timespec(ts))
+        return JS_ThrowRangeError(ctx, "epochNanoseconds out of range");
     s = js_mallocz(ctx, sizeof(*s));
     if (!s)
         return JS_EXCEPTION;
     s->ts = *ts;
-    obj = JS_NewObjectClass(ctx, (int)js_instant_class_id);
+    obj = JS_CreateFromConstructor(ctx, new_target, js_instant_class_id);
     if (JS_IsException(obj)) {
         js_free(ctx, s);
         return JS_EXCEPTION;
@@ -42,6 +44,11 @@ JSValue js_create_instant(JSContext *ctx, const JSTimeSpec *ts)
     return obj;
 }
 
+JSValue js_create_instant(JSContext *ctx, const JSTimeSpec *ts)
+{
+    return js_create_instant2(ctx, JS_UNDEFINED, ts);
+}
+
 /* --- Date math helpers --- */
 
 static int ti_is_leap_year(int64_t y)
@@ -342,32 +349,13 @@ static JSValue ti_to_temporal_instant(JSContext *ctx, JSValueConst item)
 static JSValue js_instant_constructor(JSContext *ctx, JSValueConst new_target,
                                        int argc, JSValueConst *argv)
 {
-    (void)argc;
-    JSValue obj = JS_UNDEFINED;
     JSTimeSpec ts;
-    if (JS_IsUndefined(new_target)) {
-        return JS_ThrowTypeError(ctx, "Temporal.Instant constructor cannot be called as a function");
-    }
     if (JS_IsUndefined(argv[0])) {
         return JS_ThrowTypeError(ctx, "Temporal.Instant requires an epochNanoseconds argument");
     }
     if (ti_bigint_to_timespec(ctx, argv[0], &ts) < 0)
         return JS_EXCEPTION;
-    if (!is_valid_epoch_timespec(&ts)) {
-        return JS_ThrowRangeError(ctx, "epochNanoseconds out of range");
-    }
-    {
-        JSInstantData *s = js_mallocz(ctx, sizeof(*s));
-        if (!s)
-            return JS_EXCEPTION;
-        s->ts = ts;
-        obj = js_create_temporal_object(ctx, new_target, js_instant_class_id, s);
-        if (JS_IsException(obj)) {
-            js_free(ctx, s);
-            return JS_EXCEPTION;
-        }
-        return obj;
-    }
+    return js_create_instant2(ctx, new_target, &ts);
 }
 
 /* Temporal.Instant.fromEpochMilliseconds */
@@ -408,8 +396,6 @@ static JSValue js_instant_fromEpochMilliseconds(JSContext *ctx, JSValueConst thi
     if (js_number_to_int64(ctx, val, &ms) < 0)
         return JS_EXCEPTION;
     ms_to_timespec(ms, &ts);
-    if (!is_valid_epoch_timespec(&ts))
-        return JS_ThrowRangeError(ctx, "epochMilliseconds out of range");
     return js_create_instant(ctx, &ts);
 }
 
@@ -422,8 +408,6 @@ static JSValue js_instant_fromEpochNanoseconds(JSContext *ctx, JSValueConst this
     JSTimeSpec ts;
     if (ti_bigint_to_timespec(ctx, argv[0], &ts) < 0)
         return JS_EXCEPTION;
-    if (!is_valid_epoch_timespec(&ts))
-        return JS_ThrowRangeError(ctx, "epochNanoseconds out of range");
     return js_create_instant(ctx, &ts);
 }
 
@@ -1051,8 +1035,6 @@ static JSValue js_instant_round(JSContext *ctx, JSValueConst this_val,
     
     /* Convert back to JSTimeSpec */
     ti_ns_to_timespec(rounded_ns, &result);
-    if (!is_valid_epoch_timespec(&result))
-        return JS_ThrowRangeError(ctx, "rounded value out of range");
     return js_create_instant(ctx, &result);
 }
 
@@ -1377,11 +1359,6 @@ static JSValue js_instant_add_subtract(JSContext *ctx, JSValueConst this_val,
         total_ns = -total_ns;
 
     total_ns += ti_timespec_to_ns(&s->ts);
-
-    if (total_ns < (js_int128_t)(-INSTANT_MAX_SECS) * 1000000000 ||
-        total_ns > (js_int128_t)INSTANT_MAX_SECS * 1000000000)
-        return JS_ThrowRangeError(ctx, "instant arithmetic overflow");
-
     ti_ns_to_timespec(total_ns, &result);
     return js_create_instant(ctx, &result);
 }
index ef6341e263120c3661145376ba926877a704ce04..8ce1479c9e8bd96251a2d46d6277b8c121c1fc7a 100644 (file)
@@ -23,15 +23,10 @@ static const JSClassDef js_plaindate_class = {
     .finalizer = js_plaindate_finalizer,
 };
 
-JSValue js_create_plaindate_with_calendar(JSContext *ctx, int year, int month, int day, CalendarType calendar)
+static JSValue js_create_plaindate_with_calendar2(JSContext *ctx, JSValueConst new_target, int year, int month, int day, CalendarType calendar)
 {
-    if (calendar == CAL_ISO8601 || calendar == CAL_GREGORY) {
-        /* ISO date range: -271821-04-19 to +275760-09-13 */
-        if (year < -271821 || year > 275760 ||
-            (year == -271821 && (month < 4 || (month == 4 && day < 19))) ||
-            (year == 275760 && (month > 9 || (month == 9 && day > 13))))
-            return JS_ThrowRangeError(ctx, "PlainDate out of range");
-    }
+    if (!ti_is_date_within_iso_limits(year, month, day))
+        return JS_ThrowRangeError(ctx, "PlainDate out of range");
     {
         JSPlainDateData *s;
         JSValue obj;
@@ -42,7 +37,7 @@ JSValue js_create_plaindate_with_calendar(JSContext *ctx, int year, int month, i
         s->month = month;
         s->day = day;
         s->calendar = calendar;
-        obj = JS_NewObjectClass(ctx, (int)js_plaindate_class_id);
+        obj = JS_CreateFromConstructor(ctx, new_target, js_plaindate_class_id);
         if (JS_IsException(obj)) {
             js_free(ctx, s);
             return JS_EXCEPTION;
@@ -52,6 +47,11 @@ JSValue js_create_plaindate_with_calendar(JSContext *ctx, int year, int month, i
     }
 }
 
+JSValue js_create_plaindate_with_calendar(JSContext *ctx, int year, int month, int day, CalendarType calendar)
+{
+    return js_create_plaindate_with_calendar2(ctx, JS_UNDEFINED, year, month, day, calendar);
+}
+
 JSValue js_create_plaindate(JSContext *ctx, int year, int month, int day)
 {
     return js_create_plaindate_with_calendar(ctx, year, month, day, CAL_ISO8601);
@@ -60,20 +60,12 @@ JSValue js_create_plaindate(JSContext *ctx, int year, int month, int day)
 JSValue ti_create_plaindate_from_fields(JSContext *ctx, TIPreparedFields *fields,
                                          int overflow_constrain)
 {
-    int year, month, day;
-    JSValue obj;
+    int year, month, day, iso_y, iso_m, iso_d;
 
     if (ti_resolve_date_from_fields(ctx, fields, overflow_constrain, &year, &month, &day, 1) < 0)
         return JS_EXCEPTION;
-
-    {
-        int iso_y = year, iso_m = month, iso_d = day;
-        if (fields->calendar != CAL_ISO8601 && fields->calendar != CAL_GREGORY) {
-            ti_calendar_date_to_iso(fields->calendar, year, month, day, &iso_y, &iso_m, &iso_d);
-        }
-        obj = js_create_plaindate_with_calendar(ctx, iso_y, iso_m, iso_d, fields->calendar);
-    }
-    return obj;
+    ti_calendar_date_to_iso(fields->calendar, year, month, day, &iso_y, &iso_m, &iso_d);
+    return js_create_plaindate_with_calendar(ctx, iso_y, iso_m, iso_d, fields->calendar);
 }
 
 int pd_is_leap_year(int year)
@@ -84,6 +76,8 @@ int pd_is_leap_year(int year)
 int pd_days_in_month(int year, int month)
 {
     static const int days[] = {31,28,31,30,31,30,31,31,30,31,30,31};
+    if (month < 1 || month > 12)
+        return 30; /* fail safe */
     if (month == 2 && pd_is_leap_year(year))
         return 29;
     return days[month - 1];
@@ -139,9 +133,7 @@ static JSValue js_plaindate_constructor(JSContext *ctx, JSValueConst new_target,
                                          int argc, JSValueConst *argv)
 {
     int year, month, day;
-    if (JS_IsUndefined(new_target)) {
-        return JS_ThrowTypeError(ctx, "Temporal.PlainDate constructor cannot be called as a function");
-    }
+    CalendarType calendar = CAL_ISO8601;
     if (pd_to_integer_with_truncation(ctx, argv[0], &year) < 0)
         return JS_EXCEPTION;
     if (pd_to_integer_with_truncation(ctx, argv[1], &month) < 0)
@@ -150,33 +142,12 @@ static JSValue js_plaindate_constructor(JSContext *ctx, JSValueConst new_target,
         return JS_EXCEPTION;
     if (!pd_is_valid_date(year, month, day))
         return JS_ThrowRangeError(ctx, "invalid PlainDate");
-    if (year < -271821 || year > 275760 ||
-        (year == -271821 && (month < 4 || (month == 4 && day < 19))) ||
-        (year == 275760 && (month > 9 || (month == 9 && day > 13))))
-        return JS_ThrowRangeError(ctx, "PlainDate out of range");
-    {
-        JSPlainDateData *s = js_mallocz(ctx, sizeof(*s));
-        JSValue obj;
-        if (!s)
-            return JS_EXCEPTION;
-        s->year = year;
-        s->month = month;
-        s->day = day;
-        s->calendar = CAL_ISO8601;
-        if (argc > 3) {
-            int cal_res = ti_validate_constructor_calendar(ctx, argv[3], &s->calendar);
-            if (cal_res < 0) {
-                js_free(ctx, s);
-                return JS_EXCEPTION;
-            }
-        }
-        obj = js_create_temporal_object(ctx, new_target, js_plaindate_class_id, s);
-        if (JS_IsException(obj)) {
-            js_free(ctx, s);
+    calendar = CAL_ISO8601;
+    if (argc > 3) {
+        if (ti_validate_constructor_calendar(ctx, argv[3], &calendar) < 0)
             return JS_EXCEPTION;
-        }
-        return obj;
     }
+    return js_create_plaindate_with_calendar2(ctx, new_target, year, month, day, calendar);
 }
 
 /* Getters */
@@ -758,17 +729,7 @@ static JSValue js_plaindate_add_subtract(JSContext *ctx, JSValueConst this_val,
         ti_calendar_date_to_iso(s->calendar, year, month, day, &iso_y, &iso_m, &iso_d);
         year = iso_y; month = iso_m; day = iso_d;
     }
-    if (year < -271821 || year > 275760)
-        return JS_ThrowRangeError(ctx, "invalid PlainDate");
-    /* Check against ISO date bounds */
-    if (year == -271821 && (month < 4 || (month == 4 && day < 19)))
-        return JS_ThrowRangeError(ctx, "invalid PlainDate");
-    if (year == 275760 && (month > 9 || (month == 9 && day > 13)))
-        return JS_ThrowRangeError(ctx, "invalid PlainDate");
-    {
-        JSValue obj = js_create_plaindate_with_calendar(ctx, year, month, day, s->calendar);
-        return obj;
-    }
+    return js_create_plaindate_with_calendar(ctx, year, month, day, s->calendar);
 }
 
 static JSValue js_plaindate_difference(JSContext *ctx, JSValueConst this_val,
@@ -1085,8 +1046,6 @@ static JSValue js_plaindate_toZonedDateTime(JSContext *ctx, JSValueConst this_va
             }
         }
     }
-    if (!is_valid_epoch_timespec(&ts))
-        return JS_ThrowRangeError(ctx, "epoch nanoseconds outside valid limits");
     return js_create_zoneddatetime(ctx, &ts, timeZone, s->calendar);
 }
 
index 38b36466227b68d219fcc50ce4c5c6945b511f40..51f2b712ed01be062089ee310236f9e6ae25a62c 100644 (file)
@@ -40,17 +40,17 @@ JSValue js_create_plaindatetime(JSContext *ctx, int year, int month, int day,
                                                   millisecond, microsecond, nanosecond, CAL_ISO8601);
 }
 
-JSValue js_create_plaindatetime_with_calendar(JSContext *ctx, int year, int month, int day,
-                                                int hour, int minute, int second,
-                                                int millisecond, int microsecond, int nanosecond,
-                                                CalendarType calendar)
+static JSValue js_create_plaindatetime_with_calendar2(JSContext *ctx, JSValueConst new_target, int year, int month, int day,
+                                                      int hour, int minute, int second,
+                                                      int millisecond, int microsecond, int nanosecond,
+                                                      CalendarType calendar)
 {
     JSPlainDateTimeData *s;
     JSValue obj;
-    if ((calendar == CAL_ISO8601 || calendar == CAL_GREGORY) &&
-        !pdt_is_within_limits(year, month, day, hour, minute, second,
-                              millisecond, microsecond, nanosecond))
+    if (!pdt_is_within_limits(year, month, day, hour, minute, second,
+                              millisecond, microsecond, nanosecond)) {
         return JS_ThrowRangeError(ctx, "PlainDateTime outside valid ISO range");
+    }
     s = js_mallocz(ctx, sizeof(*s));
     if (!s)
         return JS_EXCEPTION;
@@ -64,7 +64,7 @@ JSValue js_create_plaindatetime_with_calendar(JSContext *ctx, int year, int mont
     s->microsecond = microsecond;
     s->nanosecond = nanosecond;
     s->calendar = calendar;
-    obj = JS_NewObjectClass(ctx, (int)js_plaindatetime_class_id);
+    obj = JS_CreateFromConstructor(ctx, new_target, js_plaindatetime_class_id);
     if (JS_IsException(obj)) {
         js_free(ctx, s);
         return JS_EXCEPTION;
@@ -73,6 +73,15 @@ JSValue js_create_plaindatetime_with_calendar(JSContext *ctx, int year, int mont
     return obj;
 }
 
+JSValue js_create_plaindatetime_with_calendar(JSContext *ctx, int year, int month, int day,
+                                                int hour, int minute, int second,
+                                                int millisecond, int microsecond, int nanosecond,
+                                                CalendarType calendar)
+{
+    return js_create_plaindatetime_with_calendar2(ctx, JS_UNDEFINED, year, month, day, hour, minute, second,
+                                                  millisecond, microsecond, nanosecond, calendar);
+}
+
 static JSValue ti_create_plaindatetime_from_fields(JSContext *ctx, TIPreparedFields *fields,
                                              int overflow_constrain)
 {
@@ -110,7 +119,7 @@ static JSValue ti_create_plaindatetime_from_fields(JSContext *ctx, TIPreparedFie
         if (ns > 999) ns = 999; else if (ns < 0) ns = 0;
     }
 
-    if (fields->calendar != CAL_ISO8601 && fields->calendar != CAL_GREGORY) {
+    {
         int iso_y, iso_m, iso_d;
         ti_calendar_date_to_iso(fields->calendar, year, month, day, &iso_y, &iso_m, &iso_d);
         year = iso_y; month = iso_m; day = iso_d;
@@ -219,9 +228,7 @@ static JSValue js_plaindatetime_constructor(JSContext *ctx, JSValueConst new_tar
 {
     int year, month, day, hour = 0, minute = 0, second = 0;
     int millisecond = 0, microsecond = 0, nanosecond = 0;
-    if (JS_IsUndefined(new_target)) {
-        return JS_ThrowTypeError(ctx, "Temporal.PlainDateTime constructor cannot be called as a function");
-    }
+    CalendarType calendar = CAL_ISO8601;
     if (JS_IsUndefined(argv[0])) {
         JS_ThrowRangeError(ctx, "value must be an integer");
         return JS_EXCEPTION;
@@ -256,40 +263,13 @@ static JSValue js_plaindatetime_constructor(JSContext *ctx, JSValueConst new_tar
         return JS_ThrowRangeError(ctx, "invalid PlainDateTime date");
     if (!pdt_is_valid_time(hour, minute, second, millisecond, microsecond, nanosecond))
         return JS_ThrowRangeError(ctx, "invalid PlainDateTime time");
-    {
-        CalendarType calendar = CAL_ISO8601;
-        if (argc > 9) {
-            int cal_res = ti_validate_constructor_calendar(ctx, argv[9], &calendar);
-            if (cal_res < 0)
-                return JS_EXCEPTION;
-        }
-        if ((calendar == CAL_ISO8601 || calendar == CAL_GREGORY) &&
-            !pdt_is_within_limits(year, month, day, hour, minute, second,
-                                   millisecond, microsecond, nanosecond))
-            return JS_ThrowRangeError(ctx, "PlainDateTime outside valid range");
-        {
-            JSPlainDateTimeData *s = js_mallocz(ctx, sizeof(*s));
-            JSValue obj;
-            if (!s)
-                return JS_EXCEPTION;
-            s->year = year;
-            s->month = month;
-            s->day = day;
-            s->hour = hour;
-            s->minute = minute;
-            s->second = second;
-            s->millisecond = millisecond;
-            s->microsecond = microsecond;
-            s->nanosecond = nanosecond;
-            s->calendar = calendar;
-        obj = js_create_temporal_object(ctx, new_target, js_plaindatetime_class_id, s);
-        if (JS_IsException(obj)) {
-            js_free(ctx, s);
+    if (argc > 9) {
+        int cal_res = ti_validate_constructor_calendar(ctx, argv[9], &calendar);
+        if (cal_res < 0)
             return JS_EXCEPTION;
-        }
-        return obj;
-        }
     }
+    return js_create_plaindatetime_with_calendar2(ctx, new_target, year, month, day, hour, minute, second,
+                                                  millisecond, microsecond, nanosecond, calendar);
 }
 
 /* Getters */
@@ -963,9 +943,6 @@ static JSValue js_plaindatetime_with(JSContext *ctx, JSValueConst this_val,
         ti_calendar_date_to_iso(s->calendar, year, month, day, &iso_y, &iso_m, &iso_d);
         year = iso_y; month = iso_m; day = iso_d;
     }
-    if (!pdt_is_within_limits(year, month, day, hour, minute, second,
-                              millisecond, microsecond, nanosecond))
-        return JS_ThrowRangeError(ctx, "PlainDateTime outside valid range");
     return js_create_plaindatetime_with_calendar(ctx, year, month, day, hour, minute, second,
                                     millisecond, microsecond, nanosecond, s->calendar);
 }
@@ -1579,8 +1556,6 @@ static JSValue js_plaindatetime_toZonedDateTime(JSContext *ctx, JSValueConst thi
                 }
             }
         }
-        if (!is_valid_epoch_timespec(&ts))
-            return JS_ThrowRangeError(ctx, "epoch nanoseconds outside valid limits");
         return js_create_zoneddatetime(ctx, &ts, timeZone, s->calendar);
     }
 }
index 9fbb4b9ac7d5764d9a9fb36d719c7604fd3f0aec..cc5b51ebfe70d0a6faf2314e232981eb72683c1e 100644 (file)
@@ -24,12 +24,11 @@ static const JSClassDef js_plainmonthday_class = {
     .finalizer = js_plainmonthday_finalizer,
 };
 
-JSValue js_create_plainmonthday_with_calendar(JSContext *ctx, int iso_year, int iso_month, int iso_day, CalendarType calendar)
+static JSValue js_create_plainmonthday_with_calendar2(JSContext *ctx, JSValueConst new_target, int iso_year, int iso_month, int iso_day, CalendarType calendar)
 {
     JSPlainMonthDayData *s;
     JSValue obj;
-    /* Validate ISO year range (ISO date limits: -271821 to 275760) */
-    if (iso_year < -271821 || iso_year > 275760)
+    if (!ti_is_date_within_iso_limits(iso_year, iso_month, iso_day))
         return JS_ThrowRangeError(ctx, "PlainMonthDay out of range");
     s = js_mallocz(ctx, sizeof(*s));
     if (!s)
@@ -38,7 +37,7 @@ JSValue js_create_plainmonthday_with_calendar(JSContext *ctx, int iso_year, int
     s->iso_month = iso_month;
     s->iso_day = iso_day;
     s->calendar = calendar;
-    obj = JS_NewObjectClass(ctx, (int)js_plainmonthday_class_id);
+    obj = JS_CreateFromConstructor(ctx, new_target, js_plainmonthday_class_id);
     if (JS_IsException(obj)) {
         js_free(ctx, s);
         return JS_EXCEPTION;
@@ -47,6 +46,11 @@ JSValue js_create_plainmonthday_with_calendar(JSContext *ctx, int iso_year, int
     return obj;
 }
 
+JSValue js_create_plainmonthday_with_calendar(JSContext *ctx, int iso_year, int iso_month, int iso_day, CalendarType calendar)
+{
+    return js_create_plainmonthday_with_calendar2(ctx, JS_UNDEFINED, iso_year, iso_month, iso_day, calendar);
+}
+
 static int pmd_to_integer_with_truncation(JSContext *ctx, JSValueConst val, int *pres)
 {
     double d;
@@ -73,9 +77,6 @@ static JSValue js_plainmonthday_constructor(JSContext *ctx, JSValueConst new_tar
                                              int argc, JSValueConst *argv)
 {
     int month, day, year = 1972;
-    if (JS_IsUndefined(new_target)) {
-        return JS_ThrowTypeError(ctx, "Temporal.PlainMonthDay constructor cannot be called as a function");
-    }
     if (pmd_to_integer_with_truncation(ctx, argv[0], &month) < 0)
         return JS_EXCEPTION;
     if (pmd_to_integer_with_truncation(ctx, argv[1], &day) < 0)
@@ -95,27 +96,7 @@ static JSValue js_plainmonthday_constructor(JSContext *ctx, JSValueConst new_tar
         }
         if (month < 1 || month > 12 || day < 1 || day > pd_days_in_month(year, month))
             return JS_ThrowRangeError(ctx, "invalid PlainMonthDay");
-        /* ISODateWithinLimits check for CreateTemporalMonthDay */
-        if (year < -271821 || (year == -271821 && (month < 4 || (month == 4 && day < 19))))
-            return JS_ThrowRangeError(ctx, "date outside supported range");
-        if (year > 275760 || (year == 275760 && (month > 9 || (month == 9 && day > 13))))
-            return JS_ThrowRangeError(ctx, "date outside supported range");
-        {
-            JSPlainMonthDayData *s = js_mallocz(ctx, sizeof(*s));
-            JSValue obj;
-            if (!s)
-                return JS_EXCEPTION;
-            s->iso_year = year;
-            s->iso_month = month;
-            s->iso_day = day;
-            s->calendar = calendar;
-            obj = js_create_temporal_object(ctx, new_target, js_plainmonthday_class_id, s);
-            if (JS_IsException(obj)) {
-                js_free(ctx, s);
-                return JS_EXCEPTION;
-            }
-            return obj;
-        }
+        return js_create_plainmonthday_with_calendar2(ctx, new_target, year, month, day, calendar);
     }
 }
 
@@ -409,6 +390,17 @@ static JSValue js_plainmonthday_with(JSContext *ctx, JSValueConst this_val,
             return JS_ThrowRangeError(ctx, "month and monthCode must agree");
         {
             int ref_year = has_year ? year_for_overflow : orig_cal_y;
+            int max_month = ti_calendar_months_in_year(s->calendar, ref_year);
+
+            if (month < 1 || month > max_month) {
+                if (!overflow_constrain)
+                    return JS_ThrowRangeError(ctx, "invalid PlainMonthDay");
+                if (month > max_month)
+                    month = max_month;
+                else if (month < 1)
+                    month = 1;
+            }
+
             int dim = ti_calendar_days_in_month(s->calendar, ref_year, month);
             if (day > dim || day < 1) {
                 if (!overflow_constrain)
index 33d46132861af90061142a3e24fb43c1b34cc234..109bcbdaab91088bf3301376adbdacf0627fefa6 100644 (file)
@@ -23,7 +23,7 @@ static const JSClassDef js_plaintime_class = {
     .finalizer = js_plaintime_finalizer,
 };
 
-JSValue js_create_plaintime(JSContext *ctx, const JSPlainTimeData *t)
+static JSValue js_create_plaintime2(JSContext *ctx, JSValueConst new_target, const JSPlainTimeData *t)
 {
     JSPlainTimeData *s;
     JSValue obj;
@@ -31,7 +31,7 @@ JSValue js_create_plaintime(JSContext *ctx, const JSPlainTimeData *t)
     if (!s)
         return JS_EXCEPTION;
     *s = *t;
-    obj = JS_NewObjectClass(ctx, (int)js_plaintime_class_id);
+    obj = JS_CreateFromConstructor(ctx, new_target, js_plaintime_class_id);
     if (JS_IsException(obj)) {
         js_free(ctx, s);
         return JS_EXCEPTION;
@@ -40,6 +40,11 @@ JSValue js_create_plaintime(JSContext *ctx, const JSPlainTimeData *t)
     return obj;
 }
 
+JSValue js_create_plaintime(JSContext *ctx, const JSPlainTimeData *t)
+{
+    return js_create_plaintime2(ctx, JS_UNDEFINED, t);
+}
+
 /* Forward declaration */
 JSValue js_plaintime_from(JSContext *ctx, JSValueConst this_val,
                            int argc, JSValueConst *argv);
@@ -89,10 +94,6 @@ static JSValue js_plaintime_constructor(JSContext *ctx, JSValueConst new_target,
                                          int argc, JSValueConst *argv)
 {
     JSPlainTimeData t = {0};
-    JSValue obj;
-    if (JS_IsUndefined(new_target)) {
-        return JS_ThrowTypeError(ctx, "Temporal.PlainTime constructor cannot be called as a function");
-    }
     if (argc > 0 && pt_to_integer_with_truncation(ctx, argv[0], &t.hour) < 0)
         return JS_EXCEPTION;
     if (argc > 1 && pt_to_integer_with_truncation(ctx, argv[1], &t.minute) < 0)
@@ -107,18 +108,7 @@ static JSValue js_plaintime_constructor(JSContext *ctx, JSValueConst new_target,
         return JS_EXCEPTION;
     if (!pt_is_valid_time(t.hour, t.minute, t.second, t.millisecond, t.microsecond, t.nanosecond))
         return JS_ThrowRangeError(ctx, "invalid PlainTime");
-    {
-        JSPlainTimeData *s = js_mallocz(ctx, sizeof(*s));
-        if (!s)
-            return JS_EXCEPTION;
-        *s = t;
-        obj = js_create_temporal_object(ctx, new_target, js_plaintime_class_id, s);
-        if (JS_IsException(obj)) {
-            js_free(ctx, s);
-            return JS_EXCEPTION;
-        }
-        return obj;
-    }
+    return js_create_plaintime2(ctx, new_target, &t);
 }
 
 /* Getters */
index 18285df8490a0b7ec51e68d7fa05ced5a5ff8d24..520244b0c618e4cd33f3b764d1df957434bdcfab 100644 (file)
@@ -23,15 +23,20 @@ static const JSClassDef js_plainyearmonth_class = {
     .finalizer = js_plainyearmonth_finalizer,
 };
 
-static JSValue js_create_plainyearmonth(JSContext *ctx, int year, int month, int referenceISODay)
+static int pym_is_valid_year_month(int year, int month)
 {
-    return js_create_plainyearmonth_with_calendar(ctx, year, month, referenceISODay, CAL_ISO8601);
+    return month >= 1 && month <= 12 &&
+           ((year > -271821 || (year == -271821 && month >= 4)) &&
+            (year < 275760 || (year == 275760 && month <= 9)));
 }
 
-JSValue js_create_plainyearmonth_with_calendar(JSContext *ctx, int year, int month, int referenceISODay, CalendarType calendar)
+static JSValue js_create_plainyearmonth_with_calendar2(JSContext *ctx, JSValueConst new_target, int year, int month, int referenceISODay, CalendarType calendar)
 {
     JSPlainYearMonthData *s;
     JSValue obj;
+    /* XXX: test the day too ? */
+    if (!pym_is_valid_year_month(year, month))
+        return JS_ThrowRangeError(ctx, "invalid PlainYearMonth");
     s = js_mallocz(ctx, sizeof(*s));
     if (!s)
         return JS_EXCEPTION;
@@ -39,7 +44,7 @@ JSValue js_create_plainyearmonth_with_calendar(JSContext *ctx, int year, int mon
     s->iso_month = month;
     s->iso_day = referenceISODay;
     s->calendar = calendar;
-    obj = JS_NewObjectClass(ctx, (int)js_plainyearmonth_class_id);
+    obj = JS_CreateFromConstructor(ctx, new_target, js_plainyearmonth_class_id);
     if (JS_IsException(obj)) {
         js_free(ctx, s);
         return JS_EXCEPTION;
@@ -48,12 +53,15 @@ JSValue js_create_plainyearmonth_with_calendar(JSContext *ctx, int year, int mon
     return obj;
 }
 
+JSValue js_create_plainyearmonth_with_calendar(JSContext *ctx, int year, int month, int referenceISODay, CalendarType calendar)
+{
+    return js_create_plainyearmonth_with_calendar2(ctx, JS_UNDEFINED, year, month, referenceISODay, calendar);
+}
+
 static JSValue ti_create_plainyearmonth_from_fields(JSContext *ctx, TIPreparedFields *fields,
                                               int overflow_constrain)
 {
     int year, month, dummy_day = 1;
-    JSValue obj;
-    JSPlainYearMonthData *s;
 
     if (ti_resolve_date_from_fields(ctx, fields, overflow_constrain, &year, &month, &dummy_day, 0) < 0)
         return JS_EXCEPTION;
@@ -72,46 +80,14 @@ static JSValue ti_create_plainyearmonth_from_fields(JSContext *ctx, TIPreparedFi
         }
     }
 
-    if (fields->calendar == CAL_ISO8601 || fields->calendar == CAL_GREGORY) {
-        int is_valid = month >= 1 && month <= 12 &&
-                       ((year > -271821 || (year == -271821 && month >= 4)) &&
-                        (year < 275760 || (year == 275760 && month <= 9)));
-        if (!is_valid)
-            return JS_ThrowRangeError(ctx, "invalid PlainYearMonth");
-    }
-
     {
-        int iso_d = 1;
+        int iso_y, iso_m, iso_d = 1;
         /* Convert calendar date to ISO for all non-ISO/non-Gregory calendars */
-        if (fields->calendar != CAL_ISO8601 && fields->calendar != CAL_GREGORY) {
-            int iso_y, iso_m;
-            ti_calendar_date_to_iso(fields->calendar, year, month, 1, &iso_y, &iso_m, &iso_d);
-            year = iso_y;
-            month = iso_m;
-        }
-        {
-            int is_valid = month >= 1 && month <= 12 &&
-                           ((year > -271821 || (year == -271821 && month >= 4)) &&
-                            (year < 275760 || (year == 275760 && month <= 9)));
-            if (!is_valid)
-                return JS_ThrowRangeError(ctx, "invalid PlainYearMonth");
-        }
-        obj = js_create_plainyearmonth(ctx, year, month, iso_d);
-    }
-    if (!JS_IsException(obj)) {
-        s = JS_GetOpaque(obj, js_plainyearmonth_class_id);
-        if (s) {
-            s->calendar = fields->calendar;
-        }
+        ti_calendar_date_to_iso(fields->calendar, year, month, 1, &iso_y, &iso_m, &iso_d);
+        year = iso_y;
+        month = iso_m;
+        return js_create_plainyearmonth_with_calendar(ctx, year, month, iso_d, fields->calendar);
     }
-    return obj;
-}
-
-static int pym_is_valid_year_month(int year, int month)
-{
-    return month >= 1 && month <= 12 &&
-           ((year > -271821 || (year == -271821 && month >= 4)) &&
-            (year < 275760 || (year == 275760 && month <= 9)));
 }
 
 static int pym_to_integer_with_truncation(JSContext *ctx, JSValueConst val, int *pres)
@@ -136,19 +112,12 @@ static JSValue js_plainyearmonth_constructor(JSContext *ctx, JSValueConst new_ta
                                               int argc, JSValueConst *argv)
 {
     int year, month, day = 1;
-    if (JS_IsUndefined(new_target)) {
-        return JS_ThrowTypeError(ctx, "Temporal.PlainYearMonth constructor cannot be called as a function");
-    }
     if (pym_to_integer_with_truncation(ctx, argv[0], &year) < 0)
         return JS_EXCEPTION;
     if (pym_to_integer_with_truncation(ctx, argv[1], &month) < 0)
         return JS_EXCEPTION;
     if (!pym_is_valid_year_month(year, month))
         return JS_ThrowRangeError(ctx, "invalid PlainYearMonth");
-    if (year < -271821 || (year == -271821 && month < 4))
-        return JS_ThrowRangeError(ctx, "invalid PlainYearMonth");
-    if (year > 275760 || (year == 275760 && month > 9))
-        return JS_ThrowRangeError(ctx, "invalid PlainYearMonth");
     {
         CalendarType calendar = CAL_ISO8601;
         if (argc > 2) {
@@ -164,22 +133,7 @@ static JSValue js_plainyearmonth_constructor(JSContext *ctx, JSValueConst new_ta
             return JS_ThrowRangeError(ctx, "invalid PlainYearMonth");
         if (argc <= 3 || JS_IsUndefined(argv[3]))
             day = 1;
-        {
-            JSPlainYearMonthData *s = js_mallocz(ctx, sizeof(*s));
-            JSValue obj;
-            if (!s)
-                return JS_EXCEPTION;
-            s->iso_year = year;
-            s->iso_month = month;
-            s->iso_day = day;
-            s->calendar = calendar;
-            obj = js_create_temporal_object(ctx, new_target, js_plainyearmonth_class_id, s);
-            if (JS_IsException(obj)) {
-                js_free(ctx, s);
-                return JS_EXCEPTION;
-            }
-            return obj;
-        }
+        return js_create_plainyearmonth_with_calendar2(ctx, new_target, year, month, day, calendar);
     }
 }
 
@@ -672,9 +626,7 @@ static JSValue js_plainyearmonth_add_subtract(JSContext *ctx, JSValueConst this_
     /* Convert ISO to calendar space for arithmetic */
     pym_get_calendar_date(s, &cal_year, &cal_month, &cal_day);
     /* Check that base date is within ISO limits */
-    if (s->iso_year < -271821 || (s->iso_year == -271821 && (s->iso_month < 4 || (s->iso_month == 4 && s->iso_day < 19))))
-        return JS_ThrowRangeError(ctx, "invalid PlainYearMonth");
-    if (s->iso_year > 275760 || (s->iso_year == 275760 && (s->iso_month > 9 || (s->iso_month == 9 && s->iso_day > 13))))
+    if (!ti_is_date_within_iso_limits(s->iso_year, s->iso_month, s->iso_day))
         return JS_ThrowRangeError(ctx, "invalid PlainYearMonth");
     {
         int orig_mc = 0, orig_has_l = 0;
@@ -705,10 +657,6 @@ static JSValue js_plainyearmonth_add_subtract(JSContext *ctx, JSValueConst this_
     {
         int iso_y, iso_m, iso_d;
         ti_calendar_date_to_iso(s->calendar, cal_year, cal_month, cal_day, &iso_y, &iso_m, &iso_d);
-        if (iso_y < -271821 || (iso_y == -271821 && iso_m < 4))
-            return JS_ThrowRangeError(ctx, "invalid PlainYearMonth");
-        if (iso_y > 275760 || (iso_y == 275760 && iso_m > 9))
-            return JS_ThrowRangeError(ctx, "invalid PlainYearMonth");
         return js_create_plainyearmonth_with_calendar(ctx, iso_y, iso_m, iso_d, s->calendar);
     }
 }
index f1ced5d7aeafddb61596bc602f93f9395d6586e5..e6bd521d3730e79c9045774d7a5279c896de5c3c 100644 (file)
@@ -3880,16 +3880,7 @@ int ti_validate_timezone_identifier(JSContext *ctx, JSValueConst v, const char *
     *out_str = str;
     return 1;
 }
-JSValue js_create_temporal_object(JSContext *ctx, JSValueConst new_target,
-                                                 JSClassID class_id, void *opaque)
-{
-    
-    JSValue obj = JS_CreateFromConstructor(ctx, new_target, class_id);
-    if (JS_IsException(obj))
-        return JS_EXCEPTION;
-    JS_SetOpaque(obj, opaque);
-    return obj;
-}
+
 int is_valid_epoch_timespec(const JSTimeSpec *ts)
 {
     if (ts->secs > INSTANT_MAX_SECS || ts->secs < -INSTANT_MAX_SECS)
index c8ba297b0fce1482e7a47bbe814d1734a6ff6d84..53801cc505cef0c304724802c72eec4e66450014 100644 (file)
@@ -209,7 +209,6 @@ void ti_append_calendar_annotation(char *buf, int buf_size, CalendarType cal, in
 int ti_canonicalize_timezone_identifier(const char *str, int64_t offset_ns, char *out, size_t out_size);
 int ti_validate_constructor_calendar(JSContext *ctx, JSValueConst v, CalendarType *out);
 int ti_validate_timezone_identifier(JSContext *ctx, JSValueConst v, const char **out_str);
-JSValue js_create_temporal_object(JSContext *ctx, JSValueConst new_target, JSClassID class_id, void *opaque);
 int is_valid_epoch_timespec(const JSTimeSpec *ts);
 
 static inline js_int128_t ti_sec_nsec_to_ns(int64_t secs, uint32_t nsecs)
index e5c0e022d199cbaa49c0ce3510d53738642d7575..2c3d79152532a535e0ab442c81b76ec10d8e7ac7 100644 (file)
@@ -36,18 +36,20 @@ static const JSClassDef js_zoneddatetime_class = {
     .finalizer = js_zoneddatetime_finalizer,
 };
 
-JSValue js_create_zoneddatetime(JSContext *ctx, const JSTimeSpec *ts,
+static JSValue js_create_zoneddatetime2(JSContext *ctx, JSValueConst new_target, const JSTimeSpec *ts,
                                         const char *timeZone, CalendarType calendar)
 {
     JSZonedDateTimeData *s;
     JSValue obj;
+    if (!is_valid_epoch_timespec(ts))
+        return JS_ThrowRangeError(ctx, "epoch nanoseconds outside valid limits");
     s = js_mallocz(ctx, sizeof(*s));
     if (!s)
         return JS_EXCEPTION;
     s->ts = *ts;
     pstrcpy(s->timeZone, sizeof(s->timeZone), timeZone);
     s->calendar = calendar;
-    obj = JS_NewObjectClass(ctx, (int)js_zoneddatetime_class_id);
+    obj = JS_CreateFromConstructor(ctx, new_target, js_zoneddatetime_class_id);
     if (JS_IsException(obj)) {
         js_free(ctx, s);
         return JS_EXCEPTION;
@@ -56,6 +58,12 @@ JSValue js_create_zoneddatetime(JSContext *ctx, const JSTimeSpec *ts,
     return obj;
 }
 
+JSValue js_create_zoneddatetime(JSContext *ctx, const JSTimeSpec *ts,
+                                const char *timeZone, CalendarType calendar)
+{
+    return js_create_zoneddatetime2(ctx, JS_UNDEFINED, ts, timeZone, calendar);
+}
+    
 /* Apply timezone offset to UTC epoch seconds to get local wall-clock time */
 /* Create a ZonedDateTime from prepared fields */
 JSValue ti_create_zoneddatetime_from_fields(JSContext *ctx, TIPreparedFields *fields,
@@ -234,9 +242,6 @@ JSValue ti_create_zoneddatetime_from_fields(JSContext *ctx, TIPreparedFields *fi
         }
     }
 
-    if (!is_valid_epoch_timespec(&ts))
-        return JS_ThrowRangeError(ctx, "epoch nanoseconds outside valid limits");
-
     return js_create_zoneddatetime(ctx, &ts, timeZone, fields->calendar);
 }
 static void zdt_apply_timezone_offset(JSContext *ctx, JSZonedDateTimeData *s,
@@ -271,15 +276,11 @@ static void zdt_apply_timezone_offset(JSContext *ctx, JSZonedDateTimeData *s,
 static JSValue js_zoneddatetime_constructor(JSContext *ctx, JSValueConst new_target,
                                              int argc, JSValueConst *argv)
 {
-    JSZonedDateTimeData *s;
     JSValue obj;
     JSTimeSpec ts;
     const char *timeZone = "UTC";
     CalendarType calendar = CAL_ISO8601;
     int tz_allocated = 0;
-    if (JS_IsUndefined(new_target)) {
-        return JS_ThrowTypeError(ctx, "Temporal.ZonedDateTime constructor cannot be called as a function");
-    }
     if (ti_bigint_to_timespec(ctx, argv[0], &ts) < 0)
         return JS_EXCEPTION;
     if (!is_valid_epoch_timespec(&ts))
@@ -298,20 +299,7 @@ static JSValue js_zoneddatetime_constructor(JSContext *ctx, JSValueConst new_tar
             return JS_EXCEPTION;
         }
     }
-    s = js_mallocz(ctx, sizeof(*s));
-    if (!s) {
-        if (tz_allocated) JS_FreeCString(ctx, timeZone);
-        return JS_EXCEPTION;
-    }
-    s->ts = ts;
-    pstrcpy(s->timeZone, sizeof(s->timeZone), timeZone);
-    s->calendar = calendar;
-    obj = js_create_temporal_object(ctx, new_target, js_zoneddatetime_class_id, s);
-    if (JS_IsException(obj)) {
-        js_free(ctx, s);
-        if (tz_allocated) JS_FreeCString(ctx, timeZone);
-        return JS_EXCEPTION;
-    }
+    obj = js_create_zoneddatetime2(ctx, new_target, &ts, timeZone, calendar);
     if (tz_allocated) JS_FreeCString(ctx, timeZone);
     return obj;
 }
@@ -902,13 +890,6 @@ static JSValue js_zoneddatetime_startOfDay(JSContext *ctx, JSValueConst this_val
     } else {
         ts.secs -= offset_sec;
     }
-    /* Validate epoch nanoseconds are within Instant limits */
-    {
-        js_int128_t epoch_ns = ti_timespec_to_ns(&ts);
-        js_int128_t limit = (js_int128_t)8640000000000LL * 1000000000LL;
-        if (epoch_ns < -limit || epoch_ns > limit)
-            return JS_ThrowRangeError(ctx, "epoch nanoseconds outside valid limits");
-    }
     return js_create_zoneddatetime(ctx, &ts, s->timeZone, s->calendar);
 }
 
@@ -1657,8 +1638,6 @@ static JSValue js_zoneddatetime_round(JSContext *ctx, JSValueConst this_val,
 
     ti_ns_to_timespec(rounded_ns, &result);
 
-    if (!is_valid_epoch_timespec(&result))
-        return JS_ThrowRangeError(ctx, "outside representable range");
     return js_create_zoneddatetime(ctx, &result, s->timeZone, s->calendar);
 }
 
@@ -2040,8 +2019,6 @@ static JSValue js_zoneddatetime_with(JSContext *ctx, JSValueConst this_val,
             }
         }
     }
-    if (!is_valid_epoch_timespec(&ts))
-        return JS_ThrowRangeError(ctx, "epoch nanoseconds outside valid limits");
     return js_create_zoneddatetime(ctx, &ts, s->timeZone, s->calendar);
 }
 
@@ -2134,8 +2111,6 @@ static JSValue js_zoneddatetime_withPlainTime(JSContext *ctx, JSValueConst this_
         }
     }
     JS_FreeValue(ctx, pt_val);
-    if (!is_valid_epoch_timespec(&ts))
-        return JS_ThrowRangeError(ctx, "invalid epoch nanoseconds");
     return js_create_zoneddatetime(ctx, &ts, s->timeZone, s->calendar);
 }
 
@@ -2755,12 +2730,6 @@ static JSValue js_zoneddatetime_add_subtract(JSContext *ctx, JSValueConst this_v
             ti_ns_to_timespec(total_ns, &ts);
         }
     }
-    {
-        js_int128_t epoch_ns = ti_timespec_to_ns(&ts);
-        js_int128_t limit = (js_int128_t)8640000000000LL * 1000000000LL;
-        if (epoch_ns < -limit || epoch_ns > limit)
-            return JS_ThrowRangeError(ctx, "epoch nanoseconds outside valid limits");
-    }
     return js_create_zoneddatetime(ctx, &ts, s->timeZone, s->calendar);
 }
 
@@ -3398,9 +3367,6 @@ JSValue js_zoneddatetime_from(JSContext *ctx, JSValueConst this_val,
                 ts.nsecs = (uint32_t)new_nsec;
             }
         }
-        /* Validate epoch nanoseconds are within valid range */
-        if (!is_valid_epoch_timespec(&ts))
-            return JS_ThrowRangeError(ctx, "invalid ZonedDateTime string");
         {
             CalendarType cal = r.has_calendar ? r.calendar : CAL_ISO8601;
             if (cal == CAL_UNKNOWN)