1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "builtin/temporal/Calendar.h"
9 #include "mozilla/Assertions.h"
10 #include "mozilla/Attributes.h"
11 #include "mozilla/EnumSet.h"
12 #include "mozilla/FloatingPoint.h"
13 #include "mozilla/Likely.h"
14 #include "mozilla/Maybe.h"
15 #include "mozilla/Range.h"
16 #include "mozilla/TextUtils.h"
27 #include "jsfriendapi.h"
31 #include "NamespaceImports.h"
33 #include "builtin/Array.h"
34 #include "builtin/String.h"
35 #include "builtin/temporal/Duration.h"
36 #include "builtin/temporal/PlainDate.h"
37 #include "builtin/temporal/PlainDateTime.h"
38 #include "builtin/temporal/PlainMonthDay.h"
39 #include "builtin/temporal/PlainTime.h"
40 #include "builtin/temporal/PlainYearMonth.h"
41 #include "builtin/temporal/Temporal.h"
42 #include "builtin/temporal/TemporalFields.h"
43 #include "builtin/temporal/TemporalParser.h"
44 #include "builtin/temporal/TemporalTypes.h"
45 #include "builtin/temporal/TemporalUnit.h"
46 #include "builtin/temporal/Wrapped.h"
47 #include "builtin/temporal/ZonedDateTime.h"
48 #include "gc/AllocKind.h"
49 #include "gc/Barrier.h"
50 #include "gc/GCEnum.h"
51 #include "gc/Tracer.h"
52 #include "js/AllocPolicy.h"
53 #include "js/CallArgs.h"
54 #include "js/CallNonGenericMethod.h"
56 #include "js/Conversions.h"
57 #include "js/ErrorReport.h"
58 #include "js/ForOfIterator.h"
59 #include "js/friend/ErrorMessages.h"
61 #include "js/GCHashTable.h"
62 #include "js/GCVector.h"
64 #include "js/Printer.h"
65 #include "js/PropertyDescriptor.h"
66 #include "js/PropertySpec.h"
67 #include "js/RootingAPI.h"
68 #include "js/TracingAPI.h"
70 #include "util/Text.h"
71 #include "vm/ArrayObject.h"
72 #include "vm/BytecodeUtil.h"
73 #include "vm/Compartment.h"
74 #include "vm/GlobalObject.h"
75 #include "vm/Interpreter.h"
76 #include "vm/JSAtomState.h"
77 #include "vm/JSContext.h"
78 #include "vm/JSObject.h"
79 #include "vm/PlainObject.h"
80 #include "vm/PropertyInfo.h"
81 #include "vm/PropertyKey.h"
85 #include "vm/StringType.h"
87 #include "vm/Compartment-inl.h"
88 #include "vm/JSAtomUtils-inl.h"
89 #include "vm/JSObject-inl.h"
90 #include "vm/NativeObject-inl.h"
91 #include "vm/ObjectOperations-inl.h"
94 using namespace js::temporal
;
96 static inline bool IsCalendar(Handle
<Value
> v
) {
97 return v
.isObject() && v
.toObject().is
<CalendarObject
>();
100 void js::temporal::CalendarValue::trace(JSTracer
* trc
) {
101 TraceRoot(trc
, &value_
, "CalendarValue::value");
104 void js::temporal::CalendarRecord::trace(JSTracer
* trc
) {
105 receiver_
.trace(trc
);
106 TraceNullableRoot(trc
, &dateAdd_
, "CalendarRecord::dateAdd");
107 TraceNullableRoot(trc
, &dateFromFields_
, "CalendarRecord::dateFromFields");
108 TraceNullableRoot(trc
, &dateUntil_
, "CalendarRecord::dateUntil");
109 TraceNullableRoot(trc
, &day_
, "CalendarRecord::day");
110 TraceNullableRoot(trc
, &fields_
, "CalendarRecord::fields");
111 TraceNullableRoot(trc
, &mergeFields_
, "CalendarRecord::mergeFields");
112 TraceNullableRoot(trc
, &monthDayFromFields_
,
113 "CalendarRecord::monthDayFromFields");
114 TraceNullableRoot(trc
, &yearMonthFromFields_
,
115 "CalendarRecord::yearMonthFromFields");
118 bool js::temporal::WrapCalendarValue(JSContext
* cx
,
119 MutableHandle
<JS::Value
> calendar
) {
120 MOZ_ASSERT(calendar
.isString() || calendar
.isObject());
121 return cx
->compartment()->wrap(cx
, calendar
);
125 * IteratorToListOfType ( iteratorRecord, elementTypes )
127 * With `elementTypes = « String »`.
129 * This implementation accepts an iterable instead of an iterator record.
131 static bool IterableToListOfStrings(JSContext
* cx
, Handle
<Value
> items
,
132 MutableHandle
<CalendarFieldNames
> list
) {
133 JS::ForOfIterator
iterator(cx
);
134 if (!iterator
.init(items
)) {
138 // Step 1. (Not applicable in our implementation.)
141 Rooted
<Value
> nextValue(cx
);
142 Rooted
<PropertyKey
> value(cx
);
146 if (!iterator
.next(&nextValue
, &done
)) {
155 // Step 2.d. (Reordered)
156 if (nextValue
.isString()) {
157 if (!PrimitiveValueToId
<CanGC
>(cx
, nextValue
, &value
)) {
160 if (!list
.append(value
)) {
167 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_IGNORE_STACK
, nextValue
,
168 nullptr, "not a string");
171 iterator
.closeThrow();
177 * IsISOLeapYear ( year )
179 static constexpr bool IsISOLeapYear(int32_t year
) {
181 return (year
% 4 == 0) && ((year
% 100 != 0) || (year
% 400 == 0));
185 * IsISOLeapYear ( year )
187 static bool IsISOLeapYear(double year
) {
189 MOZ_ASSERT(IsInteger(year
));
192 return std::fmod(year
, 4) == 0 &&
193 (std::fmod(year
, 100) != 0 || std::fmod(year
, 400) == 0);
197 * ISODaysInYear ( year )
199 int32_t js::temporal::ISODaysInYear(int32_t year
) {
201 return IsISOLeapYear(year
) ? 366 : 365;
205 * ISODaysInMonth ( year, month )
207 static constexpr int32_t ISODaysInMonth(int32_t year
, int32_t month
) {
208 MOZ_ASSERT(1 <= month
&& month
<= 12);
210 constexpr uint8_t daysInMonth
[2][13] = {
211 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
212 {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};
215 return daysInMonth
[IsISOLeapYear(year
)][month
];
219 * ISODaysInMonth ( year, month )
221 int32_t js::temporal::ISODaysInMonth(int32_t year
, int32_t month
) {
222 return ::ISODaysInMonth(year
, month
);
226 * ISODaysInMonth ( year, month )
228 int32_t js::temporal::ISODaysInMonth(double year
, int32_t month
) {
229 MOZ_ASSERT(1 <= month
&& month
<= 12);
231 static constexpr uint8_t daysInMonth
[2][13] = {
232 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
233 {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};
236 return daysInMonth
[IsISOLeapYear(year
)][month
];
242 * Compute the week day from |day| without first expanding |day| into a full
243 * date through |MakeDate(day, 0)|:
245 * WeekDay(MakeDate(day, 0))
246 * = WeekDay(day × msPerDay + 0)
247 * = WeekDay(day × msPerDay)
248 * = 𝔽(ℝ(Day(day × msPerDay) + 4𝔽) modulo 7)
249 * = 𝔽(ℝ(𝔽(floor(ℝ((day × msPerDay) / msPerDay))) + 4𝔽) modulo 7)
250 * = 𝔽(ℝ(𝔽(floor(ℝ(day))) + 4𝔽) modulo 7)
251 * = 𝔽(ℝ(𝔽(day) + 4𝔽) modulo 7)
253 static int32_t WeekDay(int32_t day
) {
254 int32_t result
= (day
+ 4) % 7;
262 * ToISODayOfWeek ( year, month, day )
264 static int32_t ToISODayOfWeek(const PlainDate
& date
) {
265 MOZ_ASSERT(ISODateTimeWithinLimits(date
));
267 // Steps 1-3. (Not applicable in our implementation.)
269 // TODO: Check if ES MakeDate + WeekDay is efficient enough.
271 // https://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week#Methods_in_computer_code
274 int32_t day
= MakeDay(date
);
277 int32_t weekday
= WeekDay(day
);
278 return weekday
!= 0 ? weekday
: 7;
281 static constexpr auto FirstDayOfMonth(int32_t year
) {
282 // The following array contains the day of year for the first day of each
283 // month, where index 0 is January, and day 0 is January 1.
284 std::array
<int32_t, 13> days
= {};
285 for (int32_t month
= 1; month
<= 12; ++month
) {
286 days
[month
] = days
[month
- 1] + ::ISODaysInMonth(year
, month
);
292 * ToISODayOfYear ( year, month, day )
294 static int32_t ToISODayOfYear(int32_t year
, int32_t month
, int32_t day
) {
295 MOZ_ASSERT(1 <= month
&& month
<= 12);
297 // First day of month arrays for non-leap and leap years.
298 constexpr decltype(FirstDayOfMonth(0)) firstDayOfMonth
[2] = {
299 FirstDayOfMonth(1), FirstDayOfMonth(0)};
301 // Steps 1-3. (Not applicable in our implementation.)
305 // Instead of first computing the date and then using DayWithinYear to map the
306 // date to the day within the year, directly lookup the first day of the month
307 // and then add the additional days.
308 return firstDayOfMonth
[IsISOLeapYear(year
)][month
- 1] + day
;
312 * ToISODayOfYear ( year, month, day )
314 int32_t js::temporal::ToISODayOfYear(const PlainDate
& date
) {
315 MOZ_ASSERT(ISODateTimeWithinLimits(date
));
318 const auto& [year
, month
, day
] = date
;
319 return ::ToISODayOfYear(year
, month
, day
);
322 static int32_t FloorDiv(int32_t dividend
, int32_t divisor
) {
323 MOZ_ASSERT(divisor
> 0);
325 int32_t quotient
= dividend
/ divisor
;
326 int32_t remainder
= dividend
% divisor
;
334 * 21.4.1.3 Year Number, DayFromYear
336 static int32_t DayFromYear(int32_t year
) {
337 return 365 * (year
- 1970) + FloorDiv(year
- 1969, 4) -
338 FloorDiv(year
- 1901, 100) + FloorDiv(year
- 1601, 400);
342 * 21.4.1.11 MakeTime ( hour, min, sec, ms )
344 static int64_t MakeTime(const PlainTime
& time
) {
345 MOZ_ASSERT(IsValidTime(time
));
347 // Step 1 (Not applicable).
350 int64_t h
= time
.hour
;
353 int64_t m
= time
.minute
;
356 int64_t s
= time
.second
;
359 int64_t milli
= time
.millisecond
;
362 return h
* ToMilliseconds(TemporalUnit::Hour
) +
363 m
* ToMilliseconds(TemporalUnit::Minute
) +
364 s
* ToMilliseconds(TemporalUnit::Second
) + milli
;
368 * 21.4.1.12 MakeDay ( year, month, date )
370 int32_t js::temporal::MakeDay(const PlainDate
& date
) {
371 MOZ_ASSERT(ISODateTimeWithinLimits(date
));
373 return DayFromYear(date
.year
) + ToISODayOfYear(date
) - 1;
377 * 21.4.1.13 MakeDate ( day, time )
379 int64_t js::temporal::MakeDate(const PlainDateTime
& dateTime
) {
380 MOZ_ASSERT(ISODateTimeWithinLimits(dateTime
));
382 // Step 1 (Not applicable).
385 int64_t tv
= MakeDay(dateTime
.date
) * ToMilliseconds(TemporalUnit::Day
) +
386 MakeTime(dateTime
.time
);
393 * 21.4.1.12 MakeDay ( year, month, date )
395 static int32_t MakeDay(int32_t year
, int32_t month
, int32_t day
) {
396 MOZ_ASSERT(1 <= month
&& month
<= 12);
398 // FIXME: spec issue - what should happen for invalid years/days?
399 return DayFromYear(year
) + ::ToISODayOfYear(year
, month
, day
) - 1;
403 * 21.4.1.13 MakeDate ( day, time )
405 int64_t js::temporal::MakeDate(int32_t year
, int32_t month
, int32_t day
) {
406 // NOTE: This version accepts values outside the valid date-time limits.
407 MOZ_ASSERT(1 <= month
&& month
<= 12);
409 // Step 1 (Not applicable).
412 int64_t tv
= ::MakeDay(year
, month
, day
) * ToMilliseconds(TemporalUnit::Day
);
418 struct YearWeek final
{
424 * ToISOWeekOfYear ( year, month, day )
426 static YearWeek
ToISOWeekOfYear(const PlainDate
& date
) {
427 MOZ_ASSERT(ISODateTimeWithinLimits(date
));
429 const auto& [year
, month
, day
] = date
;
431 // TODO: https://en.wikipedia.org/wiki/Week#The_ISO_week_date_system
432 // TODO: https://en.wikipedia.org/wiki/ISO_week_date#Algorithms
434 // Steps 1-3. (Not applicable in our implementation.)
437 int32_t doy
= ToISODayOfYear(date
);
438 int32_t dow
= ToISODayOfWeek(date
);
440 int32_t woy
= (10 + doy
- dow
) / 7;
441 MOZ_ASSERT(0 <= woy
&& woy
<= 53);
443 // An ISO year has 53 weeks if the year starts on a Thursday or if it's a
444 // leap year which starts on a Wednesday.
445 auto isLongYear
= [](int32_t year
) {
446 int32_t startOfYear
= ToISODayOfWeek({year
, 1, 1});
447 return startOfYear
== 4 || (startOfYear
== 3 && IsISOLeapYear(year
));
450 // Part of last year's last week, which is either week 52 or week 53.
452 return {year
- 1, 52 + int32_t(isLongYear(year
- 1))};
455 // Part of next year's first week if the current year isn't a long year.
456 if (woy
== 53 && !isLongYear(year
)) {
457 return {year
+ 1, 1};
464 * ISOMonthCode ( month )
466 static JSString
* ISOMonthCode(JSContext
* cx
, int32_t month
) {
467 MOZ_ASSERT(1 <= month
&& month
<= 12);
470 char monthCode
[3] = {'M', char('0' + (month
/ 10)), char('0' + (month
% 10))};
471 return NewStringCopyN
<CanGC
>(cx
, monthCode
, std::size(monthCode
));
474 template <typename T
, typename
... Ts
>
475 static bool ToPlainDate(JSObject
* temporalDateLike
, PlainDate
* result
) {
476 if (auto* obj
= temporalDateLike
->maybeUnwrapIf
<T
>()) {
477 *result
= ToPlainDate(obj
);
480 if constexpr (sizeof...(Ts
) > 0) {
481 return ToPlainDate
<Ts
...>(temporalDateLike
, result
);
486 template <typename
... Ts
>
487 static bool ToPlainDate(JSContext
* cx
, Handle
<Value
> temporalDateLike
,
489 if (temporalDateLike
.isObject()) {
490 if (ToPlainDate
<Ts
...>(&temporalDateLike
.toObject(), result
)) {
495 return ToTemporalDate(cx
, temporalDateLike
, result
);
499 template <typename CharT
>
500 static bool StringIsAsciiLowerCase(mozilla::Range
<CharT
> str
) {
501 return std::all_of(str
.begin().get(), str
.end().get(), [](CharT ch
) {
502 return mozilla::IsAscii(ch
) && !mozilla::IsAsciiUppercaseAlpha(ch
);
506 static bool StringIsAsciiLowerCase(JSLinearString
* str
) {
507 JS::AutoCheckCannotGC nogc
;
508 return str
->hasLatin1Chars()
509 ? StringIsAsciiLowerCase(str
->latin1Range(nogc
))
510 : StringIsAsciiLowerCase(str
->twoByteRange(nogc
));
514 static bool IsISO8601Calendar(JSLinearString
* id
) {
515 return StringEqualsLiteral(id
, "iso8601");
519 static bool IsISO8601Calendar(CalendarObject
* calendar
) {
520 return IsISO8601Calendar(calendar
->identifier());
524 static bool IsISO8601Calendar(JSContext
* cx
, JSString
* id
, bool* result
) {
525 JSLinearString
* linear
= id
->ensureLinear(cx
);
529 *result
= IsISO8601Calendar(linear
);
534 * IsBuiltinCalendar ( id )
536 static bool IsBuiltinCalendar(JSLinearString
* id
) {
537 // Callers must convert to lower case.
538 MOZ_ASSERT(StringIsAsciiLowerCase(id
));
541 return StringEqualsLiteral(id
, "iso8601");
544 static JSLinearString
* ThrowIfNotBuiltinCalendar(JSContext
* cx
,
545 Handle
<JSLinearString
*> id
) {
546 if (!StringIsAscii(id
)) {
547 if (auto chars
= QuoteString(cx
, id
)) {
548 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
549 JSMSG_TEMPORAL_CALENDAR_INVALID_ID
, chars
.get());
554 JSString
* lower
= StringToLowerCase(cx
, id
);
559 JSLinearString
* linear
= lower
->ensureLinear(cx
);
564 if (!IsBuiltinCalendar(linear
)) {
565 if (auto chars
= QuoteString(cx
, id
)) {
566 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
567 JSMSG_TEMPORAL_CALENDAR_INVALID_ID
, chars
.get());
575 bool js::temporal::ToBuiltinCalendar(JSContext
* cx
, Handle
<JSString
*> id
,
576 MutableHandle
<CalendarValue
> result
) {
577 Rooted
<JSLinearString
*> linear(cx
, id
->ensureLinear(cx
));
582 auto* identifier
= ThrowIfNotBuiltinCalendar(cx
, linear
);
587 result
.set(CalendarValue(identifier
));
592 * CreateTemporalCalendar ( identifier [ , newTarget ] )
594 static CalendarObject
* CreateTemporalCalendar(
595 JSContext
* cx
, const CallArgs
& args
, Handle
<JSLinearString
*> identifier
) {
597 MOZ_ASSERT(IsBuiltinCalendar(identifier
));
600 Rooted
<JSObject
*> proto(cx
);
601 if (!GetPrototypeFromBuiltinConstructor(cx
, args
, JSProto_Calendar
, &proto
)) {
605 auto* obj
= NewObjectWithClassProto
<CalendarObject
>(cx
, proto
);
611 obj
->setFixedSlot(CalendarObject::IDENTIFIER_SLOT
, StringValue(identifier
));
618 * CreateTemporalCalendar ( identifier [ , newTarget ] )
620 static CalendarObject
* CreateTemporalCalendar(
621 JSContext
* cx
, Handle
<JSLinearString
*> identifier
) {
623 MOZ_ASSERT(IsBuiltinCalendar(identifier
));
626 auto* obj
= NewBuiltinClassInstance
<CalendarObject
>(cx
);
632 obj
->setFixedSlot(CalendarObject::IDENTIFIER_SLOT
, StringValue(identifier
));
639 * ObjectImplementsTemporalCalendarProtocol ( object )
641 static bool ObjectImplementsTemporalCalendarProtocol(JSContext
* cx
,
642 Handle
<JSObject
*> object
,
644 // Step 1. (Not applicable in our implementation.)
645 MOZ_ASSERT(!object
->canUnwrapAs
<CalendarObject
>(),
646 "Calendar objects handled in the caller");
650 &JSAtomState::dateAdd
, &JSAtomState::dateFromFields
,
651 &JSAtomState::dateUntil
, &JSAtomState::day
,
652 &JSAtomState::dayOfWeek
, &JSAtomState::dayOfYear
,
653 &JSAtomState::daysInMonth
, &JSAtomState::daysInWeek
,
654 &JSAtomState::daysInYear
, &JSAtomState::fields
,
655 &JSAtomState::id
, &JSAtomState::inLeapYear
,
656 &JSAtomState::mergeFields
, &JSAtomState::month
,
657 &JSAtomState::monthCode
, &JSAtomState::monthDayFromFields
,
658 &JSAtomState::monthsInYear
, &JSAtomState::weekOfYear
,
659 &JSAtomState::year
, &JSAtomState::yearMonthFromFields
,
660 &JSAtomState::yearOfWeek
,
664 if (!HasProperty(cx
, object
, cx
->names().*key
, &has
)) {
678 template <typename T
, typename
... Ts
>
679 static bool ToTemporalCalendar(JSContext
* cx
, Handle
<JSObject
*> object
,
680 MutableHandle
<CalendarValue
> result
) {
681 if (auto* unwrapped
= object
->maybeUnwrapIf
<T
>()) {
682 result
.set(unwrapped
->calendar());
683 return result
.wrap(cx
);
686 if constexpr (sizeof...(Ts
) > 0) {
687 return ToTemporalCalendar
<Ts
...>(cx
, object
, result
);
690 result
.set(CalendarValue());
695 * ToTemporalCalendarSlotValue ( temporalCalendarLike [ , default ] )
697 bool js::temporal::ToTemporalCalendar(JSContext
* cx
,
698 Handle
<Value
> temporalCalendarLike
,
699 MutableHandle
<CalendarValue
> result
) {
700 // Step 1. (Not applicable)
703 Rooted
<Value
> calendarLike(cx
, temporalCalendarLike
);
704 if (calendarLike
.isObject()) {
705 Rooted
<JSObject
*> obj(cx
, &calendarLike
.toObject());
707 // Step 2.b. (Partial)
708 if (obj
->canUnwrapAs
<CalendarObject
>()) {
709 result
.set(CalendarValue(obj
));
714 Rooted
<CalendarValue
> calendar(cx
);
715 if (!::ToTemporalCalendar
<PlainDateObject
, PlainDateTimeObject
,
716 PlainMonthDayObject
, PlainYearMonthObject
,
717 ZonedDateTimeObject
>(cx
, obj
, &calendar
)) {
721 result
.set(calendar
);
726 bool implementsCalendarProtocol
;
727 if (!ObjectImplementsTemporalCalendarProtocol(
728 cx
, obj
, &implementsCalendarProtocol
)) {
731 if (!implementsCalendarProtocol
) {
732 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
733 JSMSG_TEMPORAL_INVALID_OBJECT
,
734 "Temporal.Calendar", obj
->getClass()->name
);
739 result
.set(CalendarValue(obj
));
744 if (!calendarLike
.isString()) {
745 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_IGNORE_STACK
,
746 calendarLike
, nullptr, "not a string");
749 Rooted
<JSString
*> str(cx
, calendarLike
.toString());
752 Rooted
<JSLinearString
*> identifier(cx
, ParseTemporalCalendarString(cx
, str
));
758 identifier
= ThrowIfNotBuiltinCalendar(cx
, identifier
);
764 result
.set(CalendarValue(identifier
));
769 * ToTemporalCalendarSlotValue ( temporalCalendarLike [ , default ] )
771 * When called with `default = "iso8601"`.
773 bool js::temporal::ToTemporalCalendarWithISODefault(
774 JSContext
* cx
, Handle
<Value
> temporalCalendarLike
,
775 MutableHandle
<CalendarValue
> result
) {
777 if (temporalCalendarLike
.isUndefined()) {
778 result
.set(CalendarValue(cx
->names().iso8601
));
783 return ToTemporalCalendar(cx
, temporalCalendarLike
, result
);
787 * GetTemporalCalendarSlotValueWithISODefault ( item )
789 bool js::temporal::GetTemporalCalendarWithISODefault(
790 JSContext
* cx
, Handle
<JSObject
*> item
,
791 MutableHandle
<CalendarValue
> result
) {
793 Rooted
<CalendarValue
> calendar(cx
);
794 if (!::ToTemporalCalendar
<PlainDateObject
, PlainDateTimeObject
,
795 PlainMonthDayObject
, PlainYearMonthObject
,
796 ZonedDateTimeObject
>(cx
, item
, &calendar
)) {
800 result
.set(calendar
);
805 Rooted
<Value
> calendarValue(cx
);
806 if (!GetProperty(cx
, item
, item
, cx
->names().calendar
, &calendarValue
)) {
811 return ToTemporalCalendarWithISODefault(cx
, calendarValue
, result
);
815 * ToTemporalCalendarIdentifier ( calendarSlotValue )
817 JSString
* js::temporal::ToTemporalCalendarIdentifier(
818 JSContext
* cx
, Handle
<CalendarValue
> calendar
) {
820 if (calendar
.isString()) {
822 MOZ_ASSERT(IsBuiltinCalendar(calendar
.toString()));
825 return calendar
.toString();
829 Rooted
<JSObject
*> calendarObj(cx
, calendar
.toObject());
830 Rooted
<Value
> identifier(cx
);
831 if (!GetProperty(cx
, calendarObj
, calendarObj
, cx
->names().id
, &identifier
)) {
836 if (!identifier
.isString()) {
837 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_IGNORE_STACK
, identifier
,
838 nullptr, "not a string");
843 return identifier
.toString();
847 * ToTemporalCalendarObject ( calendarSlotValue )
849 JSObject
* js::temporal::ToTemporalCalendarObject(
850 JSContext
* cx
, Handle
<CalendarValue
> calendar
) {
852 if (calendar
.isObject()) {
853 return calendar
.toObject();
857 Rooted
<JSLinearString
*> calendarId(cx
, calendar
.toString());
858 return CreateTemporalCalendar(cx
, calendarId
);
861 static bool Calendar_dateAdd(JSContext
* cx
, unsigned argc
, Value
* vp
);
862 static bool Calendar_dateFromFields(JSContext
* cx
, unsigned argc
, Value
* vp
);
863 static bool Calendar_dateUntil(JSContext
* cx
, unsigned argc
, Value
* vp
);
864 static bool Calendar_day(JSContext
* cx
, unsigned argc
, Value
* vp
);
865 static bool Calendar_fields(JSContext
* cx
, unsigned argc
, Value
* vp
);
866 static bool Calendar_mergeFields(JSContext
* cx
, unsigned argc
, Value
* vp
);
867 static bool Calendar_monthDayFromFields(JSContext
* cx
, unsigned argc
,
869 static bool Calendar_yearMonthFromFields(JSContext
* cx
, unsigned argc
,
873 * CalendarMethodsRecordLookup ( calendarRec, methodName )
875 static bool CalendarMethodsRecordLookup(JSContext
* cx
,
876 MutableHandle
<CalendarRecord
> calendar
,
877 CalendarMethod methodName
) {
878 // Step 1. (Not applicable in our implementation.)
881 Rooted
<JSObject
*> object(cx
, calendar
.receiver().toObject());
883 auto lookup
= [&](Handle
<PropertyName
*> name
, JSNative native
,
884 MutableHandle
<JSObject
*> result
) {
885 auto* method
= GetMethod(cx
, object
, name
);
890 // As an optimization we only store the method if the receiver is either
891 // a custom calendar object or if the method isn't the default, built-in
893 if (!object
->is
<CalendarObject
>() || !IsNativeFunction(method
, native
)) {
899 switch (methodName
) {
901 case CalendarMethod::DateAdd
:
902 return lookup(cx
->names().dateAdd
, Calendar_dateAdd
, calendar
.dateAdd());
905 case CalendarMethod::DateFromFields
:
906 return lookup(cx
->names().dateFromFields
, Calendar_dateFromFields
,
907 calendar
.dateFromFields());
910 case CalendarMethod::DateUntil
:
911 return lookup(cx
->names().dateUntil
, Calendar_dateUntil
,
912 calendar
.dateUntil());
915 case CalendarMethod::Day
:
916 return lookup(cx
->names().day
, Calendar_day
, calendar
.day());
919 case CalendarMethod::Fields
:
920 return lookup(cx
->names().fields
, Calendar_fields
, calendar
.fields());
923 case CalendarMethod::MergeFields
:
924 return lookup(cx
->names().mergeFields
, Calendar_mergeFields
,
925 calendar
.mergeFields());
928 case CalendarMethod::MonthDayFromFields
:
929 return lookup(cx
->names().monthDayFromFields
, Calendar_monthDayFromFields
,
930 calendar
.monthDayFromFields());
933 case CalendarMethod::YearMonthFromFields
:
934 return lookup(cx
->names().yearMonthFromFields
,
935 Calendar_yearMonthFromFields
,
936 calendar
.yearMonthFromFields());
939 MOZ_CRASH("invalid calendar method");
943 * CreateCalendarMethodsRecord ( calendar, methods )
945 bool js::temporal::CreateCalendarMethodsRecord(
946 JSContext
* cx
, Handle
<CalendarValue
> calendar
,
947 mozilla::EnumSet
<CalendarMethod
> methods
,
948 MutableHandle
<CalendarRecord
> result
) {
949 MOZ_ASSERT(!methods
.isEmpty());
952 result
.set(CalendarRecord
{calendar
});
955 // Remember the set of looked-up methods for assertions.
956 result
.get().lookedUp() += methods
;
959 // Built-in calendars don't perform observable lookups.
960 if (calendar
.isString()) {
965 for (auto method
: methods
) {
966 if (!CalendarMethodsRecordLookup(cx
, result
, method
)) {
975 static bool ToCalendarField(JSContext
* cx
, JSLinearString
* linear
,
976 CalendarField
* result
) {
977 if (StringEqualsLiteral(linear
, "year")) {
978 *result
= CalendarField::Year
;
981 if (StringEqualsLiteral(linear
, "month")) {
982 *result
= CalendarField::Month
;
985 if (StringEqualsLiteral(linear
, "monthCode")) {
986 *result
= CalendarField::MonthCode
;
989 if (StringEqualsLiteral(linear
, "day")) {
990 *result
= CalendarField::Day
;
993 if (auto chars
= QuoteString(cx
, linear
, '"')) {
994 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
995 JSMSG_TEMPORAL_CALENDAR_INVALID_FIELD
,
1001 static PropertyName
* ToPropertyName(JSContext
* cx
, CalendarField field
) {
1003 case CalendarField::Year
:
1004 return cx
->names().year
;
1005 case CalendarField::Month
:
1006 return cx
->names().month
;
1007 case CalendarField::MonthCode
:
1008 return cx
->names().monthCode
;
1009 case CalendarField::Day
:
1010 return cx
->names().day
;
1012 MOZ_CRASH("invalid calendar field name");
1016 static const char* ToCString(CalendarField field
) {
1018 case CalendarField::Year
:
1020 case CalendarField::Month
:
1022 case CalendarField::MonthCode
:
1024 case CalendarField::Day
:
1027 MOZ_CRASH("invalid calendar field name");
1032 * Temporal.Calendar.prototype.fields ( fields )
1034 static bool BuiltinCalendarFields(
1035 JSContext
* cx
, std::initializer_list
<CalendarField
> fieldNames
,
1036 CalendarFieldNames
& result
) {
1037 MOZ_ASSERT(result
.empty());
1039 // Steps 1-5. (Not applicable.)
1041 // Reserve space for the append operation.
1042 if (!result
.reserve(fieldNames
.size())) {
1047 for (auto fieldName
: fieldNames
) {
1048 auto* name
= ToPropertyName(cx
, fieldName
);
1050 // Steps 7.a and 7.b.i-iv. (Not applicable)
1053 result
.infallibleAppend(NameToId(name
));
1056 // Step 8. (Not applicable)
1061 static bool IsSorted(std::initializer_list
<CalendarField
> fieldNames
) {
1062 return std::is_sorted(fieldNames
.begin(), fieldNames
.end(),
1063 [](auto x
, auto y
) {
1064 auto* a
= ToCString(x
);
1065 auto* b
= ToCString(y
);
1066 return std::strcmp(a
, b
) < 0;
1072 * CalendarFields ( calendarRec, fieldNames )
1074 bool js::temporal::CalendarFields(
1075 JSContext
* cx
, Handle
<CalendarRecord
> calendar
,
1076 std::initializer_list
<CalendarField
> fieldNames
,
1077 MutableHandle
<CalendarFieldNames
> result
) {
1079 CalendarMethodsRecordHasLookedUp(calendar
, CalendarMethod::Fields
));
1082 MOZ_ASSERT(IsSorted(fieldNames
));
1083 MOZ_ASSERT(std::adjacent_find(fieldNames
.begin(), fieldNames
.end()) ==
1087 auto fields
= calendar
.fields();
1089 bool arrayIterationSane
;
1090 if (calendar
.receiver().isString()) {
1091 // "String" calendars don't perform observable array iteration.
1092 arrayIterationSane
= true;
1094 // "Object" calendars need to ensure array iteration is still sane.
1095 if (!IsArrayIterationSane(cx
, &arrayIterationSane
)) {
1100 if (arrayIterationSane
) {
1102 return BuiltinCalendarFields(cx
, fieldNames
, result
.get());
1104 // Steps 2.c-e. (Not applicable in our implementation.)
1108 // Step 3. (Inlined call to CalendarMethodsRecordCall.)
1110 auto* array
= NewDenseFullyAllocatedArray(cx
, fieldNames
.size());
1114 array
->setDenseInitializedLength(fieldNames
.size());
1116 for (size_t i
= 0; i
< fieldNames
.size(); i
++) {
1117 auto* name
= ToPropertyName(cx
, fieldNames
.begin()[i
]);
1118 array
->initDenseElement(i
, StringValue(name
));
1121 Rooted
<Value
> fieldsFn(cx
, ObjectValue(*fields
));
1122 auto thisv
= calendar
.receiver().toValue();
1123 Rooted
<Value
> fieldsArray(cx
, ObjectValue(*array
));
1124 if (!Call(cx
, fieldsFn
, thisv
, fieldsArray
, &fieldsArray
)) {
1129 if (!IterableToListOfStrings(cx
, fieldsArray
, result
)) {
1133 // The spec sorts the field names in PrepareTemporalFields. Sorting is only
1134 // needed for user-defined calendars, so our implementation performs this step
1135 // here instead of in PrepareTemporalFields.
1136 return SortTemporalFieldNames(cx
, result
.get());
1139 static bool RequireIntegralNumber(JSContext
* cx
, Handle
<Value
> value
,
1140 Handle
<PropertyName
*> name
,
1141 MutableHandle
<Value
> result
) {
1142 if (MOZ_LIKELY(value
.isInt32())) {
1147 if (value
.isDouble()) {
1148 double d
= value
.toDouble();
1149 if (js::IsInteger(d
)) {
1150 result
.setNumber(d
);
1154 if (auto str
= QuoteString(cx
, name
)) {
1155 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1156 JSMSG_TEMPORAL_INVALID_INTEGER
, str
.get());
1161 if (auto str
= QuoteString(cx
, name
)) {
1162 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1163 JSMSG_UNEXPECTED_TYPE
, str
.get(), "not a number");
1168 static bool RequireIntegralPositiveNumber(JSContext
* cx
, Handle
<Value
> value
,
1169 Handle
<PropertyName
*> name
,
1170 MutableHandle
<Value
> result
) {
1171 if (!RequireIntegralNumber(cx
, value
, name
, result
)) {
1175 if (result
.toNumber() <= 0) {
1176 if (auto str
= QuoteString(cx
, name
)) {
1177 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1178 JSMSG_TEMPORAL_INVALID_NUMBER
, str
.get());
1185 static bool RequireString(JSContext
* cx
, Handle
<Value
> value
,
1186 Handle
<PropertyName
*> name
,
1187 MutableHandle
<Value
> result
) {
1188 if (MOZ_LIKELY(value
.isString())) {
1193 if (auto str
= QuoteString(cx
, name
)) {
1194 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1195 JSMSG_UNEXPECTED_TYPE
, str
.get(), "not a string");
1200 static bool RequireBoolean(JSContext
* cx
, Handle
<Value
> value
,
1201 Handle
<PropertyName
*> name
,
1202 MutableHandle
<Value
> result
) {
1203 if (MOZ_LIKELY(value
.isBoolean())) {
1208 if (auto str
= QuoteString(cx
, name
)) {
1209 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1210 JSMSG_UNEXPECTED_TYPE
, str
.get(),
1216 using BuiltinCalendarMethod
= bool (*)(JSContext
* cx
, const PlainDate
&,
1217 MutableHandle
<Value
>);
1219 using CalendarConversion
= bool (*)(JSContext
*, Handle
<Value
>,
1220 Handle
<PropertyName
*>,
1221 MutableHandle
<Value
>);
1223 template <BuiltinCalendarMethod builtin
, CalendarConversion conversion
>
1224 static bool CallCalendarMethod(JSContext
* cx
, Handle
<PropertyName
*> name
,
1225 JSNative native
, Handle
<CalendarValue
> calendar
,
1226 Handle
<JSObject
*> dateLike
,
1227 const PlainDate
& date
,
1228 MutableHandle
<Value
> result
) {
1230 if (calendar
.isString()) {
1231 return builtin(cx
, date
, result
);
1235 Rooted
<JSObject
*> calendarObj(cx
, calendar
.toObject());
1236 JSObject
* fn
= GetMethod(cx
, calendarObj
, name
);
1241 // Fast-path for the default implementation.
1242 if (calendarObj
->is
<CalendarObject
>() && IsNativeFunction(fn
, native
)) {
1243 return builtin(cx
, date
, result
);
1246 Rooted
<JS::Value
> fnVal(cx
, ObjectValue(*fn
));
1247 Rooted
<JS::Value
> dateLikeValue(cx
, ObjectValue(*dateLike
));
1248 if (!Call(cx
, fnVal
, calendarObj
, dateLikeValue
, result
)) {
1253 return conversion(cx
, result
, name
, result
);
1257 * Temporal.Calendar.prototype.year ( temporalDateLike )
1259 static bool BuiltinCalendarYear(JSContext
* cx
, const PlainDate
& date
,
1260 MutableHandle
<Value
> result
) {
1261 // Steps 1-4. (Not applicable.)
1264 result
.setInt32(date
.year
);
1268 static bool Calendar_year(JSContext
* cx
, unsigned argc
, Value
* vp
);
1271 * CalendarYear ( calendar, dateLike )
1273 static bool CalendarYear(JSContext
* cx
, Handle
<CalendarValue
> calendar
,
1274 Handle
<JSObject
*> dateLike
, const PlainDate
& date
,
1275 MutableHandle
<Value
> result
) {
1277 return CallCalendarMethod
<BuiltinCalendarYear
, RequireIntegralNumber
>(
1278 cx
, cx
->names().year
, Calendar_year
, calendar
, dateLike
, date
, result
);
1282 * CalendarYear ( calendar, dateLike )
1284 bool js::temporal::CalendarYear(JSContext
* cx
, Handle
<CalendarValue
> calendar
,
1285 Handle
<PlainDateObject
*> dateLike
,
1286 MutableHandle
<Value
> result
) {
1287 return CalendarYear(cx
, calendar
, dateLike
, ToPlainDate(dateLike
), result
);
1291 * CalendarYear ( calendar, dateLike )
1293 bool js::temporal::CalendarYear(JSContext
* cx
, Handle
<CalendarValue
> calendar
,
1294 Handle
<PlainDateTimeObject
*> dateLike
,
1295 MutableHandle
<Value
> result
) {
1296 return CalendarYear(cx
, calendar
, dateLike
, ToPlainDate(dateLike
), result
);
1300 * CalendarYear ( calendar, dateLike )
1302 bool js::temporal::CalendarYear(JSContext
* cx
, Handle
<CalendarValue
> calendar
,
1303 Handle
<PlainYearMonthObject
*> dateLike
,
1304 MutableHandle
<Value
> result
) {
1305 return CalendarYear(cx
, calendar
, dateLike
, ToPlainDate(dateLike
), result
);
1309 * CalendarYear ( calendar, dateLike )
1311 bool js::temporal::CalendarYear(JSContext
* cx
, Handle
<CalendarValue
> calendar
,
1312 const PlainDateTime
& dateTime
,
1313 MutableHandle
<Value
> result
) {
1314 Rooted
<PlainDateTimeObject
*> dateLike(
1315 cx
, CreateTemporalDateTime(cx
, dateTime
, calendar
));
1320 return ::CalendarYear(cx
, calendar
, dateLike
, dateTime
.date
, result
);
1324 * Temporal.Calendar.prototype.month ( temporalDateLike )
1326 static bool BuiltinCalendarMonth(JSContext
* cx
, const PlainDate
& date
,
1327 MutableHandle
<Value
> result
) {
1328 // Steps 1-5. (Not applicable.)
1331 result
.setInt32(date
.month
);
1335 static bool Calendar_month(JSContext
* cx
, unsigned argc
, Value
* vp
);
1338 * CalendarMonth ( calendar, dateLike )
1340 static bool CalendarMonth(JSContext
* cx
, Handle
<CalendarValue
> calendar
,
1341 Handle
<JSObject
*> dateLike
, const PlainDate
& date
,
1342 MutableHandle
<Value
> result
) {
1344 return CallCalendarMethod
<BuiltinCalendarMonth
,
1345 RequireIntegralPositiveNumber
>(
1346 cx
, cx
->names().month
, Calendar_month
, calendar
, dateLike
, date
, result
);
1350 * CalendarMonth ( calendar, dateLike )
1352 bool js::temporal::CalendarMonth(JSContext
* cx
, Handle
<CalendarValue
> calendar
,
1353 Handle
<PlainDateObject
*> dateLike
,
1354 MutableHandle
<Value
> result
) {
1355 return CalendarMonth(cx
, calendar
, dateLike
, ToPlainDate(dateLike
), result
);
1359 * CalendarMonth ( calendar, dateLike )
1361 bool js::temporal::CalendarMonth(JSContext
* cx
, Handle
<CalendarValue
> calendar
,
1362 Handle
<PlainDateTimeObject
*> dateLike
,
1363 MutableHandle
<Value
> result
) {
1364 return CalendarMonth(cx
, calendar
, dateLike
, ToPlainDate(dateLike
), result
);
1368 * CalendarMonth ( calendar, dateLike )
1370 bool js::temporal::CalendarMonth(JSContext
* cx
, Handle
<CalendarValue
> calendar
,
1371 Handle
<PlainYearMonthObject
*> dateLike
,
1372 MutableHandle
<Value
> result
) {
1373 return CalendarMonth(cx
, calendar
, dateLike
, ToPlainDate(dateLike
), result
);
1377 * CalendarMonth ( calendar, dateLike )
1379 bool js::temporal::CalendarMonth(JSContext
* cx
, Handle
<CalendarValue
> calendar
,
1380 const PlainDateTime
& dateTime
,
1381 MutableHandle
<Value
> result
) {
1382 Rooted
<PlainDateTimeObject
*> dateLike(
1383 cx
, CreateTemporalDateTime(cx
, dateTime
, calendar
));
1388 return ::CalendarMonth(cx
, calendar
, dateLike
, dateTime
.date
, result
);
1392 * Temporal.Calendar.prototype.monthCode ( temporalDateLike )
1394 static bool BuiltinCalendarMonthCode(JSContext
* cx
, const PlainDate
& date
,
1395 MutableHandle
<Value
> result
) {
1396 // Steps 1-4. (Not applicable.)
1399 JSString
* str
= ISOMonthCode(cx
, date
.month
);
1404 result
.setString(str
);
1408 static bool Calendar_monthCode(JSContext
* cx
, unsigned argc
, Value
* vp
);
1411 * CalendarMonthCode ( calendar, dateLike )
1413 static bool CalendarMonthCode(JSContext
* cx
, Handle
<CalendarValue
> calendar
,
1414 Handle
<JSObject
*> dateLike
, const PlainDate
& date
,
1415 MutableHandle
<Value
> result
) {
1417 return CallCalendarMethod
<BuiltinCalendarMonthCode
, RequireString
>(
1418 cx
, cx
->names().monthCode
, Calendar_monthCode
, calendar
, dateLike
, date
,
1423 * CalendarMonthCode ( calendar, dateLike )
1425 bool js::temporal::CalendarMonthCode(JSContext
* cx
,
1426 Handle
<CalendarValue
> calendar
,
1427 Handle
<PlainDateObject
*> dateLike
,
1428 MutableHandle
<Value
> result
) {
1429 return CalendarMonthCode(cx
, calendar
, dateLike
, ToPlainDate(dateLike
),
1434 * CalendarMonthCode ( calendar, dateLike )
1436 bool js::temporal::CalendarMonthCode(JSContext
* cx
,
1437 Handle
<CalendarValue
> calendar
,
1438 Handle
<PlainDateTimeObject
*> dateLike
,
1439 MutableHandle
<Value
> result
) {
1440 return CalendarMonthCode(cx
, calendar
, dateLike
, ToPlainDate(dateLike
),
1445 * CalendarMonthCode ( calendar, dateLike )
1447 bool js::temporal::CalendarMonthCode(JSContext
* cx
,
1448 Handle
<CalendarValue
> calendar
,
1449 Handle
<PlainMonthDayObject
*> dateLike
,
1450 MutableHandle
<Value
> result
) {
1451 return CalendarMonthCode(cx
, calendar
, dateLike
, ToPlainDate(dateLike
),
1456 * CalendarMonthCode ( calendar, dateLike )
1458 bool js::temporal::CalendarMonthCode(JSContext
* cx
,
1459 Handle
<CalendarValue
> calendar
,
1460 Handle
<PlainYearMonthObject
*> dateLike
,
1461 MutableHandle
<Value
> result
) {
1462 return CalendarMonthCode(cx
, calendar
, dateLike
, ToPlainDate(dateLike
),
1467 * CalendarMonthCode ( calendar, dateLike )
1469 bool js::temporal::CalendarMonthCode(JSContext
* cx
,
1470 Handle
<CalendarValue
> calendar
,
1471 const PlainDateTime
& dateTime
,
1472 MutableHandle
<Value
> result
) {
1473 Rooted
<PlainDateTimeObject
*> dateLike(
1474 cx
, CreateTemporalDateTime(cx
, dateTime
, calendar
));
1479 return ::CalendarMonthCode(cx
, calendar
, dateLike
, dateTime
.date
, result
);
1483 * Temporal.Calendar.prototype.day ( temporalDateLike )
1485 static bool BuiltinCalendarDay(const PlainDate
& date
,
1486 MutableHandle
<Value
> result
) {
1487 // Steps 1-4. (Not applicable.)
1490 result
.setInt32(date
.day
);
1495 * CalendarDay ( calendarRec, dateLike )
1497 static bool CalendarDay(JSContext
* cx
, Handle
<CalendarRecord
> calendar
,
1498 Handle
<JSObject
*> dateLike
, const PlainDate
& date
,
1499 MutableHandle
<Value
> result
) {
1500 MOZ_ASSERT(CalendarMethodsRecordHasLookedUp(calendar
, CalendarMethod::Day
));
1502 // Step 2. (Reordered)
1503 auto day
= calendar
.day();
1505 return BuiltinCalendarDay(date
, result
);
1508 // Step 1. (Inlined call to CalendarMethodsRecordCall.)
1509 Rooted
<Value
> fn(cx
, ObjectValue(*day
));
1510 auto thisv
= calendar
.receiver().toValue();
1511 Rooted
<JS::Value
> dateLikeValue(cx
, ObjectValue(*dateLike
));
1512 if (!Call(cx
, fn
, thisv
, dateLikeValue
, result
)) {
1517 return RequireIntegralPositiveNumber(cx
, result
, cx
->names().day
, result
);
1521 * CalendarDay ( calendarRec, dateLike )
1523 bool js::temporal::CalendarDay(JSContext
* cx
, Handle
<CalendarValue
> calendar
,
1524 Handle
<PlainDateObject
*> dateLike
,
1525 MutableHandle
<Value
> result
) {
1526 Rooted
<CalendarRecord
> calendarRec(cx
);
1527 if (!CreateCalendarMethodsRecord(cx
, calendar
,
1529 CalendarMethod::Day
,
1535 return CalendarDay(cx
, calendarRec
, dateLike
, ToPlainDate(dateLike
), result
);
1539 * CalendarDay ( calendarRec, dateLike )
1541 bool js::temporal::CalendarDay(JSContext
* cx
, Handle
<CalendarValue
> calendar
,
1542 Handle
<PlainDateTimeObject
*> dateLike
,
1543 MutableHandle
<Value
> result
) {
1544 Rooted
<CalendarRecord
> calendarRec(cx
);
1545 if (!CreateCalendarMethodsRecord(cx
, calendar
,
1547 CalendarMethod::Day
,
1553 return CalendarDay(cx
, calendarRec
, dateLike
, ToPlainDate(dateLike
), result
);
1557 * CalendarDay ( calendarRec, dateLike )
1559 bool js::temporal::CalendarDay(JSContext
* cx
, Handle
<CalendarValue
> calendar
,
1560 Handle
<PlainMonthDayObject
*> dateLike
,
1561 MutableHandle
<Value
> result
) {
1562 Rooted
<CalendarRecord
> calendarRec(cx
);
1563 if (!CreateCalendarMethodsRecord(cx
, calendar
,
1565 CalendarMethod::Day
,
1571 return CalendarDay(cx
, calendarRec
, dateLike
, ToPlainDate(dateLike
), result
);
1575 * CalendarDay ( calendarRec, dateLike )
1577 bool js::temporal::CalendarDay(JSContext
* cx
, Handle
<CalendarRecord
> calendar
,
1578 const PlainDate
& date
,
1579 MutableHandle
<Value
> result
) {
1580 Rooted
<PlainDateObject
*> dateLike(
1581 cx
, CreateTemporalDate(cx
, date
, calendar
.receiver()));
1586 return ::CalendarDay(cx
, calendar
, dateLike
, date
, result
);
1590 * CalendarDay ( calendarRec, dateLike )
1592 bool js::temporal::CalendarDay(JSContext
* cx
, Handle
<CalendarRecord
> calendar
,
1593 const PlainDateTime
& dateTime
,
1594 MutableHandle
<Value
> result
) {
1595 Rooted
<PlainDateTimeObject
*> dateLike(
1596 cx
, CreateTemporalDateTime(cx
, dateTime
, calendar
.receiver()));
1601 return ::CalendarDay(cx
, calendar
, dateLike
, dateTime
.date
, result
);
1605 * Temporal.Calendar.prototype.dayOfWeek ( temporalDateLike )
1607 static bool BuiltinCalendarDayOfWeek(JSContext
* cx
, const PlainDate
& date
,
1608 MutableHandle
<Value
> result
) {
1609 // Steps 1-4. (Not applicable.)
1612 result
.setInt32(ToISODayOfWeek(date
));
1616 static bool Calendar_dayOfWeek(JSContext
* cx
, unsigned argc
, Value
* vp
);
1619 * CalendarDayOfWeek ( calendar, dateLike )
1621 static bool CalendarDayOfWeek(JSContext
* cx
, Handle
<CalendarValue
> calendar
,
1622 Handle
<JSObject
*> dateLike
, const PlainDate
& date
,
1623 MutableHandle
<Value
> result
) {
1625 return CallCalendarMethod
<BuiltinCalendarDayOfWeek
,
1626 RequireIntegralPositiveNumber
>(
1627 cx
, cx
->names().dayOfWeek
, Calendar_dayOfWeek
, calendar
, dateLike
, date
,
1632 * CalendarDayOfWeek ( calendar, dateLike )
1634 bool js::temporal::CalendarDayOfWeek(JSContext
* cx
,
1635 Handle
<CalendarValue
> calendar
,
1636 Handle
<PlainDateObject
*> dateLike
,
1637 MutableHandle
<Value
> result
) {
1638 return CalendarDayOfWeek(cx
, calendar
, dateLike
, ToPlainDate(dateLike
),
1643 * CalendarDayOfWeek ( calendar, dateLike )
1645 bool js::temporal::CalendarDayOfWeek(JSContext
* cx
,
1646 Handle
<CalendarValue
> calendar
,
1647 Handle
<PlainDateTimeObject
*> dateLike
,
1648 MutableHandle
<Value
> result
) {
1649 return CalendarDayOfWeek(cx
, calendar
, dateLike
, ToPlainDate(dateLike
),
1654 * CalendarDayOfWeek ( calendar, dateLike )
1656 bool js::temporal::CalendarDayOfWeek(JSContext
* cx
,
1657 Handle
<CalendarValue
> calendar
,
1658 const PlainDateTime
& dateTime
,
1659 MutableHandle
<Value
> result
) {
1660 Rooted
<PlainDateTimeObject
*> dateLike(
1661 cx
, CreateTemporalDateTime(cx
, dateTime
, calendar
));
1666 return ::CalendarDayOfWeek(cx
, calendar
, dateLike
, dateTime
.date
, result
);
1670 * Temporal.Calendar.prototype.dayOfYear ( temporalDateLike )
1672 static bool BuiltinCalendarDayOfYear(JSContext
* cx
, const PlainDate
& date
,
1673 MutableHandle
<Value
> result
) {
1674 // Steps 1-4. (Not applicable.)
1677 result
.setInt32(ToISODayOfYear(date
));
1681 static bool Calendar_dayOfYear(JSContext
* cx
, unsigned argc
, Value
* vp
);
1684 * CalendarDayOfYear ( calendar, dateLike )
1686 static bool CalendarDayOfYear(JSContext
* cx
, Handle
<CalendarValue
> calendar
,
1687 Handle
<JSObject
*> dateLike
, const PlainDate
& date
,
1688 MutableHandle
<Value
> result
) {
1690 return CallCalendarMethod
<BuiltinCalendarDayOfYear
,
1691 RequireIntegralPositiveNumber
>(
1692 cx
, cx
->names().dayOfYear
, Calendar_dayOfYear
, calendar
, dateLike
, date
,
1697 * CalendarDayOfYear ( calendar, dateLike )
1699 bool js::temporal::CalendarDayOfYear(JSContext
* cx
,
1700 Handle
<CalendarValue
> calendar
,
1701 Handle
<PlainDateObject
*> dateLike
,
1702 MutableHandle
<Value
> result
) {
1703 return CalendarDayOfYear(cx
, calendar
, dateLike
, ToPlainDate(dateLike
),
1708 * CalendarDayOfYear ( calendar, dateLike )
1710 bool js::temporal::CalendarDayOfYear(JSContext
* cx
,
1711 Handle
<CalendarValue
> calendar
,
1712 Handle
<PlainDateTimeObject
*> dateLike
,
1713 MutableHandle
<Value
> result
) {
1714 return CalendarDayOfYear(cx
, calendar
, dateLike
, ToPlainDate(dateLike
),
1719 * CalendarDayOfYear ( calendar, dateLike )
1721 bool js::temporal::CalendarDayOfYear(JSContext
* cx
,
1722 Handle
<CalendarValue
> calendar
,
1723 const PlainDateTime
& dateTime
,
1724 MutableHandle
<Value
> result
) {
1725 Rooted
<PlainDateTimeObject
*> dateLike(
1726 cx
, CreateTemporalDateTime(cx
, dateTime
, calendar
));
1731 return ::CalendarDayOfYear(cx
, calendar
, dateLike
, dateTime
.date
, result
);
1735 * Temporal.Calendar.prototype.weekOfYear ( temporalDateLike )
1737 static bool BuiltinCalendarWeekOfYear(JSContext
* cx
, const PlainDate
& date
,
1738 MutableHandle
<Value
> result
) {
1739 // Steps 1-4. (Not applicable.)
1742 result
.setInt32(ToISOWeekOfYear(date
).week
);
1746 static bool Calendar_weekOfYear(JSContext
* cx
, unsigned argc
, Value
* vp
);
1749 * CalendarWeekOfYear ( calendar, dateLike )
1751 static bool CalendarWeekOfYear(JSContext
* cx
, Handle
<CalendarValue
> calendar
,
1752 Handle
<JSObject
*> dateLike
,
1753 const PlainDate
& date
,
1754 MutableHandle
<Value
> result
) {
1756 return CallCalendarMethod
<BuiltinCalendarWeekOfYear
,
1757 RequireIntegralPositiveNumber
>(
1758 cx
, cx
->names().weekOfYear
, Calendar_weekOfYear
, calendar
, dateLike
, date
,
1763 * CalendarWeekOfYear ( calendar, dateLike )
1765 bool js::temporal::CalendarWeekOfYear(JSContext
* cx
,
1766 Handle
<CalendarValue
> calendar
,
1767 Handle
<PlainDateObject
*> dateLike
,
1768 MutableHandle
<Value
> result
) {
1769 return CalendarWeekOfYear(cx
, calendar
, dateLike
, ToPlainDate(dateLike
),
1774 * CalendarWeekOfYear ( calendar, dateLike )
1776 bool js::temporal::CalendarWeekOfYear(JSContext
* cx
,
1777 Handle
<CalendarValue
> calendar
,
1778 Handle
<PlainDateTimeObject
*> dateLike
,
1779 MutableHandle
<Value
> result
) {
1780 return CalendarWeekOfYear(cx
, calendar
, dateLike
, ToPlainDate(dateLike
),
1785 * CalendarWeekOfYear ( calendar, dateLike )
1787 bool js::temporal::CalendarWeekOfYear(JSContext
* cx
,
1788 Handle
<CalendarValue
> calendar
,
1789 const PlainDateTime
& dateTime
,
1790 MutableHandle
<Value
> result
) {
1791 Rooted
<PlainDateTimeObject
*> dateLike(
1792 cx
, CreateTemporalDateTime(cx
, dateTime
, calendar
));
1797 return ::CalendarWeekOfYear(cx
, calendar
, dateLike
, dateTime
.date
, result
);
1801 * Temporal.Calendar.prototype.yearOfWeek ( temporalDateLike )
1803 static bool BuiltinCalendarYearOfWeek(JSContext
* cx
, const PlainDate
& date
,
1804 MutableHandle
<Value
> result
) {
1805 // Steps 1-4. (Not applicable.)
1808 result
.setInt32(ToISOWeekOfYear(date
).year
);
1812 static bool Calendar_yearOfWeek(JSContext
* cx
, unsigned argc
, Value
* vp
);
1815 * CalendarYearOfWeek ( calendar, dateLike )
1817 static bool CalendarYearOfWeek(JSContext
* cx
, Handle
<CalendarValue
> calendar
,
1818 Handle
<JSObject
*> dateLike
,
1819 const PlainDate
& date
,
1820 MutableHandle
<Value
> result
) {
1822 return CallCalendarMethod
<BuiltinCalendarYearOfWeek
, RequireIntegralNumber
>(
1823 cx
, cx
->names().yearOfWeek
, Calendar_yearOfWeek
, calendar
, dateLike
, date
,
1828 * CalendarYearOfWeek ( calendar, dateLike )
1830 bool js::temporal::CalendarYearOfWeek(JSContext
* cx
,
1831 Handle
<CalendarValue
> calendar
,
1832 Handle
<PlainDateObject
*> dateLike
,
1833 MutableHandle
<Value
> result
) {
1834 return CalendarYearOfWeek(cx
, calendar
, dateLike
, ToPlainDate(dateLike
),
1839 * CalendarYearOfWeek ( calendar, dateLike )
1841 bool js::temporal::CalendarYearOfWeek(JSContext
* cx
,
1842 Handle
<CalendarValue
> calendar
,
1843 Handle
<PlainDateTimeObject
*> dateLike
,
1844 MutableHandle
<Value
> result
) {
1845 return CalendarYearOfWeek(cx
, calendar
, dateLike
, ToPlainDate(dateLike
),
1850 * CalendarYearOfWeek ( calendar, dateLike )
1852 bool js::temporal::CalendarYearOfWeek(JSContext
* cx
,
1853 Handle
<CalendarValue
> calendar
,
1854 const PlainDateTime
& dateTime
,
1855 MutableHandle
<Value
> result
) {
1856 Rooted
<PlainDateTimeObject
*> dateLike(
1857 cx
, CreateTemporalDateTime(cx
, dateTime
, calendar
));
1862 return ::CalendarYearOfWeek(cx
, calendar
, dateLike
, dateTime
.date
, result
);
1866 * Temporal.Calendar.prototype.daysInWeek ( temporalDateLike )
1868 static bool BuiltinCalendarDaysInWeek(JSContext
* cx
, const PlainDate
& date
,
1869 MutableHandle
<Value
> result
) {
1870 // Steps 1-4. (Not applicable.)
1877 static bool Calendar_daysInWeek(JSContext
* cx
, unsigned argc
, Value
* vp
);
1880 * CalendarDaysInWeek ( calendar, dateLike )
1882 static bool CalendarDaysInWeek(JSContext
* cx
, Handle
<CalendarValue
> calendar
,
1883 Handle
<JSObject
*> dateLike
,
1884 const PlainDate
& date
,
1885 MutableHandle
<Value
> result
) {
1887 return CallCalendarMethod
<BuiltinCalendarDaysInWeek
,
1888 RequireIntegralPositiveNumber
>(
1889 cx
, cx
->names().daysInWeek
, Calendar_daysInWeek
, calendar
, dateLike
, date
,
1894 * CalendarDaysInWeek ( calendar, dateLike )
1896 bool js::temporal::CalendarDaysInWeek(JSContext
* cx
,
1897 Handle
<CalendarValue
> calendar
,
1898 Handle
<PlainDateObject
*> dateLike
,
1899 MutableHandle
<Value
> result
) {
1900 return CalendarDaysInWeek(cx
, calendar
, dateLike
, ToPlainDate(dateLike
),
1905 * CalendarDaysInWeek ( calendar, dateLike )
1907 bool js::temporal::CalendarDaysInWeek(JSContext
* cx
,
1908 Handle
<CalendarValue
> calendar
,
1909 Handle
<PlainDateTimeObject
*> dateLike
,
1910 MutableHandle
<Value
> result
) {
1911 return CalendarDaysInWeek(cx
, calendar
, dateLike
, ToPlainDate(dateLike
),
1916 * CalendarDaysInWeek ( calendar, dateLike )
1918 bool js::temporal::CalendarDaysInWeek(JSContext
* cx
,
1919 Handle
<CalendarValue
> calendar
,
1920 const PlainDateTime
& dateTime
,
1921 MutableHandle
<Value
> result
) {
1922 Rooted
<PlainDateTimeObject
*> dateLike(
1923 cx
, CreateTemporalDateTime(cx
, dateTime
, calendar
));
1928 return ::CalendarDaysInWeek(cx
, calendar
, dateLike
, dateTime
.date
, result
);
1932 * Temporal.Calendar.prototype.daysInMonth ( temporalDateLike )
1934 static bool BuiltinCalendarDaysInMonth(JSContext
* cx
, const PlainDate
& date
,
1935 MutableHandle
<Value
> result
) {
1936 // Steps 1-4. (Not applicable.)
1939 result
.setInt32(::ISODaysInMonth(date
.year
, date
.month
));
1943 static bool Calendar_daysInMonth(JSContext
* cx
, unsigned argc
, Value
* vp
);
1946 * CalendarDaysInMonth ( calendar, dateLike )
1948 static bool CalendarDaysInMonth(JSContext
* cx
, Handle
<CalendarValue
> calendar
,
1949 Handle
<JSObject
*> dateLike
,
1950 const PlainDate
& date
,
1951 MutableHandle
<Value
> result
) {
1953 return CallCalendarMethod
<BuiltinCalendarDaysInMonth
,
1954 RequireIntegralPositiveNumber
>(
1955 cx
, cx
->names().daysInMonth
, Calendar_daysInMonth
, calendar
, dateLike
,
1960 * CalendarDaysInMonth ( calendar, dateLike )
1962 bool js::temporal::CalendarDaysInMonth(JSContext
* cx
,
1963 Handle
<CalendarValue
> calendar
,
1964 Handle
<PlainDateObject
*> dateLike
,
1965 MutableHandle
<Value
> result
) {
1966 return CalendarDaysInMonth(cx
, calendar
, dateLike
, ToPlainDate(dateLike
),
1971 * CalendarDaysInMonth ( calendar, dateLike )
1973 bool js::temporal::CalendarDaysInMonth(JSContext
* cx
,
1974 Handle
<CalendarValue
> calendar
,
1975 Handle
<PlainDateTimeObject
*> dateLike
,
1976 MutableHandle
<Value
> result
) {
1977 return CalendarDaysInMonth(cx
, calendar
, dateLike
, ToPlainDate(dateLike
),
1982 * CalendarDaysInMonth ( calendar, dateLike )
1984 bool js::temporal::CalendarDaysInMonth(JSContext
* cx
,
1985 Handle
<CalendarValue
> calendar
,
1986 Handle
<PlainYearMonthObject
*> dateLike
,
1987 MutableHandle
<Value
> result
) {
1988 return CalendarDaysInMonth(cx
, calendar
, dateLike
, ToPlainDate(dateLike
),
1993 * CalendarDaysInMonth ( calendar, dateLike )
1995 bool js::temporal::CalendarDaysInMonth(JSContext
* cx
,
1996 Handle
<CalendarValue
> calendar
,
1997 const PlainDateTime
& dateTime
,
1998 MutableHandle
<Value
> result
) {
1999 Rooted
<PlainDateTimeObject
*> dateLike(
2000 cx
, CreateTemporalDateTime(cx
, dateTime
, calendar
));
2005 return ::CalendarDaysInMonth(cx
, calendar
, dateLike
, dateTime
.date
, result
);
2009 * Temporal.Calendar.prototype.daysInYear ( temporalDateLike )
2011 static bool BuiltinCalendarDaysInYear(JSContext
* cx
, const PlainDate
& date
,
2012 MutableHandle
<Value
> result
) {
2013 // Steps 1-4. (Not applicable.)
2016 result
.setInt32(ISODaysInYear(date
.year
));
2020 static bool Calendar_daysInYear(JSContext
* cx
, unsigned argc
, Value
* vp
);
2023 * CalendarDaysInYear ( calendar, dateLike )
2025 static bool CalendarDaysInYear(JSContext
* cx
, Handle
<CalendarValue
> calendar
,
2026 Handle
<JSObject
*> dateLike
,
2027 const PlainDate
& date
,
2028 MutableHandle
<Value
> result
) {
2030 return CallCalendarMethod
<BuiltinCalendarDaysInYear
,
2031 RequireIntegralPositiveNumber
>(
2032 cx
, cx
->names().daysInYear
, Calendar_daysInYear
, calendar
, dateLike
, date
,
2037 * CalendarDaysInYear ( calendar, dateLike )
2039 bool js::temporal::CalendarDaysInYear(JSContext
* cx
,
2040 Handle
<CalendarValue
> calendar
,
2041 Handle
<PlainDateObject
*> dateLike
,
2042 MutableHandle
<Value
> result
) {
2043 return CalendarDaysInYear(cx
, calendar
, dateLike
, ToPlainDate(dateLike
),
2048 * CalendarDaysInYear ( calendar, dateLike )
2050 bool js::temporal::CalendarDaysInYear(JSContext
* cx
,
2051 Handle
<CalendarValue
> calendar
,
2052 Handle
<PlainDateTimeObject
*> dateLike
,
2053 MutableHandle
<Value
> result
) {
2054 return CalendarDaysInYear(cx
, calendar
, dateLike
, ToPlainDate(dateLike
),
2059 * CalendarDaysInYear ( calendar, dateLike )
2061 bool js::temporal::CalendarDaysInYear(JSContext
* cx
,
2062 Handle
<CalendarValue
> calendar
,
2063 Handle
<PlainYearMonthObject
*> dateLike
,
2064 MutableHandle
<Value
> result
) {
2065 return CalendarDaysInYear(cx
, calendar
, dateLike
, ToPlainDate(dateLike
),
2070 * CalendarDaysInYear ( calendar, dateLike )
2072 bool js::temporal::CalendarDaysInYear(JSContext
* cx
,
2073 Handle
<CalendarValue
> calendar
,
2074 const PlainDateTime
& dateTime
,
2075 MutableHandle
<Value
> result
) {
2076 Rooted
<PlainDateTimeObject
*> dateLike(
2077 cx
, CreateTemporalDateTime(cx
, dateTime
, calendar
));
2082 return ::CalendarDaysInYear(cx
, calendar
, dateLike
, dateTime
.date
, result
);
2086 * Temporal.Calendar.prototype.monthsInYear ( temporalDateLike )
2088 static bool BuiltinCalendarMonthsInYear(JSContext
* cx
, const PlainDate
& date
,
2089 MutableHandle
<Value
> result
) {
2090 // Steps 1-4. (Not applicable.)
2093 result
.setInt32(12);
2097 static bool Calendar_monthsInYear(JSContext
* cx
, unsigned argc
, Value
* vp
);
2100 * CalendarMonthsInYear ( calendar, dateLike )
2102 static bool CalendarMonthsInYear(JSContext
* cx
, Handle
<CalendarValue
> calendar
,
2103 Handle
<JSObject
*> dateLike
,
2104 const PlainDate
& date
,
2105 MutableHandle
<Value
> result
) {
2107 return CallCalendarMethod
<BuiltinCalendarMonthsInYear
,
2108 RequireIntegralPositiveNumber
>(
2109 cx
, cx
->names().monthsInYear
, Calendar_monthsInYear
, calendar
, dateLike
,
2114 * CalendarMonthsInYear ( calendar, dateLike )
2116 bool js::temporal::CalendarMonthsInYear(JSContext
* cx
,
2117 Handle
<CalendarValue
> calendar
,
2118 Handle
<PlainDateObject
*> dateLike
,
2119 MutableHandle
<Value
> result
) {
2120 return ::CalendarMonthsInYear(cx
, calendar
, dateLike
, ToPlainDate(dateLike
),
2125 * CalendarMonthsInYear ( calendar, dateLike )
2127 bool js::temporal::CalendarMonthsInYear(JSContext
* cx
,
2128 Handle
<CalendarValue
> calendar
,
2129 Handle
<PlainDateTimeObject
*> dateLike
,
2130 MutableHandle
<Value
> result
) {
2131 return ::CalendarMonthsInYear(cx
, calendar
, dateLike
, ToPlainDate(dateLike
),
2136 * CalendarMonthsInYear ( calendar, dateLike )
2138 bool js::temporal::CalendarMonthsInYear(JSContext
* cx
,
2139 Handle
<CalendarValue
> calendar
,
2140 Handle
<PlainYearMonthObject
*> dateLike
,
2141 MutableHandle
<Value
> result
) {
2142 return ::CalendarMonthsInYear(cx
, calendar
, dateLike
, ToPlainDate(dateLike
),
2147 * CalendarMonthsInYear ( calendar, dateLike )
2149 bool js::temporal::CalendarMonthsInYear(JSContext
* cx
,
2150 Handle
<CalendarValue
> calendar
,
2151 const PlainDateTime
& dateTime
,
2152 MutableHandle
<Value
> result
) {
2153 Rooted
<PlainDateTimeObject
*> dateLike(
2154 cx
, CreateTemporalDateTime(cx
, dateTime
, calendar
));
2159 return ::CalendarMonthsInYear(cx
, calendar
, dateLike
, dateTime
.date
, result
);
2163 * Temporal.Calendar.prototype.inLeapYear ( temporalDateLike )
2165 static bool BuiltinCalendarInLeapYear(JSContext
* cx
, const PlainDate
& date
,
2166 MutableHandle
<Value
> result
) {
2167 // Steps 1-4. (Not applicable.)
2170 result
.setBoolean(IsISOLeapYear(date
.year
));
2174 static bool Calendar_inLeapYear(JSContext
* cx
, unsigned argc
, Value
* vp
);
2177 * CalendarInLeapYear ( calendar, dateLike )
2179 static bool CalendarInLeapYear(JSContext
* cx
, Handle
<CalendarValue
> calendar
,
2180 Handle
<JSObject
*> dateLike
,
2181 const PlainDate
& date
,
2182 MutableHandle
<Value
> result
) {
2184 return CallCalendarMethod
<BuiltinCalendarInLeapYear
, RequireBoolean
>(
2185 cx
, cx
->names().inLeapYear
, Calendar_inLeapYear
, calendar
, dateLike
, date
,
2190 * CalendarInLeapYear ( calendar, dateLike )
2192 bool js::temporal::CalendarInLeapYear(JSContext
* cx
,
2193 Handle
<CalendarValue
> calendar
,
2194 Handle
<PlainDateObject
*> dateLike
,
2195 MutableHandle
<Value
> result
) {
2196 return ::CalendarInLeapYear(cx
, calendar
, dateLike
, ToPlainDate(dateLike
),
2201 * CalendarInLeapYear ( calendar, dateLike )
2203 bool js::temporal::CalendarInLeapYear(JSContext
* cx
,
2204 Handle
<CalendarValue
> calendar
,
2205 Handle
<PlainDateTimeObject
*> dateLike
,
2206 MutableHandle
<Value
> result
) {
2207 return ::CalendarInLeapYear(cx
, calendar
, dateLike
, ToPlainDate(dateLike
),
2212 * CalendarInLeapYear ( calendar, dateLike )
2214 bool js::temporal::CalendarInLeapYear(JSContext
* cx
,
2215 Handle
<CalendarValue
> calendar
,
2216 Handle
<PlainYearMonthObject
*> dateLike
,
2217 MutableHandle
<Value
> result
) {
2218 return ::CalendarInLeapYear(cx
, calendar
, dateLike
, ToPlainDate(dateLike
),
2223 * CalendarInLeapYear ( calendar, dateLike )
2225 bool js::temporal::CalendarInLeapYear(JSContext
* cx
,
2226 Handle
<CalendarValue
> calendar
,
2227 const PlainDateTime
& dateTime
,
2228 MutableHandle
<Value
> result
) {
2229 Rooted
<PlainDateTimeObject
*> dateLike(
2230 cx
, CreateTemporalDateTime(cx
, dateTime
, calendar
));
2235 return ::CalendarInLeapYear(cx
, calendar
, dateLike
, dateTime
.date
, result
);
2239 * ISOResolveMonth ( fields )
2241 static bool ISOResolveMonth(JSContext
* cx
,
2242 MutableHandle
<TemporalFields
> fields
) {
2243 // Step 1. (Not applicable in our implementation.)
2246 double month
= fields
.month();
2249 MOZ_ASSERT((IsInteger(month
) && month
> 0) || std::isnan(month
));
2252 Handle
<JSString
*> monthCode
= fields
.monthCode();
2257 if (std::isnan(month
)) {
2258 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
2259 JSMSG_TEMPORAL_CALENDAR_MISSING_FIELD
,
2268 // Steps 6-7. (Not applicable in our implementation.)
2271 if (monthCode
->length() != 3) {
2272 if (auto code
= QuoteString(cx
, monthCode
)) {
2273 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
2274 JSMSG_TEMPORAL_CALENDAR_INVALID_MONTHCODE
,
2280 JSLinearString
* linear
= monthCode
->ensureLinear(cx
);
2285 char16_t chars
[3] = {
2286 linear
->latin1OrTwoByteChar(0),
2287 linear
->latin1OrTwoByteChar(1),
2288 linear
->latin1OrTwoByteChar(2),
2291 // Steps 9-11. (Partial)
2292 if (chars
[0] != 'M' || !mozilla::IsAsciiDigit(chars
[1]) ||
2293 !mozilla::IsAsciiDigit(chars
[2])) {
2294 if (auto code
= QuoteString(cx
, linear
)) {
2295 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
2296 JSMSG_TEMPORAL_CALENDAR_INVALID_MONTHCODE
,
2303 int32_t monthCodeInteger
=
2304 AsciiDigitToNumber(chars
[1]) * 10 + AsciiDigitToNumber(chars
[2]);
2306 // Step 11. (Partial)
2307 if (monthCodeInteger
< 1 || monthCodeInteger
> 12) {
2308 if (auto code
= QuoteString(cx
, linear
)) {
2309 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
2310 JSMSG_TEMPORAL_CALENDAR_INVALID_MONTHCODE
,
2316 // Step 13. (Not applicable in our implementation.)
2319 if (!std::isnan(month
) && month
!= monthCodeInteger
) {
2320 if (auto code
= QuoteString(cx
, linear
)) {
2321 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
2322 JSMSG_TEMPORAL_CALENDAR_INVALID_MONTHCODE
,
2329 fields
.month() = monthCodeInteger
;
2336 * ISODateFromFields ( fields, overflow )
2338 static bool ISODateFromFields(JSContext
* cx
, Handle
<TemporalFields
> fields
,
2339 TemporalOverflow overflow
, PlainDate
* result
) {
2340 // Steps 1-2. (Not applicable in our implementation.)
2343 double year
= fields
.year();
2346 double month
= fields
.month();
2349 double day
= fields
.day();
2352 MOZ_ASSERT(!std::isnan(year
) && !std::isnan(month
) && !std::isnan(day
));
2355 RegulatedISODate regulated
;
2356 if (!RegulateISODate(cx
, year
, month
, day
, overflow
, ®ulated
)) {
2360 // The result is used to create a new PlainDateObject, so it's okay to
2361 // directly throw an error for invalid years. That way we don't have to worry
2362 // about representing doubles in PlainDate structs.
2364 if (!mozilla::NumberEqualsInt32(regulated
.year
, &intYear
)) {
2365 // CreateTemporalDate, steps 1-2.
2366 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
2367 JSMSG_TEMPORAL_PLAIN_DATE_INVALID
);
2371 *result
= {intYear
, regulated
.month
, regulated
.day
};
2376 * Temporal.Calendar.prototype.dateFromFields ( fields [ , options ] )
2378 static PlainDateObject
* BuiltinCalendarDateFromFields(
2379 JSContext
* cx
, Handle
<JSObject
*> fields
, Handle
<JSObject
*> maybeOptions
) {
2380 // Steps 1-5. (Not applicable)
2383 Rooted
<TemporalFields
> dateFields(cx
);
2384 if (!PrepareTemporalFields(cx
, fields
,
2385 {TemporalField::Day
, TemporalField::Month
,
2386 TemporalField::MonthCode
, TemporalField::Year
},
2387 {TemporalField::Day
, TemporalField::Year
},
2393 auto overflow
= TemporalOverflow::Constrain
;
2395 if (!ToTemporalOverflow(cx
, maybeOptions
, &overflow
)) {
2401 if (!ISOResolveMonth(cx
, &dateFields
)) {
2407 if (!ISODateFromFields(cx
, dateFields
, overflow
, &result
)) {
2412 Rooted
<CalendarValue
> calendar(cx
, CalendarValue(cx
->names().iso8601
));
2413 return CreateTemporalDate(cx
, result
, calendar
);
2417 * CalendarDateFromFields ( calendarRec, fields [ , options ] )
2419 static Wrapped
<PlainDateObject
*> CalendarDateFromFields(
2420 JSContext
* cx
, Handle
<CalendarRecord
> calendar
, Handle
<JSObject
*> fields
,
2421 Handle
<PlainObject
*> maybeOptions
) {
2422 MOZ_ASSERT(CalendarMethodsRecordHasLookedUp(calendar
,
2423 CalendarMethod::DateFromFields
));
2425 // Step 1. (Not applicable in our implemetation.)
2427 // Step 3. (Reordered)
2428 auto dateFromFields
= calendar
.dateFromFields();
2429 if (!dateFromFields
) {
2430 return BuiltinCalendarDateFromFields(cx
, fields
, maybeOptions
);
2433 // Step 2. (Inlined call to CalendarMethodsRecordCall.)
2435 Rooted
<Value
> dateFromFieldsFn(cx
, ObjectValue(*dateFromFields
));
2436 auto thisv
= calendar
.receiver().toValue();
2437 Rooted
<Value
> rval(cx
);
2439 FixedInvokeArgs
<2> args(cx
);
2440 args
[0].setObject(*fields
);
2442 args
[1].setObject(*maybeOptions
);
2444 args
[1].setUndefined();
2447 if (!Call(cx
, dateFromFieldsFn
, thisv
, args
, &rval
)) {
2452 if (!rval
.isObject() || !rval
.toObject().canUnwrapAs
<PlainDateObject
>()) {
2453 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_IGNORE_STACK
, rval
,
2454 nullptr, "not a PlainDate object");
2459 return &rval
.toObject();
2463 * CalendarDateFromFields ( calendarRec, fields [ , options ] )
2465 Wrapped
<PlainDateObject
*> js::temporal::CalendarDateFromFields(
2466 JSContext
* cx
, Handle
<CalendarRecord
> calendar
,
2467 Handle
<PlainObject
*> fields
) {
2469 return ::CalendarDateFromFields(cx
, calendar
, fields
, nullptr);
2473 * CalendarDateFromFields ( calendarRec, fields [ , options ] )
2475 Wrapped
<PlainDateObject
*> js::temporal::CalendarDateFromFields(
2476 JSContext
* cx
, Handle
<CalendarRecord
> calendar
, Handle
<PlainObject
*> fields
,
2477 Handle
<PlainObject
*> options
) {
2479 return ::CalendarDateFromFields(cx
, calendar
, fields
, options
);
2483 * CalendarDateFromFields ( calendarRec, fields [ , options ] )
2485 Wrapped
<PlainDateObject
*> js::temporal::CalendarDateFromFields(
2486 JSContext
* cx
, Handle
<CalendarRecord
> calendar
, Handle
<PlainObject
*> fields
,
2487 TemporalOverflow overflow
) {
2488 // FIXME: spec issue - unnecessary options object when using "constrain".
2489 // https://github.com/tc39/proposal-temporal/issues/2803
2491 Rooted
<PlainObject
*> options(cx
, NewPlainObjectWithProto(cx
, nullptr));
2496 Rooted
<Value
> value(cx
);
2497 if (overflow
== TemporalOverflow::Constrain
) {
2498 value
= StringValue(cx
->names().constrain
);
2500 MOZ_ASSERT(overflow
== TemporalOverflow::Reject
);
2501 value
= StringValue(cx
->names().reject
);
2503 if (!DefineDataProperty(cx
, options
, cx
->names().overflow
, value
)) {
2508 return ::CalendarDateFromFields(cx
, calendar
, fields
, options
);
2511 struct RegulatedISOYearMonth final
{
2517 * RegulateISOYearMonth ( year, month, overflow )
2519 static bool RegulateISOYearMonth(JSContext
* cx
, double year
, double month
,
2520 TemporalOverflow overflow
,
2521 RegulatedISOYearMonth
* result
) {
2523 MOZ_ASSERT(IsInteger(year
));
2524 MOZ_ASSERT(IsInteger(month
));
2526 // Step 2. (Not applicable in our implementation.)
2529 if (overflow
== TemporalOverflow::Constrain
) {
2531 month
= std::clamp(month
, 1.0, 12.0);
2534 *result
= {year
, int32_t(month
)};
2539 MOZ_ASSERT(overflow
== TemporalOverflow::Reject
);
2542 if (month
< 1 || month
> 12) {
2543 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
2544 JSMSG_TEMPORAL_PLAIN_YEAR_MONTH_INVALID
);
2549 *result
= {year
, int32_t(month
)};
2554 * ISOYearMonthFromFields ( fields, overflow )
2556 static bool ISOYearMonthFromFields(JSContext
* cx
, Handle
<TemporalFields
> fields
,
2557 TemporalOverflow overflow
,
2558 PlainDate
* result
) {
2559 // Steps 1-2. (Not applicable in our implementation.)
2562 double year
= fields
.year();
2565 double month
= fields
.month();
2568 MOZ_ASSERT(!std::isnan(year
) && !std::isnan(month
));
2571 RegulatedISOYearMonth regulated
;
2572 if (!RegulateISOYearMonth(cx
, year
, month
, overflow
, ®ulated
)) {
2578 // The result is used to create a new PlainYearMonthObject, so it's okay to
2579 // directly throw an error for invalid years. That way we don't have to worry
2580 // about representing doubles in PlainDate structs.
2582 if (!mozilla::NumberEqualsInt32(regulated
.year
, &intYear
)) {
2583 // CreateTemporalYearMonth, steps 1-2.
2584 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
2585 JSMSG_TEMPORAL_PLAIN_YEAR_MONTH_INVALID
);
2589 *result
= {intYear
, regulated
.month
, 1};
2594 * Temporal.Calendar.prototype.yearMonthFromFields ( fields [ , options ] )
2596 static PlainYearMonthObject
* BuiltinCalendarYearMonthFromFields(
2597 JSContext
* cx
, Handle
<JSObject
*> fields
, Handle
<JSObject
*> maybeOptions
) {
2598 // Steps 1-5. (Not applicable)
2601 Rooted
<TemporalFields
> dateFields(cx
);
2602 if (!PrepareTemporalFields(
2604 {TemporalField::Month
, TemporalField::MonthCode
, TemporalField::Year
},
2605 {TemporalField::Year
}, &dateFields
)) {
2610 auto overflow
= TemporalOverflow::Constrain
;
2612 if (!ToTemporalOverflow(cx
, maybeOptions
, &overflow
)) {
2618 if (!ISOResolveMonth(cx
, &dateFields
)) {
2624 if (!ISOYearMonthFromFields(cx
, dateFields
, overflow
, &result
)) {
2629 Rooted
<CalendarValue
> calendar(cx
, CalendarValue(cx
->names().iso8601
));
2630 return CreateTemporalYearMonth(cx
, result
, calendar
);
2634 * CalendarYearMonthFromFields ( calendarRec, fields [ , options ] )
2636 static Wrapped
<PlainYearMonthObject
*> CalendarYearMonthFromFields(
2637 JSContext
* cx
, Handle
<CalendarRecord
> calendar
, Handle
<JSObject
*> fields
,
2638 Handle
<PlainObject
*> maybeOptions
) {
2639 MOZ_ASSERT(CalendarMethodsRecordHasLookedUp(
2640 calendar
, CalendarMethod::YearMonthFromFields
));
2642 // Step 1. (Not applicable in our implementation.)
2644 // Step 3. (Reordered)
2645 auto yearMonthFromFields
= calendar
.yearMonthFromFields();
2646 if (!yearMonthFromFields
) {
2647 return BuiltinCalendarYearMonthFromFields(cx
, fields
, maybeOptions
);
2650 // Step 2. (Inlined call to CalendarMethodsRecordCall.)
2652 Rooted
<Value
> yearMonthFromFieldsFn(cx
, ObjectValue(*yearMonthFromFields
));
2653 auto thisv
= calendar
.receiver().toValue();
2654 Rooted
<Value
> rval(cx
);
2656 FixedInvokeArgs
<2> args(cx
);
2657 args
[0].setObject(*fields
);
2659 args
[1].setObject(*maybeOptions
);
2661 args
[1].setUndefined();
2664 if (!Call(cx
, yearMonthFromFieldsFn
, thisv
, args
, &rval
)) {
2669 if (!rval
.isObject() ||
2670 !rval
.toObject().canUnwrapAs
<PlainYearMonthObject
>()) {
2671 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_IGNORE_STACK
, rval
,
2672 nullptr, "not a PlainYearMonth object");
2677 return &rval
.toObject();
2681 * CalendarYearMonthFromFields ( calendarRec, fields [ , options ] )
2683 Wrapped
<PlainYearMonthObject
*> js::temporal::CalendarYearMonthFromFields(
2684 JSContext
* cx
, Handle
<CalendarRecord
> calendar
,
2685 Handle
<PlainObject
*> fields
) {
2687 return ::CalendarYearMonthFromFields(cx
, calendar
, fields
, nullptr);
2691 * CalendarYearMonthFromFields ( calendarRec, fields [ , options ] )
2693 Wrapped
<PlainYearMonthObject
*> js::temporal::CalendarYearMonthFromFields(
2694 JSContext
* cx
, Handle
<CalendarRecord
> calendar
,
2695 Handle
<PlainYearMonthObject
*> fields
) {
2697 return ::CalendarYearMonthFromFields(cx
, calendar
, fields
, nullptr);
2701 * CalendarYearMonthFromFields ( calendarRec, fields [ , options ] )
2703 Wrapped
<PlainYearMonthObject
*> js::temporal::CalendarYearMonthFromFields(
2704 JSContext
* cx
, Handle
<CalendarRecord
> calendar
, Handle
<PlainObject
*> fields
,
2705 Handle
<PlainObject
*> options
) {
2707 return ::CalendarYearMonthFromFields(cx
, calendar
, fields
, options
);
2711 * ISOMonthDayFromFields ( fields, overflow )
2713 static bool ISOMonthDayFromFields(JSContext
* cx
, Handle
<TemporalFields
> fields
,
2714 TemporalOverflow overflow
,
2715 PlainDate
* result
) {
2716 // Steps 1-2. (Not applicable in our implementation.)
2719 double month
= fields
.month();
2722 double day
= fields
.day();
2725 MOZ_ASSERT(!std::isnan(month
));
2726 MOZ_ASSERT(!std::isnan(day
));
2729 double year
= fields
.year();
2732 int32_t referenceISOYear
= 1972;
2735 double y
= std::isnan(year
) ? referenceISOYear
: year
;
2736 RegulatedISODate regulated
;
2737 if (!RegulateISODate(cx
, y
, month
, day
, overflow
, ®ulated
)) {
2742 *result
= {referenceISOYear
, regulated
.month
, regulated
.day
};
2747 * Temporal.Calendar.prototype.monthDayFromFields ( fields [ , options ] )
2749 static PlainMonthDayObject
* BuiltinCalendarMonthDayFromFields(
2750 JSContext
* cx
, Handle
<JSObject
*> fields
, Handle
<JSObject
*> maybeOptions
) {
2751 // Steps 1-5. (Not applicable)
2754 Rooted
<TemporalFields
> dateFields(cx
);
2755 if (!PrepareTemporalFields(cx
, fields
,
2756 {TemporalField::Day
, TemporalField::Month
,
2757 TemporalField::MonthCode
, TemporalField::Year
},
2758 {TemporalField::Day
}, &dateFields
)) {
2763 auto overflow
= TemporalOverflow::Constrain
;
2765 if (!ToTemporalOverflow(cx
, maybeOptions
, &overflow
)) {
2771 if (!ISOResolveMonth(cx
, &dateFields
)) {
2777 if (!ISOMonthDayFromFields(cx
, dateFields
, overflow
, &result
)) {
2782 Rooted
<CalendarValue
> calendar(cx
, CalendarValue(cx
->names().iso8601
));
2783 return CreateTemporalMonthDay(cx
, result
, calendar
);
2787 * CalendarMonthDayFromFields ( calendarRec, fields [ , options ] )
2789 static Wrapped
<PlainMonthDayObject
*> CalendarMonthDayFromFields(
2790 JSContext
* cx
, Handle
<CalendarRecord
> calendar
, Handle
<JSObject
*> fields
,
2791 Handle
<PlainObject
*> maybeOptions
) {
2792 MOZ_ASSERT(CalendarMethodsRecordHasLookedUp(
2793 calendar
, CalendarMethod::MonthDayFromFields
));
2795 // Step 1. (Not applicable in our implementation.)
2797 // Step 3. (Reordered)
2798 auto monthDayFromFields
= calendar
.monthDayFromFields();
2799 if (!monthDayFromFields
) {
2800 return BuiltinCalendarMonthDayFromFields(cx
, fields
, maybeOptions
);
2803 // Step 2. (Inlined call to CalendarMethodsRecordCall.)
2805 Rooted
<Value
> monthDayFromFieldsFn(cx
, ObjectValue(*monthDayFromFields
));
2806 auto thisv
= calendar
.receiver().toValue();
2807 Rooted
<Value
> rval(cx
);
2809 FixedInvokeArgs
<2> args(cx
);
2810 args
[0].setObject(*fields
);
2812 args
[1].setObject(*maybeOptions
);
2814 args
[1].setUndefined();
2817 if (!Call(cx
, monthDayFromFieldsFn
, thisv
, args
, &rval
)) {
2822 if (!rval
.isObject() || !rval
.toObject().canUnwrapAs
<PlainMonthDayObject
>()) {
2823 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_IGNORE_STACK
, rval
,
2824 nullptr, "not a PlainMonthDay object");
2829 return &rval
.toObject();
2833 * CalendarMonthDayFromFields ( calendarRec, fields [ , options ] )
2835 Wrapped
<PlainMonthDayObject
*> js::temporal::CalendarMonthDayFromFields(
2836 JSContext
* cx
, Handle
<CalendarRecord
> calendar
,
2837 Handle
<PlainObject
*> fields
) {
2839 return ::CalendarMonthDayFromFields(cx
, calendar
, fields
, nullptr);
2843 * CalendarMonthDayFromFields ( calendarRec, fields [ , options ] )
2845 Wrapped
<PlainMonthDayObject
*> js::temporal::CalendarMonthDayFromFields(
2846 JSContext
* cx
, Handle
<CalendarRecord
> calendar
,
2847 Handle
<PlainMonthDayObject
*> fields
) {
2849 return ::CalendarMonthDayFromFields(cx
, calendar
, fields
, nullptr);
2853 * CalendarMonthDayFromFields ( calendarRec, fields [ , options ] )
2855 Wrapped
<PlainMonthDayObject
*> js::temporal::CalendarMonthDayFromFields(
2856 JSContext
* cx
, Handle
<CalendarRecord
> calendar
, Handle
<PlainObject
*> fields
,
2857 Handle
<PlainObject
*> options
) {
2859 return ::CalendarMonthDayFromFields(cx
, calendar
, fields
, options
);
2862 using PropertyHashSet
= JS::GCHashSet
<JS::PropertyKey
>;
2863 using PropertyVector
= JS::StackGCVector
<JS::PropertyKey
>;
2866 * ISOFieldKeysToIgnore ( keys )
2868 static bool ISOFieldKeysToIgnore(JSContext
* cx
, const PropertyVector
& keys
,
2869 PropertyHashSet
& ignoredKeys
) {
2870 MOZ_ASSERT(ignoredKeys
.empty(), "expected an empty output hashset");
2872 // Step 1. (Not applicable in our implementation.)
2874 if (!ignoredKeys
.reserve(keys
.length())) {
2879 bool seenMonthOrMonthCode
= false;
2880 for (const auto& key
: keys
) {
2881 // Reorder the substeps in order to use |putNew| instead of |put|, because
2882 // the former is slightly faster.
2885 if (key
.isAtom(cx
->names().month
) || key
.isAtom(cx
->names().monthCode
)) {
2887 if (!seenMonthOrMonthCode
) {
2888 seenMonthOrMonthCode
= true;
2890 // Add both keys at once.
2891 if (!ignoredKeys
.putNew(NameToId(cx
->names().month
)) ||
2892 !ignoredKeys
.putNew(NameToId(cx
->names().monthCode
))) {
2898 if (!ignoredKeys
.putNew(key
)) {
2909 * Temporal.Calendar.prototype.mergeFields ( fields, additionalFields )
2911 static PlainObject
* BuiltinCalendarMergeFields(
2912 JSContext
* cx
, Handle
<JSObject
*> fields
,
2913 Handle
<JSObject
*> additionalFields
) {
2914 // NOTE: This function is only called from CalendarMergeFields and its
2915 // result is always passed to PrepareTemporalFields. PrepareTemporalFields
2916 // sorts the incoming property keys, so it doesn't matter in which order the
2917 // properties are created in this function. This allows to reorder the steps
2918 // to process |additionalFields| first.
2920 // TODO: Consider additionally passing the fieldNames from the succeeding
2921 // PrepareTemporalFields call, so we don't have to process unnecessary keys.
2923 // Steps 1-2. (Not applicable in our implementation.)
2926 Rooted
<PlainObject
*> merged(cx
, NewPlainObjectWithProto(cx
, nullptr));
2931 // Contrary to the spec, add the properties from |additionalFields| first.
2933 // Step 6. (Not applicable in our implementation.)
2937 // PrepareTemporalFields ignores symbol property keys, so we don't need to
2938 // pass JSITER_SYMBOLS. |additionalFields| contains no non-enumerable
2939 // properties, therefore JSITER_HIDDEN isn't needed, too.
2940 JS::RootedVector
<PropertyKey
> keys(cx
);
2941 if (!GetPropertyKeys(cx
, additionalFields
, JSITER_OWNONLY
, &keys
)) {
2945 // Steps 9-11. (Not applicable in our implementation.)
2948 Rooted
<PropertyHashSet
> ignoredKeys(cx
, PropertyHashSet(cx
));
2949 if (!ignoredKeys
.reserve(keys
.length())) {
2953 // Steps 7-8, 12, and 15.
2954 Rooted
<mozilla::Maybe
<PropertyDescriptor
>> desc(cx
);
2955 Rooted
<Value
> propValue(cx
);
2956 bool seenMonthOrMonthCode
= false;
2957 for (size_t i
= 0; i
< keys
.length(); i
++) {
2958 Handle
<PropertyKey
> key
= keys
[i
];
2960 if (!GetOwnPropertyDescriptor(cx
, additionalFields
, key
, &desc
)) {
2964 propValue
.set(desc
->value());
2966 // Skip |undefined| properties per step 8.
2967 if (propValue
.isUndefined()) {
2972 if (!DefineDataProperty(cx
, merged
, key
, propValue
)) {
2976 // Step 12. (Inlined ISOFieldKeysToIgnore)
2977 if (key
.isAtom(cx
->names().month
) || key
.isAtom(cx
->names().monthCode
)) {
2978 // ISOFieldKeysToIgnore, steps 2.b-c.
2979 if (!seenMonthOrMonthCode
) {
2980 seenMonthOrMonthCode
= true;
2982 if (!ignoredKeys
.putNew(NameToId(cx
->names().month
)) ||
2983 !ignoredKeys
.putNew(NameToId(cx
->names().monthCode
))) {
2988 // ISOFieldKeysToIgnore, step 2.a.
2989 if (!ignoredKeys
.putNew(key
)) {
2995 // Now add the properties from |field|.
2997 // Step 3. (Not applicable in our implementation.)
2999 // Reuse |keys| to avoid extra allocations.
3004 // See above why neither JSITER_SYMBOLS nor JSITER_HIDDEN is needed.
3005 if (!GetPropertyKeys(cx
, fields
, JSITER_OWNONLY
, &keys
)) {
3009 // Steps 4-5 and 14.
3010 for (size_t i
= 0; i
< keys
.length(); i
++) {
3011 Handle
<PropertyKey
> key
= keys
[i
];
3013 // Skip ignored keys per step 14.
3014 if (ignoredKeys
.has(key
)) {
3018 if (!GetOwnPropertyDescriptor(cx
, fields
, key
, &desc
)) {
3022 propValue
.set(desc
->value());
3024 // Skip |undefined| properties per step 5.
3025 if (propValue
.isUndefined()) {
3030 if (!DefineDataProperty(cx
, merged
, key
, propValue
)) {
3040 static bool IsPlainDataObject(PlainObject
* obj
) {
3041 for (ShapePropertyIter
<NoGC
> iter(obj
->shape()); !iter
.done(); iter
++) {
3042 if (iter
->flags() != PropertyFlags::defaultDataPropFlags
) {
3051 * CalendarMergeFields ( calendarRec, fields, additionalFields )
3053 JSObject
* js::temporal::CalendarMergeFields(
3054 JSContext
* cx
, Handle
<CalendarRecord
> calendar
, Handle
<PlainObject
*> fields
,
3055 Handle
<PlainObject
*> additionalFields
) {
3057 CalendarMethodsRecordHasLookedUp(calendar
, CalendarMethod::MergeFields
));
3059 MOZ_ASSERT(IsPlainDataObject(fields
));
3060 MOZ_ASSERT(IsPlainDataObject(additionalFields
));
3062 // Step 2. (Reordered)
3063 auto mergeFields
= calendar
.mergeFields();
3065 return BuiltinCalendarMergeFields(cx
, fields
, additionalFields
);
3068 // Step 1. (Inlined call to CalendarMethodsRecordCall.)
3070 Rooted
<Value
> mergeFieldsFn(cx
, ObjectValue(*mergeFields
));
3071 auto thisv
= calendar
.receiver().toValue();
3072 Rooted
<Value
> result(cx
);
3074 FixedInvokeArgs
<2> args(cx
);
3075 args
[0].setObject(*fields
);
3076 args
[1].setObject(*additionalFields
);
3078 if (!Call(cx
, mergeFieldsFn
, thisv
, args
, &result
)) {
3083 return RequireObject(cx
, result
);
3087 * Temporal.Calendar.prototype.dateAdd ( date, duration [ , options ] )
3089 static bool BuiltinCalendarAdd(JSContext
* cx
, const PlainDate
& date
,
3090 const NormalizedDuration
& duration
,
3091 Handle
<JSObject
*> options
, PlainDate
* result
) {
3092 MOZ_ASSERT(IsValidISODate(date
));
3093 MOZ_ASSERT(IsValidDuration(duration
));
3095 // Steps 1-6. (Not applicable)
3098 auto overflow
= TemporalOverflow::Constrain
;
3100 if (!ToTemporalOverflow(cx
, options
, &overflow
)) {
3106 const auto& timeDuration
= duration
.time
;
3109 auto balanceResult
= BalanceTimeDuration(timeDuration
, TemporalUnit::Day
);
3112 auto addDuration
= DateDuration
{
3113 duration
.date
.years
,
3114 duration
.date
.months
,
3115 duration
.date
.weeks
,
3116 duration
.date
.days
+ balanceResult
.days
,
3118 return AddISODate(cx
, date
, addDuration
, overflow
, result
);
3122 * Temporal.Calendar.prototype.dateAdd ( date, duration [ , options ] )
3124 static bool BuiltinCalendarAdd(JSContext
* cx
, const PlainDate
& date
,
3125 const DateDuration
& duration
,
3126 Handle
<JSObject
*> options
, PlainDate
* result
) {
3127 // Steps 1-6. (Not applicable)
3129 // Step 8. (Reordered)
3130 auto normalized
= CreateNormalizedDurationRecord(duration
, {});
3133 return BuiltinCalendarAdd(cx
, date
, normalized
, options
, result
);
3137 * Temporal.Calendar.prototype.dateAdd ( date, duration [ , options ] )
3139 static PlainDateObject
* BuiltinCalendarAdd(JSContext
* cx
, const PlainDate
& date
,
3140 const DateDuration
& duration
,
3141 Handle
<JSObject
*> options
) {
3144 if (!BuiltinCalendarAdd(cx
, date
, duration
, options
, &result
)) {
3149 Rooted
<CalendarValue
> calendar(cx
, CalendarValue(cx
->names().iso8601
));
3150 return CreateTemporalDate(cx
, result
, calendar
);
3154 * Temporal.Calendar.prototype.dateAdd ( date, duration [ , options ] )
3156 static PlainDateObject
* BuiltinCalendarAdd(JSContext
* cx
, const PlainDate
& date
,
3157 const Duration
& duration
,
3158 Handle
<JSObject
*> options
) {
3159 // Steps 1-6. (Not applicable)
3161 // Step 8. (Reordered)
3162 auto normalized
= CreateNormalizedDurationRecord(duration
);
3166 if (!BuiltinCalendarAdd(cx
, date
, normalized
, options
, &result
)) {
3171 Rooted
<CalendarValue
> calendar(cx
, CalendarValue(cx
->names().iso8601
));
3172 return CreateTemporalDate(cx
, result
, calendar
);
3176 * CalendarDateAdd ( calendarRec, date, duration [ , options ] )
3178 static Wrapped
<PlainDateObject
*> CalendarDateAddSlow(
3179 JSContext
* cx
, Handle
<CalendarRecord
> calendar
,
3180 Handle
<Wrapped
<PlainDateObject
*>> date
,
3181 Handle
<Wrapped
<DurationObject
*>> duration
, Handle
<JSObject
*> options
) {
3183 CalendarMethodsRecordHasLookedUp(calendar
, CalendarMethod::DateAdd
));
3184 MOZ_ASSERT(calendar
.receiver().isObject());
3185 MOZ_ASSERT(calendar
.dateAdd());
3187 // Step 1. (Not applicable).
3189 // Step 2. (Inlined call to CalendarMethodsRecordCall.)
3190 Rooted
<JS::Value
> dateAdd(cx
, ObjectValue(*calendar
.dateAdd()));
3191 auto thisv
= calendar
.receiver().toValue();
3192 Rooted
<Value
> rval(cx
);
3194 FixedInvokeArgs
<3> args(cx
);
3195 args
[0].setObject(*date
);
3196 args
[1].setObject(*duration
);
3198 args
[2].setObject(*options
);
3200 args
[2].setUndefined();
3203 if (!Call(cx
, dateAdd
, thisv
, args
, &rval
)) {
3207 // Step 3. (Not applicable)
3208 MOZ_ASSERT(!CalendarMethodsRecordIsBuiltin(calendar
));
3211 if (!rval
.isObject() || !rval
.toObject().canUnwrapAs
<PlainDateObject
>()) {
3212 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_IGNORE_STACK
, rval
,
3213 nullptr, "not a PlainDate object");
3218 return &rval
.toObject();
3222 * CalendarDateAdd ( calendarRec, date, duration [ , options ] )
3224 static Wrapped
<PlainDateObject
*> CalendarDateAdd(
3225 JSContext
* cx
, Handle
<CalendarRecord
> calendar
,
3226 Handle
<Wrapped
<PlainDateObject
*>> date
, const Duration
& duration
,
3227 Handle
<JSObject
*> options
) {
3229 CalendarMethodsRecordHasLookedUp(calendar
, CalendarMethod::DateAdd
));
3231 // Step 1. (Not applicable).
3233 // Step 3. (Reordered)
3234 if (!calendar
.dateAdd()) {
3235 auto* unwrappedDate
= date
.unwrap(cx
);
3236 if (!unwrappedDate
) {
3239 auto date
= ToPlainDate(unwrappedDate
);
3241 return BuiltinCalendarAdd(cx
, date
, duration
, options
);
3245 Rooted
<DurationObject
*> durationObj(cx
, CreateTemporalDuration(cx
, duration
));
3249 return CalendarDateAddSlow(cx
, calendar
, date
, durationObj
, options
);
3253 * CalendarDateAdd ( calendarRec, date, duration [ , options ] )
3255 static Wrapped
<PlainDateObject
*> CalendarDateAdd(
3256 JSContext
* cx
, Handle
<CalendarRecord
> calendar
,
3257 Handle
<Wrapped
<PlainDateObject
*>> date
, const DateDuration
& duration
,
3258 Handle
<JSObject
*> options
) {
3260 CalendarMethodsRecordHasLookedUp(calendar
, CalendarMethod::DateAdd
));
3262 // Step 1. (Not applicable).
3264 // Step 3. (Reordered)
3265 if (!calendar
.dateAdd()) {
3266 auto* unwrappedDate
= date
.unwrap(cx
);
3267 if (!unwrappedDate
) {
3270 auto date
= ToPlainDate(unwrappedDate
);
3272 return BuiltinCalendarAdd(cx
, date
, duration
, options
);
3276 Rooted
<DurationObject
*> durationObj(
3277 cx
, CreateTemporalDuration(cx
, duration
.toDuration()));
3281 return CalendarDateAddSlow(cx
, calendar
, date
, durationObj
, options
);
3285 * CalendarDateAdd ( calendarRec, date, duration [ , options ] )
3287 static Wrapped
<PlainDateObject
*> CalendarDateAdd(
3288 JSContext
* cx
, Handle
<CalendarRecord
> calendar
,
3289 Handle
<Wrapped
<PlainDateObject
*>> date
,
3290 Handle
<Wrapped
<DurationObject
*>> duration
, Handle
<JSObject
*> options
) {
3292 CalendarMethodsRecordHasLookedUp(calendar
, CalendarMethod::DateAdd
));
3294 // Step 1. (Not applicable).
3296 // Step 3. (Reordered)
3297 if (!calendar
.dateAdd()) {
3298 auto* unwrappedDate
= date
.unwrap(cx
);
3299 if (!unwrappedDate
) {
3302 auto date
= ToPlainDate(unwrappedDate
);
3304 auto* unwrappedDuration
= duration
.unwrap(cx
);
3305 if (!unwrappedDuration
) {
3308 auto duration
= ToDuration(unwrappedDuration
);
3310 return BuiltinCalendarAdd(cx
, date
, duration
, options
);
3314 return CalendarDateAddSlow(cx
, calendar
, date
, duration
, options
);
3318 * CalendarDateAdd ( calendarRec, date, duration [ , options ] )
3320 static bool CalendarDateAdd(JSContext
* cx
, Handle
<CalendarRecord
> calendar
,
3321 Handle
<Wrapped
<PlainDateObject
*>> date
,
3322 const DateDuration
& duration
,
3323 Handle
<JSObject
*> options
, PlainDate
* result
) {
3325 CalendarMethodsRecordHasLookedUp(calendar
, CalendarMethod::DateAdd
));
3327 // Step 1. (Not applicable).
3329 // Step 3. (Reordered)
3330 if (!calendar
.dateAdd()) {
3331 auto* unwrappedDate
= date
.unwrap(cx
);
3332 if (!unwrappedDate
) {
3335 auto date
= ToPlainDate(unwrappedDate
);
3337 return BuiltinCalendarAdd(cx
, date
, duration
, options
, result
);
3342 Rooted
<DurationObject
*> durationObj(
3343 cx
, CreateTemporalDuration(cx
, duration
.toDuration()));
3348 auto obj
= CalendarDateAddSlow(cx
, calendar
, date
, durationObj
, options
);
3353 *result
= ToPlainDate(&obj
.unwrap());
3358 * CalendarDateAdd ( calendarRec, date, duration [ , options ] )
3360 static bool CalendarDateAdd(JSContext
* cx
, Handle
<CalendarRecord
> calendar
,
3361 const PlainDate
& date
, const DateDuration
& duration
,
3362 Handle
<JSObject
*> options
, PlainDate
* result
) {
3364 CalendarMethodsRecordHasLookedUp(calendar
, CalendarMethod::DateAdd
));
3366 // Step 1. (Not applicable).
3368 // Step 3. (Reordered)
3369 if (!calendar
.dateAdd()) {
3370 return BuiltinCalendarAdd(cx
, date
, duration
, options
, result
);
3375 Rooted
<PlainDateObject
*> dateObj(
3376 cx
, CreateTemporalDate(cx
, date
, calendar
.receiver()));
3381 Rooted
<DurationObject
*> durationObj(
3382 cx
, CreateTemporalDuration(cx
, duration
.toDuration()));
3387 auto obj
= CalendarDateAddSlow(cx
, calendar
, dateObj
, durationObj
, options
);
3392 *result
= ToPlainDate(&obj
.unwrap());
3397 * CalendarDateAdd ( calendarRec, date, duration [ , options ] )
3399 Wrapped
<PlainDateObject
*> js::temporal::CalendarDateAdd(
3400 JSContext
* cx
, Handle
<CalendarRecord
> calendar
,
3401 Handle
<Wrapped
<PlainDateObject
*>> date
, const Duration
& duration
,
3402 Handle
<JSObject
*> options
) {
3403 // Step 1. (Not applicable).
3406 return ::CalendarDateAdd(cx
, calendar
, date
, duration
, options
);
3410 * CalendarDateAdd ( calendarRec, date, duration [ , options ] )
3412 Wrapped
<PlainDateObject
*> js::temporal::CalendarDateAdd(
3413 JSContext
* cx
, Handle
<CalendarRecord
> calendar
,
3414 Handle
<Wrapped
<PlainDateObject
*>> date
, const DateDuration
& duration
) {
3416 Handle
<JSObject
*> options
= nullptr;
3419 return ::CalendarDateAdd(cx
, calendar
, date
, duration
, options
);
3423 * CalendarDateAdd ( calendarRec, date, duration [ , options ] )
3425 Wrapped
<PlainDateObject
*> js::temporal::CalendarDateAdd(
3426 JSContext
* cx
, Handle
<CalendarRecord
> calendar
,
3427 Handle
<Wrapped
<PlainDateObject
*>> date
,
3428 Handle
<Wrapped
<DurationObject
*>> duration
) {
3430 Handle
<JSObject
*> options
= nullptr;
3433 return ::CalendarDateAdd(cx
, calendar
, date
, duration
, options
);
3437 * CalendarDateAdd ( calendarRec, date, duration [ , options ] )
3439 Wrapped
<PlainDateObject
*> js::temporal::CalendarDateAdd(
3440 JSContext
* cx
, Handle
<CalendarRecord
> calendar
,
3441 Handle
<Wrapped
<PlainDateObject
*>> date
,
3442 Handle
<Wrapped
<DurationObject
*>> duration
, Handle
<JSObject
*> options
) {
3443 // Step 1. (Not applicable).
3446 return ::CalendarDateAdd(cx
, calendar
, date
, duration
, options
);
3450 * CalendarDateAdd ( calendarRec, date, duration [ , options ] )
3452 bool js::temporal::CalendarDateAdd(JSContext
* cx
,
3453 Handle
<CalendarRecord
> calendar
,
3454 const PlainDate
& date
,
3455 const DateDuration
& duration
,
3456 PlainDate
* result
) {
3458 Handle
<JSObject
*> options
= nullptr;
3461 return ::CalendarDateAdd(cx
, calendar
, date
, duration
, options
, result
);
3465 * CalendarDateAdd ( calendarRec, date, duration [ , options ] )
3467 bool js::temporal::CalendarDateAdd(JSContext
* cx
,
3468 Handle
<CalendarRecord
> calendar
,
3469 const PlainDate
& date
,
3470 const DateDuration
& duration
,
3471 Handle
<JSObject
*> options
,
3472 PlainDate
* result
) {
3473 // Step 1. (Not applicable)
3476 return ::CalendarDateAdd(cx
, calendar
, date
, duration
, options
, result
);
3480 * CalendarDateAdd ( calendarRec, date, duration [ , options ] )
3482 bool js::temporal::CalendarDateAdd(JSContext
* cx
,
3483 Handle
<CalendarRecord
> calendar
,
3484 Handle
<Wrapped
<PlainDateObject
*>> date
,
3485 const DateDuration
& duration
,
3486 PlainDate
* result
) {
3488 Handle
<JSObject
*> options
= nullptr;
3491 return ::CalendarDateAdd(cx
, calendar
, date
, duration
, options
, result
);
3495 * Temporal.Calendar.prototype.dateUntil ( one, two [ , options ] )
3497 static Duration
BuiltinCalendarDateUntil(const PlainDate
& one
,
3498 const PlainDate
& two
,
3499 TemporalUnit largestUnit
) {
3500 // Steps 1-3. (Not applicable)
3502 // Steps 4-8. (Not applicable)
3505 auto difference
= DifferenceISODate(one
, two
, largestUnit
);
3508 return difference
.toDuration();
3512 * Temporal.Calendar.prototype.dateUntil ( one, two [ , options ] )
3514 static bool BuiltinCalendarDateUntil(JSContext
* cx
,
3515 Handle
<Wrapped
<PlainDateObject
*>> one
,
3516 Handle
<Wrapped
<PlainDateObject
*>> two
,
3517 TemporalUnit largestUnit
,
3519 MOZ_ASSERT(largestUnit
<= TemporalUnit::Day
);
3521 auto* unwrappedOne
= one
.unwrap(cx
);
3522 if (!unwrappedOne
) {
3525 auto dateOne
= ToPlainDate(unwrappedOne
);
3527 auto* unwrappedTwo
= two
.unwrap(cx
);
3528 if (!unwrappedTwo
) {
3531 auto dateTwo
= ToPlainDate(unwrappedTwo
);
3534 *result
= BuiltinCalendarDateUntil(dateOne
, dateTwo
, largestUnit
);
3538 static bool CalendarDateUntilSlow(JSContext
* cx
,
3539 Handle
<CalendarRecord
> calendar
,
3540 Handle
<Wrapped
<PlainDateObject
*>> one
,
3541 Handle
<Wrapped
<PlainDateObject
*>> two
,
3542 Handle
<JSObject
*> options
, Duration
* result
) {
3544 CalendarMethodsRecordHasLookedUp(calendar
, CalendarMethod::DateUntil
));
3545 MOZ_ASSERT(calendar
.receiver().isObject());
3546 MOZ_ASSERT(calendar
.dateUntil());
3548 // Step 1. (Inlined call to CalendarMethodsRecordCall.)
3549 Rooted
<JS::Value
> dateUntil(cx
, ObjectValue(*calendar
.dateUntil()));
3550 auto thisv
= calendar
.receiver().toValue();
3551 Rooted
<Value
> rval(cx
);
3553 FixedInvokeArgs
<3> args(cx
);
3554 args
[0].setObject(*one
);
3555 args
[1].setObject(*two
);
3556 args
[2].setObject(*options
);
3558 if (!Call(cx
, dateUntil
, thisv
, args
, &rval
)) {
3562 // Step 2. (Not applicable)
3563 MOZ_ASSERT(!CalendarMethodsRecordIsBuiltin(calendar
));
3566 if (!rval
.isObject() || !rval
.toObject().canUnwrapAs
<DurationObject
>()) {
3567 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_IGNORE_STACK
, rval
,
3568 nullptr, "not a Duration object");
3573 *result
= ToDuration(&rval
.toObject().unwrapAs
<DurationObject
>());
3578 * CalendarDateUntil ( calendarRec, one, two, options )
3580 bool js::temporal::CalendarDateUntil(JSContext
* cx
,
3581 Handle
<CalendarRecord
> calendar
,
3582 Handle
<Wrapped
<PlainDateObject
*>> one
,
3583 Handle
<Wrapped
<PlainDateObject
*>> two
,
3584 TemporalUnit largestUnit
,
3585 Handle
<PlainObject
*> options
,
3588 CalendarMethodsRecordHasLookedUp(calendar
, CalendarMethod::DateUntil
));
3590 // As an optimization, our implementation only adds |largestUnit| to the
3591 // options object when taking the slow-path.
3593 // The object must be extensible, otherwise we'd need to throw an error when
3594 // attempting to add the "largestUnit" property to a non-extensible object.
3595 MOZ_ASSERT(options
->isExtensible());
3597 // Similarily, if there's an existing "largestUnit" property, this property
3598 // must be configurable.
3599 auto largestUnitProp
= options
->lookupPure(cx
->names().largestUnit
);
3600 MOZ_ASSERT_IF(largestUnitProp
, largestUnitProp
->configurable());
3603 // Step 2. (Reordered)
3604 if (!calendar
.dateUntil()) {
3605 return BuiltinCalendarDateUntil(cx
, one
, two
, largestUnit
, result
);
3608 Rooted
<Value
> value(cx
, StringValue(TemporalUnitToString(cx
, largestUnit
)));
3609 if (!DefineDataProperty(cx
, options
, cx
->names().largestUnit
, value
)) {
3614 return CalendarDateUntilSlow(cx
, calendar
, one
, two
, options
, result
);
3618 * CalendarDateUntil ( calendarRec, one, two, options )
3620 bool js::temporal::CalendarDateUntil(JSContext
* cx
,
3621 Handle
<CalendarRecord
> calendar
,
3622 Handle
<Wrapped
<PlainDateObject
*>> one
,
3623 Handle
<Wrapped
<PlainDateObject
*>> two
,
3624 TemporalUnit largestUnit
,
3627 CalendarMethodsRecordHasLookedUp(calendar
, CalendarMethod::DateUntil
));
3628 MOZ_ASSERT(largestUnit
<= TemporalUnit::Day
);
3630 // Step 2. (Reordered)
3631 if (!calendar
.dateUntil()) {
3632 return BuiltinCalendarDateUntil(cx
, one
, two
, largestUnit
, result
);
3635 Rooted
<PlainObject
*> untilOptions(cx
, NewPlainObjectWithProto(cx
, nullptr));
3636 if (!untilOptions
) {
3640 Rooted
<Value
> value(cx
, StringValue(TemporalUnitToString(cx
, largestUnit
)));
3641 if (!DefineDataProperty(cx
, untilOptions
, cx
->names().largestUnit
, value
)) {
3646 return CalendarDateUntilSlow(cx
, calendar
, one
, two
, untilOptions
, result
);
3650 * CalendarEquals ( one, two )
3652 bool js::temporal::CalendarEquals(JSContext
* cx
, Handle
<CalendarValue
> one
,
3653 Handle
<CalendarValue
> two
, bool* equals
) {
3655 if (one
.isObject() && two
.isObject() && one
.toObject() == two
.toObject()) {
3661 Rooted
<JSString
*> calendarOne(cx
, ToTemporalCalendarIdentifier(cx
, one
));
3667 JSString
* calendarTwo
= ToTemporalCalendarIdentifier(cx
, two
);
3673 return EqualStrings(cx
, calendarOne
, calendarTwo
, equals
);
3677 * CalendarEquals ( one, two )
3679 bool js::temporal::CalendarEqualsOrThrow(JSContext
* cx
,
3680 Handle
<CalendarValue
> one
,
3681 Handle
<CalendarValue
> two
) {
3683 if (one
.isObject() && two
.isObject() && one
.toObject() == two
.toObject()) {
3688 Rooted
<JSString
*> calendarOne(cx
, ToTemporalCalendarIdentifier(cx
, one
));
3694 JSString
* calendarTwo
= ToTemporalCalendarIdentifier(cx
, two
);
3701 if (!EqualStrings(cx
, calendarOne
, calendarTwo
, &equals
)) {
3708 // Throw an error when the calendar identifiers don't match. Used when unequal
3709 // calendars throw a RangeError.
3710 if (auto charsOne
= QuoteString(cx
, calendarOne
)) {
3711 if (auto charsTwo
= QuoteString(cx
, calendarTwo
)) {
3712 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
3713 JSMSG_TEMPORAL_CALENDAR_INCOMPATIBLE
,
3714 charsOne
.get(), charsTwo
.get());
3721 * ConsolidateCalendars ( one, two )
3723 bool js::temporal::ConsolidateCalendars(JSContext
* cx
,
3724 Handle
<CalendarValue
> one
,
3725 Handle
<CalendarValue
> two
,
3726 MutableHandle
<CalendarValue
> result
) {
3728 if (one
.isObject() && two
.isObject() && one
.toObject() == two
.toObject()) {
3734 Rooted
<JSString
*> calendarOne(cx
, ToTemporalCalendarIdentifier(cx
, one
));
3740 Rooted
<JSString
*> calendarTwo(cx
, ToTemporalCalendarIdentifier(cx
, two
));
3747 if (!EqualStrings(cx
, calendarOne
, calendarTwo
, &equals
)) {
3756 bool isoCalendarOne
;
3757 if (!IsISO8601Calendar(cx
, calendarOne
, &isoCalendarOne
)) {
3760 if (isoCalendarOne
) {
3766 bool isoCalendarTwo
;
3767 if (!IsISO8601Calendar(cx
, calendarTwo
, &isoCalendarTwo
)) {
3770 if (isoCalendarTwo
) {
3776 if (auto charsOne
= QuoteString(cx
, calendarOne
)) {
3777 if (auto charsTwo
= QuoteString(cx
, calendarTwo
)) {
3778 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
3779 JSMSG_TEMPORAL_CALENDAR_INCOMPATIBLE
,
3780 charsOne
.get(), charsTwo
.get());
3787 * Temporal.Calendar ( id )
3789 static bool CalendarConstructor(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3790 CallArgs args
= CallArgsFromVp(argc
, vp
);
3793 if (!ThrowIfNotConstructing(cx
, args
, "Temporal.Calendar")) {
3798 if (!args
.requireAtLeast(cx
, "Temporal.Calendar", 1)) {
3802 if (!args
[0].isString()) {
3803 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_SEARCH_STACK
, args
[0],
3804 nullptr, "not a string");
3808 Rooted
<JSLinearString
*> identifier(cx
, args
[0].toString()->ensureLinear(cx
));
3814 identifier
= ThrowIfNotBuiltinCalendar(cx
, identifier
);
3820 auto* calendar
= CreateTemporalCalendar(cx
, args
, identifier
);
3825 args
.rval().setObject(*calendar
);
3830 * Temporal.Calendar.from ( item )
3832 static bool Calendar_from(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3833 CallArgs args
= CallArgsFromVp(argc
, vp
);
3836 Rooted
<CalendarValue
> calendar(cx
);
3837 if (!ToTemporalCalendar(cx
, args
.get(0), &calendar
)) {
3842 auto* obj
= ToTemporalCalendarObject(cx
, calendar
);
3847 args
.rval().setObject(*obj
);
3852 * get Temporal.Calendar.prototype.id
3854 static bool Calendar_id(JSContext
* cx
, const CallArgs
& args
) {
3855 auto* calendar
= &args
.thisv().toObject().as
<CalendarObject
>();
3858 args
.rval().setString(calendar
->identifier());
3863 * get Temporal.Calendar.prototype.id
3865 static bool Calendar_id(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3867 CallArgs args
= CallArgsFromVp(argc
, vp
);
3868 return CallNonGenericMethod
<IsCalendar
, Calendar_id
>(cx
, args
);
3872 * Temporal.Calendar.prototype.dateFromFields ( fields [ , options ] )
3874 static bool Calendar_dateFromFields(JSContext
* cx
, const CallArgs
& args
) {
3876 MOZ_ASSERT(IsISO8601Calendar(&args
.thisv().toObject().as
<CalendarObject
>()));
3879 Rooted
<JSObject
*> fields(
3880 cx
, RequireObjectArg(cx
, "fields", "dateFromFields", args
.get(0)));
3886 Rooted
<JSObject
*> options(cx
);
3887 if (args
.hasDefined(1)) {
3888 options
= RequireObjectArg(cx
, "options", "dateFromFields", args
[1]);
3895 auto* obj
= BuiltinCalendarDateFromFields(cx
, fields
, options
);
3900 args
.rval().setObject(*obj
);
3905 * Temporal.Calendar.prototype.dateFromFields ( fields [ , options ] )
3907 static bool Calendar_dateFromFields(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3909 CallArgs args
= CallArgsFromVp(argc
, vp
);
3910 return CallNonGenericMethod
<IsCalendar
, Calendar_dateFromFields
>(cx
, args
);
3914 * Temporal.Calendar.prototype.yearMonthFromFields ( fields [ , options ] )
3916 static bool Calendar_yearMonthFromFields(JSContext
* cx
, const CallArgs
& args
) {
3918 MOZ_ASSERT(IsISO8601Calendar(&args
.thisv().toObject().as
<CalendarObject
>()));
3921 Rooted
<JSObject
*> fields(
3922 cx
, RequireObjectArg(cx
, "fields", "yearMonthFromFields", args
.get(0)));
3928 Rooted
<JSObject
*> options(cx
);
3929 if (args
.hasDefined(1)) {
3930 options
= RequireObjectArg(cx
, "options", "yearMonthFromFields", args
[1]);
3937 auto* obj
= BuiltinCalendarYearMonthFromFields(cx
, fields
, options
);
3942 args
.rval().setObject(*obj
);
3947 * Temporal.Calendar.prototype.yearMonthFromFields ( fields [ , options ] )
3949 static bool Calendar_yearMonthFromFields(JSContext
* cx
, unsigned argc
,
3952 CallArgs args
= CallArgsFromVp(argc
, vp
);
3953 return CallNonGenericMethod
<IsCalendar
, Calendar_yearMonthFromFields
>(cx
,
3958 * Temporal.Calendar.prototype.monthDayFromFields ( fields [ , options ] )
3960 static bool Calendar_monthDayFromFields(JSContext
* cx
, const CallArgs
& args
) {
3962 MOZ_ASSERT(IsISO8601Calendar(&args
.thisv().toObject().as
<CalendarObject
>()));
3965 Rooted
<JSObject
*> fields(
3966 cx
, RequireObjectArg(cx
, "fields", "monthDayFromFields", args
.get(0)));
3972 Rooted
<JSObject
*> options(cx
);
3973 if (args
.hasDefined(1)) {
3974 options
= RequireObjectArg(cx
, "options", "monthDayFromFields", args
[1]);
3981 auto* obj
= BuiltinCalendarMonthDayFromFields(cx
, fields
, options
);
3986 args
.rval().setObject(*obj
);
3991 * Temporal.Calendar.prototype.monthDayFromFields ( fields [ , options ] )
3993 static bool Calendar_monthDayFromFields(JSContext
* cx
, unsigned argc
,
3996 CallArgs args
= CallArgsFromVp(argc
, vp
);
3997 return CallNonGenericMethod
<IsCalendar
, Calendar_monthDayFromFields
>(cx
,
4002 * Temporal.Calendar.prototype.dateAdd ( date, duration [ , options ] )
4004 static bool Calendar_dateAdd(JSContext
* cx
, const CallArgs
& args
) {
4006 MOZ_ASSERT(IsISO8601Calendar(&args
.thisv().toObject().as
<CalendarObject
>()));
4010 if (!ToTemporalDate(cx
, args
.get(0), &date
)) {
4016 if (!ToTemporalDuration(cx
, args
.get(1), &duration
)) {
4021 Rooted
<JSObject
*> options(cx
);
4022 if (args
.hasDefined(2)) {
4023 options
= RequireObjectArg(cx
, "options", "dateAdd", args
[2]);
4030 auto* obj
= BuiltinCalendarAdd(cx
, date
, duration
, options
);
4035 args
.rval().setObject(*obj
);
4040 * Temporal.Calendar.prototype.dateAdd ( date, duration [ , options ] )
4042 static bool Calendar_dateAdd(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4044 CallArgs args
= CallArgsFromVp(argc
, vp
);
4045 return CallNonGenericMethod
<IsCalendar
, Calendar_dateAdd
>(cx
, args
);
4049 * Temporal.Calendar.prototype.dateUntil ( one, two [ , options ] )
4051 static bool Calendar_dateUntil(JSContext
* cx
, const CallArgs
& args
) {
4053 MOZ_ASSERT(IsISO8601Calendar(&args
.thisv().toObject().as
<CalendarObject
>()));
4057 if (!ToTemporalDate(cx
, args
.get(0), &one
)) {
4063 if (!ToTemporalDate(cx
, args
.get(1), &two
)) {
4068 auto largestUnit
= TemporalUnit::Day
;
4069 if (args
.hasDefined(2)) {
4070 Rooted
<JSObject
*> options(
4071 cx
, RequireObjectArg(cx
, "options", "dateUntil", args
[2]));
4077 if (!GetTemporalUnit(cx
, options
, TemporalUnitKey::LargestUnit
,
4078 TemporalUnitGroup::Date
, &largestUnit
)) {
4084 auto duration
= BuiltinCalendarDateUntil(one
, two
, largestUnit
);
4086 auto* obj
= CreateTemporalDuration(cx
, duration
);
4091 args
.rval().setObject(*obj
);
4096 * Temporal.Calendar.prototype.dateUntil ( one, two [ , options ] )
4098 static bool Calendar_dateUntil(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4100 CallArgs args
= CallArgsFromVp(argc
, vp
);
4101 return CallNonGenericMethod
<IsCalendar
, Calendar_dateUntil
>(cx
, args
);
4105 * Temporal.Calendar.prototype.year ( temporalDateLike )
4107 static bool Calendar_year(JSContext
* cx
, const CallArgs
& args
) {
4109 MOZ_ASSERT(IsISO8601Calendar(&args
.thisv().toObject().as
<CalendarObject
>()));
4113 if (!ToPlainDate
<PlainDateObject
, PlainDateTimeObject
, PlainYearMonthObject
>(
4114 cx
, args
.get(0), &date
)) {
4119 return BuiltinCalendarYear(cx
, date
, args
.rval());
4123 * Temporal.Calendar.prototype.year ( temporalDateLike )
4125 static bool Calendar_year(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4127 CallArgs args
= CallArgsFromVp(argc
, vp
);
4128 return CallNonGenericMethod
<IsCalendar
, Calendar_year
>(cx
, args
);
4132 * Temporal.Calendar.prototype.month ( temporalDateLike )
4134 static bool Calendar_month(JSContext
* cx
, const CallArgs
& args
) {
4136 MOZ_ASSERT(IsISO8601Calendar(&args
.thisv().toObject().as
<CalendarObject
>()));
4138 Handle
<Value
> temporalDateLike
= args
.get(0);
4141 if (temporalDateLike
.isObject() &&
4142 temporalDateLike
.toObject().canUnwrapAs
<PlainMonthDayObject
>()) {
4143 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_SEARCH_STACK
,
4144 temporalDateLike
, nullptr, "a PlainMonthDay object");
4150 if (!ToPlainDate
<PlainDateObject
, PlainDateTimeObject
, PlainYearMonthObject
>(
4151 cx
, temporalDateLike
, &date
)) {
4156 return BuiltinCalendarMonth(cx
, date
, args
.rval());
4160 * Temporal.Calendar.prototype.month ( temporalDateLike )
4162 static bool Calendar_month(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4164 CallArgs args
= CallArgsFromVp(argc
, vp
);
4165 return CallNonGenericMethod
<IsCalendar
, Calendar_month
>(cx
, args
);
4169 * Temporal.Calendar.prototype.monthCode ( temporalDateLike )
4171 static bool Calendar_monthCode(JSContext
* cx
, const CallArgs
& args
) {
4173 MOZ_ASSERT(IsISO8601Calendar(&args
.thisv().toObject().as
<CalendarObject
>()));
4177 if (!ToPlainDate
<PlainDateObject
, PlainDateTimeObject
, PlainMonthDayObject
,
4178 PlainYearMonthObject
>(cx
, args
.get(0), &date
)) {
4183 return BuiltinCalendarMonthCode(cx
, date
, args
.rval());
4187 * Temporal.Calendar.prototype.monthCode ( temporalDateLike )
4189 static bool Calendar_monthCode(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4191 CallArgs args
= CallArgsFromVp(argc
, vp
);
4192 return CallNonGenericMethod
<IsCalendar
, Calendar_monthCode
>(cx
, args
);
4196 * Temporal.Calendar.prototype.day ( temporalDateLike )
4198 static bool Calendar_day(JSContext
* cx
, const CallArgs
& args
) {
4200 MOZ_ASSERT(IsISO8601Calendar(&args
.thisv().toObject().as
<CalendarObject
>()));
4204 if (!ToPlainDate
<PlainDateObject
, PlainDateTimeObject
, PlainMonthDayObject
>(
4205 cx
, args
.get(0), &date
)) {
4210 return BuiltinCalendarDay(date
, args
.rval());
4214 * Temporal.Calendar.prototype.day ( temporalDateLike )
4216 static bool Calendar_day(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4218 CallArgs args
= CallArgsFromVp(argc
, vp
);
4219 return CallNonGenericMethod
<IsCalendar
, Calendar_day
>(cx
, args
);
4223 * Temporal.Calendar.prototype.dayOfWeek ( temporalDateLike )
4225 static bool Calendar_dayOfWeek(JSContext
* cx
, const CallArgs
& args
) {
4227 MOZ_ASSERT(IsISO8601Calendar(&args
.thisv().toObject().as
<CalendarObject
>()));
4231 if (!ToTemporalDate(cx
, args
.get(0), &date
)) {
4236 return BuiltinCalendarDayOfWeek(cx
, date
, args
.rval());
4240 * Temporal.Calendar.prototype.dayOfWeek ( temporalDateLike )
4242 static bool Calendar_dayOfWeek(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4244 CallArgs args
= CallArgsFromVp(argc
, vp
);
4245 return CallNonGenericMethod
<IsCalendar
, Calendar_dayOfWeek
>(cx
, args
);
4249 * Temporal.Calendar.prototype.dayOfYear ( temporalDateLike )
4251 static bool Calendar_dayOfYear(JSContext
* cx
, const CallArgs
& args
) {
4253 MOZ_ASSERT(IsISO8601Calendar(&args
.thisv().toObject().as
<CalendarObject
>()));
4257 if (!ToTemporalDate(cx
, args
.get(0), &date
)) {
4262 return BuiltinCalendarDayOfYear(cx
, date
, args
.rval());
4266 * Temporal.Calendar.prototype.dayOfYear ( temporalDateLike )
4268 static bool Calendar_dayOfYear(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4270 CallArgs args
= CallArgsFromVp(argc
, vp
);
4271 return CallNonGenericMethod
<IsCalendar
, Calendar_dayOfYear
>(cx
, args
);
4275 * Temporal.Calendar.prototype.weekOfYear ( temporalDateLike )
4277 static bool Calendar_weekOfYear(JSContext
* cx
, const CallArgs
& args
) {
4279 MOZ_ASSERT(IsISO8601Calendar(&args
.thisv().toObject().as
<CalendarObject
>()));
4283 if (!ToTemporalDate(cx
, args
.get(0), &date
)) {
4288 return BuiltinCalendarWeekOfYear(cx
, date
, args
.rval());
4292 * Temporal.Calendar.prototype.weekOfYear ( temporalDateLike )
4294 static bool Calendar_weekOfYear(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4296 CallArgs args
= CallArgsFromVp(argc
, vp
);
4297 return CallNonGenericMethod
<IsCalendar
, Calendar_weekOfYear
>(cx
, args
);
4301 * Temporal.Calendar.prototype.yearOfWeek ( temporalDateLike )
4303 static bool Calendar_yearOfWeek(JSContext
* cx
, const CallArgs
& args
) {
4305 MOZ_ASSERT(IsISO8601Calendar(&args
.thisv().toObject().as
<CalendarObject
>()));
4309 if (!ToTemporalDate(cx
, args
.get(0), &date
)) {
4314 return BuiltinCalendarYearOfWeek(cx
, date
, args
.rval());
4318 * Temporal.Calendar.prototype.yearOfWeek ( temporalDateLike )
4320 static bool Calendar_yearOfWeek(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4322 CallArgs args
= CallArgsFromVp(argc
, vp
);
4323 return CallNonGenericMethod
<IsCalendar
, Calendar_yearOfWeek
>(cx
, args
);
4327 * Temporal.Calendar.prototype.daysInWeek ( temporalDateLike )
4329 static bool Calendar_daysInWeek(JSContext
* cx
, const CallArgs
& args
) {
4331 MOZ_ASSERT(IsISO8601Calendar(&args
.thisv().toObject().as
<CalendarObject
>()));
4335 if (!ToTemporalDate(cx
, args
.get(0), &date
)) {
4340 return BuiltinCalendarDaysInWeek(cx
, date
, args
.rval());
4344 * Temporal.Calendar.prototype.daysInWeek ( temporalDateLike )
4346 static bool Calendar_daysInWeek(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4348 CallArgs args
= CallArgsFromVp(argc
, vp
);
4349 return CallNonGenericMethod
<IsCalendar
, Calendar_daysInWeek
>(cx
, args
);
4353 * Temporal.Calendar.prototype.daysInMonth ( temporalDateLike )
4355 static bool Calendar_daysInMonth(JSContext
* cx
, const CallArgs
& args
) {
4357 MOZ_ASSERT(IsISO8601Calendar(&args
.thisv().toObject().as
<CalendarObject
>()));
4361 if (!ToPlainDate
<PlainDateObject
, PlainDateTimeObject
, PlainYearMonthObject
>(
4362 cx
, args
.get(0), &date
)) {
4367 return BuiltinCalendarDaysInMonth(cx
, date
, args
.rval());
4371 * Temporal.Calendar.prototype.daysInMonth ( temporalDateLike )
4373 static bool Calendar_daysInMonth(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4375 CallArgs args
= CallArgsFromVp(argc
, vp
);
4376 return CallNonGenericMethod
<IsCalendar
, Calendar_daysInMonth
>(cx
, args
);
4380 * Temporal.Calendar.prototype.daysInYear ( temporalDateLike )
4382 static bool Calendar_daysInYear(JSContext
* cx
, const CallArgs
& args
) {
4384 MOZ_ASSERT(IsISO8601Calendar(&args
.thisv().toObject().as
<CalendarObject
>()));
4388 if (!ToPlainDate
<PlainDateObject
, PlainDateTimeObject
, PlainYearMonthObject
>(
4389 cx
, args
.get(0), &date
)) {
4394 return BuiltinCalendarDaysInYear(cx
, date
, args
.rval());
4398 * Temporal.Calendar.prototype.daysInYear ( temporalDateLike )
4400 static bool Calendar_daysInYear(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4402 CallArgs args
= CallArgsFromVp(argc
, vp
);
4403 return CallNonGenericMethod
<IsCalendar
, Calendar_daysInYear
>(cx
, args
);
4407 * Temporal.Calendar.prototype.monthsInYear ( temporalDateLike )
4409 static bool Calendar_monthsInYear(JSContext
* cx
, const CallArgs
& args
) {
4411 MOZ_ASSERT(IsISO8601Calendar(&args
.thisv().toObject().as
<CalendarObject
>()));
4415 if (!ToPlainDate
<PlainDateObject
, PlainDateTimeObject
, PlainYearMonthObject
>(
4416 cx
, args
.get(0), &date
)) {
4421 return BuiltinCalendarMonthsInYear(cx
, date
, args
.rval());
4425 * Temporal.Calendar.prototype.monthsInYear ( temporalDateLike )
4427 static bool Calendar_monthsInYear(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4429 CallArgs args
= CallArgsFromVp(argc
, vp
);
4430 return CallNonGenericMethod
<IsCalendar
, Calendar_monthsInYear
>(cx
, args
);
4434 * Temporal.Calendar.prototype.inLeapYear ( temporalDateLike )
4436 static bool Calendar_inLeapYear(JSContext
* cx
, const CallArgs
& args
) {
4438 MOZ_ASSERT(IsISO8601Calendar(&args
.thisv().toObject().as
<CalendarObject
>()));
4442 if (!ToPlainDate
<PlainDateObject
, PlainDateTimeObject
, PlainYearMonthObject
>(
4443 cx
, args
.get(0), &date
)) {
4448 return BuiltinCalendarInLeapYear(cx
, date
, args
.rval());
4452 * Temporal.Calendar.prototype.inLeapYear ( temporalDateLike )
4454 static bool Calendar_inLeapYear(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4456 CallArgs args
= CallArgsFromVp(argc
, vp
);
4457 return CallNonGenericMethod
<IsCalendar
, Calendar_inLeapYear
>(cx
, args
);
4461 * Temporal.Calendar.prototype.fields ( fields )
4463 static bool Calendar_fields(JSContext
* cx
, const CallArgs
& args
) {
4465 MOZ_ASSERT(IsISO8601Calendar(&args
.thisv().toObject().as
<CalendarObject
>()));
4468 JS::ForOfIterator
iterator(cx
);
4469 if (!iterator
.init(args
.get(0))) {
4474 JS::RootedVector
<Value
> fieldNames(cx
);
4475 mozilla::EnumSet
<CalendarField
> seen
;
4478 Rooted
<Value
> nextValue(cx
);
4479 Rooted
<JSLinearString
*> linear(cx
);
4483 if (!iterator
.next(&nextValue
, &done
)) {
4490 NewDenseCopiedArray(cx
, fieldNames
.length(), fieldNames
.begin());
4495 args
.rval().setObject(*array
);
4500 if (!nextValue
.isString()) {
4502 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_IGNORE_STACK
, nextValue
,
4503 nullptr, "not a string");
4506 iterator
.closeThrow();
4510 linear
= nextValue
.toString()->ensureLinear(cx
);
4515 // Step 6.e. (Reordered)
4516 CalendarField field
;
4517 if (!ToCalendarField(cx
, linear
, &field
)) {
4518 iterator
.closeThrow();
4523 if (seen
.contains(field
)) {
4525 if (auto chars
= QuoteString(cx
, linear
, '"')) {
4526 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
4527 JSMSG_TEMPORAL_CALENDAR_DUPLICATE_FIELD
,
4532 iterator
.closeThrow();
4537 if (!fieldNames
.append(nextValue
)) {
4545 * Temporal.Calendar.prototype.fields ( fields )
4547 static bool Calendar_fields(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4549 CallArgs args
= CallArgsFromVp(argc
, vp
);
4550 return CallNonGenericMethod
<IsCalendar
, Calendar_fields
>(cx
, args
);
4554 * Temporal.Calendar.prototype.mergeFields ( fields, additionalFields )
4556 static bool Calendar_mergeFields(JSContext
* cx
, const CallArgs
& args
) {
4557 // Step 7. (Reordered)
4558 MOZ_ASSERT(IsISO8601Calendar(&args
.thisv().toObject().as
<CalendarObject
>()));
4561 Rooted
<JSObject
*> fields(cx
, JS::ToObject(cx
, args
.get(0)));
4566 Rooted
<PlainObject
*> fieldsCopy(
4567 cx
, SnapshotOwnPropertiesIgnoreUndefined(cx
, fields
));
4573 Rooted
<JSObject
*> additionalFields(cx
, JS::ToObject(cx
, args
.get(1)));
4574 if (!additionalFields
) {
4578 Rooted
<PlainObject
*> additionalFieldsCopy(
4579 cx
, SnapshotOwnPropertiesIgnoreUndefined(cx
, additionalFields
));
4580 if (!additionalFieldsCopy
) {
4586 // JSITER_HIDDEN doesn't need to be passed, because CopyDataProperties creates
4587 // all properties as enumerable.
4588 JS::RootedVector
<PropertyKey
> additionalKeys(cx
);
4589 if (!GetPropertyKeys(cx
, additionalFieldsCopy
,
4590 JSITER_OWNONLY
| JSITER_SYMBOLS
, &additionalKeys
)) {
4595 Rooted
<PropertyHashSet
> overriddenKeys(cx
, PropertyHashSet(cx
));
4596 if (!ISOFieldKeysToIgnore(cx
, additionalKeys
, overriddenKeys
.get())) {
4601 Rooted
<PlainObject
*> merged(cx
, NewPlainObjectWithProto(cx
, nullptr));
4608 // JSITER_HIDDEN doesn't need to be passed, because CopyDataProperties creates
4609 // all properties as enumerable.
4610 JS::RootedVector
<PropertyKey
> fieldsKeys(cx
);
4611 if (!GetPropertyKeys(cx
, fieldsCopy
, JSITER_OWNONLY
| JSITER_SYMBOLS
,
4617 Rooted
<Value
> propValue(cx
);
4618 for (size_t i
= 0; i
< fieldsKeys
.length(); i
++) {
4619 Handle
<PropertyKey
> key
= fieldsKeys
[i
];
4622 if (overriddenKeys
.has(key
)) {
4623 if (!GetProperty(cx
, additionalFieldsCopy
, additionalFieldsCopy
, key
,
4628 // Step 12.c. (Reordered)
4629 if (propValue
.isUndefined()) {
4630 // The property can be undefined if the key is "month" or "monthCode".
4631 MOZ_ASSERT(key
.isAtom(cx
->names().month
) ||
4632 key
.isAtom(cx
->names().monthCode
));
4637 if (!GetProperty(cx
, fieldsCopy
, fieldsCopy
, key
, &propValue
)) {
4641 // All properties of |fieldsCopy| have a non-undefined value.
4642 MOZ_ASSERT(!propValue
.isUndefined());
4646 if (!DefineDataProperty(cx
, merged
, key
, propValue
)) {
4652 if (!CopyDataProperties(cx
, merged
, additionalFieldsCopy
)) {
4657 args
.rval().setObject(*merged
);
4662 * Temporal.Calendar.prototype.mergeFields ( fields, additionalFields )
4664 static bool Calendar_mergeFields(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4666 CallArgs args
= CallArgsFromVp(argc
, vp
);
4667 return CallNonGenericMethod
<IsCalendar
, Calendar_mergeFields
>(cx
, args
);
4671 * Temporal.Calendar.prototype.toString ( )
4673 static bool Calendar_toString(JSContext
* cx
, const CallArgs
& args
) {
4674 auto* calendar
= &args
.thisv().toObject().as
<CalendarObject
>();
4677 args
.rval().setString(calendar
->identifier());
4682 * Temporal.Calendar.prototype.toString ( )
4684 static bool Calendar_toString(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4686 CallArgs args
= CallArgsFromVp(argc
, vp
);
4687 return CallNonGenericMethod
<IsCalendar
, Calendar_toString
>(cx
, args
);
4691 * Temporal.Calendar.prototype.toJSON ( )
4693 static bool Calendar_toJSON(JSContext
* cx
, const CallArgs
& args
) {
4694 auto* calendar
= &args
.thisv().toObject().as
<CalendarObject
>();
4697 args
.rval().setString(calendar
->identifier());
4702 * Temporal.Calendar.prototype.toJSON ( )
4704 static bool Calendar_toJSON(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4706 CallArgs args
= CallArgsFromVp(argc
, vp
);
4707 return CallNonGenericMethod
<IsCalendar
, Calendar_toJSON
>(cx
, args
);
4710 const JSClass
CalendarObject::class_
= {
4711 "Temporal.Calendar",
4712 JSCLASS_HAS_RESERVED_SLOTS(CalendarObject::SLOT_COUNT
) |
4713 JSCLASS_HAS_CACHED_PROTO(JSProto_Calendar
),
4715 &CalendarObject::classSpec_
,
4718 const JSClass
& CalendarObject::protoClass_
= PlainObject::class_
;
4720 static const JSFunctionSpec Calendar_methods
[] = {
4721 JS_FN("from", Calendar_from
, 1, 0),
4725 static const JSFunctionSpec Calendar_prototype_methods
[] = {
4726 JS_FN("dateFromFields", Calendar_dateFromFields
, 1, 0),
4727 JS_FN("yearMonthFromFields", Calendar_yearMonthFromFields
, 1, 0),
4728 JS_FN("monthDayFromFields", Calendar_monthDayFromFields
, 1, 0),
4729 JS_FN("dateAdd", Calendar_dateAdd
, 2, 0),
4730 JS_FN("dateUntil", Calendar_dateUntil
, 2, 0),
4731 JS_FN("year", Calendar_year
, 1, 0),
4732 JS_FN("month", Calendar_month
, 1, 0),
4733 JS_FN("monthCode", Calendar_monthCode
, 1, 0),
4734 JS_FN("day", Calendar_day
, 1, 0),
4735 JS_FN("dayOfWeek", Calendar_dayOfWeek
, 1, 0),
4736 JS_FN("dayOfYear", Calendar_dayOfYear
, 1, 0),
4737 JS_FN("weekOfYear", Calendar_weekOfYear
, 1, 0),
4738 JS_FN("yearOfWeek", Calendar_yearOfWeek
, 1, 0),
4739 JS_FN("daysInWeek", Calendar_daysInWeek
, 1, 0),
4740 JS_FN("daysInMonth", Calendar_daysInMonth
, 1, 0),
4741 JS_FN("daysInYear", Calendar_daysInYear
, 1, 0),
4742 JS_FN("monthsInYear", Calendar_monthsInYear
, 1, 0),
4743 JS_FN("inLeapYear", Calendar_inLeapYear
, 1, 0),
4744 JS_FN("fields", Calendar_fields
, 1, 0),
4745 JS_FN("mergeFields", Calendar_mergeFields
, 2, 0),
4746 JS_FN("toString", Calendar_toString
, 0, 0),
4747 JS_FN("toJSON", Calendar_toJSON
, 0, 0),
4751 static const JSPropertySpec Calendar_prototype_properties
[] = {
4752 JS_PSG("id", Calendar_id
, 0),
4753 JS_STRING_SYM_PS(toStringTag
, "Temporal.Calendar", JSPROP_READONLY
),
4757 const ClassSpec
CalendarObject::classSpec_
= {
4758 GenericCreateConstructor
<CalendarConstructor
, 1, gc::AllocKind::FUNCTION
>,
4759 GenericCreatePrototype
<CalendarObject
>,
4762 Calendar_prototype_methods
,
4763 Calendar_prototype_properties
,
4765 ClassSpec::DontDefineConstructor
,
4768 struct MOZ_STACK_CLASS CalendarNameAndNative final
{
4773 static CalendarNameAndNative
GetCalendarNameAndNative(JSContext
* cx
,
4774 CalendarField fieldName
) {
4775 switch (fieldName
) {
4776 case CalendarField::Year
:
4777 return {cx
->names().year
, Calendar_year
};
4778 case CalendarField::Month
:
4779 return {cx
->names().month
, Calendar_month
};
4780 case CalendarField::MonthCode
:
4781 return {cx
->names().monthCode
, Calendar_monthCode
};
4782 case CalendarField::Day
:
4783 return {cx
->names().day
, Calendar_day
};
4785 MOZ_CRASH("invalid temporal field name");
4788 bool js::temporal::IsBuiltinAccess(
4789 JSContext
* cx
, Handle
<CalendarObject
*> calendar
,
4790 std::initializer_list
<CalendarField
> fieldNames
) {
4791 // Don't optimize when the object has any own properties which may shadow the
4792 // built-in methods.
4793 if (!calendar
->empty()) {
4797 JSObject
* proto
= cx
->global()->maybeGetPrototype(JSProto_Calendar
);
4799 // Don't attempt to optimize when the class isn't yet initialized.
4804 // Don't optimize when the prototype isn't the built-in prototype.
4805 if (calendar
->staticPrototype() != proto
) {
4809 auto* nproto
= &proto
->as
<NativeObject
>();
4810 for (auto fieldName
: fieldNames
) {
4811 auto [name
, native
] = GetCalendarNameAndNative(cx
, fieldName
);
4812 auto prop
= nproto
->lookupPure(name
);
4814 // Return if the property isn't a data property.
4815 if (!prop
|| !prop
->isDataProperty()) {
4819 // Return if the property isn't the initial method.
4820 if (!IsNativeFunction(nproto
->getSlot(prop
->slot()), native
)) {
4825 // TODO: Pass accessor list from caller to avoid excessive checks.
4827 // Additionally check the various calendar fields operations.
4828 for (const auto& [name
, native
] : (CalendarNameAndNative
[]){
4829 {cx
->names().fields
, Calendar_fields
},
4830 {cx
->names().mergeFields
, Calendar_mergeFields
},
4831 {cx
->names().dateFromFields
, Calendar_dateFromFields
},
4832 {cx
->names().monthDayFromFields
, Calendar_monthDayFromFields
},
4833 {cx
->names().yearMonthFromFields
, Calendar_yearMonthFromFields
},
4835 auto prop
= nproto
->lookupPure(name
);
4837 // Return if the property isn't a data property.
4838 if (!prop
|| !prop
->isDataProperty()) {
4842 // Return if the property isn't the initial method.
4843 if (!IsNativeFunction(nproto
->getSlot(prop
->slot()), native
)) {
4848 // CalendarFields observably uses array iteration.
4849 bool arrayIterationSane
;
4850 if (!IsArrayIterationSane(cx
, &arrayIterationSane
)) {
4851 cx
->recoverFromOutOfMemory();
4854 if (!arrayIterationSane
) {
4858 // Success! The access can be optimized.