tor-browser

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

PlainMonthDay.cpp (22246B)


      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/PlainMonthDay.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/PlainDate.h"
     18 #include "builtin/temporal/PlainDateTime.h"
     19 #include "builtin/temporal/PlainYearMonth.h"
     20 #include "builtin/temporal/Temporal.h"
     21 #include "builtin/temporal/TemporalParser.h"
     22 #include "builtin/temporal/TemporalTypes.h"
     23 #include "builtin/temporal/ToString.h"
     24 #include "gc/AllocKind.h"
     25 #include "gc/Barrier.h"
     26 #include "gc/GCEnum.h"
     27 #include "js/CallArgs.h"
     28 #include "js/CallNonGenericMethod.h"
     29 #include "js/Class.h"
     30 #include "js/ErrorReport.h"
     31 #include "js/friend/ErrorMessages.h"
     32 #include "js/PropertyDescriptor.h"
     33 #include "js/PropertySpec.h"
     34 #include "js/RootingAPI.h"
     35 #include "js/TypeDecls.h"
     36 #include "js/Value.h"
     37 #include "vm/BytecodeUtil.h"
     38 #include "vm/GlobalObject.h"
     39 #include "vm/JSAtomState.h"
     40 #include "vm/JSContext.h"
     41 #include "vm/JSObject.h"
     42 #include "vm/PlainObject.h"
     43 #include "vm/StringType.h"
     44 
     45 #include "vm/JSObject-inl.h"
     46 #include "vm/NativeObject-inl.h"
     47 
     48 using namespace js;
     49 using namespace js::temporal;
     50 
     51 static inline bool IsPlainMonthDay(Handle<Value> v) {
     52  return v.isObject() && v.toObject().is<PlainMonthDayObject>();
     53 }
     54 
     55 /**
     56 * CreateTemporalMonthDay ( isoDate, calendar [ , newTarget ] )
     57 */
     58 static PlainMonthDayObject* CreateTemporalMonthDay(
     59    JSContext* cx, const CallArgs& args, const ISODate& isoDate,
     60    Handle<CalendarValue> calendar) {
     61  // Step 1.
     62  if (!ISODateWithinLimits(isoDate)) {
     63    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
     64                              JSMSG_TEMPORAL_PLAIN_MONTH_DAY_INVALID);
     65    return nullptr;
     66  }
     67 
     68  // Steps 2-3.
     69  Rooted<JSObject*> proto(cx);
     70  if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_PlainMonthDay,
     71                                          &proto)) {
     72    return nullptr;
     73  }
     74 
     75  auto* object = NewObjectWithClassProto<PlainMonthDayObject>(cx, proto);
     76  if (!object) {
     77    return nullptr;
     78  }
     79 
     80  // Step 4.
     81  auto packedDate = PackedDate::pack(isoDate);
     82  object->initFixedSlot(PlainMonthDayObject::PACKED_DATE_SLOT,
     83                        PrivateUint32Value(packedDate.value));
     84 
     85  // Step 5.
     86  object->initFixedSlot(PlainMonthDayObject::CALENDAR_SLOT,
     87                        calendar.toSlotValue());
     88 
     89  // Step 6.
     90  return object;
     91 }
     92 
     93 /**
     94 * CreateTemporalMonthDay ( isoDate, calendar [ , newTarget ] )
     95 */
     96 PlainMonthDayObject* js::temporal::CreateTemporalMonthDay(
     97    JSContext* cx, Handle<PlainMonthDay> monthDay) {
     98  MOZ_ASSERT(IsValidISODate(monthDay));
     99 
    100  // Step 1.
    101  MOZ_ASSERT(ISODateWithinLimits(monthDay));
    102 
    103  // Steps 2-3.
    104  auto* object = NewBuiltinClassInstance<PlainMonthDayObject>(cx);
    105  if (!object) {
    106    return nullptr;
    107  }
    108 
    109  // Step 4.
    110  auto packedDate = PackedDate::pack(monthDay);
    111  object->initFixedSlot(PlainMonthDayObject::PACKED_DATE_SLOT,
    112                        PrivateUint32Value(packedDate.value));
    113 
    114  // Step 5.
    115  object->initFixedSlot(PlainMonthDayObject::CALENDAR_SLOT,
    116                        monthDay.calendar().toSlotValue());
    117 
    118  // Step 6.
    119  return object;
    120 }
    121 
    122 /**
    123 * CreateTemporalMonthDay ( isoDate, calendar [ , newTarget ] )
    124 */
    125 bool js::temporal::CreateTemporalMonthDay(JSContext* cx, const ISODate& isoDate,
    126                                          Handle<CalendarValue> calendar,
    127                                          MutableHandle<PlainMonthDay> result) {
    128  MOZ_ASSERT(IsValidISODate(isoDate));
    129 
    130  // Step 1.
    131  if (!ISODateWithinLimits(isoDate)) {
    132    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    133                              JSMSG_TEMPORAL_PLAIN_MONTH_DAY_INVALID);
    134    return false;
    135  }
    136 
    137  // Steps 2-6.
    138  result.set(PlainMonthDay{isoDate, calendar});
    139  return true;
    140 }
    141 
    142 struct MonthDayOptions {
    143  TemporalOverflow overflow = TemporalOverflow::Constrain;
    144 };
    145 
    146 /**
    147 * ToTemporalMonthDay ( item [ , options ] )
    148 */
    149 static bool ToTemporalMonthDayOptions(JSContext* cx, Handle<Value> options,
    150                                      MonthDayOptions* result) {
    151  if (options.isUndefined()) {
    152    *result = {};
    153    return true;
    154  }
    155 
    156  // NOTE: |options| are only passed from `Temporal.PlainMonthDay.from`.
    157 
    158  Rooted<JSObject*> resolvedOptions(
    159      cx, RequireObjectArg(cx, "options", "from", options));
    160  if (!resolvedOptions) {
    161    return false;
    162  }
    163 
    164  auto overflow = TemporalOverflow::Constrain;
    165  if (!GetTemporalOverflowOption(cx, resolvedOptions, &overflow)) {
    166    return false;
    167  }
    168 
    169  *result = {overflow};
    170  return true;
    171 }
    172 
    173 /**
    174 * ToTemporalMonthDay ( item [ , options ] )
    175 */
    176 static bool ToTemporalMonthDay(JSContext* cx, Handle<JSObject*> item,
    177                               Handle<Value> options,
    178                               MutableHandle<PlainMonthDay> result) {
    179  // Step 2.a.
    180  if (auto* plainMonthDay = item->maybeUnwrapIf<PlainMonthDayObject>()) {
    181    auto date = plainMonthDay->date();
    182    Rooted<CalendarValue> calendar(cx, plainMonthDay->calendar());
    183    if (!calendar.wrap(cx)) {
    184      return false;
    185    }
    186 
    187    // Steps 2.a.i-ii.
    188    MonthDayOptions ignoredOptions;
    189    if (!ToTemporalMonthDayOptions(cx, options, &ignoredOptions)) {
    190      return false;
    191    }
    192 
    193    // Step 2.a.iii.
    194    result.set(PlainMonthDay{date, calendar});
    195    return true;
    196  }
    197 
    198  // Step 2.b.
    199  Rooted<CalendarValue> calendar(cx);
    200  if (!GetTemporalCalendarWithISODefault(cx, item, &calendar)) {
    201    return false;
    202  }
    203 
    204  // Step 2.c.
    205  Rooted<CalendarFields> fields(cx);
    206  if (!PrepareCalendarFields(cx, calendar, item,
    207                             {
    208                                 CalendarField::Year,
    209                                 CalendarField::Month,
    210                                 CalendarField::MonthCode,
    211                                 CalendarField::Day,
    212                             },
    213                             &fields)) {
    214    return false;
    215  }
    216 
    217  // Steps 2.d-e.
    218  MonthDayOptions resolvedOptions;
    219  if (!ToTemporalMonthDayOptions(cx, options, &resolvedOptions)) {
    220    return false;
    221  }
    222  auto [overflow] = resolvedOptions;
    223 
    224  // Step 2.f.
    225  return CalendarMonthDayFromFields(cx, calendar, fields, overflow, result);
    226 }
    227 
    228 /**
    229 * ToTemporalMonthDay ( item [ , options ] )
    230 */
    231 static bool ToTemporalMonthDay(JSContext* cx, Handle<Value> item,
    232                               Handle<Value> options,
    233                               MutableHandle<PlainMonthDay> result) {
    234  // Step 1. (Not applicable in our implementation.)
    235 
    236  // Step 2.
    237  if (item.isObject()) {
    238    Rooted<JSObject*> itemObj(cx, &item.toObject());
    239    return ToTemporalMonthDay(cx, itemObj, options, result);
    240  }
    241 
    242  // Step 3.
    243  if (!item.isString()) {
    244    ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, item,
    245                     nullptr, "not a string");
    246    return false;
    247  }
    248  Rooted<JSString*> string(cx, item.toString());
    249 
    250  // Step 4.
    251  ISODate date;
    252  bool hasYear;
    253  Rooted<JSString*> calendarString(cx);
    254  if (!ParseTemporalMonthDayString(cx, string, &date, &hasYear,
    255                                   &calendarString)) {
    256    return false;
    257  }
    258 
    259  // Steps 5-7.
    260  Rooted<CalendarValue> calendar(cx, CalendarValue(CalendarId::ISO8601));
    261  if (calendarString) {
    262    if (!CanonicalizeCalendar(cx, calendarString, &calendar)) {
    263      return false;
    264    }
    265  }
    266 
    267  // Steps 8-9.
    268  MonthDayOptions ignoredOptions;
    269  if (!ToTemporalMonthDayOptions(cx, options, &ignoredOptions)) {
    270    return false;
    271  }
    272 
    273  // Step 10.
    274  if (calendar.identifier() == CalendarId::ISO8601) {
    275    // Step 10.a.
    276    constexpr int32_t referenceISOYear = 1972;
    277 
    278    // Step 10.b.
    279    auto isoDate = ISODate{referenceISOYear, date.month, date.day};
    280 
    281    // Step 10.c.
    282    return CreateTemporalMonthDay(cx, isoDate, calendar, result);
    283  }
    284 
    285  // Steps 11-12.
    286  Rooted<PlainMonthDay> monthDay(cx);
    287  if (!CreateTemporalMonthDay(cx, date, calendar, &monthDay)) {
    288    return false;
    289  }
    290 
    291  // Step 13.
    292  Rooted<CalendarFields> fields(cx);
    293  if (!ISODateToFields(cx, monthDay, &fields)) {
    294    return false;
    295  }
    296 
    297  // Steps 14-15.
    298  return CalendarMonthDayFromFields(cx, calendar, fields,
    299                                    TemporalOverflow::Constrain, result);
    300 }
    301 
    302 /**
    303 * ToTemporalMonthDay ( item [ , options ] )
    304 */
    305 static bool ToTemporalMonthDay(JSContext* cx, Handle<Value> item,
    306                               MutableHandle<PlainMonthDay> result) {
    307  return ToTemporalMonthDay(cx, item, UndefinedHandleValue, result);
    308 }
    309 
    310 /**
    311 * Temporal.PlainMonthDay ( isoMonth, isoDay [ , calendar [ , referenceISOYear ]
    312 * ] )
    313 */
    314 static bool PlainMonthDayConstructor(JSContext* cx, unsigned argc, Value* vp) {
    315  CallArgs args = CallArgsFromVp(argc, vp);
    316 
    317  // Step 1.
    318  if (!ThrowIfNotConstructing(cx, args, "Temporal.PlainMonthDay")) {
    319    return false;
    320  }
    321 
    322  // Step 3.
    323  double isoMonth;
    324  if (!ToIntegerWithTruncation(cx, args.get(0), "month", &isoMonth)) {
    325    return false;
    326  }
    327 
    328  // Step 4.
    329  double isoDay;
    330  if (!ToIntegerWithTruncation(cx, args.get(1), "day", &isoDay)) {
    331    return false;
    332  }
    333 
    334  // Steps 5-7.
    335  Rooted<CalendarValue> calendar(cx, CalendarValue(CalendarId::ISO8601));
    336  if (args.hasDefined(2)) {
    337    // Step 6.
    338    if (!args[2].isString()) {
    339      ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, args[2],
    340                       nullptr, "not a string");
    341      return false;
    342    }
    343 
    344    // Step 7.
    345    Rooted<JSString*> calendarString(cx, args[2].toString());
    346    if (!CanonicalizeCalendar(cx, calendarString, &calendar)) {
    347      return false;
    348    }
    349  }
    350 
    351  // Steps 2 and 8.
    352  double isoYear = 1972;
    353  if (args.hasDefined(3)) {
    354    if (!ToIntegerWithTruncation(cx, args[3], "year", &isoYear)) {
    355      return false;
    356    }
    357  }
    358 
    359  // Step 9.
    360  if (!ThrowIfInvalidISODate(cx, isoYear, isoMonth, isoDay)) {
    361    return false;
    362  }
    363 
    364  // Step 10.
    365  auto isoDate = ISODate{int32_t(isoYear), int32_t(isoMonth), int32_t(isoDay)};
    366 
    367  // Step 9.
    368  auto* monthDay = CreateTemporalMonthDay(cx, args, isoDate, calendar);
    369  if (!monthDay) {
    370    return false;
    371  }
    372 
    373  args.rval().setObject(*monthDay);
    374  return true;
    375 }
    376 
    377 /**
    378 * Temporal.PlainMonthDay.from ( item [ , options ] )
    379 */
    380 static bool PlainMonthDay_from(JSContext* cx, unsigned argc, Value* vp) {
    381  CallArgs args = CallArgsFromVp(argc, vp);
    382 
    383  // Step 1.
    384  Rooted<PlainMonthDay> monthDay(cx);
    385  if (!ToTemporalMonthDay(cx, args.get(0), args.get(1), &monthDay)) {
    386    return false;
    387  }
    388 
    389  auto* result = CreateTemporalMonthDay(cx, monthDay);
    390  if (!result) {
    391    return false;
    392  }
    393 
    394  args.rval().setObject(*result);
    395  return true;
    396 }
    397 
    398 /**
    399 * get Temporal.PlainMonthDay.prototype.calendarId
    400 */
    401 static bool PlainMonthDay_calendarId(JSContext* cx, const CallArgs& args) {
    402  auto* monthDay = &args.thisv().toObject().as<PlainMonthDayObject>();
    403 
    404  // Step 3.
    405  auto* str =
    406      NewStringCopy<CanGC>(cx, CalendarIdentifier(monthDay->calendar()));
    407  if (!str) {
    408    return false;
    409  }
    410 
    411  args.rval().setString(str);
    412  return true;
    413 }
    414 
    415 /**
    416 * get Temporal.PlainMonthDay.prototype.calendarId
    417 */
    418 static bool PlainMonthDay_calendarId(JSContext* cx, unsigned argc, Value* vp) {
    419  // Steps 1-2.
    420  CallArgs args = CallArgsFromVp(argc, vp);
    421  return CallNonGenericMethod<IsPlainMonthDay, PlainMonthDay_calendarId>(cx,
    422                                                                         args);
    423 }
    424 
    425 /**
    426 * get Temporal.PlainMonthDay.prototype.monthCode
    427 */
    428 static bool PlainMonthDay_monthCode(JSContext* cx, const CallArgs& args) {
    429  auto* monthDay = &args.thisv().toObject().as<PlainMonthDayObject>();
    430  Rooted<CalendarValue> calendar(cx, monthDay->calendar());
    431 
    432  // Step 3.
    433  return CalendarMonthCode(cx, calendar, monthDay->date(), args.rval());
    434 }
    435 
    436 /**
    437 * get Temporal.PlainMonthDay.prototype.monthCode
    438 */
    439 static bool PlainMonthDay_monthCode(JSContext* cx, unsigned argc, Value* vp) {
    440  // Steps 1-2.
    441  CallArgs args = CallArgsFromVp(argc, vp);
    442  return CallNonGenericMethod<IsPlainMonthDay, PlainMonthDay_monthCode>(cx,
    443                                                                        args);
    444 }
    445 
    446 /**
    447 * get Temporal.PlainMonthDay.prototype.day
    448 */
    449 static bool PlainMonthDay_day(JSContext* cx, const CallArgs& args) {
    450  auto* monthDay = &args.thisv().toObject().as<PlainMonthDayObject>();
    451  Rooted<CalendarValue> calendar(cx, monthDay->calendar());
    452 
    453  // Step 3.
    454  return CalendarDay(cx, calendar, monthDay->date(), args.rval());
    455 }
    456 
    457 /**
    458 * get Temporal.PlainMonthDay.prototype.day
    459 */
    460 static bool PlainMonthDay_day(JSContext* cx, unsigned argc, Value* vp) {
    461  // Steps 1-2.
    462  CallArgs args = CallArgsFromVp(argc, vp);
    463  return CallNonGenericMethod<IsPlainMonthDay, PlainMonthDay_day>(cx, args);
    464 }
    465 
    466 /**
    467 * Temporal.PlainMonthDay.prototype.with ( temporalMonthDayLike [ , options ] )
    468 */
    469 static bool PlainMonthDay_with(JSContext* cx, const CallArgs& args) {
    470  Rooted<PlainMonthDay> monthDay(
    471      cx, &args.thisv().toObject().as<PlainMonthDayObject>());
    472 
    473  // Step 3.
    474  Rooted<JSObject*> temporalMonthDayLike(
    475      cx, RequireObjectArg(cx, "temporalMonthDayLike", "with", args.get(0)));
    476  if (!temporalMonthDayLike) {
    477    return false;
    478  }
    479  if (!ThrowIfTemporalLikeObject(cx, temporalMonthDayLike)) {
    480    return false;
    481  }
    482 
    483  // Step 4.
    484  auto calendar = monthDay.calendar();
    485 
    486  // Step 5.
    487  Rooted<CalendarFields> fields(cx);
    488  if (!ISODateToFields(cx, monthDay, &fields)) {
    489    return false;
    490  }
    491 
    492  // Step 6.
    493  Rooted<CalendarFields> partialMonthDay(cx);
    494  if (!PreparePartialCalendarFields(cx, calendar, temporalMonthDayLike,
    495                                    {
    496                                        CalendarField::Year,
    497                                        CalendarField::Month,
    498                                        CalendarField::MonthCode,
    499                                        CalendarField::Day,
    500                                    },
    501                                    &partialMonthDay)) {
    502    return false;
    503  }
    504  MOZ_ASSERT(!partialMonthDay.keys().isEmpty());
    505 
    506  // Step 7.
    507  fields = CalendarMergeFields(calendar, fields, partialMonthDay);
    508 
    509  // Steps 8-9.
    510  auto overflow = TemporalOverflow::Constrain;
    511  if (args.hasDefined(1)) {
    512    // Step 8.
    513    Rooted<JSObject*> options(cx,
    514                              RequireObjectArg(cx, "options", "with", args[1]));
    515    if (!options) {
    516      return false;
    517    }
    518 
    519    // Step 9.
    520    if (!GetTemporalOverflowOption(cx, options, &overflow)) {
    521      return false;
    522    }
    523  }
    524 
    525  // Step 10.
    526  Rooted<PlainMonthDay> result(cx);
    527  if (!CalendarMonthDayFromFields(cx, calendar, fields, overflow, &result)) {
    528    return false;
    529  }
    530  MOZ_ASSERT(ISODateWithinLimits(result));
    531 
    532  // Step 11.
    533  auto* obj = CreateTemporalMonthDay(cx, result);
    534  if (!obj) {
    535    return false;
    536  }
    537 
    538  args.rval().setObject(*obj);
    539  return true;
    540 }
    541 
    542 /**
    543 * Temporal.PlainMonthDay.prototype.with ( temporalMonthDayLike [ , options ] )
    544 */
    545 static bool PlainMonthDay_with(JSContext* cx, unsigned argc, Value* vp) {
    546  // Steps 1-2.
    547  CallArgs args = CallArgsFromVp(argc, vp);
    548  return CallNonGenericMethod<IsPlainMonthDay, PlainMonthDay_with>(cx, args);
    549 }
    550 
    551 /**
    552 * Temporal.PlainMonthDay.prototype.equals ( other )
    553 */
    554 static bool PlainMonthDay_equals(JSContext* cx, const CallArgs& args) {
    555  auto* monthDay = &args.thisv().toObject().as<PlainMonthDayObject>();
    556  auto date = monthDay->date();
    557  Rooted<CalendarValue> calendar(cx, monthDay->calendar());
    558 
    559  // Step 3.
    560  Rooted<PlainMonthDay> other(cx);
    561  if (!ToTemporalMonthDay(cx, args.get(0), &other)) {
    562    return false;
    563  }
    564 
    565  // Steps 4-7.
    566  bool equals =
    567      date == other.date() && CalendarEquals(calendar, other.calendar());
    568 
    569  args.rval().setBoolean(equals);
    570  return true;
    571 }
    572 
    573 /**
    574 * Temporal.PlainMonthDay.prototype.equals ( other )
    575 */
    576 static bool PlainMonthDay_equals(JSContext* cx, unsigned argc, Value* vp) {
    577  // Steps 1-2.
    578  CallArgs args = CallArgsFromVp(argc, vp);
    579  return CallNonGenericMethod<IsPlainMonthDay, PlainMonthDay_equals>(cx, args);
    580 }
    581 
    582 /**
    583 * Temporal.PlainMonthDay.prototype.toString ( [ options ] )
    584 */
    585 static bool PlainMonthDay_toString(JSContext* cx, const CallArgs& args) {
    586  Rooted<PlainMonthDayObject*> monthDay(
    587      cx, &args.thisv().toObject().as<PlainMonthDayObject>());
    588 
    589  auto showCalendar = ShowCalendar::Auto;
    590  if (args.hasDefined(0)) {
    591    // Step 3.
    592    Rooted<JSObject*> options(
    593        cx, RequireObjectArg(cx, "options", "toString", args[0]));
    594    if (!options) {
    595      return false;
    596    }
    597 
    598    // Step 4.
    599    if (!GetTemporalShowCalendarNameOption(cx, options, &showCalendar)) {
    600      return false;
    601    }
    602  }
    603 
    604  // Step 5.
    605  JSString* str = TemporalMonthDayToString(cx, monthDay, showCalendar);
    606  if (!str) {
    607    return false;
    608  }
    609 
    610  args.rval().setString(str);
    611  return true;
    612 }
    613 
    614 /**
    615 * Temporal.PlainMonthDay.prototype.toString ( [ options ] )
    616 */
    617 static bool PlainMonthDay_toString(JSContext* cx, unsigned argc, Value* vp) {
    618  // Steps 1-2.
    619  CallArgs args = CallArgsFromVp(argc, vp);
    620  return CallNonGenericMethod<IsPlainMonthDay, PlainMonthDay_toString>(cx,
    621                                                                       args);
    622 }
    623 
    624 /**
    625 * Temporal.PlainMonthDay.prototype.toLocaleString ( [ locales [ , options ] ] )
    626 */
    627 static bool PlainMonthDay_toLocaleString(JSContext* cx, const CallArgs& args) {
    628  // Steps 3-4.
    629  return intl::TemporalObjectToLocaleString(cx, args,
    630                                            intl::DateTimeFormatKind::Date);
    631 }
    632 
    633 /**
    634 * Temporal.PlainMonthDay.prototype.toLocaleString ( [ locales [ , options ] ] )
    635 */
    636 static bool PlainMonthDay_toLocaleString(JSContext* cx, unsigned argc,
    637                                         Value* vp) {
    638  // Steps 1-2.
    639  CallArgs args = CallArgsFromVp(argc, vp);
    640  return CallNonGenericMethod<IsPlainMonthDay, PlainMonthDay_toLocaleString>(
    641      cx, args);
    642 }
    643 
    644 /**
    645 * Temporal.PlainMonthDay.prototype.toJSON ( )
    646 */
    647 static bool PlainMonthDay_toJSON(JSContext* cx, const CallArgs& args) {
    648  Rooted<PlainMonthDayObject*> monthDay(
    649      cx, &args.thisv().toObject().as<PlainMonthDayObject>());
    650 
    651  // Step 3.
    652  JSString* str = TemporalMonthDayToString(cx, monthDay, ShowCalendar::Auto);
    653  if (!str) {
    654    return false;
    655  }
    656 
    657  args.rval().setString(str);
    658  return true;
    659 }
    660 
    661 /**
    662 * Temporal.PlainMonthDay.prototype.toJSON ( )
    663 */
    664 static bool PlainMonthDay_toJSON(JSContext* cx, unsigned argc, Value* vp) {
    665  // Steps 1-2.
    666  CallArgs args = CallArgsFromVp(argc, vp);
    667  return CallNonGenericMethod<IsPlainMonthDay, PlainMonthDay_toJSON>(cx, args);
    668 }
    669 
    670 /**
    671 *  Temporal.PlainMonthDay.prototype.valueOf ( )
    672 */
    673 static bool PlainMonthDay_valueOf(JSContext* cx, unsigned argc, Value* vp) {
    674  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
    675                            "PlainMonthDay", "primitive type");
    676  return false;
    677 }
    678 
    679 /**
    680 * Temporal.PlainMonthDay.prototype.toPlainDate ( item )
    681 */
    682 static bool PlainMonthDay_toPlainDate(JSContext* cx, const CallArgs& args) {
    683  Rooted<PlainMonthDay> monthDay(
    684      cx, &args.thisv().toObject().as<PlainMonthDayObject>());
    685 
    686  // Step 3.
    687  Rooted<JSObject*> item(
    688      cx, RequireObjectArg(cx, "item", "toPlainDate", args.get(0)));
    689  if (!item) {
    690    return false;
    691  }
    692 
    693  // Step 4.
    694  auto calendar = monthDay.calendar();
    695 
    696  // Step 5.
    697  Rooted<CalendarFields> fields(cx);
    698  if (!ISODateToFields(cx, monthDay, &fields)) {
    699    return false;
    700  }
    701 
    702  // Step 6.
    703  Rooted<CalendarFields> inputFields(cx);
    704  if (!PrepareCalendarFields(cx, calendar, item,
    705                             {
    706                                 CalendarField::Year,
    707                             },
    708                             &inputFields)) {
    709    return false;
    710  }
    711 
    712  // Step 7.
    713  fields = CalendarMergeFields(calendar, fields, inputFields);
    714 
    715  // Step 8.
    716  Rooted<PlainDate> date(cx);
    717  if (!CalendarDateFromFields(cx, calendar, fields, TemporalOverflow::Constrain,
    718                              &date)) {
    719    return false;
    720  }
    721  MOZ_ASSERT(ISODateWithinLimits(date));
    722 
    723  // Step 9.
    724  auto* obj = CreateTemporalDate(cx, date);
    725  if (!obj) {
    726    return false;
    727  }
    728 
    729  args.rval().setObject(*obj);
    730  return true;
    731 }
    732 
    733 /**
    734 * Temporal.PlainMonthDay.prototype.toPlainDate ( item )
    735 */
    736 static bool PlainMonthDay_toPlainDate(JSContext* cx, unsigned argc, Value* vp) {
    737  // Steps 1-2.
    738  CallArgs args = CallArgsFromVp(argc, vp);
    739  return CallNonGenericMethod<IsPlainMonthDay, PlainMonthDay_toPlainDate>(cx,
    740                                                                          args);
    741 }
    742 
    743 const JSClass PlainMonthDayObject::class_ = {
    744    "Temporal.PlainMonthDay",
    745    JSCLASS_HAS_RESERVED_SLOTS(PlainMonthDayObject::SLOT_COUNT) |
    746        JSCLASS_HAS_CACHED_PROTO(JSProto_PlainMonthDay),
    747    JS_NULL_CLASS_OPS,
    748    &PlainMonthDayObject::classSpec_,
    749 };
    750 
    751 const JSClass& PlainMonthDayObject::protoClass_ = PlainObject::class_;
    752 
    753 static const JSFunctionSpec PlainMonthDay_methods[] = {
    754    JS_FN("from", PlainMonthDay_from, 1, 0),
    755    JS_FS_END,
    756 };
    757 
    758 static const JSFunctionSpec PlainMonthDay_prototype_methods[] = {
    759    JS_FN("with", PlainMonthDay_with, 1, 0),
    760    JS_FN("equals", PlainMonthDay_equals, 1, 0),
    761    JS_FN("toString", PlainMonthDay_toString, 0, 0),
    762    JS_FN("toLocaleString", PlainMonthDay_toLocaleString, 0, 0),
    763    JS_FN("toJSON", PlainMonthDay_toJSON, 0, 0),
    764    JS_FN("valueOf", PlainMonthDay_valueOf, 0, 0),
    765    JS_FN("toPlainDate", PlainMonthDay_toPlainDate, 1, 0),
    766    JS_FS_END,
    767 };
    768 
    769 static const JSPropertySpec PlainMonthDay_prototype_properties[] = {
    770    JS_PSG("calendarId", PlainMonthDay_calendarId, 0),
    771    JS_PSG("monthCode", PlainMonthDay_monthCode, 0),
    772    JS_PSG("day", PlainMonthDay_day, 0),
    773    JS_STRING_SYM_PS(toStringTag, "Temporal.PlainMonthDay", JSPROP_READONLY),
    774    JS_PS_END,
    775 };
    776 
    777 const ClassSpec PlainMonthDayObject::classSpec_ = {
    778    GenericCreateConstructor<PlainMonthDayConstructor, 2,
    779                             gc::AllocKind::FUNCTION>,
    780    GenericCreatePrototype<PlainMonthDayObject>,
    781    PlainMonthDay_methods,
    782    nullptr,
    783    PlainMonthDay_prototype_methods,
    784    PlainMonthDay_prototype_properties,
    785    nullptr,
    786    ClassSpec::DontDefineConstructor,
    787 };