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 };