1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "builtin/temporal/PlainYearMonth.h"
9 #include "mozilla/Assertions.h"
11 #include <type_traits>
16 #include "NamespaceImports.h"
18 #include "builtin/temporal/Calendar.h"
19 #include "builtin/temporal/Duration.h"
20 #include "builtin/temporal/PlainDate.h"
21 #include "builtin/temporal/Temporal.h"
22 #include "builtin/temporal/TemporalFields.h"
23 #include "builtin/temporal/TemporalParser.h"
24 #include "builtin/temporal/TemporalRoundingMode.h"
25 #include "builtin/temporal/TemporalTypes.h"
26 #include "builtin/temporal/TemporalUnit.h"
27 #include "builtin/temporal/ToString.h"
28 #include "builtin/temporal/Wrapped.h"
29 #include "ds/IdValuePair.h"
30 #include "gc/AllocKind.h"
31 #include "gc/Barrier.h"
32 #include "js/AllocPolicy.h"
33 #include "js/CallArgs.h"
34 #include "js/CallNonGenericMethod.h"
36 #include "js/ErrorReport.h"
37 #include "js/friend/ErrorMessages.h"
38 #include "js/GCVector.h"
40 #include "js/PropertyDescriptor.h"
41 #include "js/PropertySpec.h"
42 #include "js/RootingAPI.h"
43 #include "js/TypeDecls.h"
45 #include "vm/BytecodeUtil.h"
46 #include "vm/GlobalObject.h"
47 #include "vm/JSAtomState.h"
48 #include "vm/JSContext.h"
49 #include "vm/JSObject.h"
50 #include "vm/ObjectOperations.h"
51 #include "vm/PlainObject.h"
52 #include "vm/StringType.h"
54 #include "vm/JSObject-inl.h"
55 #include "vm/NativeObject-inl.h"
58 using namespace js::temporal
;
60 static inline bool IsPlainYearMonth(Handle
<Value
> v
) {
61 return v
.isObject() && v
.toObject().is
<PlainYearMonthObject
>();
65 * ISOYearMonthWithinLimits ( year, month )
68 static bool ISOYearMonthWithinLimits(T year
, int32_t month
) {
69 static_assert(std::is_same_v
<T
, int32_t> || std::is_same_v
<T
, double>);
72 MOZ_ASSERT(IsInteger(year
));
73 MOZ_ASSERT(1 <= month
&& month
<= 12);
76 if (year
< -271821 || year
> 275760) {
81 if (year
== -271821 && month
< 4) {
86 if (year
== 275760 && month
> 9) {
95 * CreateTemporalYearMonth ( isoYear, isoMonth, calendar, referenceISODay [ ,
98 static PlainYearMonthObject
* CreateTemporalYearMonth(
99 JSContext
* cx
, const CallArgs
& args
, double isoYear
, double isoMonth
,
100 double isoDay
, Handle
<CalendarValue
> calendar
) {
101 MOZ_ASSERT(IsInteger(isoYear
));
102 MOZ_ASSERT(IsInteger(isoMonth
));
103 MOZ_ASSERT(IsInteger(isoDay
));
106 if (!ThrowIfInvalidISODate(cx
, isoYear
, isoMonth
, isoDay
)) {
110 // FIXME: spec issue - Consider calling ISODateTimeWithinLimits to include
111 // testing |referenceISODay|?
114 if (!ISOYearMonthWithinLimits(isoYear
, int32_t(isoMonth
))) {
115 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
116 JSMSG_TEMPORAL_PLAIN_YEAR_MONTH_INVALID
);
121 Rooted
<JSObject
*> proto(cx
);
122 if (!GetPrototypeFromBuiltinConstructor(cx
, args
, JSProto_PlainYearMonth
,
127 auto* obj
= NewObjectWithClassProto
<PlainYearMonthObject
>(cx
, proto
);
133 obj
->setFixedSlot(PlainYearMonthObject::ISO_YEAR_SLOT
,
134 Int32Value(int32_t(isoYear
)));
137 obj
->setFixedSlot(PlainYearMonthObject::ISO_MONTH_SLOT
,
138 Int32Value(int32_t(isoMonth
)));
141 obj
->setFixedSlot(PlainYearMonthObject::CALENDAR_SLOT
, calendar
.toValue());
144 obj
->setFixedSlot(PlainYearMonthObject::ISO_DAY_SLOT
,
145 Int32Value(int32_t(isoDay
)));
152 * CreateTemporalYearMonth ( isoYear, isoMonth, calendar, referenceISODay [ ,
155 PlainYearMonthObject
* js::temporal::CreateTemporalYearMonth(
156 JSContext
* cx
, const PlainDate
& date
, Handle
<CalendarValue
> calendar
) {
157 const auto& [isoYear
, isoMonth
, isoDay
] = date
;
160 if (!ThrowIfInvalidISODate(cx
, date
)) {
164 // FIXME: spec issue - Consider calling ISODateTimeWithinLimits to include
165 // testing |referenceISODay|?
168 if (!ISOYearMonthWithinLimits(isoYear
, isoMonth
)) {
169 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
170 JSMSG_TEMPORAL_PLAIN_YEAR_MONTH_INVALID
);
175 auto* obj
= NewBuiltinClassInstance
<PlainYearMonthObject
>(cx
);
181 obj
->setFixedSlot(PlainYearMonthObject::ISO_YEAR_SLOT
, Int32Value(isoYear
));
184 obj
->setFixedSlot(PlainYearMonthObject::ISO_MONTH_SLOT
, Int32Value(isoMonth
));
187 obj
->setFixedSlot(PlainYearMonthObject::CALENDAR_SLOT
, calendar
.toValue());
190 obj
->setFixedSlot(PlainYearMonthObject::ISO_DAY_SLOT
, Int32Value(isoDay
));
197 * ToTemporalYearMonth ( item [ , options ] )
199 static Wrapped
<PlainYearMonthObject
*> ToTemporalYearMonth(
200 JSContext
* cx
, Handle
<Value
> item
,
201 Handle
<JSObject
*> maybeOptions
= nullptr) {
202 // Step 1. (Not applicable in our implementation.)
205 Rooted
<PlainObject
*> maybeResolvedOptions(cx
);
207 maybeResolvedOptions
= SnapshotOwnProperties(cx
, maybeOptions
);
208 if (!maybeResolvedOptions
) {
214 if (item
.isObject()) {
215 Rooted
<JSObject
*> itemObj(cx
, &item
.toObject());
218 if (itemObj
->canUnwrapAs
<PlainYearMonthObject
>()) {
223 Rooted
<CalendarValue
> calendarValue(cx
);
224 if (!GetTemporalCalendarWithISODefault(cx
, itemObj
, &calendarValue
)) {
229 Rooted
<CalendarRecord
> calendar(cx
);
230 if (!CreateCalendarMethodsRecord(cx
, calendarValue
,
232 CalendarMethod::Fields
,
233 CalendarMethod::YearMonthFromFields
,
240 JS::RootedVector
<PropertyKey
> fieldNames(cx
);
241 if (!CalendarFields(cx
, calendar
,
242 {CalendarField::Month
, CalendarField::MonthCode
,
243 CalendarField::Year
},
249 Rooted
<PlainObject
*> fields(cx
,
250 PrepareTemporalFields(cx
, itemObj
, fieldNames
));
256 if (maybeResolvedOptions
) {
257 return CalendarYearMonthFromFields(cx
, calendar
, fields
,
258 maybeResolvedOptions
);
260 return CalendarYearMonthFromFields(cx
, calendar
, fields
);
264 if (!item
.isString()) {
265 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_IGNORE_STACK
, item
,
266 nullptr, "not a string");
269 Rooted
<JSString
*> string(cx
, item
.toString());
273 Rooted
<JSString
*> calendarString(cx
);
274 if (!ParseTemporalYearMonthString(cx
, string
, &result
, &calendarString
)) {
279 Rooted
<CalendarValue
> calendarValue(cx
, CalendarValue(cx
->names().iso8601
));
280 if (calendarString
) {
281 if (!ToBuiltinCalendar(cx
, calendarString
, &calendarValue
)) {
287 if (maybeResolvedOptions
) {
288 TemporalOverflow ignored
;
289 if (!ToTemporalOverflow(cx
, maybeResolvedOptions
, &ignored
)) {
295 Rooted
<PlainYearMonthObject
*> obj(
296 cx
, CreateTemporalYearMonth(cx
, result
, calendarValue
));
302 Rooted
<CalendarRecord
> calendar(cx
);
303 if (!CreateCalendarMethodsRecord(cx
, calendarValue
,
305 CalendarMethod::YearMonthFromFields
,
311 // FIXME: spec issue - reorder note to appear directly before
312 // CalendarYearMonthFromFields
315 return CalendarYearMonthFromFields(cx
, calendar
, obj
);
319 * ToTemporalYearMonth ( item [ , options ] )
321 static bool ToTemporalYearMonth(JSContext
* cx
, Handle
<Value
> item
,
323 auto obj
= ToTemporalYearMonth(cx
, item
);
328 *result
= ToPlainDate(&obj
.unwrap());
333 * ToTemporalYearMonth ( item [ , options ] )
335 static bool ToTemporalYearMonth(JSContext
* cx
, Handle
<Value
> item
,
337 MutableHandle
<CalendarValue
> calendar
) {
338 auto* obj
= ToTemporalYearMonth(cx
, item
).unwrapOrNull();
343 *result
= ToPlainDate(obj
);
344 calendar
.set(obj
->calendar());
345 return calendar
.wrap(cx
);
349 * DifferenceTemporalPlainYearMonth ( operation, yearMonth, other, options )
351 static bool DifferenceTemporalPlainYearMonth(JSContext
* cx
,
352 TemporalDifference operation
,
353 const CallArgs
& args
) {
354 Rooted
<PlainYearMonthObject
*> yearMonth(
355 cx
, &args
.thisv().toObject().as
<PlainYearMonthObject
>());
357 // Step 1. (Not applicable in our implementation.)
360 auto otherYearMonth
= ToTemporalYearMonth(cx
, args
.get(0));
361 if (!otherYearMonth
) {
364 auto* unwrappedOtherYearMonth
= &otherYearMonth
.unwrap();
365 auto otherYearMonthDate
= ToPlainDate(unwrappedOtherYearMonth
);
367 Rooted
<Wrapped
<PlainYearMonthObject
*>> other(cx
, otherYearMonth
);
368 Rooted
<CalendarValue
> otherCalendar(cx
, unwrappedOtherYearMonth
->calendar());
369 if (!otherCalendar
.wrap(cx
)) {
374 Rooted
<CalendarValue
> calendar(cx
, yearMonth
->calendar());
377 if (!CalendarEqualsOrThrow(cx
, calendar
, otherCalendar
)) {
382 DifferenceSettings settings
;
383 Rooted
<PlainObject
*> resolvedOptions(cx
);
384 if (args
.hasDefined(1)) {
385 Rooted
<JSObject
*> options(
386 cx
, RequireObjectArg(cx
, "options", ToName(operation
), args
[1]));
392 resolvedOptions
= SnapshotOwnProperties(cx
, options
);
393 if (!resolvedOptions
) {
398 if (!GetDifferenceSettings(cx
, operation
, resolvedOptions
,
399 TemporalUnitGroup::Date
, TemporalUnit::Month
,
400 TemporalUnit::Month
, TemporalUnit::Year
,
409 TemporalRoundingMode::Trunc
,
415 if (ToPlainDate(yearMonth
) == otherYearMonthDate
) {
416 auto* obj
= CreateTemporalDuration(cx
, {});
421 args
.rval().setObject(*obj
);
426 // FIXME: spec issue - duplicate CreateDataPropertyOrThrow for "largestUnit".
429 Rooted
<CalendarRecord
> calendarRec(cx
);
430 if (!CreateCalendarMethodsRecord(cx
, calendar
,
432 CalendarMethod::DateAdd
,
433 CalendarMethod::DateFromFields
,
434 CalendarMethod::DateUntil
,
435 CalendarMethod::Fields
,
442 JS::RootedVector
<PropertyKey
> fieldNames(cx
);
443 if (!CalendarFields(cx
, calendarRec
,
444 {CalendarField::MonthCode
, CalendarField::Year
},
450 Rooted
<PlainObject
*> thisFields(
451 cx
, PrepareTemporalFields(cx
, yearMonth
, fieldNames
));
457 Value one
= Int32Value(1);
458 auto handleOne
= Handle
<Value
>::fromMarkedLocation(&one
);
459 if (!DefineDataProperty(cx
, thisFields
, cx
->names().day
, handleOne
)) {
464 Rooted
<Wrapped
<PlainDateObject
*>> thisDate(
465 cx
, CalendarDateFromFields(cx
, calendarRec
, thisFields
));
471 Rooted
<PlainObject
*> otherFields(
472 cx
, PrepareTemporalFields(cx
, other
, fieldNames
));
478 if (!DefineDataProperty(cx
, otherFields
, cx
->names().day
, handleOne
)) {
483 Rooted
<Wrapped
<PlainDateObject
*>> otherDate(
484 cx
, CalendarDateFromFields(cx
, calendarRec
, otherFields
));
491 if (resolvedOptions
) {
493 if (!CalendarDateUntil(cx
, calendarRec
, thisDate
, otherDate
,
494 settings
.largestUnit
, resolvedOptions
, &until
)) {
499 if (!CalendarDateUntil(cx
, calendarRec
, thisDate
, otherDate
,
500 settings
.largestUnit
, &until
)) {
505 // We only care about years and months here, all other fields are set to zero.
506 auto dateDuration
= DateDuration
{int64_t(until
.years
), int64_t(until
.months
)};
509 if (settings
.smallestUnit
!= TemporalUnit::Month
||
510 settings
.roundingIncrement
!= Increment
{1}) {
512 NormalizedDuration roundResult
;
513 if (!RoundDuration(cx
, {dateDuration
, {}}, settings
.roundingIncrement
,
514 settings
.smallestUnit
, settings
.roundingMode
, thisDate
,
515 calendarRec
, &roundResult
)) {
521 DateDuration
{roundResult
.date
.years
, roundResult
.date
.months
};
522 if (!temporal::BalanceDateDurationRelative(
523 cx
, toBalance
, settings
.largestUnit
, settings
.smallestUnit
,
524 thisDate
, calendarRec
, &dateDuration
)) {
531 Duration
{double(dateDuration
.years
), double(dateDuration
.months
)};
532 if (operation
== TemporalDifference::Since
) {
533 duration
= duration
.negate();
536 auto* obj
= CreateTemporalDuration(cx
, duration
);
541 args
.rval().setObject(*obj
);
545 enum class PlainYearMonthDuration
{ Add
, Subtract
};
548 * AddDurationToOrSubtractDurationFromPlainYearMonth ( operation, yearMonth,
549 * temporalDurationLike, options )
551 static bool AddDurationToOrSubtractDurationFromPlainYearMonth(
552 JSContext
* cx
, PlainYearMonthDuration operation
, const CallArgs
& args
) {
553 Rooted
<PlainYearMonthObject
*> yearMonth(
554 cx
, &args
.thisv().toObject().as
<PlainYearMonthObject
>());
558 if (!ToTemporalDurationRecord(cx
, args
.get(0), &duration
)) {
563 if (operation
== PlainYearMonthDuration::Subtract
) {
564 duration
= duration
.negate();
568 Rooted
<JSObject
*> options(cx
);
569 if (args
.hasDefined(1)) {
571 operation
== PlainYearMonthDuration::Add
? "add" : "subtract";
572 options
= RequireObjectArg(cx
, "options", name
, args
[1]);
574 // TODO: Avoid creating an options object if not necessary.
575 options
= NewPlainObjectWithProto(cx
, nullptr);
582 auto timeDuration
= NormalizeTimeDuration(duration
);
585 auto balancedTime
= BalanceTimeDuration(timeDuration
, TemporalUnit::Day
);
587 // Steps 6 and 16. (Reordered)
588 Duration durationToAdd
= {
592 duration
.days
+ double(balancedTime
.days
),
596 int32_t sign
= DurationSign(durationToAdd
);
599 Rooted
<CalendarValue
> calendarValue(cx
, yearMonth
->calendar());
600 Rooted
<CalendarRecord
> calendar(cx
);
601 if (!CreateCalendarMethodsRecord(cx
, calendarValue
,
603 CalendarMethod::DateAdd
,
604 CalendarMethod::DateFromFields
,
606 CalendarMethod::Fields
,
607 CalendarMethod::YearMonthFromFields
,
614 JS::RootedVector
<PropertyKey
> fieldNames(cx
);
615 if (!CalendarFields(cx
, calendar
,
616 {CalendarField::MonthCode
, CalendarField::Year
},
622 Rooted
<PlainObject
*> fields(cx
,
623 PrepareTemporalFields(cx
, yearMonth
, fieldNames
));
629 Rooted
<PlainObject
*> fieldsCopy(cx
, SnapshotOwnProperties(cx
, fields
));
635 Value one
= Int32Value(1);
636 auto handleOne
= Handle
<Value
>::fromMarkedLocation(&one
);
637 if (!DefineDataProperty(cx
, fields
, cx
->names().day
, handleOne
)) {
642 Rooted
<Wrapped
<PlainDateObject
*>> intermediateDate(
643 cx
, CalendarDateFromFields(cx
, calendar
, fields
));
644 if (!intermediateDate
) {
649 Rooted
<Wrapped
<PlainDateObject
*>> date(cx
);
651 // |intermediateDate| is initialized to the first day of |yearMonth|'s
652 // month. Compute the last day of |yearMonth|'s month by first adding one
653 // month and then subtracting one day.
655 // This is roughly equivalent to these calls:
657 // js> var ym = new Temporal.PlainYearMonth(2023, 1);
658 // js> ym.toPlainDate({day: 1}).add({months: 1}).subtract({days: 1}).day
661 // For many calendars this is equivalent to `ym.daysInMonth`, except when
662 // some days are skipped, for example consider the Julian-to-Gregorian
663 // calendar transition.
666 auto oneMonthDuration
= DateDuration
{0, 1};
669 Rooted
<Wrapped
<PlainDateObject
*>> nextMonth(
670 cx
, CalendarDateAdd(cx
, calendar
, intermediateDate
, oneMonthDuration
));
675 auto* unwrappedNextMonth
= nextMonth
.unwrap(cx
);
676 if (!unwrappedNextMonth
) {
679 auto nextMonthDate
= ToPlainDate(unwrappedNextMonth
);
682 PlainDate endOfMonthISO
;
683 if (!AddISODate(cx
, nextMonthDate
, {0, 0, 0, -1},
684 TemporalOverflow::Constrain
, &endOfMonthISO
)) {
689 Rooted
<PlainDateWithCalendar
> endOfMonth(cx
);
690 if (!CreateTemporalDate(cx
, endOfMonthISO
, calendar
.receiver(),
696 Rooted
<Value
> day(cx
);
697 if (!CalendarDay(cx
, calendar
, endOfMonth
.date(), &day
)) {
702 if (!DefineDataProperty(cx
, fieldsCopy
, cx
->names().day
, day
)) {
707 date
= CalendarDateFromFields(cx
, calendar
, fieldsCopy
);
713 date
= intermediateDate
;
716 // Step 16. (Moved above)
719 Rooted
<PlainObject
*> optionsCopy(cx
, SnapshotOwnProperties(cx
, options
));
725 Rooted
<Wrapped
<PlainDateObject
*>> addedDate(
726 cx
, AddDate(cx
, calendar
, date
, durationToAdd
, options
));
732 Rooted
<PlainObject
*> addedDateFields(
733 cx
, PrepareTemporalFields(cx
, addedDate
, fieldNames
));
734 if (!addedDateFields
) {
740 CalendarYearMonthFromFields(cx
, calendar
, addedDateFields
, optionsCopy
);
745 args
.rval().setObject(*obj
);
750 * Temporal.PlainYearMonth ( isoYear, isoMonth [ , calendarLike [ ,
751 * referenceISODay ] ] )
753 static bool PlainYearMonthConstructor(JSContext
* cx
, unsigned argc
, Value
* vp
) {
754 CallArgs args
= CallArgsFromVp(argc
, vp
);
757 if (!ThrowIfNotConstructing(cx
, args
, "Temporal.PlainYearMonth")) {
763 if (!ToIntegerWithTruncation(cx
, args
.get(0), "year", &isoYear
)) {
769 if (!ToIntegerWithTruncation(cx
, args
.get(1), "month", &isoMonth
)) {
774 Rooted
<CalendarValue
> calendar(cx
);
775 if (!ToTemporalCalendarWithISODefault(cx
, args
.get(2), &calendar
)) {
781 if (args
.hasDefined(3)) {
782 if (!ToIntegerWithTruncation(cx
, args
[3], "day", &isoDay
)) {
789 CreateTemporalYearMonth(cx
, args
, isoYear
, isoMonth
, isoDay
, calendar
);
794 args
.rval().setObject(*yearMonth
);
799 * Temporal.PlainYearMonth.from ( item [ , options ] )
801 static bool PlainYearMonth_from(JSContext
* cx
, unsigned argc
, Value
* vp
) {
802 CallArgs args
= CallArgsFromVp(argc
, vp
);
805 Rooted
<JSObject
*> options(cx
);
806 if (args
.hasDefined(1)) {
807 options
= RequireObjectArg(cx
, "options", "from", args
[1]);
814 if (args
.get(0).isObject()) {
815 JSObject
* item
= &args
[0].toObject();
817 if (auto* yearMonth
= item
->maybeUnwrapIf
<PlainYearMonthObject
>()) {
818 auto date
= ToPlainDate(yearMonth
);
820 Rooted
<CalendarValue
> calendar(cx
, yearMonth
->calendar());
821 if (!calendar
.wrap(cx
)) {
827 TemporalOverflow ignored
;
828 if (!ToTemporalOverflow(cx
, options
, &ignored
)) {
834 auto* obj
= CreateTemporalYearMonth(cx
, date
, calendar
);
839 args
.rval().setObject(*obj
);
845 auto obj
= ToTemporalYearMonth(cx
, args
.get(0), options
);
850 args
.rval().setObject(*obj
);
855 * Temporal.PlainYearMonth.compare ( one, two )
857 static bool PlainYearMonth_compare(JSContext
* cx
, unsigned argc
, Value
* vp
) {
858 CallArgs args
= CallArgsFromVp(argc
, vp
);
862 if (!ToTemporalYearMonth(cx
, args
.get(0), &one
)) {
868 if (!ToTemporalYearMonth(cx
, args
.get(1), &two
)) {
873 args
.rval().setInt32(CompareISODate(one
, two
));
878 * get Temporal.PlainYearMonth.prototype.calendarId
880 static bool PlainYearMonth_calendarId(JSContext
* cx
, const CallArgs
& args
) {
881 auto* yearMonth
= &args
.thisv().toObject().as
<PlainYearMonthObject
>();
882 Rooted
<CalendarValue
> calendar(cx
, yearMonth
->calendar());
885 auto* calendarId
= ToTemporalCalendarIdentifier(cx
, calendar
);
890 args
.rval().setString(calendarId
);
895 * get Temporal.PlainYearMonth.prototype.calendarId
897 static bool PlainYearMonth_calendarId(JSContext
* cx
, unsigned argc
, Value
* vp
) {
899 CallArgs args
= CallArgsFromVp(argc
, vp
);
900 return CallNonGenericMethod
<IsPlainYearMonth
, PlainYearMonth_calendarId
>(
905 * get Temporal.PlainYearMonth.prototype.year
907 static bool PlainYearMonth_year(JSContext
* cx
, const CallArgs
& args
) {
909 Rooted
<PlainYearMonthObject
*> yearMonth(
910 cx
, &args
.thisv().toObject().as
<PlainYearMonthObject
>());
911 Rooted
<CalendarValue
> calendar(cx
, yearMonth
->calendar());
914 return CalendarYear(cx
, calendar
, yearMonth
, args
.rval());
918 * get Temporal.PlainYearMonth.prototype.year
920 static bool PlainYearMonth_year(JSContext
* cx
, unsigned argc
, Value
* vp
) {
922 CallArgs args
= CallArgsFromVp(argc
, vp
);
923 return CallNonGenericMethod
<IsPlainYearMonth
, PlainYearMonth_year
>(cx
, args
);
927 * get Temporal.PlainYearMonth.prototype.month
929 static bool PlainYearMonth_month(JSContext
* cx
, const CallArgs
& args
) {
931 Rooted
<PlainYearMonthObject
*> yearMonth(
932 cx
, &args
.thisv().toObject().as
<PlainYearMonthObject
>());
933 Rooted
<CalendarValue
> calendar(cx
, yearMonth
->calendar());
936 return CalendarMonth(cx
, calendar
, yearMonth
, args
.rval());
940 * get Temporal.PlainYearMonth.prototype.month
942 static bool PlainYearMonth_month(JSContext
* cx
, unsigned argc
, Value
* vp
) {
944 CallArgs args
= CallArgsFromVp(argc
, vp
);
945 return CallNonGenericMethod
<IsPlainYearMonth
, PlainYearMonth_month
>(cx
, args
);
949 * get Temporal.PlainYearMonth.prototype.monthCode
951 static bool PlainYearMonth_monthCode(JSContext
* cx
, const CallArgs
& args
) {
953 Rooted
<PlainYearMonthObject
*> yearMonth(
954 cx
, &args
.thisv().toObject().as
<PlainYearMonthObject
>());
955 Rooted
<CalendarValue
> calendar(cx
, yearMonth
->calendar());
958 return CalendarMonthCode(cx
, calendar
, yearMonth
, args
.rval());
962 * get Temporal.PlainYearMonth.prototype.monthCode
964 static bool PlainYearMonth_monthCode(JSContext
* cx
, unsigned argc
, Value
* vp
) {
966 CallArgs args
= CallArgsFromVp(argc
, vp
);
967 return CallNonGenericMethod
<IsPlainYearMonth
, PlainYearMonth_monthCode
>(cx
,
972 * get Temporal.PlainYearMonth.prototype.daysInYear
974 static bool PlainYearMonth_daysInYear(JSContext
* cx
, const CallArgs
& args
) {
976 Rooted
<PlainYearMonthObject
*> yearMonth(
977 cx
, &args
.thisv().toObject().as
<PlainYearMonthObject
>());
978 Rooted
<CalendarValue
> calendar(cx
, yearMonth
->calendar());
981 return CalendarDaysInYear(cx
, calendar
, yearMonth
, args
.rval());
985 * get Temporal.PlainYearMonth.prototype.daysInYear
987 static bool PlainYearMonth_daysInYear(JSContext
* cx
, unsigned argc
, Value
* vp
) {
989 CallArgs args
= CallArgsFromVp(argc
, vp
);
990 return CallNonGenericMethod
<IsPlainYearMonth
, PlainYearMonth_daysInYear
>(
995 * get Temporal.PlainYearMonth.prototype.daysInMonth
997 static bool PlainYearMonth_daysInMonth(JSContext
* cx
, const CallArgs
& args
) {
999 Rooted
<PlainYearMonthObject
*> yearMonth(
1000 cx
, &args
.thisv().toObject().as
<PlainYearMonthObject
>());
1001 Rooted
<CalendarValue
> calendar(cx
, yearMonth
->calendar());
1004 return CalendarDaysInMonth(cx
, calendar
, yearMonth
, args
.rval());
1008 * get Temporal.PlainYearMonth.prototype.daysInMonth
1010 static bool PlainYearMonth_daysInMonth(JSContext
* cx
, unsigned argc
,
1013 CallArgs args
= CallArgsFromVp(argc
, vp
);
1014 return CallNonGenericMethod
<IsPlainYearMonth
, PlainYearMonth_daysInMonth
>(
1019 * get Temporal.PlainYearMonth.prototype.monthsInYear
1021 static bool PlainYearMonth_monthsInYear(JSContext
* cx
, const CallArgs
& args
) {
1023 Rooted
<PlainYearMonthObject
*> yearMonth(
1024 cx
, &args
.thisv().toObject().as
<PlainYearMonthObject
>());
1025 Rooted
<CalendarValue
> calendar(cx
, yearMonth
->calendar());
1028 return CalendarMonthsInYear(cx
, calendar
, yearMonth
, args
.rval());
1032 * get Temporal.PlainYearMonth.prototype.monthsInYear
1034 static bool PlainYearMonth_monthsInYear(JSContext
* cx
, unsigned argc
,
1037 CallArgs args
= CallArgsFromVp(argc
, vp
);
1038 return CallNonGenericMethod
<IsPlainYearMonth
, PlainYearMonth_monthsInYear
>(
1043 * get Temporal.PlainYearMonth.prototype.inLeapYear
1045 static bool PlainYearMonth_inLeapYear(JSContext
* cx
, const CallArgs
& args
) {
1047 Rooted
<PlainYearMonthObject
*> yearMonth(
1048 cx
, &args
.thisv().toObject().as
<PlainYearMonthObject
>());
1049 Rooted
<CalendarValue
> calendar(cx
, yearMonth
->calendar());
1052 return CalendarInLeapYear(cx
, calendar
, yearMonth
, args
.rval());
1056 * get Temporal.PlainYearMonth.prototype.inLeapYear
1058 static bool PlainYearMonth_inLeapYear(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1060 CallArgs args
= CallArgsFromVp(argc
, vp
);
1061 return CallNonGenericMethod
<IsPlainYearMonth
, PlainYearMonth_inLeapYear
>(
1066 * Temporal.PlainYearMonth.prototype.with ( temporalYearMonthLike [ , options ]
1069 static bool PlainYearMonth_with(JSContext
* cx
, const CallArgs
& args
) {
1070 Rooted
<PlainYearMonthObject
*> yearMonth(
1071 cx
, &args
.thisv().toObject().as
<PlainYearMonthObject
>());
1072 Rooted
<CalendarValue
> calendarValue(cx
, yearMonth
->calendar());
1075 Rooted
<JSObject
*> temporalYearMonthLike(
1076 cx
, RequireObjectArg(cx
, "temporalYearMonthLike", "with", args
.get(0)));
1077 if (!temporalYearMonthLike
) {
1080 if (!ThrowIfTemporalLikeObject(cx
, temporalYearMonthLike
)) {
1085 Rooted
<PlainObject
*> resolvedOptions(cx
);
1086 if (args
.hasDefined(1)) {
1087 Rooted
<JSObject
*> options(cx
,
1088 RequireObjectArg(cx
, "options", "with", args
[1]));
1092 resolvedOptions
= SnapshotOwnProperties(cx
, options
);
1094 resolvedOptions
= NewPlainObjectWithProto(cx
, nullptr);
1096 if (!resolvedOptions
) {
1101 Rooted
<CalendarRecord
> calendar(cx
);
1102 if (!CreateCalendarMethodsRecord(cx
, calendarValue
,
1104 CalendarMethod::Fields
,
1105 CalendarMethod::MergeFields
,
1106 CalendarMethod::YearMonthFromFields
,
1113 JS::RootedVector
<PropertyKey
> fieldNames(cx
);
1114 if (!CalendarFields(
1116 {CalendarField::Month
, CalendarField::MonthCode
, CalendarField::Year
},
1122 Rooted
<PlainObject
*> fields(cx
,
1123 PrepareTemporalFields(cx
, yearMonth
, fieldNames
));
1129 Rooted
<PlainObject
*> partialYearMonth(
1130 cx
, PreparePartialTemporalFields(cx
, temporalYearMonthLike
, fieldNames
));
1131 if (!partialYearMonth
) {
1136 Rooted
<JSObject
*> mergedFields(
1137 cx
, CalendarMergeFields(cx
, calendar
, fields
, partialYearMonth
));
1138 if (!mergedFields
) {
1143 fields
= PrepareTemporalFields(cx
, mergedFields
, fieldNames
);
1149 auto obj
= CalendarYearMonthFromFields(cx
, calendar
, fields
, resolvedOptions
);
1154 args
.rval().setObject(*obj
);
1159 * Temporal.PlainYearMonth.prototype.with ( temporalYearMonthLike [ , options ]
1162 static bool PlainYearMonth_with(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1164 CallArgs args
= CallArgsFromVp(argc
, vp
);
1165 return CallNonGenericMethod
<IsPlainYearMonth
, PlainYearMonth_with
>(cx
, args
);
1169 * Temporal.PlainYearMonth.prototype.add ( temporalDurationLike [ , options ] )
1171 static bool PlainYearMonth_add(JSContext
* cx
, const CallArgs
& args
) {
1173 return AddDurationToOrSubtractDurationFromPlainYearMonth(
1174 cx
, PlainYearMonthDuration::Add
, args
);
1178 * Temporal.PlainYearMonth.prototype.add ( temporalDurationLike [ , options ] )
1180 static bool PlainYearMonth_add(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1182 CallArgs args
= CallArgsFromVp(argc
, vp
);
1183 return CallNonGenericMethod
<IsPlainYearMonth
, PlainYearMonth_add
>(cx
, args
);
1187 * Temporal.PlainYearMonth.prototype.subtract ( temporalDurationLike [ , options
1190 static bool PlainYearMonth_subtract(JSContext
* cx
, const CallArgs
& args
) {
1192 return AddDurationToOrSubtractDurationFromPlainYearMonth(
1193 cx
, PlainYearMonthDuration::Subtract
, args
);
1197 * Temporal.PlainYearMonth.prototype.subtract ( temporalDurationLike [ , options
1200 static bool PlainYearMonth_subtract(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1202 CallArgs args
= CallArgsFromVp(argc
, vp
);
1203 return CallNonGenericMethod
<IsPlainYearMonth
, PlainYearMonth_subtract
>(cx
,
1208 * Temporal.PlainYearMonth.prototype.until ( other [ , options ] )
1210 static bool PlainYearMonth_until(JSContext
* cx
, const CallArgs
& args
) {
1212 return DifferenceTemporalPlainYearMonth(cx
, TemporalDifference::Until
, args
);
1216 * Temporal.PlainYearMonth.prototype.until ( other [ , options ] )
1218 static bool PlainYearMonth_until(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1220 CallArgs args
= CallArgsFromVp(argc
, vp
);
1221 return CallNonGenericMethod
<IsPlainYearMonth
, PlainYearMonth_until
>(cx
, args
);
1225 * Temporal.PlainYearMonth.prototype.since ( other [ , options ] )
1227 static bool PlainYearMonth_since(JSContext
* cx
, const CallArgs
& args
) {
1229 return DifferenceTemporalPlainYearMonth(cx
, TemporalDifference::Since
, args
);
1233 * Temporal.PlainYearMonth.prototype.since ( other [ , options ] )
1235 static bool PlainYearMonth_since(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1237 CallArgs args
= CallArgsFromVp(argc
, vp
);
1238 return CallNonGenericMethod
<IsPlainYearMonth
, PlainYearMonth_since
>(cx
, args
);
1242 * Temporal.PlainYearMonth.prototype.equals ( other )
1244 static bool PlainYearMonth_equals(JSContext
* cx
, const CallArgs
& args
) {
1245 auto* yearMonth
= &args
.thisv().toObject().as
<PlainYearMonthObject
>();
1246 auto date
= ToPlainDate(yearMonth
);
1247 Rooted
<CalendarValue
> calendar(cx
, yearMonth
->calendar());
1251 Rooted
<CalendarValue
> otherCalendar(cx
);
1252 if (!ToTemporalYearMonth(cx
, args
.get(0), &other
, &otherCalendar
)) {
1257 bool equals
= date
== other
;
1258 if (equals
&& !CalendarEquals(cx
, calendar
, otherCalendar
, &equals
)) {
1262 args
.rval().setBoolean(equals
);
1267 * Temporal.PlainYearMonth.prototype.equals ( other )
1269 static bool PlainYearMonth_equals(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1271 CallArgs args
= CallArgsFromVp(argc
, vp
);
1272 return CallNonGenericMethod
<IsPlainYearMonth
, PlainYearMonth_equals
>(cx
,
1277 * Temporal.PlainYearMonth.prototype.toString ( [ options ] )
1279 static bool PlainYearMonth_toString(JSContext
* cx
, const CallArgs
& args
) {
1280 Rooted
<PlainYearMonthObject
*> yearMonth(
1281 cx
, &args
.thisv().toObject().as
<PlainYearMonthObject
>());
1283 auto showCalendar
= CalendarOption::Auto
;
1284 if (args
.hasDefined(0)) {
1286 Rooted
<JSObject
*> options(
1287 cx
, RequireObjectArg(cx
, "options", "toString", args
[0]));
1293 if (!ToCalendarNameOption(cx
, options
, &showCalendar
)) {
1299 JSString
* str
= TemporalYearMonthToString(cx
, yearMonth
, showCalendar
);
1304 args
.rval().setString(str
);
1309 * Temporal.PlainYearMonth.prototype.toString ( [ options ] )
1311 static bool PlainYearMonth_toString(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1313 CallArgs args
= CallArgsFromVp(argc
, vp
);
1314 return CallNonGenericMethod
<IsPlainYearMonth
, PlainYearMonth_toString
>(cx
,
1319 * Temporal.PlainYearMonth.prototype.toLocaleString ( [ locales [ , options ] ]
1322 static bool PlainYearMonth_toLocaleString(JSContext
* cx
, const CallArgs
& args
) {
1323 Rooted
<PlainYearMonthObject
*> yearMonth(
1324 cx
, &args
.thisv().toObject().as
<PlainYearMonthObject
>());
1328 TemporalYearMonthToString(cx
, yearMonth
, CalendarOption::Auto
);
1333 args
.rval().setString(str
);
1338 * Temporal.PlainYearMonth.prototype.toLocaleString ( [ locales [ , options ] ]
1341 static bool PlainYearMonth_toLocaleString(JSContext
* cx
, unsigned argc
,
1344 CallArgs args
= CallArgsFromVp(argc
, vp
);
1345 return CallNonGenericMethod
<IsPlainYearMonth
, PlainYearMonth_toLocaleString
>(
1350 * Temporal.PlainYearMonth.prototype.toJSON ( )
1352 static bool PlainYearMonth_toJSON(JSContext
* cx
, const CallArgs
& args
) {
1353 Rooted
<PlainYearMonthObject
*> yearMonth(
1354 cx
, &args
.thisv().toObject().as
<PlainYearMonthObject
>());
1358 TemporalYearMonthToString(cx
, yearMonth
, CalendarOption::Auto
);
1363 args
.rval().setString(str
);
1368 * Temporal.PlainYearMonth.prototype.toJSON ( )
1370 static bool PlainYearMonth_toJSON(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1372 CallArgs args
= CallArgsFromVp(argc
, vp
);
1373 return CallNonGenericMethod
<IsPlainYearMonth
, PlainYearMonth_toJSON
>(cx
,
1378 * Temporal.PlainYearMonth.prototype.valueOf ( )
1380 static bool PlainYearMonth_valueOf(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1381 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, JSMSG_CANT_CONVERT_TO
,
1382 "PlainYearMonth", "primitive type");
1387 * Temporal.PlainYearMonth.prototype.toPlainDate ( item )
1389 static bool PlainYearMonth_toPlainDate(JSContext
* cx
, const CallArgs
& args
) {
1390 Rooted
<PlainYearMonthObject
*> yearMonth(
1391 cx
, &args
.thisv().toObject().as
<PlainYearMonthObject
>());
1394 Rooted
<JSObject
*> item(
1395 cx
, RequireObjectArg(cx
, "item", "toPlainDate", args
.get(0)));
1401 Rooted
<CalendarValue
> calendarValue(cx
, yearMonth
->calendar());
1402 Rooted
<CalendarRecord
> calendar(cx
);
1403 if (!CreateCalendarMethodsRecord(cx
, calendarValue
,
1405 CalendarMethod::DateFromFields
,
1406 CalendarMethod::Fields
,
1407 CalendarMethod::MergeFields
,
1414 JS::RootedVector
<PropertyKey
> receiverFieldNames(cx
);
1415 if (!CalendarFields(cx
, calendar
,
1416 {CalendarField::MonthCode
, CalendarField::Year
},
1417 &receiverFieldNames
)) {
1422 Rooted
<PlainObject
*> fields(
1423 cx
, PrepareTemporalFields(cx
, yearMonth
, receiverFieldNames
));
1429 JS::RootedVector
<PropertyKey
> inputFieldNames(cx
);
1430 if (!CalendarFields(cx
, calendar
, {CalendarField::Day
}, &inputFieldNames
)) {
1435 Rooted
<PlainObject
*> inputFields(
1436 cx
, PrepareTemporalFields(cx
, item
, inputFieldNames
));
1442 Rooted
<JSObject
*> mergedFields(
1443 cx
, CalendarMergeFields(cx
, calendar
, fields
, inputFields
));
1444 if (!mergedFields
) {
1449 JS::RootedVector
<PropertyKey
> concatenatedFieldNames(cx
);
1450 if (!ConcatTemporalFieldNames(receiverFieldNames
, inputFieldNames
,
1451 concatenatedFieldNames
.get())) {
1456 Rooted
<PlainObject
*> mergedFromConcatenatedFields(
1457 cx
, PrepareTemporalFields(cx
, mergedFields
, concatenatedFieldNames
));
1458 if (!mergedFromConcatenatedFields
) {
1463 auto obj
= CalendarDateFromFields(cx
, calendar
, mergedFromConcatenatedFields
,
1464 TemporalOverflow::Constrain
);
1469 args
.rval().setObject(*obj
);
1474 * Temporal.PlainYearMonth.prototype.toPlainDate ( item )
1476 static bool PlainYearMonth_toPlainDate(JSContext
* cx
, unsigned argc
,
1479 CallArgs args
= CallArgsFromVp(argc
, vp
);
1480 return CallNonGenericMethod
<IsPlainYearMonth
, PlainYearMonth_toPlainDate
>(
1485 * Temporal.PlainYearMonth.prototype.getISOFields ( )
1487 static bool PlainYearMonth_getISOFields(JSContext
* cx
, const CallArgs
& args
) {
1488 Rooted
<PlainYearMonthObject
*> yearMonth(
1489 cx
, &args
.thisv().toObject().as
<PlainYearMonthObject
>());
1492 Rooted
<IdValueVector
> fields(cx
, IdValueVector(cx
));
1495 if (!fields
.emplaceBack(NameToId(cx
->names().calendar
),
1496 yearMonth
->calendar().toValue())) {
1501 if (!fields
.emplaceBack(NameToId(cx
->names().isoDay
),
1502 Int32Value(yearMonth
->isoDay()))) {
1507 if (!fields
.emplaceBack(NameToId(cx
->names().isoMonth
),
1508 Int32Value(yearMonth
->isoMonth()))) {
1513 if (!fields
.emplaceBack(NameToId(cx
->names().isoYear
),
1514 Int32Value(yearMonth
->isoYear()))) {
1519 auto* obj
= NewPlainObjectWithUniqueNames(cx
, fields
);
1524 args
.rval().setObject(*obj
);
1529 * Temporal.PlainYearMonth.prototype.getISOFields ( )
1531 static bool PlainYearMonth_getISOFields(JSContext
* cx
, unsigned argc
,
1534 CallArgs args
= CallArgsFromVp(argc
, vp
);
1535 return CallNonGenericMethod
<IsPlainYearMonth
, PlainYearMonth_getISOFields
>(
1540 * Temporal.PlainYearMonth.prototype.getCalendar ( )
1542 static bool PlainYearMonth_getCalendar(JSContext
* cx
, const CallArgs
& args
) {
1543 auto* yearMonth
= &args
.thisv().toObject().as
<PlainYearMonthObject
>();
1544 Rooted
<CalendarValue
> calendar(cx
, yearMonth
->calendar());
1547 auto* obj
= ToTemporalCalendarObject(cx
, calendar
);
1552 args
.rval().setObject(*obj
);
1557 * Temporal.PlainYearMonth.prototype.getCalendar ( )
1559 static bool PlainYearMonth_getCalendar(JSContext
* cx
, unsigned argc
,
1562 CallArgs args
= CallArgsFromVp(argc
, vp
);
1563 return CallNonGenericMethod
<IsPlainYearMonth
, PlainYearMonth_getCalendar
>(
1567 const JSClass
PlainYearMonthObject::class_
= {
1568 "Temporal.PlainYearMonth",
1569 JSCLASS_HAS_RESERVED_SLOTS(PlainYearMonthObject::SLOT_COUNT
) |
1570 JSCLASS_HAS_CACHED_PROTO(JSProto_PlainYearMonth
),
1572 &PlainYearMonthObject::classSpec_
,
1575 const JSClass
& PlainYearMonthObject::protoClass_
= PlainObject::class_
;
1577 static const JSFunctionSpec PlainYearMonth_methods
[] = {
1578 JS_FN("from", PlainYearMonth_from
, 1, 0),
1579 JS_FN("compare", PlainYearMonth_compare
, 2, 0),
1583 static const JSFunctionSpec PlainYearMonth_prototype_methods
[] = {
1584 JS_FN("with", PlainYearMonth_with
, 1, 0),
1585 JS_FN("add", PlainYearMonth_add
, 1, 0),
1586 JS_FN("subtract", PlainYearMonth_subtract
, 1, 0),
1587 JS_FN("until", PlainYearMonth_until
, 1, 0),
1588 JS_FN("since", PlainYearMonth_since
, 1, 0),
1589 JS_FN("equals", PlainYearMonth_equals
, 1, 0),
1590 JS_FN("toString", PlainYearMonth_toString
, 0, 0),
1591 JS_FN("toLocaleString", PlainYearMonth_toLocaleString
, 0, 0),
1592 JS_FN("toJSON", PlainYearMonth_toJSON
, 0, 0),
1593 JS_FN("valueOf", PlainYearMonth_valueOf
, 0, 0),
1594 JS_FN("toPlainDate", PlainYearMonth_toPlainDate
, 1, 0),
1595 JS_FN("getISOFields", PlainYearMonth_getISOFields
, 0, 0),
1596 JS_FN("getCalendar", PlainYearMonth_getCalendar
, 0, 0),
1600 static const JSPropertySpec PlainYearMonth_prototype_properties
[] = {
1601 JS_PSG("calendarId", PlainYearMonth_calendarId
, 0),
1602 JS_PSG("year", PlainYearMonth_year
, 0),
1603 JS_PSG("month", PlainYearMonth_month
, 0),
1604 JS_PSG("monthCode", PlainYearMonth_monthCode
, 0),
1605 JS_PSG("daysInYear", PlainYearMonth_daysInYear
, 0),
1606 JS_PSG("daysInMonth", PlainYearMonth_daysInMonth
, 0),
1607 JS_PSG("monthsInYear", PlainYearMonth_monthsInYear
, 0),
1608 JS_PSG("inLeapYear", PlainYearMonth_inLeapYear
, 0),
1609 JS_STRING_SYM_PS(toStringTag
, "Temporal.PlainYearMonth", JSPROP_READONLY
),
1613 const ClassSpec
PlainYearMonthObject::classSpec_
= {
1614 GenericCreateConstructor
<PlainYearMonthConstructor
, 2,
1615 gc::AllocKind::FUNCTION
>,
1616 GenericCreatePrototype
<PlainYearMonthObject
>,
1617 PlainYearMonth_methods
,
1619 PlainYearMonth_prototype_methods
,
1620 PlainYearMonth_prototype_properties
,
1622 ClassSpec::DontDefineConstructor
,