Bug 1874684 - Part 33: Defer allocation of options object for CalendarDateFromFields...
[gecko.git] / js / src / builtin / temporal / PlainYearMonth.cpp
blob9e78ba07daa2b0fc0ecc4c1e740689ac7125af56
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>
12 #include <utility>
14 #include "jsnum.h"
15 #include "jspubtd.h"
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"
35 #include "js/Class.h"
36 #include "js/ErrorReport.h"
37 #include "js/friend/ErrorMessages.h"
38 #include "js/GCVector.h"
39 #include "js/Id.h"
40 #include "js/PropertyDescriptor.h"
41 #include "js/PropertySpec.h"
42 #include "js/RootingAPI.h"
43 #include "js/TypeDecls.h"
44 #include "js/Value.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"
57 using namespace js;
58 using namespace js::temporal;
60 static inline bool IsPlainYearMonth(Handle<Value> v) {
61 return v.isObject() && v.toObject().is<PlainYearMonthObject>();
64 /**
65 * ISOYearMonthWithinLimits ( year, month )
67 template <typename T>
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>);
71 // Step 1.
72 MOZ_ASSERT(IsInteger(year));
73 MOZ_ASSERT(1 <= month && month <= 12);
75 // Step 2.
76 if (year < -271821 || year > 275760) {
77 return false;
80 // Step 3.
81 if (year == -271821 && month < 4) {
82 return false;
85 // Step 4.
86 if (year == 275760 && month > 9) {
87 return false;
90 // Step 5.
91 return true;
94 /**
95 * CreateTemporalYearMonth ( isoYear, isoMonth, calendar, referenceISODay [ ,
96 * newTarget ] )
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));
105 // Step 1.
106 if (!ThrowIfInvalidISODate(cx, isoYear, isoMonth, isoDay)) {
107 return nullptr;
110 // FIXME: spec issue - Consider calling ISODateTimeWithinLimits to include
111 // testing |referenceISODay|?
113 // Step 2.
114 if (!ISOYearMonthWithinLimits(isoYear, int32_t(isoMonth))) {
115 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
116 JSMSG_TEMPORAL_PLAIN_YEAR_MONTH_INVALID);
117 return nullptr;
120 // Steps 3-4.
121 Rooted<JSObject*> proto(cx);
122 if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_PlainYearMonth,
123 &proto)) {
124 return nullptr;
127 auto* obj = NewObjectWithClassProto<PlainYearMonthObject>(cx, proto);
128 if (!obj) {
129 return nullptr;
132 // Step 5.
133 obj->setFixedSlot(PlainYearMonthObject::ISO_YEAR_SLOT,
134 Int32Value(int32_t(isoYear)));
136 // Step 6.
137 obj->setFixedSlot(PlainYearMonthObject::ISO_MONTH_SLOT,
138 Int32Value(int32_t(isoMonth)));
140 // Step 7.
141 obj->setFixedSlot(PlainYearMonthObject::CALENDAR_SLOT, calendar.toValue());
143 // Step 8.
144 obj->setFixedSlot(PlainYearMonthObject::ISO_DAY_SLOT,
145 Int32Value(int32_t(isoDay)));
147 // Step 9.
148 return obj;
152 * CreateTemporalYearMonth ( isoYear, isoMonth, calendar, referenceISODay [ ,
153 * newTarget ] )
155 PlainYearMonthObject* js::temporal::CreateTemporalYearMonth(
156 JSContext* cx, const PlainDate& date, Handle<CalendarValue> calendar) {
157 const auto& [isoYear, isoMonth, isoDay] = date;
159 // Step 1.
160 if (!ThrowIfInvalidISODate(cx, date)) {
161 return nullptr;
164 // FIXME: spec issue - Consider calling ISODateTimeWithinLimits to include
165 // testing |referenceISODay|?
167 // Step 2.
168 if (!ISOYearMonthWithinLimits(isoYear, isoMonth)) {
169 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
170 JSMSG_TEMPORAL_PLAIN_YEAR_MONTH_INVALID);
171 return nullptr;
174 // Steps 3-4.
175 auto* obj = NewBuiltinClassInstance<PlainYearMonthObject>(cx);
176 if (!obj) {
177 return nullptr;
180 // Step 5.
181 obj->setFixedSlot(PlainYearMonthObject::ISO_YEAR_SLOT, Int32Value(isoYear));
183 // Step 6.
184 obj->setFixedSlot(PlainYearMonthObject::ISO_MONTH_SLOT, Int32Value(isoMonth));
186 // Step 7.
187 obj->setFixedSlot(PlainYearMonthObject::CALENDAR_SLOT, calendar.toValue());
189 // Step 8.
190 obj->setFixedSlot(PlainYearMonthObject::ISO_DAY_SLOT, Int32Value(isoDay));
192 // Step 9.
193 return obj;
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.)
204 // Step 2.
205 Rooted<PlainObject*> maybeResolvedOptions(cx);
206 if (maybeOptions) {
207 maybeResolvedOptions = SnapshotOwnProperties(cx, maybeOptions);
208 if (!maybeResolvedOptions) {
209 return nullptr;
213 // Step 3.
214 if (item.isObject()) {
215 Rooted<JSObject*> itemObj(cx, &item.toObject());
217 // Step 3.a.
218 if (itemObj->canUnwrapAs<PlainYearMonthObject>()) {
219 return itemObj;
222 // Step 3.b.
223 Rooted<CalendarValue> calendarValue(cx);
224 if (!GetTemporalCalendarWithISODefault(cx, itemObj, &calendarValue)) {
225 return nullptr;
228 // Step 3.c.
229 Rooted<CalendarRecord> calendar(cx);
230 if (!CreateCalendarMethodsRecord(cx, calendarValue,
232 CalendarMethod::Fields,
233 CalendarMethod::YearMonthFromFields,
235 &calendar)) {
236 return nullptr;
239 // Step 3.d.
240 JS::RootedVector<PropertyKey> fieldNames(cx);
241 if (!CalendarFields(cx, calendar,
242 {CalendarField::Month, CalendarField::MonthCode,
243 CalendarField::Year},
244 &fieldNames)) {
245 return nullptr;
248 // Step 3.e.
249 Rooted<PlainObject*> fields(cx,
250 PrepareTemporalFields(cx, itemObj, fieldNames));
251 if (!fields) {
252 return nullptr;
255 // Step 3.f.
256 if (maybeResolvedOptions) {
257 return CalendarYearMonthFromFields(cx, calendar, fields,
258 maybeResolvedOptions);
260 return CalendarYearMonthFromFields(cx, calendar, fields);
263 // Step 4.
264 if (!item.isString()) {
265 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, item,
266 nullptr, "not a string");
267 return nullptr;
269 Rooted<JSString*> string(cx, item.toString());
271 // Step 5.
272 PlainDate result;
273 Rooted<JSString*> calendarString(cx);
274 if (!ParseTemporalYearMonthString(cx, string, &result, &calendarString)) {
275 return nullptr;
278 // Steps 6-9.
279 Rooted<CalendarValue> calendarValue(cx, CalendarValue(cx->names().iso8601));
280 if (calendarString) {
281 if (!ToBuiltinCalendar(cx, calendarString, &calendarValue)) {
282 return nullptr;
286 // Step 10.
287 if (maybeResolvedOptions) {
288 TemporalOverflow ignored;
289 if (!ToTemporalOverflow(cx, maybeResolvedOptions, &ignored)) {
290 return nullptr;
294 // Step 11.
295 Rooted<PlainYearMonthObject*> obj(
296 cx, CreateTemporalYearMonth(cx, result, calendarValue));
297 if (!obj) {
298 return nullptr;
301 // Step 12.
302 Rooted<CalendarRecord> calendar(cx);
303 if (!CreateCalendarMethodsRecord(cx, calendarValue,
305 CalendarMethod::YearMonthFromFields,
307 &calendar)) {
308 return nullptr;
311 // FIXME: spec issue - reorder note to appear directly before
312 // CalendarYearMonthFromFields
314 // Steps 13-14.
315 return CalendarYearMonthFromFields(cx, calendar, obj);
319 * ToTemporalYearMonth ( item [ , options ] )
321 static bool ToTemporalYearMonth(JSContext* cx, Handle<Value> item,
322 PlainDate* result) {
323 auto obj = ToTemporalYearMonth(cx, item);
324 if (!obj) {
325 return false;
328 *result = ToPlainDate(&obj.unwrap());
329 return true;
333 * ToTemporalYearMonth ( item [ , options ] )
335 static bool ToTemporalYearMonth(JSContext* cx, Handle<Value> item,
336 PlainDate* result,
337 MutableHandle<CalendarValue> calendar) {
338 auto* obj = ToTemporalYearMonth(cx, item).unwrapOrNull();
339 if (!obj) {
340 return false;
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.)
359 // Step 2.
360 auto otherYearMonth = ToTemporalYearMonth(cx, args.get(0));
361 if (!otherYearMonth) {
362 return false;
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)) {
370 return false;
373 // Step 3.
374 Rooted<CalendarValue> calendar(cx, yearMonth->calendar());
376 // Step 4.
377 if (!CalendarEqualsOrThrow(cx, calendar, otherCalendar)) {
378 return false;
381 // Steps 5-6.
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]));
387 if (!options) {
388 return false;
391 // Step 5.
392 resolvedOptions = SnapshotOwnProperties(cx, options);
393 if (!resolvedOptions) {
394 return false;
397 // Step 6.
398 if (!GetDifferenceSettings(cx, operation, resolvedOptions,
399 TemporalUnitGroup::Date, TemporalUnit::Month,
400 TemporalUnit::Month, TemporalUnit::Year,
401 &settings)) {
402 return false;
404 } else {
405 // Steps 5-6.
406 settings = {
407 TemporalUnit::Month,
408 TemporalUnit::Year,
409 TemporalRoundingMode::Trunc,
410 Increment{1},
414 // Step 7.
415 if (ToPlainDate(yearMonth) == otherYearMonthDate) {
416 auto* obj = CreateTemporalDuration(cx, {});
417 if (!obj) {
418 return false;
421 args.rval().setObject(*obj);
422 return true;
425 // Step 8.
426 // FIXME: spec issue - duplicate CreateDataPropertyOrThrow for "largestUnit".
428 // Step 9.
429 Rooted<CalendarRecord> calendarRec(cx);
430 if (!CreateCalendarMethodsRecord(cx, calendar,
432 CalendarMethod::DateAdd,
433 CalendarMethod::DateFromFields,
434 CalendarMethod::DateUntil,
435 CalendarMethod::Fields,
437 &calendarRec)) {
438 return false;
441 // Step 10.
442 JS::RootedVector<PropertyKey> fieldNames(cx);
443 if (!CalendarFields(cx, calendarRec,
444 {CalendarField::MonthCode, CalendarField::Year},
445 &fieldNames)) {
446 return false;
449 // Step 11.
450 Rooted<PlainObject*> thisFields(
451 cx, PrepareTemporalFields(cx, yearMonth, fieldNames));
452 if (!thisFields) {
453 return false;
456 // Step 12.
457 Value one = Int32Value(1);
458 auto handleOne = Handle<Value>::fromMarkedLocation(&one);
459 if (!DefineDataProperty(cx, thisFields, cx->names().day, handleOne)) {
460 return false;
463 // Step 13.
464 Rooted<Wrapped<PlainDateObject*>> thisDate(
465 cx, CalendarDateFromFields(cx, calendarRec, thisFields));
466 if (!thisDate) {
467 return false;
470 // Step 14.
471 Rooted<PlainObject*> otherFields(
472 cx, PrepareTemporalFields(cx, other, fieldNames));
473 if (!otherFields) {
474 return false;
477 // Step 15.
478 if (!DefineDataProperty(cx, otherFields, cx->names().day, handleOne)) {
479 return false;
482 // Step 16.
483 Rooted<Wrapped<PlainDateObject*>> otherDate(
484 cx, CalendarDateFromFields(cx, calendarRec, otherFields));
485 if (!otherDate) {
486 return false;
489 // Steps 17-18.
490 Duration until;
491 if (resolvedOptions) {
492 // Steps 17-18.
493 if (!CalendarDateUntil(cx, calendarRec, thisDate, otherDate,
494 settings.largestUnit, resolvedOptions, &until)) {
495 return false;
497 } else {
498 // Steps 17-18.
499 if (!CalendarDateUntil(cx, calendarRec, thisDate, otherDate,
500 settings.largestUnit, &until)) {
501 return false;
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)};
508 // Step 19.
509 if (settings.smallestUnit != TemporalUnit::Month ||
510 settings.roundingIncrement != Increment{1}) {
511 // Steps 19.a-b.
512 NormalizedDuration roundResult;
513 if (!RoundDuration(cx, {dateDuration, {}}, settings.roundingIncrement,
514 settings.smallestUnit, settings.roundingMode, thisDate,
515 calendarRec, &roundResult)) {
516 return false;
519 // Step 19.c.
520 auto toBalance =
521 DateDuration{roundResult.date.years, roundResult.date.months};
522 if (!temporal::BalanceDateDurationRelative(
523 cx, toBalance, settings.largestUnit, settings.smallestUnit,
524 thisDate, calendarRec, &dateDuration)) {
525 return false;
529 // Step 20.
530 auto duration =
531 Duration{double(dateDuration.years), double(dateDuration.months)};
532 if (operation == TemporalDifference::Since) {
533 duration = duration.negate();
536 auto* obj = CreateTemporalDuration(cx, duration);
537 if (!obj) {
538 return false;
541 args.rval().setObject(*obj);
542 return true;
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>());
556 // Step 1.
557 Duration duration;
558 if (!ToTemporalDurationRecord(cx, args.get(0), &duration)) {
559 return false;
562 // Step 2.
563 if (operation == PlainYearMonthDuration::Subtract) {
564 duration = duration.negate();
567 // Step 3.
568 Rooted<JSObject*> options(cx);
569 if (args.hasDefined(1)) {
570 const char* name =
571 operation == PlainYearMonthDuration::Add ? "add" : "subtract";
572 options = RequireObjectArg(cx, "options", name, args[1]);
573 } else {
574 // TODO: Avoid creating an options object if not necessary.
575 options = NewPlainObjectWithProto(cx, nullptr);
577 if (!options) {
578 return false;
581 // Step 4.
582 auto timeDuration = NormalizeTimeDuration(duration);
584 // Step 5.
585 auto balancedTime = BalanceTimeDuration(timeDuration, TemporalUnit::Day);
587 // Steps 6 and 16. (Reordered)
588 Duration durationToAdd = {
589 duration.years,
590 duration.months,
591 duration.weeks,
592 duration.days + double(balancedTime.days),
595 // Step 7.
596 int32_t sign = DurationSign(durationToAdd);
598 // Step 8.
599 Rooted<CalendarValue> calendarValue(cx, yearMonth->calendar());
600 Rooted<CalendarRecord> calendar(cx);
601 if (!CreateCalendarMethodsRecord(cx, calendarValue,
603 CalendarMethod::DateAdd,
604 CalendarMethod::DateFromFields,
605 CalendarMethod::Day,
606 CalendarMethod::Fields,
607 CalendarMethod::YearMonthFromFields,
609 &calendar)) {
610 return false;
613 // Step 9.
614 JS::RootedVector<PropertyKey> fieldNames(cx);
615 if (!CalendarFields(cx, calendar,
616 {CalendarField::MonthCode, CalendarField::Year},
617 &fieldNames)) {
618 return false;
621 // Step 10.
622 Rooted<PlainObject*> fields(cx,
623 PrepareTemporalFields(cx, yearMonth, fieldNames));
624 if (!fields) {
625 return false;
628 // Step 11.
629 Rooted<PlainObject*> fieldsCopy(cx, SnapshotOwnProperties(cx, fields));
630 if (!fieldsCopy) {
631 return false;
634 // Step 12.
635 Value one = Int32Value(1);
636 auto handleOne = Handle<Value>::fromMarkedLocation(&one);
637 if (!DefineDataProperty(cx, fields, cx->names().day, handleOne)) {
638 return false;
641 // Step 13.
642 Rooted<Wrapped<PlainDateObject*>> intermediateDate(
643 cx, CalendarDateFromFields(cx, calendar, fields));
644 if (!intermediateDate) {
645 return false;
648 // Steps 14-15.
649 Rooted<Wrapped<PlainDateObject*>> date(cx);
650 if (sign < 0) {
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
659 // 31
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.
665 // Step 14.a.
666 auto oneMonthDuration = DateDuration{0, 1};
668 // Step 14.b.
669 Rooted<Wrapped<PlainDateObject*>> nextMonth(
670 cx, CalendarDateAdd(cx, calendar, intermediateDate, oneMonthDuration));
671 if (!nextMonth) {
672 return false;
675 auto* unwrappedNextMonth = nextMonth.unwrap(cx);
676 if (!unwrappedNextMonth) {
677 return false;
679 auto nextMonthDate = ToPlainDate(unwrappedNextMonth);
681 // Step 14.c.
682 PlainDate endOfMonthISO;
683 if (!AddISODate(cx, nextMonthDate, {0, 0, 0, -1},
684 TemporalOverflow::Constrain, &endOfMonthISO)) {
685 return false;
688 // Step 14.d.
689 Rooted<PlainDateWithCalendar> endOfMonth(cx);
690 if (!CreateTemporalDate(cx, endOfMonthISO, calendar.receiver(),
691 &endOfMonth)) {
692 return false;
695 // Step 14.e.
696 Rooted<Value> day(cx);
697 if (!CalendarDay(cx, calendar, endOfMonth.date(), &day)) {
698 return false;
701 // Step 14.f.
702 if (!DefineDataProperty(cx, fieldsCopy, cx->names().day, day)) {
703 return false;
706 // Step 14.g.
707 date = CalendarDateFromFields(cx, calendar, fieldsCopy);
708 if (!date) {
709 return false;
711 } else {
712 // Step 15.a.
713 date = intermediateDate;
716 // Step 16. (Moved above)
718 // Step 17.
719 Rooted<PlainObject*> optionsCopy(cx, SnapshotOwnProperties(cx, options));
720 if (!optionsCopy) {
721 return false;
724 // Step 18.
725 Rooted<Wrapped<PlainDateObject*>> addedDate(
726 cx, AddDate(cx, calendar, date, durationToAdd, options));
727 if (!addedDate) {
728 return false;
731 // Step 19.
732 Rooted<PlainObject*> addedDateFields(
733 cx, PrepareTemporalFields(cx, addedDate, fieldNames));
734 if (!addedDateFields) {
735 return false;
738 // Step 20.
739 auto obj =
740 CalendarYearMonthFromFields(cx, calendar, addedDateFields, optionsCopy);
741 if (!obj) {
742 return false;
745 args.rval().setObject(*obj);
746 return true;
750 * Temporal.PlainYearMonth ( isoYear, isoMonth [ , calendarLike [ ,
751 * referenceISODay ] ] )
753 static bool PlainYearMonthConstructor(JSContext* cx, unsigned argc, Value* vp) {
754 CallArgs args = CallArgsFromVp(argc, vp);
756 // Step 1.
757 if (!ThrowIfNotConstructing(cx, args, "Temporal.PlainYearMonth")) {
758 return false;
761 // Step 3.
762 double isoYear;
763 if (!ToIntegerWithTruncation(cx, args.get(0), "year", &isoYear)) {
764 return false;
767 // Step 4.
768 double isoMonth;
769 if (!ToIntegerWithTruncation(cx, args.get(1), "month", &isoMonth)) {
770 return false;
773 // Step 5.
774 Rooted<CalendarValue> calendar(cx);
775 if (!ToTemporalCalendarWithISODefault(cx, args.get(2), &calendar)) {
776 return false;
779 // Steps 2 and 6.
780 double isoDay = 1;
781 if (args.hasDefined(3)) {
782 if (!ToIntegerWithTruncation(cx, args[3], "day", &isoDay)) {
783 return false;
787 // Step 7.
788 auto* yearMonth =
789 CreateTemporalYearMonth(cx, args, isoYear, isoMonth, isoDay, calendar);
790 if (!yearMonth) {
791 return false;
794 args.rval().setObject(*yearMonth);
795 return true;
799 * Temporal.PlainYearMonth.from ( item [ , options ] )
801 static bool PlainYearMonth_from(JSContext* cx, unsigned argc, Value* vp) {
802 CallArgs args = CallArgsFromVp(argc, vp);
804 // Step 1.
805 Rooted<JSObject*> options(cx);
806 if (args.hasDefined(1)) {
807 options = RequireObjectArg(cx, "options", "from", args[1]);
808 if (!options) {
809 return false;
813 // Step 2.
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)) {
822 return false;
825 if (options) {
826 // Step 2.a.
827 TemporalOverflow ignored;
828 if (!ToTemporalOverflow(cx, options, &ignored)) {
829 return false;
833 // Step 2.b.
834 auto* obj = CreateTemporalYearMonth(cx, date, calendar);
835 if (!obj) {
836 return false;
839 args.rval().setObject(*obj);
840 return true;
844 // Step 3.
845 auto obj = ToTemporalYearMonth(cx, args.get(0), options);
846 if (!obj) {
847 return false;
850 args.rval().setObject(*obj);
851 return true;
855 * Temporal.PlainYearMonth.compare ( one, two )
857 static bool PlainYearMonth_compare(JSContext* cx, unsigned argc, Value* vp) {
858 CallArgs args = CallArgsFromVp(argc, vp);
860 // Step 1.
861 PlainDate one;
862 if (!ToTemporalYearMonth(cx, args.get(0), &one)) {
863 return false;
866 // Step 2.
867 PlainDate two;
868 if (!ToTemporalYearMonth(cx, args.get(1), &two)) {
869 return false;
872 // Step 3.
873 args.rval().setInt32(CompareISODate(one, two));
874 return true;
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());
884 // Step 3.
885 auto* calendarId = ToTemporalCalendarIdentifier(cx, calendar);
886 if (!calendarId) {
887 return false;
890 args.rval().setString(calendarId);
891 return true;
895 * get Temporal.PlainYearMonth.prototype.calendarId
897 static bool PlainYearMonth_calendarId(JSContext* cx, unsigned argc, Value* vp) {
898 // Steps 1-2.
899 CallArgs args = CallArgsFromVp(argc, vp);
900 return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_calendarId>(
901 cx, args);
905 * get Temporal.PlainYearMonth.prototype.year
907 static bool PlainYearMonth_year(JSContext* cx, const CallArgs& args) {
908 // Step 3.
909 Rooted<PlainYearMonthObject*> yearMonth(
910 cx, &args.thisv().toObject().as<PlainYearMonthObject>());
911 Rooted<CalendarValue> calendar(cx, yearMonth->calendar());
913 // Step 4.
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) {
921 // Steps 1-2.
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) {
930 // Step 3.
931 Rooted<PlainYearMonthObject*> yearMonth(
932 cx, &args.thisv().toObject().as<PlainYearMonthObject>());
933 Rooted<CalendarValue> calendar(cx, yearMonth->calendar());
935 // Step 4.
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) {
943 // Steps 1-2.
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) {
952 // Step 3.
953 Rooted<PlainYearMonthObject*> yearMonth(
954 cx, &args.thisv().toObject().as<PlainYearMonthObject>());
955 Rooted<CalendarValue> calendar(cx, yearMonth->calendar());
957 // Step 4.
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) {
965 // Steps 1-2.
966 CallArgs args = CallArgsFromVp(argc, vp);
967 return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_monthCode>(cx,
968 args);
972 * get Temporal.PlainYearMonth.prototype.daysInYear
974 static bool PlainYearMonth_daysInYear(JSContext* cx, const CallArgs& args) {
975 // Step 3.
976 Rooted<PlainYearMonthObject*> yearMonth(
977 cx, &args.thisv().toObject().as<PlainYearMonthObject>());
978 Rooted<CalendarValue> calendar(cx, yearMonth->calendar());
980 // Step 4.
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) {
988 // Steps 1-2.
989 CallArgs args = CallArgsFromVp(argc, vp);
990 return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_daysInYear>(
991 cx, args);
995 * get Temporal.PlainYearMonth.prototype.daysInMonth
997 static bool PlainYearMonth_daysInMonth(JSContext* cx, const CallArgs& args) {
998 // Step 3.
999 Rooted<PlainYearMonthObject*> yearMonth(
1000 cx, &args.thisv().toObject().as<PlainYearMonthObject>());
1001 Rooted<CalendarValue> calendar(cx, yearMonth->calendar());
1003 // Step 4.
1004 return CalendarDaysInMonth(cx, calendar, yearMonth, args.rval());
1008 * get Temporal.PlainYearMonth.prototype.daysInMonth
1010 static bool PlainYearMonth_daysInMonth(JSContext* cx, unsigned argc,
1011 Value* vp) {
1012 // Steps 1-2.
1013 CallArgs args = CallArgsFromVp(argc, vp);
1014 return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_daysInMonth>(
1015 cx, args);
1019 * get Temporal.PlainYearMonth.prototype.monthsInYear
1021 static bool PlainYearMonth_monthsInYear(JSContext* cx, const CallArgs& args) {
1022 // Step 3.
1023 Rooted<PlainYearMonthObject*> yearMonth(
1024 cx, &args.thisv().toObject().as<PlainYearMonthObject>());
1025 Rooted<CalendarValue> calendar(cx, yearMonth->calendar());
1027 // Step 4.
1028 return CalendarMonthsInYear(cx, calendar, yearMonth, args.rval());
1032 * get Temporal.PlainYearMonth.prototype.monthsInYear
1034 static bool PlainYearMonth_monthsInYear(JSContext* cx, unsigned argc,
1035 Value* vp) {
1036 // Steps 1-2.
1037 CallArgs args = CallArgsFromVp(argc, vp);
1038 return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_monthsInYear>(
1039 cx, args);
1043 * get Temporal.PlainYearMonth.prototype.inLeapYear
1045 static bool PlainYearMonth_inLeapYear(JSContext* cx, const CallArgs& args) {
1046 // Step 3.
1047 Rooted<PlainYearMonthObject*> yearMonth(
1048 cx, &args.thisv().toObject().as<PlainYearMonthObject>());
1049 Rooted<CalendarValue> calendar(cx, yearMonth->calendar());
1051 // Step 4.
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) {
1059 // Steps 1-2.
1060 CallArgs args = CallArgsFromVp(argc, vp);
1061 return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_inLeapYear>(
1062 cx, args);
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());
1074 // Step 3.
1075 Rooted<JSObject*> temporalYearMonthLike(
1076 cx, RequireObjectArg(cx, "temporalYearMonthLike", "with", args.get(0)));
1077 if (!temporalYearMonthLike) {
1078 return false;
1080 if (!ThrowIfTemporalLikeObject(cx, temporalYearMonthLike)) {
1081 return false;
1084 // Step 4.
1085 Rooted<PlainObject*> resolvedOptions(cx);
1086 if (args.hasDefined(1)) {
1087 Rooted<JSObject*> options(cx,
1088 RequireObjectArg(cx, "options", "with", args[1]));
1089 if (!options) {
1090 return false;
1092 resolvedOptions = SnapshotOwnProperties(cx, options);
1093 } else {
1094 resolvedOptions = NewPlainObjectWithProto(cx, nullptr);
1096 if (!resolvedOptions) {
1097 return false;
1100 // Step 5.
1101 Rooted<CalendarRecord> calendar(cx);
1102 if (!CreateCalendarMethodsRecord(cx, calendarValue,
1104 CalendarMethod::Fields,
1105 CalendarMethod::MergeFields,
1106 CalendarMethod::YearMonthFromFields,
1108 &calendar)) {
1109 return false;
1112 // Step 6.
1113 JS::RootedVector<PropertyKey> fieldNames(cx);
1114 if (!CalendarFields(
1115 cx, calendar,
1116 {CalendarField::Month, CalendarField::MonthCode, CalendarField::Year},
1117 &fieldNames)) {
1118 return false;
1121 // Step 7.
1122 Rooted<PlainObject*> fields(cx,
1123 PrepareTemporalFields(cx, yearMonth, fieldNames));
1124 if (!fields) {
1125 return false;
1128 // Step 8.
1129 Rooted<PlainObject*> partialYearMonth(
1130 cx, PreparePartialTemporalFields(cx, temporalYearMonthLike, fieldNames));
1131 if (!partialYearMonth) {
1132 return false;
1135 // Step 9.
1136 Rooted<JSObject*> mergedFields(
1137 cx, CalendarMergeFields(cx, calendar, fields, partialYearMonth));
1138 if (!mergedFields) {
1139 return false;
1142 // Step 10.
1143 fields = PrepareTemporalFields(cx, mergedFields, fieldNames);
1144 if (!fields) {
1145 return false;
1148 // Step 11.
1149 auto obj = CalendarYearMonthFromFields(cx, calendar, fields, resolvedOptions);
1150 if (!obj) {
1151 return false;
1154 args.rval().setObject(*obj);
1155 return true;
1159 * Temporal.PlainYearMonth.prototype.with ( temporalYearMonthLike [ , options ]
1162 static bool PlainYearMonth_with(JSContext* cx, unsigned argc, Value* vp) {
1163 // Steps 1-2.
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) {
1172 // Step 3.
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) {
1181 // Steps 1-2.
1182 CallArgs args = CallArgsFromVp(argc, vp);
1183 return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_add>(cx, args);
1187 * Temporal.PlainYearMonth.prototype.subtract ( temporalDurationLike [ , options
1188 * ] )
1190 static bool PlainYearMonth_subtract(JSContext* cx, const CallArgs& args) {
1191 // Step 3.
1192 return AddDurationToOrSubtractDurationFromPlainYearMonth(
1193 cx, PlainYearMonthDuration::Subtract, args);
1197 * Temporal.PlainYearMonth.prototype.subtract ( temporalDurationLike [ , options
1198 * ] )
1200 static bool PlainYearMonth_subtract(JSContext* cx, unsigned argc, Value* vp) {
1201 // Steps 1-2.
1202 CallArgs args = CallArgsFromVp(argc, vp);
1203 return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_subtract>(cx,
1204 args);
1208 * Temporal.PlainYearMonth.prototype.until ( other [ , options ] )
1210 static bool PlainYearMonth_until(JSContext* cx, const CallArgs& args) {
1211 // Step 3.
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) {
1219 // Steps 1-2.
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) {
1228 // Step 3.
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) {
1236 // Steps 1-2.
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());
1249 // Step 3.
1250 PlainDate other;
1251 Rooted<CalendarValue> otherCalendar(cx);
1252 if (!ToTemporalYearMonth(cx, args.get(0), &other, &otherCalendar)) {
1253 return false;
1256 // Steps 4-7.
1257 bool equals = date == other;
1258 if (equals && !CalendarEquals(cx, calendar, otherCalendar, &equals)) {
1259 return false;
1262 args.rval().setBoolean(equals);
1263 return true;
1267 * Temporal.PlainYearMonth.prototype.equals ( other )
1269 static bool PlainYearMonth_equals(JSContext* cx, unsigned argc, Value* vp) {
1270 // Steps 1-2.
1271 CallArgs args = CallArgsFromVp(argc, vp);
1272 return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_equals>(cx,
1273 args);
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)) {
1285 // Step 3.
1286 Rooted<JSObject*> options(
1287 cx, RequireObjectArg(cx, "options", "toString", args[0]));
1288 if (!options) {
1289 return false;
1292 // Step 4.
1293 if (!ToCalendarNameOption(cx, options, &showCalendar)) {
1294 return false;
1298 // Step 5.
1299 JSString* str = TemporalYearMonthToString(cx, yearMonth, showCalendar);
1300 if (!str) {
1301 return false;
1304 args.rval().setString(str);
1305 return true;
1309 * Temporal.PlainYearMonth.prototype.toString ( [ options ] )
1311 static bool PlainYearMonth_toString(JSContext* cx, unsigned argc, Value* vp) {
1312 // Steps 1-2.
1313 CallArgs args = CallArgsFromVp(argc, vp);
1314 return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_toString>(cx,
1315 args);
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>());
1326 // Step 3.
1327 JSString* str =
1328 TemporalYearMonthToString(cx, yearMonth, CalendarOption::Auto);
1329 if (!str) {
1330 return false;
1333 args.rval().setString(str);
1334 return true;
1338 * Temporal.PlainYearMonth.prototype.toLocaleString ( [ locales [ , options ] ]
1341 static bool PlainYearMonth_toLocaleString(JSContext* cx, unsigned argc,
1342 Value* vp) {
1343 // Steps 1-2.
1344 CallArgs args = CallArgsFromVp(argc, vp);
1345 return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_toLocaleString>(
1346 cx, args);
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>());
1356 // Step 3.
1357 JSString* str =
1358 TemporalYearMonthToString(cx, yearMonth, CalendarOption::Auto);
1359 if (!str) {
1360 return false;
1363 args.rval().setString(str);
1364 return true;
1368 * Temporal.PlainYearMonth.prototype.toJSON ( )
1370 static bool PlainYearMonth_toJSON(JSContext* cx, unsigned argc, Value* vp) {
1371 // Steps 1-2.
1372 CallArgs args = CallArgsFromVp(argc, vp);
1373 return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_toJSON>(cx,
1374 args);
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");
1383 return false;
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>());
1393 // Step 3.
1394 Rooted<JSObject*> item(
1395 cx, RequireObjectArg(cx, "item", "toPlainDate", args.get(0)));
1396 if (!item) {
1397 return false;
1400 // Step 4.
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,
1409 &calendar)) {
1410 return false;
1413 // Step 5.
1414 JS::RootedVector<PropertyKey> receiverFieldNames(cx);
1415 if (!CalendarFields(cx, calendar,
1416 {CalendarField::MonthCode, CalendarField::Year},
1417 &receiverFieldNames)) {
1418 return false;
1421 // Step 6.
1422 Rooted<PlainObject*> fields(
1423 cx, PrepareTemporalFields(cx, yearMonth, receiverFieldNames));
1424 if (!fields) {
1425 return false;
1428 // Step 7.
1429 JS::RootedVector<PropertyKey> inputFieldNames(cx);
1430 if (!CalendarFields(cx, calendar, {CalendarField::Day}, &inputFieldNames)) {
1431 return false;
1434 // Step 8.
1435 Rooted<PlainObject*> inputFields(
1436 cx, PrepareTemporalFields(cx, item, inputFieldNames));
1437 if (!inputFields) {
1438 return false;
1441 // Step 9.
1442 Rooted<JSObject*> mergedFields(
1443 cx, CalendarMergeFields(cx, calendar, fields, inputFields));
1444 if (!mergedFields) {
1445 return false;
1448 // Step 10.
1449 JS::RootedVector<PropertyKey> concatenatedFieldNames(cx);
1450 if (!ConcatTemporalFieldNames(receiverFieldNames, inputFieldNames,
1451 concatenatedFieldNames.get())) {
1452 return false;
1455 // Step 11.
1456 Rooted<PlainObject*> mergedFromConcatenatedFields(
1457 cx, PrepareTemporalFields(cx, mergedFields, concatenatedFieldNames));
1458 if (!mergedFromConcatenatedFields) {
1459 return false;
1462 // Steps 12-14.
1463 auto obj = CalendarDateFromFields(cx, calendar, mergedFromConcatenatedFields,
1464 TemporalOverflow::Constrain);
1465 if (!obj) {
1466 return false;
1469 args.rval().setObject(*obj);
1470 return true;
1474 * Temporal.PlainYearMonth.prototype.toPlainDate ( item )
1476 static bool PlainYearMonth_toPlainDate(JSContext* cx, unsigned argc,
1477 Value* vp) {
1478 // Steps 1-2.
1479 CallArgs args = CallArgsFromVp(argc, vp);
1480 return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_toPlainDate>(
1481 cx, args);
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>());
1491 // Step 3.
1492 Rooted<IdValueVector> fields(cx, IdValueVector(cx));
1494 // Step 4.
1495 if (!fields.emplaceBack(NameToId(cx->names().calendar),
1496 yearMonth->calendar().toValue())) {
1497 return false;
1500 // Step 5.
1501 if (!fields.emplaceBack(NameToId(cx->names().isoDay),
1502 Int32Value(yearMonth->isoDay()))) {
1503 return false;
1506 // Step 6.
1507 if (!fields.emplaceBack(NameToId(cx->names().isoMonth),
1508 Int32Value(yearMonth->isoMonth()))) {
1509 return false;
1512 // Step 7.
1513 if (!fields.emplaceBack(NameToId(cx->names().isoYear),
1514 Int32Value(yearMonth->isoYear()))) {
1515 return false;
1518 // Step 8.
1519 auto* obj = NewPlainObjectWithUniqueNames(cx, fields);
1520 if (!obj) {
1521 return false;
1524 args.rval().setObject(*obj);
1525 return true;
1529 * Temporal.PlainYearMonth.prototype.getISOFields ( )
1531 static bool PlainYearMonth_getISOFields(JSContext* cx, unsigned argc,
1532 Value* vp) {
1533 // Steps 1-2.
1534 CallArgs args = CallArgsFromVp(argc, vp);
1535 return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_getISOFields>(
1536 cx, args);
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());
1546 // Step 3.
1547 auto* obj = ToTemporalCalendarObject(cx, calendar);
1548 if (!obj) {
1549 return false;
1552 args.rval().setObject(*obj);
1553 return true;
1557 * Temporal.PlainYearMonth.prototype.getCalendar ( )
1559 static bool PlainYearMonth_getCalendar(JSContext* cx, unsigned argc,
1560 Value* vp) {
1561 // Steps 1-2.
1562 CallArgs args = CallArgsFromVp(argc, vp);
1563 return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_getCalendar>(
1564 cx, args);
1567 const JSClass PlainYearMonthObject::class_ = {
1568 "Temporal.PlainYearMonth",
1569 JSCLASS_HAS_RESERVED_SLOTS(PlainYearMonthObject::SLOT_COUNT) |
1570 JSCLASS_HAS_CACHED_PROTO(JSProto_PlainYearMonth),
1571 JS_NULL_CLASS_OPS,
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),
1580 JS_FS_END,
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),
1597 JS_FS_END,
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),
1610 JS_PS_END,
1613 const ClassSpec PlainYearMonthObject::classSpec_ = {
1614 GenericCreateConstructor<PlainYearMonthConstructor, 2,
1615 gc::AllocKind::FUNCTION>,
1616 GenericCreatePrototype<PlainYearMonthObject>,
1617 PlainYearMonth_methods,
1618 nullptr,
1619 PlainYearMonth_prototype_methods,
1620 PlainYearMonth_prototype_properties,
1621 nullptr,
1622 ClassSpec::DontDefineConstructor,