tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

PlainYearMonth.cpp (38736B)


      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/. */
      6 
      7 #include "builtin/temporal/PlainYearMonth.h"
      8 
      9 #include "mozilla/Assertions.h"
     10 
     11 #include "jspubtd.h"
     12 #include "NamespaceImports.h"
     13 
     14 #include "builtin/intl/DateTimeFormat.h"
     15 #include "builtin/temporal/Calendar.h"
     16 #include "builtin/temporal/CalendarFields.h"
     17 #include "builtin/temporal/Duration.h"
     18 #include "builtin/temporal/Instant.h"
     19 #include "builtin/temporal/PlainDate.h"
     20 #include "builtin/temporal/PlainDateTime.h"
     21 #include "builtin/temporal/PlainMonthDay.h"
     22 #include "builtin/temporal/Temporal.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/TimeZone.h"
     28 #include "builtin/temporal/ToString.h"
     29 #include "gc/AllocKind.h"
     30 #include "gc/Barrier.h"
     31 #include "gc/GCEnum.h"
     32 #include "js/CallArgs.h"
     33 #include "js/CallNonGenericMethod.h"
     34 #include "js/Class.h"
     35 #include "js/ErrorReport.h"
     36 #include "js/friend/ErrorMessages.h"
     37 #include "js/PropertyDescriptor.h"
     38 #include "js/PropertySpec.h"
     39 #include "js/RootingAPI.h"
     40 #include "js/TypeDecls.h"
     41 #include "js/Value.h"
     42 #include "vm/BytecodeUtil.h"
     43 #include "vm/GlobalObject.h"
     44 #include "vm/JSAtomState.h"
     45 #include "vm/JSContext.h"
     46 #include "vm/JSObject.h"
     47 #include "vm/PlainObject.h"
     48 #include "vm/StringType.h"
     49 
     50 #include "vm/JSObject-inl.h"
     51 #include "vm/NativeObject-inl.h"
     52 
     53 using namespace js;
     54 using namespace js::temporal;
     55 
     56 static inline bool IsPlainYearMonth(Handle<Value> v) {
     57  return v.isObject() && v.toObject().is<PlainYearMonthObject>();
     58 }
     59 
     60 /**
     61 * ISOYearMonthWithinLimits ( isoDate )
     62 */
     63 bool js::temporal::ISOYearMonthWithinLimits(const ISODate& isoDate) {
     64  MOZ_ASSERT(IsValidISODate(isoDate));
     65 
     66  constexpr auto min = ISODate::min();
     67  constexpr auto max = ISODate::max();
     68 
     69  const auto& year = isoDate.year;
     70 
     71  // Fast-path when the input is definitely in range.
     72  if (min.year < year && year < max.year) {
     73    return true;
     74  }
     75 
     76  // Check |isoDate| is within the valid limits.
     77  if (year < 0) {
     78    return isoDate >= ISODate{min.year, min.month, 1};
     79  }
     80  return isoDate < ISODate{max.year, max.month + 1, 1};
     81 }
     82 
     83 /**
     84 * CreateTemporalYearMonth ( isoDate, calendar [ , newTarget ] )
     85 */
     86 static PlainYearMonthObject* CreateTemporalYearMonth(
     87    JSContext* cx, const CallArgs& args, const ISODate& isoDate,
     88    Handle<CalendarValue> calendar) {
     89  // Step 1.
     90  if (!ISOYearMonthWithinLimits(isoDate)) {
     91    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
     92                              JSMSG_TEMPORAL_PLAIN_YEAR_MONTH_INVALID);
     93    return nullptr;
     94  }
     95 
     96  // Steps 2-3.
     97  Rooted<JSObject*> proto(cx);
     98  if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_PlainYearMonth,
     99                                          &proto)) {
    100    return nullptr;
    101  }
    102 
    103  auto* object = NewObjectWithClassProto<PlainYearMonthObject>(cx, proto);
    104  if (!object) {
    105    return nullptr;
    106  }
    107 
    108  // Step 4.
    109  auto packedDate = PackedDate::pack(isoDate);
    110  object->initFixedSlot(PlainYearMonthObject::PACKED_DATE_SLOT,
    111                        PrivateUint32Value(packedDate.value));
    112 
    113  // Step 5.
    114  object->initFixedSlot(PlainYearMonthObject::CALENDAR_SLOT,
    115                        calendar.toSlotValue());
    116 
    117  // Step 6.
    118  return object;
    119 }
    120 
    121 /**
    122 * CreateTemporalYearMonth ( isoDate, calendar [ , newTarget ] )
    123 */
    124 PlainYearMonthObject* js::temporal::CreateTemporalYearMonth(
    125    JSContext* cx, Handle<PlainYearMonth> yearMonth) {
    126  MOZ_ASSERT(IsValidISODate(yearMonth));
    127 
    128  // Step 1.
    129  MOZ_ASSERT(ISOYearMonthWithinLimits(yearMonth));
    130 
    131  // Steps 2-3.
    132  auto* object = NewBuiltinClassInstance<PlainYearMonthObject>(cx);
    133  if (!object) {
    134    return nullptr;
    135  }
    136 
    137  // Step 4.
    138  auto packedDate = PackedDate::pack(yearMonth);
    139  object->initFixedSlot(PlainYearMonthObject::PACKED_DATE_SLOT,
    140                        PrivateUint32Value(packedDate.value));
    141 
    142  // Step 5.
    143  object->initFixedSlot(PlainYearMonthObject::CALENDAR_SLOT,
    144                        yearMonth.calendar().toSlotValue());
    145 
    146  // Step 6.
    147  return object;
    148 }
    149 
    150 /**
    151 * CreateTemporalYearMonth ( isoDate, calendar [ , newTarget ] )
    152 */
    153 bool js::temporal::CreateTemporalYearMonth(
    154    JSContext* cx, const ISODate& isoDate, Handle<CalendarValue> calendar,
    155    MutableHandle<PlainYearMonth> result) {
    156  MOZ_ASSERT(IsValidISODate(isoDate));
    157 
    158  // Step 1.
    159  if (!ISOYearMonthWithinLimits(isoDate)) {
    160    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    161                              JSMSG_TEMPORAL_PLAIN_YEAR_MONTH_INVALID);
    162    return false;
    163  }
    164 
    165  // Steps 2-6.
    166  result.set(PlainYearMonth{isoDate, calendar});
    167  return true;
    168 }
    169 
    170 struct YearMonthOptions {
    171  TemporalOverflow overflow = TemporalOverflow::Constrain;
    172 };
    173 
    174 /**
    175 * ToTemporalYearMonth ( item [ , options ] )
    176 */
    177 static bool ToTemporalYearMonthOptions(JSContext* cx, Handle<Value> options,
    178                                       YearMonthOptions* result) {
    179  if (options.isUndefined()) {
    180    *result = {};
    181    return true;
    182  }
    183 
    184  // NOTE: |options| are only passed from `Temporal.PlainYearMonth.from`.
    185 
    186  Rooted<JSObject*> resolvedOptions(
    187      cx, RequireObjectArg(cx, "options", "from", options));
    188  if (!resolvedOptions) {
    189    return false;
    190  }
    191 
    192  auto overflow = TemporalOverflow::Constrain;
    193  if (!GetTemporalOverflowOption(cx, resolvedOptions, &overflow)) {
    194    return false;
    195  }
    196 
    197  *result = {overflow};
    198  return true;
    199 }
    200 
    201 /**
    202 * ToTemporalYearMonth ( item [ , options ] )
    203 */
    204 static bool ToTemporalYearMonth(JSContext* cx, Handle<JSObject*> item,
    205                                Handle<Value> options,
    206                                MutableHandle<PlainYearMonth> result) {
    207  // Step 1. (Not applicable in our implementation.)
    208 
    209  // Step 2.a.
    210  if (auto* plainYearMonth = item->maybeUnwrapIf<PlainYearMonthObject>()) {
    211    auto date = plainYearMonth->date();
    212    Rooted<CalendarValue> calendar(cx, plainYearMonth->calendar());
    213    if (!calendar.wrap(cx)) {
    214      return false;
    215    }
    216 
    217    // Steps 2.a.i-ii.
    218    YearMonthOptions ignoredOptions;
    219    if (!ToTemporalYearMonthOptions(cx, options, &ignoredOptions)) {
    220      return false;
    221    }
    222 
    223    // Step 2.a.iii.
    224    result.set(PlainYearMonth{date, calendar});
    225    return true;
    226  }
    227 
    228  // Step 2.b.
    229  Rooted<CalendarValue> calendar(cx);
    230  if (!GetTemporalCalendarWithISODefault(cx, item, &calendar)) {
    231    return false;
    232  }
    233 
    234  // Step 2.c.
    235  Rooted<CalendarFields> fields(cx);
    236  if (!PrepareCalendarFields(cx, calendar, item,
    237                             {
    238                                 CalendarField::Year,
    239                                 CalendarField::Month,
    240                                 CalendarField::MonthCode,
    241                             },
    242                             &fields)) {
    243    return false;
    244  }
    245 
    246  // Steps 2.d-e.
    247  YearMonthOptions resolvedOptions;
    248  if (!ToTemporalYearMonthOptions(cx, options, &resolvedOptions)) {
    249    return false;
    250  }
    251  auto [overflow] = resolvedOptions;
    252 
    253  // Step 2.f.
    254  return CalendarYearMonthFromFields(cx, calendar, fields, overflow, result);
    255 }
    256 
    257 /**
    258 * ToTemporalYearMonth ( item [ , options ] )
    259 */
    260 static bool ToTemporalYearMonth(JSContext* cx, Handle<Value> item,
    261                                Handle<Value> options,
    262                                MutableHandle<PlainYearMonth> result) {
    263  // Step 1. (Not applicable in our implementation.)
    264 
    265  // Step 2.
    266  if (item.isObject()) {
    267    Rooted<JSObject*> itemObj(cx, &item.toObject());
    268    return ToTemporalYearMonth(cx, itemObj, options, result);
    269  }
    270 
    271  // Step 3.
    272  if (!item.isString()) {
    273    ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, item,
    274                     nullptr, "not a string");
    275    return false;
    276  }
    277  Rooted<JSString*> string(cx, item.toString());
    278 
    279  // Step 4.
    280  ISODate date;
    281  Rooted<JSString*> calendarString(cx);
    282  if (!ParseTemporalYearMonthString(cx, string, &date, &calendarString)) {
    283    return false;
    284  }
    285 
    286  // Steps 5-7.
    287  Rooted<CalendarValue> calendar(cx, CalendarValue(CalendarId::ISO8601));
    288  if (calendarString) {
    289    if (!CanonicalizeCalendar(cx, calendarString, &calendar)) {
    290      return false;
    291    }
    292  }
    293 
    294  // Steps 8-9.
    295  YearMonthOptions ignoredOptions;
    296  if (!ToTemporalYearMonthOptions(cx, options, &ignoredOptions)) {
    297    return false;
    298  }
    299 
    300  // Step 10-11.
    301  Rooted<PlainYearMonth> yearMonth(cx);
    302  if (!CreateTemporalYearMonth(cx, date, calendar, &yearMonth)) {
    303    return false;
    304  }
    305 
    306  // Step 12.
    307  Rooted<CalendarFields> fields(cx);
    308  if (!ISODateToFields(cx, yearMonth, &fields)) {
    309    return false;
    310  }
    311 
    312  // Steps 13-14.
    313  return CalendarYearMonthFromFields(cx, calendar, fields,
    314                                     TemporalOverflow::Constrain, result);
    315 }
    316 
    317 /**
    318 * ToTemporalYearMonth ( item [ , options ] )
    319 */
    320 static bool ToTemporalYearMonth(JSContext* cx, Handle<Value> item,
    321                                MutableHandle<PlainYearMonth> result) {
    322  return ToTemporalYearMonth(cx, item, UndefinedHandleValue, result);
    323 }
    324 
    325 /**
    326 * DifferenceTemporalPlainYearMonth ( operation, yearMonth, other, options )
    327 */
    328 static bool DifferenceTemporalPlainYearMonth(JSContext* cx,
    329                                             TemporalDifference operation,
    330                                             const CallArgs& args) {
    331  Rooted<PlainYearMonth> yearMonth(
    332      cx, &args.thisv().toObject().as<PlainYearMonthObject>());
    333 
    334  // Step 1.
    335  Rooted<PlainYearMonth> other(cx);
    336  if (!ToTemporalYearMonth(cx, args.get(0), &other)) {
    337    return false;
    338  }
    339 
    340  // Step 2.
    341  auto calendar = yearMonth.calendar();
    342 
    343  // Step 3.
    344  if (!CalendarEquals(calendar, other.calendar())) {
    345    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    346                              JSMSG_TEMPORAL_CALENDAR_INCOMPATIBLE,
    347                              CalendarIdentifier(calendar).data(),
    348                              CalendarIdentifier(other.calendar()).data());
    349    return false;
    350  }
    351 
    352  // Steps 4-5.
    353  DifferenceSettings settings;
    354  Rooted<PlainObject*> resolvedOptions(cx);
    355  if (args.hasDefined(1)) {
    356    // Step 4.
    357    Rooted<JSObject*> options(
    358        cx, RequireObjectArg(cx, "options", ToName(operation), args[1]));
    359    if (!options) {
    360      return false;
    361    }
    362 
    363    // Step 5.
    364    if (!GetDifferenceSettings(cx, operation, options, TemporalUnitGroup::Date,
    365                               TemporalUnit::Month, TemporalUnit::Month,
    366                               TemporalUnit::Year, &settings)) {
    367      return false;
    368    }
    369  } else {
    370    // Steps 4-5.
    371    settings = {
    372        TemporalUnit::Month,
    373        TemporalUnit::Year,
    374        TemporalRoundingMode::Trunc,
    375        Increment{1},
    376    };
    377  }
    378 
    379  // Step 6.
    380  if (yearMonth.date() == other.date()) {
    381    auto* obj = CreateTemporalDuration(cx, {});
    382    if (!obj) {
    383      return false;
    384    }
    385 
    386    args.rval().setObject(*obj);
    387    return true;
    388  }
    389 
    390  // Step 7.
    391  Rooted<CalendarFields> thisFields(cx);
    392  if (!ISODateToFields(cx, yearMonth, &thisFields)) {
    393    return false;
    394  }
    395 
    396  // Step 8.
    397  MOZ_ASSERT(!thisFields.has(CalendarField::Day));
    398  thisFields.setDay(1);
    399 
    400  // Step 9.
    401  Rooted<PlainDate> thisDate(cx);
    402  if (!CalendarDateFromFields(cx, calendar, thisFields,
    403                              TemporalOverflow::Constrain, &thisDate)) {
    404    return false;
    405  }
    406 
    407  // Step 10.
    408  Rooted<CalendarFields> otherFields(cx);
    409  if (!ISODateToFields(cx, other, &otherFields)) {
    410    return false;
    411  }
    412 
    413  // Step 11.
    414  MOZ_ASSERT(!otherFields.has(CalendarField::Day));
    415  otherFields.setDay(1);
    416 
    417  // Step 12.
    418  Rooted<PlainDate> otherDate(cx);
    419  if (!CalendarDateFromFields(cx, calendar, otherFields,
    420                              TemporalOverflow::Constrain, &otherDate)) {
    421    return false;
    422  }
    423 
    424  // Step 13.
    425  DateDuration until;
    426  if (!CalendarDateUntil(cx, calendar, thisDate, otherDate,
    427                         settings.largestUnit, &until)) {
    428    return false;
    429  }
    430 
    431  // Step 14. (Inlined AdjustDateDurationRecord)
    432  //
    433  // We only care about years and months here, all other fields are set to zero.
    434  auto dateDuration = DateDuration{until.years, until.months};
    435 
    436  // Step 15.
    437  auto duration = InternalDuration{dateDuration, {}};
    438 
    439  // Step 16.
    440  if (settings.smallestUnit != TemporalUnit::Month ||
    441      settings.roundingIncrement != Increment{1}) {
    442    // Step 16.a.
    443    auto isoDateTime = ISODateTime{thisDate, {}};
    444 
    445    // Step 16.b.
    446    auto originEpochNs = GetUTCEpochNanoseconds(isoDateTime);
    447 
    448    // Step 16.c.
    449    auto isoDateTimeOther = ISODateTime{otherDate, {}};
    450 
    451    // Step 16.d.
    452    auto destEpochNs = GetUTCEpochNanoseconds(isoDateTimeOther);
    453 
    454    // Step 16.e.
    455    Rooted<TimeZoneValue> timeZone(cx, TimeZoneValue{});
    456    if (!RoundRelativeDuration(
    457            cx, duration, originEpochNs, destEpochNs, isoDateTime, timeZone,
    458            calendar, settings.largestUnit, settings.roundingIncrement,
    459            settings.smallestUnit, settings.roundingMode, &duration)) {
    460      return false;
    461    }
    462  }
    463  MOZ_ASSERT(IsValidDuration(duration));
    464  MOZ_ASSERT(duration.date.weeks == 0);
    465  MOZ_ASSERT(duration.date.days == 0);
    466  MOZ_ASSERT(duration.time == TimeDuration{});
    467 
    468  // Step 17. (Inlined TemporalDurationFromInternal)
    469  auto result = duration.date.toDuration();
    470 
    471  // Step 18.
    472  if (operation == TemporalDifference::Since) {
    473    result = result.negate();
    474  }
    475 
    476  // Step 19.
    477  auto* obj = CreateTemporalDuration(cx, result);
    478  if (!obj) {
    479    return false;
    480  }
    481 
    482  args.rval().setObject(*obj);
    483  return true;
    484 }
    485 
    486 /**
    487 * AddDurationToYearMonth ( operation, yearMonth, temporalDurationLike, options
    488 * )
    489 */
    490 static bool AddDurationToYearMonth(JSContext* cx, TemporalAddDuration operation,
    491                                   const CallArgs& args) {
    492  Rooted<PlainYearMonth> yearMonth(
    493      cx, &args.thisv().toObject().as<PlainYearMonthObject>());
    494 
    495  // Step 1.
    496  Duration duration;
    497  if (!ToTemporalDuration(cx, args.get(0), &duration)) {
    498    return false;
    499  }
    500 
    501  // Step 2.
    502  if (operation == TemporalAddDuration::Subtract) {
    503    duration = duration.negate();
    504  }
    505 
    506  // Steps 3-4.
    507  auto overflow = TemporalOverflow::Constrain;
    508  if (args.hasDefined(1)) {
    509    // Step 3.
    510    Rooted<JSObject*> options(
    511        cx, RequireObjectArg(cx, "options", ToName(operation), args[1]));
    512    if (!options) {
    513      return false;
    514    }
    515 
    516    // Step 4.
    517    if (!GetTemporalOverflowOption(cx, options, &overflow)) {
    518      return false;
    519    }
    520  }
    521 
    522  // Step 5.
    523  int32_t sign = DurationSign(duration);
    524 
    525  // Step 6.
    526  auto calendar = yearMonth.calendar();
    527 
    528  // Step 7.
    529  Rooted<CalendarFields> fields(cx);
    530  if (!ISODateToFields(cx, yearMonth, &fields)) {
    531    return false;
    532  }
    533 
    534  // Step 8.
    535  MOZ_ASSERT(!fields.has(CalendarField::Day));
    536  fields.setDay(1);
    537 
    538  // Step 9.
    539  Rooted<PlainDate> intermediateDate(cx);
    540  if (!CalendarDateFromFields(cx, calendar, fields, TemporalOverflow::Constrain,
    541                              &intermediateDate)) {
    542    return false;
    543  }
    544 
    545  // Steps 10-11.
    546  ISODate date;
    547  if (sign < 0) {
    548    // |intermediateDate| is initialized to the first day of |yearMonth|'s
    549    // month. Compute the last day of |yearMonth|'s month by first adding one
    550    // month and then subtracting one day.
    551    //
    552    // This is roughly equivalent to these calls:
    553    //
    554    // js> var ym = new Temporal.PlainYearMonth(2023, 1);
    555    // js> ym.toPlainDate({day: 1}).add({months: 1}).subtract({days: 1}).day
    556    // 31
    557    //
    558    // For many calendars this is equivalent to `ym.daysInMonth`, except when
    559    // some days are skipped, for example consider the Julian-to-Gregorian
    560    // calendar transition.
    561 
    562    // Step 10.a.
    563    auto oneMonthDuration = DateDuration{0, 1};
    564 
    565    // Step 10.b.
    566    ISODate nextMonth;
    567    if (!CalendarDateAdd(cx, calendar, intermediateDate, oneMonthDuration,
    568                         TemporalOverflow::Constrain, &nextMonth)) {
    569      return false;
    570    }
    571 
    572    // Step 10.c.
    573    date = BalanceISODate(nextMonth, -1);
    574 
    575    // Step 10.d.
    576    MOZ_ASSERT(ISODateWithinLimits(date));
    577  } else {
    578    // Step 11.a.
    579    date = intermediateDate;
    580  }
    581 
    582  // Steps 12.
    583  auto durationToAdd = ToDateDurationRecordWithoutTime(duration);
    584 
    585  // Step 13.
    586  ISODate addedDate;
    587  if (!CalendarDateAdd(cx, calendar, date, durationToAdd, overflow,
    588                       &addedDate)) {
    589    return false;
    590  }
    591  MOZ_ASSERT(ISODateWithinLimits(addedDate));
    592 
    593  Rooted<PlainYearMonth> addedYearMonth(cx,
    594                                        PlainYearMonth{addedDate, calendar});
    595 
    596  // Step 14.
    597  Rooted<CalendarFields> addedDateFields(cx);
    598  if (!ISODateToFields(cx, addedYearMonth, &addedDateFields)) {
    599    return false;
    600  }
    601 
    602  // Step 15.
    603  Rooted<PlainYearMonth> result(cx);
    604  if (!CalendarYearMonthFromFields(cx, calendar, addedDateFields, overflow,
    605                                   &result)) {
    606    return false;
    607  }
    608 
    609  // Step 16.
    610  auto* obj = CreateTemporalYearMonth(cx, result);
    611  if (!obj) {
    612    return false;
    613  }
    614 
    615  args.rval().setObject(*obj);
    616  return true;
    617 }
    618 
    619 /**
    620 * Temporal.PlainYearMonth ( isoYear, isoMonth [ , calendar [ , referenceISODay
    621 * ] ] )
    622 */
    623 static bool PlainYearMonthConstructor(JSContext* cx, unsigned argc, Value* vp) {
    624  CallArgs args = CallArgsFromVp(argc, vp);
    625 
    626  // Step 1.
    627  if (!ThrowIfNotConstructing(cx, args, "Temporal.PlainYearMonth")) {
    628    return false;
    629  }
    630 
    631  // Step 3.
    632  double isoYear;
    633  if (!ToIntegerWithTruncation(cx, args.get(0), "year", &isoYear)) {
    634    return false;
    635  }
    636 
    637  // Step 4.
    638  double isoMonth;
    639  if (!ToIntegerWithTruncation(cx, args.get(1), "month", &isoMonth)) {
    640    return false;
    641  }
    642 
    643  // Steps 5-7.
    644  Rooted<CalendarValue> calendar(cx, CalendarValue(CalendarId::ISO8601));
    645  if (args.hasDefined(2)) {
    646    // Step 6.
    647    if (!args[2].isString()) {
    648      ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, args[2],
    649                       nullptr, "not a string");
    650      return false;
    651    }
    652 
    653    // Step 7.
    654    Rooted<JSString*> calendarString(cx, args[2].toString());
    655    if (!CanonicalizeCalendar(cx, calendarString, &calendar)) {
    656      return false;
    657    }
    658  }
    659 
    660  // Steps 2 and 8.
    661  double isoDay = 1;
    662  if (args.hasDefined(3)) {
    663    if (!ToIntegerWithTruncation(cx, args[3], "day", &isoDay)) {
    664      return false;
    665    }
    666  }
    667 
    668  // Step 9.
    669  if (!ThrowIfInvalidISODate(cx, isoYear, isoMonth, isoDay)) {
    670    return false;
    671  }
    672 
    673  // Step 10.
    674  auto isoDate = ISODate{int32_t(isoYear), int32_t(isoMonth), int32_t(isoDay)};
    675 
    676  // Step 11.
    677  auto* yearMonth = CreateTemporalYearMonth(cx, args, isoDate, calendar);
    678  if (!yearMonth) {
    679    return false;
    680  }
    681 
    682  args.rval().setObject(*yearMonth);
    683  return true;
    684 }
    685 
    686 /**
    687 * Temporal.PlainYearMonth.from ( item [ , options ] )
    688 */
    689 static bool PlainYearMonth_from(JSContext* cx, unsigned argc, Value* vp) {
    690  CallArgs args = CallArgsFromVp(argc, vp);
    691 
    692  // Step 1.
    693  Rooted<PlainYearMonth> yearMonth(cx);
    694  if (!ToTemporalYearMonth(cx, args.get(0), args.get(1), &yearMonth)) {
    695    return false;
    696  }
    697 
    698  auto* result = CreateTemporalYearMonth(cx, yearMonth);
    699  if (!result) {
    700    return false;
    701  }
    702 
    703  args.rval().setObject(*result);
    704  return true;
    705 }
    706 
    707 /**
    708 * Temporal.PlainYearMonth.compare ( one, two )
    709 */
    710 static bool PlainYearMonth_compare(JSContext* cx, unsigned argc, Value* vp) {
    711  CallArgs args = CallArgsFromVp(argc, vp);
    712 
    713  // Step 1.
    714  Rooted<PlainYearMonth> one(cx);
    715  if (!ToTemporalYearMonth(cx, args.get(0), &one)) {
    716    return false;
    717  }
    718 
    719  // Step 2.
    720  Rooted<PlainYearMonth> two(cx);
    721  if (!ToTemporalYearMonth(cx, args.get(1), &two)) {
    722    return false;
    723  }
    724 
    725  // Step 3.
    726  args.rval().setInt32(CompareISODate(one, two));
    727  return true;
    728 }
    729 
    730 /**
    731 * get Temporal.PlainYearMonth.prototype.calendarId
    732 */
    733 static bool PlainYearMonth_calendarId(JSContext* cx, const CallArgs& args) {
    734  auto* yearMonth = &args.thisv().toObject().as<PlainYearMonthObject>();
    735 
    736  // Step 3.
    737  auto* str =
    738      NewStringCopy<CanGC>(cx, CalendarIdentifier(yearMonth->calendar()));
    739  if (!str) {
    740    return false;
    741  }
    742 
    743  args.rval().setString(str);
    744  return true;
    745 }
    746 
    747 /**
    748 * get Temporal.PlainYearMonth.prototype.calendarId
    749 */
    750 static bool PlainYearMonth_calendarId(JSContext* cx, unsigned argc, Value* vp) {
    751  // Steps 1-2.
    752  CallArgs args = CallArgsFromVp(argc, vp);
    753  return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_calendarId>(
    754      cx, args);
    755 }
    756 
    757 /**
    758 * get Temporal.PlainYearMonth.prototype.era
    759 */
    760 static bool PlainYearMonth_era(JSContext* cx, const CallArgs& args) {
    761  auto* yearMonth = &args.thisv().toObject().as<PlainYearMonthObject>();
    762  Rooted<CalendarValue> calendar(cx, yearMonth->calendar());
    763 
    764  // Step 3.
    765  return CalendarEra(cx, calendar, yearMonth->date(), args.rval());
    766 }
    767 
    768 /**
    769 * get Temporal.PlainYearMonth.prototype.era
    770 */
    771 static bool PlainYearMonth_era(JSContext* cx, unsigned argc, Value* vp) {
    772  // Steps 1-2.
    773  CallArgs args = CallArgsFromVp(argc, vp);
    774  return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_era>(cx, args);
    775 }
    776 
    777 /**
    778 * get Temporal.PlainYearMonth.prototype.eraYear
    779 */
    780 static bool PlainYearMonth_eraYear(JSContext* cx, const CallArgs& args) {
    781  auto* yearMonth = &args.thisv().toObject().as<PlainYearMonthObject>();
    782  Rooted<CalendarValue> calendar(cx, yearMonth->calendar());
    783 
    784  // Steps 3-5.
    785  return CalendarEraYear(cx, calendar, yearMonth->date(), args.rval());
    786 }
    787 
    788 /**
    789 * get Temporal.PlainYearMonth.prototype.eraYear
    790 */
    791 static bool PlainYearMonth_eraYear(JSContext* cx, unsigned argc, Value* vp) {
    792  // Steps 1-2.
    793  CallArgs args = CallArgsFromVp(argc, vp);
    794  return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_eraYear>(cx,
    795                                                                        args);
    796 }
    797 
    798 /**
    799 * get Temporal.PlainYearMonth.prototype.year
    800 */
    801 static bool PlainYearMonth_year(JSContext* cx, const CallArgs& args) {
    802  auto* yearMonth = &args.thisv().toObject().as<PlainYearMonthObject>();
    803  Rooted<CalendarValue> calendar(cx, yearMonth->calendar());
    804 
    805  // Step 3.
    806  return CalendarYear(cx, calendar, yearMonth->date(), args.rval());
    807 }
    808 
    809 /**
    810 * get Temporal.PlainYearMonth.prototype.year
    811 */
    812 static bool PlainYearMonth_year(JSContext* cx, unsigned argc, Value* vp) {
    813  // Steps 1-2.
    814  CallArgs args = CallArgsFromVp(argc, vp);
    815  return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_year>(cx, args);
    816 }
    817 
    818 /**
    819 * get Temporal.PlainYearMonth.prototype.month
    820 */
    821 static bool PlainYearMonth_month(JSContext* cx, const CallArgs& args) {
    822  auto* yearMonth = &args.thisv().toObject().as<PlainYearMonthObject>();
    823  Rooted<CalendarValue> calendar(cx, yearMonth->calendar());
    824 
    825  // Step 3.
    826  return CalendarMonth(cx, calendar, yearMonth->date(), args.rval());
    827 }
    828 
    829 /**
    830 * get Temporal.PlainYearMonth.prototype.month
    831 */
    832 static bool PlainYearMonth_month(JSContext* cx, unsigned argc, Value* vp) {
    833  // Steps 1-2.
    834  CallArgs args = CallArgsFromVp(argc, vp);
    835  return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_month>(cx, args);
    836 }
    837 
    838 /**
    839 * get Temporal.PlainYearMonth.prototype.monthCode
    840 */
    841 static bool PlainYearMonth_monthCode(JSContext* cx, const CallArgs& args) {
    842  auto* yearMonth = &args.thisv().toObject().as<PlainYearMonthObject>();
    843  Rooted<CalendarValue> calendar(cx, yearMonth->calendar());
    844 
    845  // Step 3.
    846  return CalendarMonthCode(cx, calendar, yearMonth->date(), args.rval());
    847 }
    848 
    849 /**
    850 * get Temporal.PlainYearMonth.prototype.monthCode
    851 */
    852 static bool PlainYearMonth_monthCode(JSContext* cx, unsigned argc, Value* vp) {
    853  // Steps 1-2.
    854  CallArgs args = CallArgsFromVp(argc, vp);
    855  return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_monthCode>(cx,
    856                                                                          args);
    857 }
    858 
    859 /**
    860 * get Temporal.PlainYearMonth.prototype.daysInYear
    861 */
    862 static bool PlainYearMonth_daysInYear(JSContext* cx, const CallArgs& args) {
    863  auto* yearMonth = &args.thisv().toObject().as<PlainYearMonthObject>();
    864  Rooted<CalendarValue> calendar(cx, yearMonth->calendar());
    865 
    866  // Step 3.
    867  return CalendarDaysInYear(cx, calendar, yearMonth->date(), args.rval());
    868 }
    869 
    870 /**
    871 * get Temporal.PlainYearMonth.prototype.daysInYear
    872 */
    873 static bool PlainYearMonth_daysInYear(JSContext* cx, unsigned argc, Value* vp) {
    874  // Steps 1-2.
    875  CallArgs args = CallArgsFromVp(argc, vp);
    876  return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_daysInYear>(
    877      cx, args);
    878 }
    879 
    880 /**
    881 * get Temporal.PlainYearMonth.prototype.daysInMonth
    882 */
    883 static bool PlainYearMonth_daysInMonth(JSContext* cx, const CallArgs& args) {
    884  auto* yearMonth = &args.thisv().toObject().as<PlainYearMonthObject>();
    885  Rooted<CalendarValue> calendar(cx, yearMonth->calendar());
    886 
    887  // Step 3.
    888  return CalendarDaysInMonth(cx, calendar, yearMonth->date(), args.rval());
    889 }
    890 
    891 /**
    892 * get Temporal.PlainYearMonth.prototype.daysInMonth
    893 */
    894 static bool PlainYearMonth_daysInMonth(JSContext* cx, unsigned argc,
    895                                       Value* vp) {
    896  // Steps 1-2.
    897  CallArgs args = CallArgsFromVp(argc, vp);
    898  return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_daysInMonth>(
    899      cx, args);
    900 }
    901 
    902 /**
    903 * get Temporal.PlainYearMonth.prototype.monthsInYear
    904 */
    905 static bool PlainYearMonth_monthsInYear(JSContext* cx, const CallArgs& args) {
    906  auto* yearMonth = &args.thisv().toObject().as<PlainYearMonthObject>();
    907  Rooted<CalendarValue> calendar(cx, yearMonth->calendar());
    908 
    909  // Step 3.
    910  return CalendarMonthsInYear(cx, calendar, yearMonth->date(), args.rval());
    911 }
    912 
    913 /**
    914 * get Temporal.PlainYearMonth.prototype.monthsInYear
    915 */
    916 static bool PlainYearMonth_monthsInYear(JSContext* cx, unsigned argc,
    917                                        Value* vp) {
    918  // Steps 1-2.
    919  CallArgs args = CallArgsFromVp(argc, vp);
    920  return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_monthsInYear>(
    921      cx, args);
    922 }
    923 
    924 /**
    925 * get Temporal.PlainYearMonth.prototype.inLeapYear
    926 */
    927 static bool PlainYearMonth_inLeapYear(JSContext* cx, const CallArgs& args) {
    928  auto* yearMonth = &args.thisv().toObject().as<PlainYearMonthObject>();
    929  Rooted<CalendarValue> calendar(cx, yearMonth->calendar());
    930 
    931  // Step 3.
    932  return CalendarInLeapYear(cx, calendar, yearMonth->date(), args.rval());
    933 }
    934 
    935 /**
    936 * get Temporal.PlainYearMonth.prototype.inLeapYear
    937 */
    938 static bool PlainYearMonth_inLeapYear(JSContext* cx, unsigned argc, Value* vp) {
    939  // Steps 1-2.
    940  CallArgs args = CallArgsFromVp(argc, vp);
    941  return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_inLeapYear>(
    942      cx, args);
    943 }
    944 
    945 /**
    946 * Temporal.PlainYearMonth.prototype.with ( temporalYearMonthLike [ , options ]
    947 * )
    948 */
    949 static bool PlainYearMonth_with(JSContext* cx, const CallArgs& args) {
    950  Rooted<PlainYearMonth> yearMonth(
    951      cx, &args.thisv().toObject().as<PlainYearMonthObject>());
    952 
    953  // Step 3.
    954  Rooted<JSObject*> temporalYearMonthLike(
    955      cx, RequireObjectArg(cx, "temporalYearMonthLike", "with", args.get(0)));
    956  if (!temporalYearMonthLike) {
    957    return false;
    958  }
    959  if (!ThrowIfTemporalLikeObject(cx, temporalYearMonthLike)) {
    960    return false;
    961  }
    962 
    963  // Step 4.
    964  auto calendar = yearMonth.calendar();
    965 
    966  // Step 5.
    967  Rooted<CalendarFields> fields(cx);
    968  if (!ISODateToFields(cx, yearMonth, &fields)) {
    969    return false;
    970  }
    971 
    972  // Step 6.
    973  Rooted<CalendarFields> partialYearMonth(cx);
    974  if (!PreparePartialCalendarFields(cx, calendar, temporalYearMonthLike,
    975                                    {
    976                                        CalendarField::Year,
    977                                        CalendarField::Month,
    978                                        CalendarField::MonthCode,
    979                                    },
    980                                    &partialYearMonth)) {
    981    return false;
    982  }
    983  MOZ_ASSERT(!partialYearMonth.keys().isEmpty());
    984 
    985  // Step 7.
    986  fields = CalendarMergeFields(calendar, fields, partialYearMonth);
    987 
    988  // Steps 8-9.
    989  auto overflow = TemporalOverflow::Constrain;
    990  if (args.hasDefined(1)) {
    991    // Step 8.
    992    Rooted<JSObject*> options(cx,
    993                              RequireObjectArg(cx, "options", "with", args[1]));
    994    if (!options) {
    995      return false;
    996    }
    997 
    998    // Step 9.
    999    if (!GetTemporalOverflowOption(cx, options, &overflow)) {
   1000      return false;
   1001    }
   1002  }
   1003 
   1004  // Step 10.
   1005  Rooted<PlainYearMonth> result(cx);
   1006  if (!CalendarYearMonthFromFields(cx, calendar, fields, overflow, &result)) {
   1007    return false;
   1008  }
   1009 
   1010  // Step 11.
   1011  auto* obj = CreateTemporalYearMonth(cx, result);
   1012  if (!obj) {
   1013    return false;
   1014  }
   1015 
   1016  args.rval().setObject(*obj);
   1017  return true;
   1018 }
   1019 
   1020 /**
   1021 * Temporal.PlainYearMonth.prototype.with ( temporalYearMonthLike [ , options ]
   1022 * )
   1023 */
   1024 static bool PlainYearMonth_with(JSContext* cx, unsigned argc, Value* vp) {
   1025  // Steps 1-2.
   1026  CallArgs args = CallArgsFromVp(argc, vp);
   1027  return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_with>(cx, args);
   1028 }
   1029 
   1030 /**
   1031 * Temporal.PlainYearMonth.prototype.add ( temporalDurationLike [ , options ] )
   1032 */
   1033 static bool PlainYearMonth_add(JSContext* cx, const CallArgs& args) {
   1034  // Step 3.
   1035  return AddDurationToYearMonth(cx, TemporalAddDuration::Add, args);
   1036 }
   1037 
   1038 /**
   1039 * Temporal.PlainYearMonth.prototype.add ( temporalDurationLike [ , options ] )
   1040 */
   1041 static bool PlainYearMonth_add(JSContext* cx, unsigned argc, Value* vp) {
   1042  // Steps 1-2.
   1043  CallArgs args = CallArgsFromVp(argc, vp);
   1044  return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_add>(cx, args);
   1045 }
   1046 
   1047 /**
   1048 * Temporal.PlainYearMonth.prototype.subtract ( temporalDurationLike [ , options
   1049 * ] )
   1050 */
   1051 static bool PlainYearMonth_subtract(JSContext* cx, const CallArgs& args) {
   1052  // Step 3.
   1053  return AddDurationToYearMonth(cx, TemporalAddDuration::Subtract, args);
   1054 }
   1055 
   1056 /**
   1057 * Temporal.PlainYearMonth.prototype.subtract ( temporalDurationLike [ , options
   1058 * ] )
   1059 */
   1060 static bool PlainYearMonth_subtract(JSContext* cx, unsigned argc, Value* vp) {
   1061  // Steps 1-2.
   1062  CallArgs args = CallArgsFromVp(argc, vp);
   1063  return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_subtract>(cx,
   1064                                                                         args);
   1065 }
   1066 
   1067 /**
   1068 * Temporal.PlainYearMonth.prototype.until ( other [ , options ] )
   1069 */
   1070 static bool PlainYearMonth_until(JSContext* cx, const CallArgs& args) {
   1071  // Step 3.
   1072  return DifferenceTemporalPlainYearMonth(cx, TemporalDifference::Until, args);
   1073 }
   1074 
   1075 /**
   1076 * Temporal.PlainYearMonth.prototype.until ( other [ , options ] )
   1077 */
   1078 static bool PlainYearMonth_until(JSContext* cx, unsigned argc, Value* vp) {
   1079  // Steps 1-2.
   1080  CallArgs args = CallArgsFromVp(argc, vp);
   1081  return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_until>(cx, args);
   1082 }
   1083 
   1084 /**
   1085 * Temporal.PlainYearMonth.prototype.since ( other [ , options ] )
   1086 */
   1087 static bool PlainYearMonth_since(JSContext* cx, const CallArgs& args) {
   1088  // Step 3.
   1089  return DifferenceTemporalPlainYearMonth(cx, TemporalDifference::Since, args);
   1090 }
   1091 
   1092 /**
   1093 * Temporal.PlainYearMonth.prototype.since ( other [ , options ] )
   1094 */
   1095 static bool PlainYearMonth_since(JSContext* cx, unsigned argc, Value* vp) {
   1096  // Steps 1-2.
   1097  CallArgs args = CallArgsFromVp(argc, vp);
   1098  return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_since>(cx, args);
   1099 }
   1100 
   1101 /**
   1102 * Temporal.PlainYearMonth.prototype.equals ( other )
   1103 */
   1104 static bool PlainYearMonth_equals(JSContext* cx, const CallArgs& args) {
   1105  auto* yearMonth = &args.thisv().toObject().as<PlainYearMonthObject>();
   1106  auto date = yearMonth->date();
   1107  Rooted<CalendarValue> calendar(cx, yearMonth->calendar());
   1108 
   1109  // Step 3.
   1110  Rooted<PlainYearMonth> other(cx);
   1111  if (!ToTemporalYearMonth(cx, args.get(0), &other)) {
   1112    return false;
   1113  }
   1114 
   1115  // Steps 4-7.
   1116  bool equals =
   1117      date == other.date() && CalendarEquals(calendar, other.calendar());
   1118 
   1119  args.rval().setBoolean(equals);
   1120  return true;
   1121 }
   1122 
   1123 /**
   1124 * Temporal.PlainYearMonth.prototype.equals ( other )
   1125 */
   1126 static bool PlainYearMonth_equals(JSContext* cx, unsigned argc, Value* vp) {
   1127  // Steps 1-2.
   1128  CallArgs args = CallArgsFromVp(argc, vp);
   1129  return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_equals>(cx,
   1130                                                                       args);
   1131 }
   1132 
   1133 /**
   1134 * Temporal.PlainYearMonth.prototype.toString ( [ options ] )
   1135 */
   1136 static bool PlainYearMonth_toString(JSContext* cx, const CallArgs& args) {
   1137  Rooted<PlainYearMonthObject*> yearMonth(
   1138      cx, &args.thisv().toObject().as<PlainYearMonthObject>());
   1139 
   1140  auto showCalendar = ShowCalendar::Auto;
   1141  if (args.hasDefined(0)) {
   1142    // Step 3.
   1143    Rooted<JSObject*> options(
   1144        cx, RequireObjectArg(cx, "options", "toString", args[0]));
   1145    if (!options) {
   1146      return false;
   1147    }
   1148 
   1149    // Step 4.
   1150    if (!GetTemporalShowCalendarNameOption(cx, options, &showCalendar)) {
   1151      return false;
   1152    }
   1153  }
   1154 
   1155  // Step 5.
   1156  JSString* str = TemporalYearMonthToString(cx, yearMonth, showCalendar);
   1157  if (!str) {
   1158    return false;
   1159  }
   1160 
   1161  args.rval().setString(str);
   1162  return true;
   1163 }
   1164 
   1165 /**
   1166 * Temporal.PlainYearMonth.prototype.toString ( [ options ] )
   1167 */
   1168 static bool PlainYearMonth_toString(JSContext* cx, unsigned argc, Value* vp) {
   1169  // Steps 1-2.
   1170  CallArgs args = CallArgsFromVp(argc, vp);
   1171  return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_toString>(cx,
   1172                                                                         args);
   1173 }
   1174 
   1175 /**
   1176 * Temporal.PlainYearMonth.prototype.toLocaleString ( [ locales [ , options ] ]
   1177 * )
   1178 */
   1179 static bool PlainYearMonth_toLocaleString(JSContext* cx, const CallArgs& args) {
   1180  // Steps 3-4.
   1181  return intl::TemporalObjectToLocaleString(cx, args,
   1182                                            intl::DateTimeFormatKind::Date);
   1183 }
   1184 
   1185 /**
   1186 * Temporal.PlainYearMonth.prototype.toLocaleString ( [ locales [ , options ] ]
   1187 * )
   1188 */
   1189 static bool PlainYearMonth_toLocaleString(JSContext* cx, unsigned argc,
   1190                                          Value* vp) {
   1191  // Steps 1-2.
   1192  CallArgs args = CallArgsFromVp(argc, vp);
   1193  return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_toLocaleString>(
   1194      cx, args);
   1195 }
   1196 
   1197 /**
   1198 * Temporal.PlainYearMonth.prototype.toJSON ( )
   1199 */
   1200 static bool PlainYearMonth_toJSON(JSContext* cx, const CallArgs& args) {
   1201  Rooted<PlainYearMonthObject*> yearMonth(
   1202      cx, &args.thisv().toObject().as<PlainYearMonthObject>());
   1203 
   1204  // Step 3.
   1205  JSString* str = TemporalYearMonthToString(cx, yearMonth, ShowCalendar::Auto);
   1206  if (!str) {
   1207    return false;
   1208  }
   1209 
   1210  args.rval().setString(str);
   1211  return true;
   1212 }
   1213 
   1214 /**
   1215 * Temporal.PlainYearMonth.prototype.toJSON ( )
   1216 */
   1217 static bool PlainYearMonth_toJSON(JSContext* cx, unsigned argc, Value* vp) {
   1218  // Steps 1-2.
   1219  CallArgs args = CallArgsFromVp(argc, vp);
   1220  return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_toJSON>(cx,
   1221                                                                       args);
   1222 }
   1223 
   1224 /**
   1225 *  Temporal.PlainYearMonth.prototype.valueOf ( )
   1226 */
   1227 static bool PlainYearMonth_valueOf(JSContext* cx, unsigned argc, Value* vp) {
   1228  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
   1229                            "PlainYearMonth", "primitive type");
   1230  return false;
   1231 }
   1232 
   1233 /**
   1234 * Temporal.PlainYearMonth.prototype.toPlainDate ( item )
   1235 */
   1236 static bool PlainYearMonth_toPlainDate(JSContext* cx, const CallArgs& args) {
   1237  Rooted<PlainYearMonth> yearMonth(
   1238      cx, &args.thisv().toObject().as<PlainYearMonthObject>());
   1239 
   1240  // Step 3.
   1241  Rooted<JSObject*> item(
   1242      cx, RequireObjectArg(cx, "item", "toPlainDate", args.get(0)));
   1243  if (!item) {
   1244    return false;
   1245  }
   1246 
   1247  // Step 4.
   1248  auto calendar = yearMonth.calendar();
   1249 
   1250  // Step 5.
   1251  Rooted<CalendarFields> fields(cx);
   1252  if (!ISODateToFields(cx, yearMonth, &fields)) {
   1253    return false;
   1254  }
   1255 
   1256  // Step 6.
   1257  Rooted<CalendarFields> inputFields(cx);
   1258  if (!PrepareCalendarFields(cx, calendar, item,
   1259                             {
   1260                                 CalendarField::Day,
   1261                             },
   1262                             &inputFields)) {
   1263    return false;
   1264  }
   1265 
   1266  // Step 7.
   1267  fields = CalendarMergeFields(calendar, fields, inputFields);
   1268 
   1269  // Step 8.
   1270  Rooted<PlainDate> result(cx);
   1271  if (!CalendarDateFromFields(cx, calendar, fields, TemporalOverflow::Constrain,
   1272                              &result)) {
   1273    return false;
   1274  }
   1275 
   1276  // Step 9.
   1277  auto* obj = CreateTemporalDate(cx, result);
   1278  if (!obj) {
   1279    return false;
   1280  }
   1281 
   1282  args.rval().setObject(*obj);
   1283  return true;
   1284 }
   1285 
   1286 /**
   1287 * Temporal.PlainYearMonth.prototype.toPlainDate ( item )
   1288 */
   1289 static bool PlainYearMonth_toPlainDate(JSContext* cx, unsigned argc,
   1290                                       Value* vp) {
   1291  // Steps 1-2.
   1292  CallArgs args = CallArgsFromVp(argc, vp);
   1293  return CallNonGenericMethod<IsPlainYearMonth, PlainYearMonth_toPlainDate>(
   1294      cx, args);
   1295 }
   1296 
   1297 const JSClass PlainYearMonthObject::class_ = {
   1298    "Temporal.PlainYearMonth",
   1299    JSCLASS_HAS_RESERVED_SLOTS(PlainYearMonthObject::SLOT_COUNT) |
   1300        JSCLASS_HAS_CACHED_PROTO(JSProto_PlainYearMonth),
   1301    JS_NULL_CLASS_OPS,
   1302    &PlainYearMonthObject::classSpec_,
   1303 };
   1304 
   1305 const JSClass& PlainYearMonthObject::protoClass_ = PlainObject::class_;
   1306 
   1307 static const JSFunctionSpec PlainYearMonth_methods[] = {
   1308    JS_FN("from", PlainYearMonth_from, 1, 0),
   1309    JS_FN("compare", PlainYearMonth_compare, 2, 0),
   1310    JS_FS_END,
   1311 };
   1312 
   1313 static const JSFunctionSpec PlainYearMonth_prototype_methods[] = {
   1314    JS_FN("with", PlainYearMonth_with, 1, 0),
   1315    JS_FN("add", PlainYearMonth_add, 1, 0),
   1316    JS_FN("subtract", PlainYearMonth_subtract, 1, 0),
   1317    JS_FN("until", PlainYearMonth_until, 1, 0),
   1318    JS_FN("since", PlainYearMonth_since, 1, 0),
   1319    JS_FN("equals", PlainYearMonth_equals, 1, 0),
   1320    JS_FN("toString", PlainYearMonth_toString, 0, 0),
   1321    JS_FN("toLocaleString", PlainYearMonth_toLocaleString, 0, 0),
   1322    JS_FN("toJSON", PlainYearMonth_toJSON, 0, 0),
   1323    JS_FN("valueOf", PlainYearMonth_valueOf, 0, 0),
   1324    JS_FN("toPlainDate", PlainYearMonth_toPlainDate, 1, 0),
   1325    JS_FS_END,
   1326 };
   1327 
   1328 static const JSPropertySpec PlainYearMonth_prototype_properties[] = {
   1329    JS_PSG("calendarId", PlainYearMonth_calendarId, 0),
   1330    JS_PSG("era", PlainYearMonth_era, 0),
   1331    JS_PSG("eraYear", PlainYearMonth_eraYear, 0),
   1332    JS_PSG("year", PlainYearMonth_year, 0),
   1333    JS_PSG("month", PlainYearMonth_month, 0),
   1334    JS_PSG("monthCode", PlainYearMonth_monthCode, 0),
   1335    JS_PSG("daysInYear", PlainYearMonth_daysInYear, 0),
   1336    JS_PSG("daysInMonth", PlainYearMonth_daysInMonth, 0),
   1337    JS_PSG("monthsInYear", PlainYearMonth_monthsInYear, 0),
   1338    JS_PSG("inLeapYear", PlainYearMonth_inLeapYear, 0),
   1339    JS_STRING_SYM_PS(toStringTag, "Temporal.PlainYearMonth", JSPROP_READONLY),
   1340    JS_PS_END,
   1341 };
   1342 
   1343 const ClassSpec PlainYearMonthObject::classSpec_ = {
   1344    GenericCreateConstructor<PlainYearMonthConstructor, 2,
   1345                             gc::AllocKind::FUNCTION>,
   1346    GenericCreatePrototype<PlainYearMonthObject>,
   1347    PlainYearMonth_methods,
   1348    nullptr,
   1349    PlainYearMonth_prototype_methods,
   1350    PlainYearMonth_prototype_properties,
   1351    nullptr,
   1352    ClassSpec::DontDefineConstructor,
   1353 };