Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "builtin/temporal/Calendar.h"
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/EnumSet.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/Likely.h"
#include "mozilla/Maybe.h"
#include "mozilla/Range.h"
#include "mozilla/TextUtils.h"
#include <algorithm>
#include <array>
#include <cmath>
#include <cstring>
#include <iterator>
#include <stddef.h>
#include <stdint.h>
#include <utility>
#include "jsfriendapi.h"
#include "jsnum.h"
#include "jspubtd.h"
#include "jstypes.h"
#include "NamespaceImports.h"
#include "builtin/Array.h"
#include "builtin/String.h"
#include "builtin/temporal/Duration.h"
#include "builtin/temporal/PlainDate.h"
#include "builtin/temporal/PlainDateTime.h"
#include "builtin/temporal/PlainMonthDay.h"
#include "builtin/temporal/PlainTime.h"
#include "builtin/temporal/PlainYearMonth.h"
#include "builtin/temporal/Temporal.h"
#include "builtin/temporal/TemporalFields.h"
#include "builtin/temporal/TemporalParser.h"
#include "builtin/temporal/TemporalTypes.h"
#include "builtin/temporal/TemporalUnit.h"
#include "builtin/temporal/Wrapped.h"
#include "builtin/temporal/ZonedDateTime.h"
#include "gc/AllocKind.h"
#include "gc/Barrier.h"
#include "gc/GCEnum.h"
#include "gc/Tracer.h"
#include "js/AllocPolicy.h"
#include "js/CallArgs.h"
#include "js/CallNonGenericMethod.h"
#include "js/Class.h"
#include "js/Conversions.h"
#include "js/ErrorReport.h"
#include "js/ForOfIterator.h"
#include "js/friend/ErrorMessages.h"
#include "js/GCAPI.h"
#include "js/GCHashTable.h"
#include "js/GCVector.h"
#include "js/Id.h"
#include "js/Printer.h"
#include "js/PropertyDescriptor.h"
#include "js/PropertySpec.h"
#include "js/RootingAPI.h"
#include "js/TracingAPI.h"
#include "js/Value.h"
#include "util/Text.h"
#include "vm/ArrayObject.h"
#include "vm/BytecodeUtil.h"
#include "vm/Compartment.h"
#include "vm/GlobalObject.h"
#include "vm/Interpreter.h"
#include "vm/JSAtomState.h"
#include "vm/JSContext.h"
#include "vm/JSObject.h"
#include "vm/PlainObject.h"
#include "vm/PropertyInfo.h"
#include "vm/PropertyKey.h"
#include "vm/Realm.h"
#include "vm/Shape.h"
#include "vm/Stack.h"
#include "vm/StringType.h"
#include "vm/Compartment-inl.h"
#include "vm/JSAtomUtils-inl.h"
#include "vm/JSObject-inl.h"
#include "vm/NativeObject-inl.h"
#include "vm/ObjectOperations-inl.h"
using namespace js;
using namespace js::temporal;
static inline bool IsCalendar(Handle<Value> v) {
return v.isObject() && v.toObject().is<CalendarObject>();
}
void js::temporal::CalendarValue::trace(JSTracer* trc) {
TraceRoot(trc, &value_, "CalendarValue::value");
}
void js::temporal::CalendarRecord::trace(JSTracer* trc) {
receiver_.trace(trc);
TraceNullableRoot(trc, &dateAdd_, "CalendarRecord::dateAdd");
TraceNullableRoot(trc, &dateFromFields_, "CalendarRecord::dateFromFields");
TraceNullableRoot(trc, &dateUntil_, "CalendarRecord::dateUntil");
TraceNullableRoot(trc, &day_, "CalendarRecord::day");
TraceNullableRoot(trc, &fields_, "CalendarRecord::fields");
TraceNullableRoot(trc, &mergeFields_, "CalendarRecord::mergeFields");
TraceNullableRoot(trc, &monthDayFromFields_,
"CalendarRecord::monthDayFromFields");
TraceNullableRoot(trc, &yearMonthFromFields_,
"CalendarRecord::yearMonthFromFields");
}
bool js::temporal::WrapCalendarValue(JSContext* cx,
MutableHandle<JS::Value> calendar) {
MOZ_ASSERT(calendar.isString() || calendar.isObject());
return cx->compartment()->wrap(cx, calendar);
}
/**
* IteratorToListOfType ( iteratorRecord, elementTypes )
*
* With `elementTypes = ยซ String ยป`.
*
* This implementation accepts an iterable instead of an iterator record.
*/
static bool IterableToListOfStrings(JSContext* cx, Handle<Value> items,
MutableHandle<CalendarFieldNames> list) {
JS::ForOfIterator iterator(cx);
if (!iterator.init(items)) {
return false;
}
// Step 1. (Not applicable in our implementation.)
// Steps 2-3.
Rooted<Value> nextValue(cx);
Rooted<PropertyKey> value(cx);
while (true) {
bool done;
if (!iterator.next(&nextValue, &done)) {
return false;
}
if (done) {
break;
}
if (nextValue.isString()) {
if (!PrimitiveValueToId<CanGC>(cx, nextValue, &value)) {
return false;
}
if (!list.append(value)) {
return false;
}
continue;
}
ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, nextValue,
nullptr, "not a string");
iterator.closeThrow();
return false;
}
// Step 4.
return true;
}
/**
* IsISOLeapYear ( year )
*/
static constexpr bool IsISOLeapYear(int32_t year) {
// Steps 1-5.
return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0));
}
/**
* IsISOLeapYear ( year )
*/
static bool IsISOLeapYear(double year) {
// Step 1.
MOZ_ASSERT(IsInteger(year));
// Steps 2-5.
return std::fmod(year, 4) == 0 &&
(std::fmod(year, 100) != 0 || std::fmod(year, 400) == 0);
}
/**
* ISODaysInYear ( year )
*/
int32_t js::temporal::ISODaysInYear(int32_t year) {
// Steps 1-3.
return IsISOLeapYear(year) ? 366 : 365;
}
/**
* ISODaysInMonth ( year, month )
*/
static constexpr int32_t ISODaysInMonth(int32_t year, int32_t month) {
MOZ_ASSERT(1 <= month && month <= 12);
constexpr uint8_t daysInMonth[2][13] = {
{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};
// Steps 1-4.
return daysInMonth[IsISOLeapYear(year)][month];
}
/**
* ISODaysInMonth ( year, month )
*/
int32_t js::temporal::ISODaysInMonth(int32_t year, int32_t month) {
return ::ISODaysInMonth(year, month);
}
/**
* ISODaysInMonth ( year, month )
*/
int32_t js::temporal::ISODaysInMonth(double year, int32_t month) {
MOZ_ASSERT(1 <= month && month <= 12);
static constexpr uint8_t daysInMonth[2][13] = {
{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};
// Steps 1-4.
return daysInMonth[IsISOLeapYear(year)][month];
}
/**
* 21.4.1.6 Week Day
*
* Compute the week day from |day| without first expanding |day| into a full
* date through |MakeDate(day, 0)|:
*
* WeekDay(MakeDate(day, 0))
* = WeekDay(day ร— msPerDay + 0)
* = WeekDay(day ร— msPerDay)
* = ๐”ฝ(โ„(Day(day ร— msPerDay) + 4๐”ฝ) modulo 7)
* = ๐”ฝ(โ„(๐”ฝ(floor(โ„((day ร— msPerDay) / msPerDay))) + 4๐”ฝ) modulo 7)
* = ๐”ฝ(โ„(๐”ฝ(floor(โ„(day))) + 4๐”ฝ) modulo 7)
* = ๐”ฝ(โ„(๐”ฝ(day) + 4๐”ฝ) modulo 7)
*/
static int32_t WeekDay(int32_t day) {
int32_t result = (day + 4) % 7;
if (result < 0) {
result += 7;
}
return result;
}
/**
* ToISODayOfWeek ( year, month, day )
*/
static int32_t ToISODayOfWeek(const PlainDate& date) {
MOZ_ASSERT(ISODateTimeWithinLimits(date));
// Steps 1-3. (Not applicable in our implementation.)
// TODO: Check if ES MakeDate + WeekDay is efficient enough.
//
// Step 4.
int32_t day = MakeDay(date);
// Step 5.
int32_t weekday = WeekDay(day);
return weekday != 0 ? weekday : 7;
}
static constexpr auto FirstDayOfMonth(int32_t year) {
// The following array contains the day of year for the first day of each
// month, where index 0 is January, and day 0 is January 1.
std::array<int32_t, 13> days = {};
for (int32_t month = 1; month <= 12; ++month) {
days[month] = days[month - 1] + ::ISODaysInMonth(year, month);
}
return days;
}
/**
* ToISODayOfYear ( year, month, day )
*/
static int32_t ToISODayOfYear(int32_t year, int32_t month, int32_t day) {
MOZ_ASSERT(1 <= month && month <= 12);
// First day of month arrays for non-leap and leap years.
constexpr decltype(FirstDayOfMonth(0)) firstDayOfMonth[2] = {
FirstDayOfMonth(1), FirstDayOfMonth(0)};
// Steps 1-3. (Not applicable in our implementation.)
// Steps 4-5.
//
// Instead of first computing the date and then using DayWithinYear to map the
// date to the day within the year, directly lookup the first day of the month
// and then add the additional days.
return firstDayOfMonth[IsISOLeapYear(year)][month - 1] + day;
}
/**
* ToISODayOfYear ( year, month, day )
*/
int32_t js::temporal::ToISODayOfYear(const PlainDate& date) {
MOZ_ASSERT(ISODateTimeWithinLimits(date));
// Steps 1-5.
auto& [year, month, day] = date;
return ::ToISODayOfYear(year, month, day);
}
static int32_t FloorDiv(int32_t dividend, int32_t divisor) {
MOZ_ASSERT(divisor > 0);
int32_t quotient = dividend / divisor;
int32_t remainder = dividend % divisor;
if (remainder < 0) {
quotient -= 1;
}
return quotient;
}
/**
* 21.4.1.3 Year Number, DayFromYear
*/
static int32_t DayFromYear(int32_t year) {
return 365 * (year - 1970) + FloorDiv(year - 1969, 4) -
FloorDiv(year - 1901, 100) + FloorDiv(year - 1601, 400);
}
/**
* 21.4.1.11 MakeTime ( hour, min, sec, ms )
*/
static int64_t MakeTime(const PlainTime& time) {
MOZ_ASSERT(IsValidTime(time));
// Step 1 (Not applicable).
// Step 2.
int64_t h = time.hour;
// Step 3.
int64_t m = time.minute;
// Step 4.
int64_t s = time.second;
// Step 5.
int64_t milli = time.millisecond;
// Steps 6-7.
return h * ToMilliseconds(TemporalUnit::Hour) +
m * ToMilliseconds(TemporalUnit::Minute) +
s * ToMilliseconds(TemporalUnit::Second) + milli;
}
/**
* 21.4.1.12 MakeDay ( year, month, date )
*/
int32_t js::temporal::MakeDay(const PlainDate& date) {
MOZ_ASSERT(ISODateTimeWithinLimits(date));
return DayFromYear(date.year) + ToISODayOfYear(date) - 1;
}
/**
* 21.4.1.13 MakeDate ( day, time )
*/
int64_t js::temporal::MakeDate(const PlainDateTime& dateTime) {
MOZ_ASSERT(ISODateTimeWithinLimits(dateTime));
// Step 1 (Not applicable).
// Steps 2-3.
int64_t tv = MakeDay(dateTime.date) * ToMilliseconds(TemporalUnit::Day) +
MakeTime(dateTime.time);
// Step 4.
return tv;
}
/**
* 21.4.1.12 MakeDay ( year, month, date )
*/
static int32_t MakeDay(int32_t year, int32_t month, int32_t day) {
MOZ_ASSERT(1 <= month && month <= 12);
// FIXME: spec issue - what should happen for invalid years/days?
return DayFromYear(year) + ::ToISODayOfYear(year, month, day) - 1;
}
/**
* 21.4.1.13 MakeDate ( day, time )
*/
int64_t js::temporal::MakeDate(int32_t year, int32_t month, int32_t day) {
// NOTE: This version accepts values outside the valid date-time limits.
MOZ_ASSERT(1 <= month && month <= 12);
// Step 1 (Not applicable).
// Steps 2-3.
int64_t tv = ::MakeDay(year, month, day) * ToMilliseconds(TemporalUnit::Day);
// Step 4.
return tv;
}
struct YearWeek final {
int32_t year = 0;
int32_t week = 0;
};
/**
* ToISOWeekOfYear ( year, month, day )
*/
static YearWeek ToISOWeekOfYear(const PlainDate& date) {
MOZ_ASSERT(ISODateTimeWithinLimits(date));
auto& [year, month, day] = date;
// Steps 1-3. (Not applicable in our implementation.)
// Steps 4-5.
int32_t doy = ToISODayOfYear(date);
int32_t dow = ToISODayOfWeek(date);
int32_t woy = (10 + doy - dow) / 7;
MOZ_ASSERT(0 <= woy && woy <= 53);
// An ISO year has 53 weeks if the year starts on a Thursday or if it's a
// leap year which starts on a Wednesday.
auto isLongYear = [](int32_t year) {
int32_t startOfYear = ToISODayOfWeek({year, 1, 1});
return startOfYear == 4 || (startOfYear == 3 && IsISOLeapYear(year));
};
// Part of last year's last week, which is either week 52 or week 53.
if (woy == 0) {
return {year - 1, 52 + int32_t(isLongYear(year - 1))};
}
// Part of next year's first week if the current year isn't a long year.
if (woy == 53 && !isLongYear(year)) {
return {year + 1, 1};
}
return {year, woy};
}
/**
* ISOMonthCode ( month )
*/
static JSString* ISOMonthCode(JSContext* cx, int32_t month) {
MOZ_ASSERT(1 <= month && month <= 12);
// Steps 1-2.
char monthCode[3] = {'M', char('0' + (month / 10)), char('0' + (month % 10))};
return NewStringCopyN<CanGC>(cx, monthCode, std::size(monthCode));
}
template <typename T, typename... Ts>
static bool ToPlainDate(JSObject* temporalDateLike, PlainDate* result) {
if (auto* obj = temporalDateLike->maybeUnwrapIf<T>()) {
*result = ToPlainDate(obj);
return true;
}
if constexpr (sizeof...(Ts) > 0) {
return ToPlainDate<Ts...>(temporalDateLike, result);
}
return false;
}
template <typename... Ts>
static bool ToPlainDate(JSContext* cx, Handle<Value> temporalDateLike,
PlainDate* result) {
if (temporalDateLike.isObject()) {
if (ToPlainDate<Ts...>(&temporalDateLike.toObject(), result)) {
return true;
}
}
return ToTemporalDate(cx, temporalDateLike, result);
}
#ifdef DEBUG
template <typename CharT>
static bool StringIsAsciiLowerCase(mozilla::Range<CharT> str) {
return std::all_of(str.begin().get(), str.end().get(), [](CharT ch) {
return mozilla::IsAscii(ch) && !mozilla::IsAsciiUppercaseAlpha(ch);
});
}
static bool StringIsAsciiLowerCase(JSLinearString* str) {
JS::AutoCheckCannotGC nogc;
return str->hasLatin1Chars()
? StringIsAsciiLowerCase(str->latin1Range(nogc))
: StringIsAsciiLowerCase(str->twoByteRange(nogc));
}
#endif
static bool IsISO8601Calendar(JSLinearString* id) {
return StringEqualsLiteral(id, "iso8601");
}
#ifdef DEBUG
static bool IsISO8601Calendar(CalendarObject* calendar) {
return IsISO8601Calendar(calendar->identifier());
}
#endif
static bool IsISO8601Calendar(JSContext* cx, JSString* id, bool* result) {
JSLinearString* linear = id->ensureLinear(cx);
if (!linear) {
return false;
}
*result = IsISO8601Calendar(linear);
return true;
}
/**
* IsBuiltinCalendar ( id )
*/
static bool IsBuiltinCalendar(JSLinearString* id) {
// Callers must convert to lower case.
MOZ_ASSERT(StringIsAsciiLowerCase(id));
// Steps 1-3.
return StringEqualsLiteral(id, "iso8601");
}
static JSLinearString* ThrowIfNotBuiltinCalendar(JSContext* cx,
Handle<JSLinearString*> id) {
if (!StringIsAscii(id)) {
if (auto chars = QuoteString(cx, id)) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
JSMSG_TEMPORAL_CALENDAR_INVALID_ID, chars.get());
}
return nullptr;
}
JSString* lower = StringToLowerCase(cx, id);
if (!lower) {
return nullptr;
}
JSLinearString* linear = lower->ensureLinear(cx);
if (!linear) {
return nullptr;
}
if (!IsBuiltinCalendar(linear)) {
if (auto chars = QuoteString(cx, id)) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
JSMSG_TEMPORAL_CALENDAR_INVALID_ID, chars.get());
}
return nullptr;
}
return linear;
}
bool js::temporal::ToBuiltinCalendar(JSContext* cx, Handle<JSString*> id,
MutableHandle<CalendarValue> result) {
Rooted<JSLinearString*> linear(cx, id->ensureLinear(cx));
if (!linear) {
return false;
}
auto* identifier = ThrowIfNotBuiltinCalendar(cx, linear);
if (!identifier) {
return false;
}
result.set(CalendarValue(identifier));
return true;
}
/**
* CreateTemporalCalendar ( identifier [ , newTarget ] )
*/
static CalendarObject* CreateTemporalCalendar(
JSContext* cx, const CallArgs& args, Handle<JSLinearString*> identifier) {
// Step 1.
MOZ_ASSERT(IsBuiltinCalendar(identifier));
// Steps 2-3.
Rooted<JSObject*> proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Calendar, &proto)) {
return nullptr;
}
auto* obj = NewObjectWithClassProto<CalendarObject>(cx, proto);
if (!obj) {
return nullptr;
}
// Step 4.
obj->setFixedSlot(CalendarObject::IDENTIFIER_SLOT, StringValue(identifier));
// Step 5.
return obj;
}
/**
* CreateTemporalCalendar ( identifier [ , newTarget ] )
*/
static CalendarObject* CreateTemporalCalendar(
JSContext* cx, Handle<JSLinearString*> identifier) {
// Step 1.
MOZ_ASSERT(IsBuiltinCalendar(identifier));
// Steps 2-3.
auto* obj = NewBuiltinClassInstance<CalendarObject>(cx);
if (!obj) {
return nullptr;
}
// Step 4.
obj->setFixedSlot(CalendarObject::IDENTIFIER_SLOT, StringValue(identifier));
// Step 5.
return obj;
}
/**
* ObjectImplementsTemporalCalendarProtocol ( object )
*/
static bool ObjectImplementsTemporalCalendarProtocol(JSContext* cx,
Handle<JSObject*> object,
bool* result) {
// Step 1. (Not applicable in our implementation.)
MOZ_ASSERT(!object->canUnwrapAs<CalendarObject>(),
"Calendar objects handled in the caller");
// Step 2.
for (auto key : {
&JSAtomState::dateAdd, &JSAtomState::dateFromFields,
&JSAtomState::dateUntil, &JSAtomState::day,
&JSAtomState::dayOfWeek, &JSAtomState::dayOfYear,
&JSAtomState::daysInMonth, &JSAtomState::daysInWeek,
&JSAtomState::daysInYear, &JSAtomState::fields,
&JSAtomState::id, &JSAtomState::inLeapYear,
&JSAtomState::mergeFields, &JSAtomState::month,
&JSAtomState::monthCode, &JSAtomState::monthDayFromFields,
&JSAtomState::monthsInYear, &JSAtomState::weekOfYear,
&JSAtomState::year, &JSAtomState::yearMonthFromFields,
&JSAtomState::yearOfWeek,
}) {
// Step 2.a.
bool has;
if (!HasProperty(cx, object, cx->names().*key, &has)) {
return false;
}
if (!has) {
*result = false;
return true;
}
}
// Step 3.
*result = true;
return true;
}
template <typename T, typename... Ts>
static bool ToTemporalCalendar(JSContext* cx, Handle<JSObject*> object,
MutableHandle<CalendarValue> result) {
if (auto* unwrapped = object->maybeUnwrapIf<T>()) {
result.set(unwrapped->calendar());
return result.wrap(cx);
}
if constexpr (sizeof...(Ts) > 0) {
return ToTemporalCalendar<Ts...>(cx, object, result);
}
result.set(CalendarValue());
return true;
}
/**
* ToTemporalCalendarSlotValue ( temporalCalendarLike [ , default ] )
*/
bool js::temporal::ToTemporalCalendar(JSContext* cx,
Handle<Value> temporalCalendarLike,
MutableHandle<CalendarValue> result) {
// Step 1. (Not applicable)
// Step 2.
Rooted<Value> calendarLike(cx, temporalCalendarLike);
if (calendarLike.isObject()) {
Rooted<JSObject*> obj(cx, &calendarLike.toObject());
// Step 2.b. (Partial)
if (obj->canUnwrapAs<CalendarObject>()) {
result.set(CalendarValue(obj));
return true;
}
// Step 2.a.
Rooted<CalendarValue> calendar(cx);
if (!::ToTemporalCalendar<PlainDateObject, PlainDateTimeObject,
PlainMonthDayObject, PlainYearMonthObject,
ZonedDateTimeObject>(cx, obj, &calendar)) {
return false;
}
if (calendar) {
result.set(calendar);
return true;
}
// Step 2.b.
bool implementsCalendarProtocol;
if (!ObjectImplementsTemporalCalendarProtocol(
cx, obj, &implementsCalendarProtocol)) {
return false;
}
if (!implementsCalendarProtocol) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
JSMSG_TEMPORAL_INVALID_OBJECT,
"Temporal.Calendar", obj->getClass()->name);
return false;
}
// Step 2.c.
result.set(CalendarValue(obj));
return true;
}
// Step 3.
if (!calendarLike.isString()) {
ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK,
calendarLike, nullptr, "not a string");
return false;
}
Rooted<JSString*> str(cx, calendarLike.toString());
// Step 4.
Rooted<JSLinearString*> identifier(cx, ParseTemporalCalendarString(cx, str));
if (!identifier) {
return false;
}
// Step 5.
identifier = ThrowIfNotBuiltinCalendar(cx, identifier);
if (!identifier) {
return false;
}
// Step 6.
result.set(CalendarValue(identifier));
return true;
}
/**
* ToTemporalCalendarSlotValue ( temporalCalendarLike [ , default ] )
*
* When called with `default = "iso8601"`.
*/
bool js::temporal::ToTemporalCalendarWithISODefault(
JSContext* cx, Handle<Value> temporalCalendarLike,
MutableHandle<CalendarValue> result) {
// Step 1.
if (temporalCalendarLike.isUndefined()) {
result.set(CalendarValue(cx->names().iso8601));
return true;
}
// Steps 2-6.
return ToTemporalCalendar(cx, temporalCalendarLike, result);
}
/**
* GetTemporalCalendarSlotValueWithISODefault ( item )
*/
bool js::temporal::GetTemporalCalendarWithISODefault(
JSContext* cx, Handle<JSObject*> item,
MutableHandle<CalendarValue> result) {
// Step 1.
Rooted<CalendarValue> calendar(cx);
if (!::ToTemporalCalendar<PlainDateObject, PlainDateTimeObject,
PlainMonthDayObject, PlainYearMonthObject,
ZonedDateTimeObject>(cx, item, &calendar)) {
return false;
}
if (calendar) {
result.set(calendar);
return true;
}
// Step 2.
Rooted<Value> calendarValue(cx);
if (!GetProperty(cx, item, item, cx->names().calendar, &calendarValue)) {
return false;
}
// Step 3.
return ToTemporalCalendarWithISODefault(cx, calendarValue, result);
}
/**
* ToTemporalCalendarIdentifier ( calendarSlotValue )
*/
JSString* js::temporal::ToTemporalCalendarIdentifier(
JSContext* cx, Handle<CalendarValue> calendar) {
// Step 1.
if (calendar.isString()) {
// Step 1.a.
MOZ_ASSERT(IsBuiltinCalendar(calendar.toString()));
// Step 1.b.
return calendar.toString();
}
// Step 2.
Rooted<JSObject*> calendarObj(cx, calendar.toObject());
Rooted<Value> identifier(cx);
if (!GetProperty(cx, calendarObj, calendarObj, cx->names().id, &identifier)) {
return nullptr;
}
// Step 3.
if (!identifier.isString()) {
ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, identifier,
nullptr, "not a string");
return nullptr;
}
// Step 4.
return identifier.toString();
}
/**
* ToTemporalCalendarObject ( calendarSlotValue )
*/
JSObject* js::temporal::ToTemporalCalendarObject(
JSContext* cx, Handle<CalendarValue> calendar) {
// Step 1.
if (calendar.isObject()) {
return calendar.toObject();
}
// Step 2.
Rooted<JSLinearString*> calendarId(cx, calendar.toString());
return CreateTemporalCalendar(cx, calendarId);
}
static bool Calendar_dateAdd(JSContext* cx, unsigned argc, Value* vp);
static bool Calendar_dateFromFields(JSContext* cx, unsigned argc, Value* vp);
static bool Calendar_dateUntil(JSContext* cx, unsigned argc, Value* vp);
static bool Calendar_day(JSContext* cx, unsigned argc, Value* vp);
static bool Calendar_fields(JSContext* cx, unsigned argc, Value* vp);
static bool Calendar_mergeFields(JSContext* cx, unsigned argc, Value* vp);
static bool Calendar_monthDayFromFields(JSContext* cx, unsigned argc,
Value* vp);
static bool Calendar_yearMonthFromFields(JSContext* cx, unsigned argc,
Value* vp);
/**
* CalendarMethodsRecordLookup ( calendarRec, methodName )
*/
static bool CalendarMethodsRecordLookup(JSContext* cx,
MutableHandle<CalendarRecord> calendar,
CalendarMethod methodName) {
// Step 1. (Not applicable in our implementation.)
// Steps 2-10.
Rooted<JSObject*> object(cx, calendar.receiver().toObject());
auto lookup = [&](Handle<PropertyName*> name, JSNative native,
MutableHandle<JSObject*> result) {
auto* method = GetMethod(cx, object, name);
if (!method) {
return false;
}
// As an optimization we only store the method if the receiver is either
// a custom calendar object or if the method isn't the default, built-in
// calender method.
if (!object->is<CalendarObject>() || !IsNativeFunction(method, native)) {
result.set(method);
}
return true;
};
switch (methodName) {
// Steps 2 and 10.
case CalendarMethod::DateAdd:
return lookup(cx->names().dateAdd, Calendar_dateAdd, calendar.dateAdd());
// Steps 3 and 10.
case CalendarMethod::DateFromFields:
return lookup(cx->names().dateFromFields, Calendar_dateFromFields,
calendar.dateFromFields());
// Steps 4 and 10.
case CalendarMethod::DateUntil:
return lookup(cx->names().dateUntil, Calendar_dateUntil,
calendar.dateUntil());
// Steps 5 and 10.
case CalendarMethod::Day:
return lookup(cx->names().day, Calendar_day, calendar.day());
// Steps 6 and 10.
case CalendarMethod::Fields:
return lookup(cx->names().fields, Calendar_fields, calendar.fields());
// Steps 7 and 10.
case CalendarMethod::MergeFields:
return lookup(cx->names().mergeFields, Calendar_mergeFields,
calendar.mergeFields());
// Steps 8 and 10.
case CalendarMethod::MonthDayFromFields:
return lookup(cx->names().monthDayFromFields, Calendar_monthDayFromFields,
calendar.monthDayFromFields());
// Steps 9 and 10.
case CalendarMethod::YearMonthFromFields:
return lookup(cx->names().yearMonthFromFields,
Calendar_yearMonthFromFields,
calendar.yearMonthFromFields());
}
MOZ_CRASH("invalid calendar method");
}
/**
* CreateCalendarMethodsRecord ( calendar, methods )
*/
bool js::temporal::CreateCalendarMethodsRecord(
JSContext* cx, Handle<CalendarValue> calendar,
mozilla::EnumSet<CalendarMethod> methods,
MutableHandle<CalendarRecord> result) {
MOZ_ASSERT(!methods.isEmpty());
// Step 1.
result.set(CalendarRecord{calendar});
#ifdef DEBUG
// Remember the set of looked-up methods for assertions.
result.get().lookedUp() += methods;
#endif
// Built-in calendars don't perform observable lookups.
if (calendar.isString()) {
return true;
}
// Step 2.
for (auto method : methods) {
if (!CalendarMethodsRecordLookup(cx, result, method)) {
return false;
}
}
// Step 3.
return true;
}
static bool ToCalendarField(JSContext* cx, JSLinearString* linear,
CalendarField* result) {
if (StringEqualsLiteral(linear, "year")) {
*result = CalendarField::Year;
return true;
}
if (StringEqualsLiteral(linear, "month")) {
*result = CalendarField::Month;
return true;
}
if (StringEqualsLiteral(linear, "monthCode")) {
*result = CalendarField::MonthCode;
return true;
}
if (StringEqualsLiteral(linear, "day")) {
*result = CalendarField::Day;
return true;
}
if (auto chars = QuoteString(cx, linear, '"')) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
JSMSG_TEMPORAL_CALENDAR_INVALID_FIELD,
chars.get());
}
return false;
}
static PropertyName* ToPropertyName(JSContext* cx, CalendarField field) {
switch (field) {
case CalendarField::Year:
return cx->names().year;
case CalendarField::Month:
return cx->names().month;
case CalendarField::MonthCode:
return cx->names().monthCode;
case CalendarField::Day:
return cx->names().day;
}
MOZ_CRASH("invalid calendar field name");
}
#ifdef DEBUG
static const char* ToCString(CalendarField field) {
switch (field) {
case CalendarField::Year:
return "year";
case CalendarField::Month:
return "month";
case CalendarField::MonthCode:
return "monthCode";
case CalendarField::Day:
return "day";
}
MOZ_CRASH("invalid calendar field name");
}
#endif
/**
* Temporal.Calendar.prototype.fields ( fields )
*/
static bool BuiltinCalendarFields(
JSContext* cx, std::initializer_list<CalendarField> fieldNames,
CalendarFieldNames& result) {
MOZ_ASSERT(result.empty());
// Steps 1-5. (Not applicable.)
// Reserve space for the append operation.
if (!result.reserve(fieldNames.size())) {
return false;
}
// Steps 6-7.
for (auto fieldName : fieldNames) {
auto* name = ToPropertyName(cx, fieldName);
// Steps 7.a and 7.b.i-iv. (Not applicable)
// Step 7.b.v.
result.infallibleAppend(NameToId(name));
}
// Step 8. (Not applicable)
return true;
}
#ifdef DEBUG
static bool IsSorted(std::initializer_list<CalendarField> fieldNames) {
return std::is_sorted(fieldNames.begin(), fieldNames.end(),
[](auto x, auto y) {
auto* a = ToCString(x);
auto* b = ToCString(y);
return std::strcmp(a, b) < 0;
});
}
#endif
/**
* CalendarFields ( calendarRec, fieldNames )
*/
bool js::temporal::CalendarFields(
JSContext* cx, Handle<CalendarRecord> calendar,
std::initializer_list<CalendarField> fieldNames,
MutableHandle<CalendarFieldNames> result) {
MOZ_ASSERT(
CalendarMethodsRecordHasLookedUp(calendar, CalendarMethod::Fields));
// FIXME: spec issue - the input is already sorted, let's assert this, too.
MOZ_ASSERT(IsSorted(fieldNames));
// FIXME: spec issue - the input shouldn't have duplicate elements. Let's
// assert this, too.
MOZ_ASSERT(std::adjacent_find(fieldNames.begin(), fieldNames.end()) ==
fieldNames.end());
// Step 1.
auto fields = calendar.fields();
if (!fields) {
bool arrayIterationSane;
if (calendar.receiver().isString()) {
// "String" calendars don't perform observable array iteration.
arrayIterationSane = true;
} else {
// "Object" calendars need to ensure array iteration is still sane.
if (!IsArrayIterationSane(cx, &arrayIterationSane)) {
return false;
}
}
if (arrayIterationSane) {
// Steps 1.a-b. (Not applicable in our implementation.)
// Step 1.c.
return BuiltinCalendarFields(cx, fieldNames, result.get());
// Steps 1.d-f. (Not applicable in our implementation.)
}
}
// Step 2. (Inlined call to CalendarMethodsRecordCall.)
auto* array = NewDenseFullyAllocatedArray(cx, fieldNames.size());
if (!array) {
return false;
}
array->setDenseInitializedLength(fieldNames.size());
for (size_t i = 0; i < fieldNames.size(); i++) {
auto* name = ToPropertyName(cx, fieldNames.begin()[i]);
array->initDenseElement(i, StringValue(name));
}
Rooted<Value> fieldsFn(cx, ObjectValue(*fields));
auto thisv = calendar.receiver().toValue();
Rooted<Value> fieldsArray(cx, ObjectValue(*array));
if (!Call(cx, fieldsFn, thisv, fieldsArray, &fieldsArray)) {
return false;
}
// Steps 3-4.
if (!IterableToListOfStrings(cx, fieldsArray, result)) {
return false;
}
// The spec sorts the field names in PrepareTemporalFields. Sorting is only
// needed for user-defined calendars, so our implementation performs this step
// here instead of in PrepareTemporalFields.
return SortTemporalFieldNames(cx, result.get());
}
static bool RequireIntegralNumber(JSContext* cx, Handle<Value> value,
Handle<PropertyName*> name,
MutableHandle<Value> result) {
if (MOZ_LIKELY(value.isInt32())) {
result.set(value);
return true;
}
if (value.isDouble()) {
double d = value.toDouble();
if (js::IsInteger(d)) {
result.setNumber(d);
return true;
}
if (auto str = QuoteString(cx, name)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_TEMPORAL_INVALID_INTEGER, str.get());
}
return false;
}
if (auto str = QuoteString(cx, name)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_UNEXPECTED_TYPE, str.get(), "not a number");
}
return false;
}
static bool RequireIntegralPositiveNumber(JSContext* cx, Handle<Value> value,
Handle<PropertyName*> name,
MutableHandle<Value> result) {
if (!RequireIntegralNumber(cx, value, name, result)) {
return false;
}
if (result.toNumber() <= 0) {
if (auto str = QuoteString(cx, name)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_TEMPORAL_INVALID_NUMBER, str.get());
}
return false;
}
return true;
}
static bool RequireString(JSContext* cx, Handle<Value> value,
Handle<PropertyName*> name,
MutableHandle<Value> result) {
if (MOZ_LIKELY(value.isString())) {
result.set(value);
return true;
}
if (auto str = QuoteString(cx, name)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_UNEXPECTED_TYPE, str.get(), "not a string");
}
return false;
}
static bool RequireBoolean(JSContext* cx, Handle<Value> value,
Handle<PropertyName*> name,
MutableHandle<Value> result) {
if (MOZ_LIKELY(value.isBoolean())) {
result.set(value);
return true;
}
if (auto str = QuoteString(cx, name)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_UNEXPECTED_TYPE, str.get(),
"not a boolean");
}
return false;
}
using BuiltinCalendarMethod = bool (*)(JSContext* cx, const PlainDate&,
MutableHandle<Value>);
using CalendarConversion = bool (*)(JSContext*, Handle<Value>,
Handle<PropertyName*>,
MutableHandle<Value>);
template <BuiltinCalendarMethod builtin, CalendarConversion conversion>
static bool CallCalendarMethod(JSContext* cx, Handle<PropertyName*> name,
JSNative native, Handle<CalendarValue> calendar,
Handle<JSObject*> dateLike,
const PlainDate& date,
MutableHandle<Value> result) {
// Step 1.
if (calendar.isString()) {
return builtin(cx, date, result);
}
// Step 2.
Rooted<JSObject*> calendarObj(cx, calendar.toObject());
JSObject* fn = GetMethod(cx, calendarObj, name);
if (!fn) {
return false;
}
// Fast-path for the default implementation.
if (calendarObj->is<CalendarObject>() && IsNativeFunction(fn, native)) {
return builtin(cx, date, result);
}
Rooted<JS::Value> fnVal(cx, ObjectValue(*fn));
Rooted<JS::Value> dateLikeValue(cx, ObjectValue(*dateLike));
if (!Call(cx, fnVal, calendarObj, dateLikeValue, result)) {
return false;
}
// Steps 3-5.
return conversion(cx, result, name, result);
}
/**
* Temporal.Calendar.prototype.year ( temporalDateLike )
*/
static bool BuiltinCalendarYear(JSContext* cx, const PlainDate& date,
MutableHandle<Value> result) {
// Steps 1-4. (Not applicable.)
// Steps 5-6.
result.setInt32(date.year);
return true;
}
static bool Calendar_year(JSContext* cx, unsigned argc, Value* vp);
/**
* CalendarYear ( calendar, dateLike )
*/
static bool CalendarYear(JSContext* cx, Handle<CalendarValue> calendar,
Handle<JSObject*> dateLike, const PlainDate& date,
MutableHandle<Value> result) {
// Steps 1-5.
return CallCalendarMethod<BuiltinCalendarYear, RequireIntegralNumber>(
cx, cx->names().year, Calendar_year, calendar, dateLike, date, result);
}
/**
* CalendarYear ( calendar, dateLike )
*/
bool js::temporal::CalendarYear(JSContext* cx, Handle<CalendarValue> calendar,
Handle<PlainDateObject*> dateLike,
MutableHandle<Value> result) {
return CalendarYear(cx, calendar, dateLike, ToPlainDate(dateLike), result);
}
/**
* CalendarYear ( calendar, dateLike )
*/
bool js::temporal::CalendarYear(JSContext* cx, Handle<CalendarValue> calendar,
Handle<PlainDateTimeObject*> dateLike,
MutableHandle<Value> result) {
return CalendarYear(cx, calendar, dateLike, ToPlainDate(dateLike), result);
}
/**
* CalendarYear ( calendar, dateLike )
*/
bool js::temporal::CalendarYear(JSContext* cx, Handle<CalendarValue> calendar,
Handle<PlainYearMonthObject*> dateLike,
MutableHandle<Value> result) {
return CalendarYear(cx, calendar, dateLike, ToPlainDate(dateLike), result);
}
/**
* CalendarYear ( calendar, dateLike )
*/
bool js::temporal::CalendarYear(JSContext* cx, Handle<CalendarValue> calendar,
const PlainDateTime& dateTime,
MutableHandle<Value> result) {
Rooted<PlainDateTimeObject*> dateLike(
cx, CreateTemporalDateTime(cx, dateTime, calendar));
if (!dateLike) {
return false;
}
return ::CalendarYear(cx, calendar, dateLike, dateTime.date, result);
}
/**
* Temporal.Calendar.prototype.month ( temporalDateLike )
*/
static bool BuiltinCalendarMonth(JSContext* cx, const PlainDate& date,
MutableHandle<Value> result) {
// Steps 1-5. (Not applicable.)
// Steps 6-7.
result.setInt32(date.month);
return true;
}
static bool Calendar_month(JSContext* cx, unsigned argc, Value* vp);
/**
* CalendarMonth ( calendar, dateLike )
*/
static bool CalendarMonth(JSContext* cx, Handle<CalendarValue> calendar,
Handle<JSObject*> dateLike, const PlainDate& date,
MutableHandle<Value> result) {
// Steps 1-6.
return CallCalendarMethod<BuiltinCalendarMonth,
RequireIntegralPositiveNumber>(
cx, cx->names().month, Calendar_month, calendar, dateLike, date, result);
}
/**
* CalendarMonth ( calendar, dateLike )
*/
bool js::temporal::CalendarMonth(JSContext* cx, Handle<CalendarValue> calendar,
Handle<PlainDateObject*> dateLike,
MutableHandle<Value> result) {
return CalendarMonth(cx, calendar, dateLike, ToPlainDate(dateLike), result);
}
/**
* CalendarMonth ( calendar, dateLike )
*/
bool js::temporal::CalendarMonth(JSContext* cx, Handle<CalendarValue> calendar,
Handle<PlainDateTimeObject*> dateLike,
MutableHandle<Value> result) {
return CalendarMonth(cx, calendar, dateLike, ToPlainDate(dateLike), result);
}
/**
* CalendarMonth ( calendar, dateLike )
*/
bool js::temporal::CalendarMonth(JSContext* cx, Handle<CalendarValue> calendar,
Handle<PlainYearMonthObject*> dateLike,
MutableHandle<Value> result) {
return CalendarMonth(cx, calendar, dateLike, ToPlainDate(dateLike), result);
}
/**
* CalendarMonth ( calendar, dateLike )
*/
bool js::temporal::CalendarMonth(JSContext* cx, Handle<CalendarValue> calendar,
const PlainDateTime& dateTime,
MutableHandle<Value> result) {
Rooted<PlainDateTimeObject*> dateLike(
cx, CreateTemporalDateTime(cx, dateTime, calendar));
if (!dateLike) {
return false;
}
return ::CalendarMonth(cx, calendar, dateLike, dateTime.date, result);
}
/**
* Temporal.Calendar.prototype.monthCode ( temporalDateLike )
*/
static bool BuiltinCalendarMonthCode(JSContext* cx, const PlainDate& date,
MutableHandle<Value> result) {
// Steps 1-4. (Not applicable.)
// Steps 5-6.
JSString* str = ISOMonthCode(cx, date.month);
if (!str) {
return false;
}
result.setString(str);
return true;
}
static bool Calendar_monthCode(JSContext* cx, unsigned argc, Value* vp);
/**
* CalendarMonthCode ( calendar, dateLike )
*/
static bool CalendarMonthCode(JSContext* cx, Handle<CalendarValue> calendar,
Handle<JSObject*> dateLike, const PlainDate& date,
MutableHandle<Value> result) {
// Steps 1-4.
return CallCalendarMethod<BuiltinCalendarMonthCode, RequireString>(
cx, cx->names().monthCode, Calendar_monthCode, calendar, dateLike, date,
result);
}
/**
* CalendarMonthCode ( calendar, dateLike )
*/
bool js::temporal::CalendarMonthCode(JSContext* cx,
Handle<CalendarValue> calendar,
Handle<PlainDateObject*> dateLike,
MutableHandle<Value> result) {
return CalendarMonthCode(cx, calendar, dateLike, ToPlainDate(dateLike),
result);
}
/**
* CalendarMonthCode ( calendar, dateLike )
*/
bool js::temporal::CalendarMonthCode(JSContext* cx,
Handle<CalendarValue> calendar,
Handle<PlainDateTimeObject*> dateLike,
MutableHandle<Value> result) {
return CalendarMonthCode(cx, calendar, dateLike, ToPlainDate(dateLike),
result);
}
/**
* CalendarMonthCode ( calendar, dateLike )
*/
bool js::temporal::CalendarMonthCode(JSContext* cx,
Handle<CalendarValue> calendar,
Handle<PlainMonthDayObject*> dateLike,
MutableHandle<Value> result) {
return CalendarMonthCode(cx, calendar, dateLike, ToPlainDate(dateLike),
result);
}
/**
* CalendarMonthCode ( calendar, dateLike )
*/
bool js::temporal::CalendarMonthCode(JSContext* cx,
Handle<CalendarValue> calendar,
Handle<PlainYearMonthObject*> dateLike,
MutableHandle<Value> result) {
return CalendarMonthCode(cx, calendar, dateLike, ToPlainDate(dateLike),
result);
}
/**
* CalendarMonthCode ( calendar, dateLike )
*/
bool js::temporal::CalendarMonthCode(JSContext* cx,
Handle<CalendarValue> calendar,
const PlainDateTime& dateTime,
MutableHandle<Value> result) {
Rooted<PlainDateTimeObject*> dateLike(
cx, CreateTemporalDateTime(cx, dateTime, calendar));
if (!dateLike) {
return false;
}
return ::CalendarMonthCode(cx, calendar, dateLike, dateTime.date, result);
}
/**
* Temporal.Calendar.prototype.day ( temporalDateLike )
*/
static bool BuiltinCalendarDay(const PlainDate& date,
MutableHandle<Value> result) {
// Steps 1-4. (Not applicable.)
// Steps 5-6.
result.setInt32(date.day);
return true;
}
/**
* CalendarDay ( calendarRec, dateLike )
*/
static bool CalendarDay(JSContext* cx, Handle<CalendarRecord> calendar,
Handle<JSObject*> dateLike, const PlainDate& date,
MutableHandle<Value> result) {
MOZ_ASSERT(CalendarMethodsRecordHasLookedUp(calendar, CalendarMethod::Day));
// Step 2. (Reordered)
auto day = calendar.day();
if (!day) {
return BuiltinCalendarDay(date, result);
}
// Step 1. (Inlined call to CalendarMethodsRecordCall.)
Rooted<Value> fn(cx, ObjectValue(*day));
auto thisv = calendar.receiver().toValue();
Rooted<JS::Value> dateLikeValue(cx, ObjectValue(*dateLike));
if (!Call(cx, fn, thisv, dateLikeValue, result)) {
return false;
}
// Steps 3-6.
return RequireIntegralPositiveNumber(cx, result, cx->names().day, result);
}
/**
* CalendarDay ( calendarRec, dateLike )
*/
bool js::temporal::CalendarDay(JSContext* cx, Handle<CalendarValue> calendar,
Handle<PlainDateObject*> dateLike,
MutableHandle<Value> result) {
Rooted<CalendarRecord> calendarRec(cx);
if (!CreateCalendarMethodsRecord(cx, calendar,
{
CalendarMethod::Day,
},
&calendarRec)) {
return false;
}
return CalendarDay(cx, calendarRec, dateLike, ToPlainDate(dateLike), result);
}
/**
* CalendarDay ( calendarRec, dateLike )
*/
bool js::temporal::CalendarDay(JSContext* cx, Handle<CalendarValue> calendar,
Handle<PlainDateTimeObject*> dateLike,
MutableHandle<Value> result) {
Rooted<CalendarRecord> calendarRec(cx);
if (!CreateCalendarMethodsRecord(cx, calendar,
{
CalendarMethod::Day,
},
&calendarRec)) {
return false;
}
return CalendarDay(cx, calendarRec, dateLike, ToPlainDate(dateLike), result);
}
/**
* CalendarDay ( calendarRec, dateLike )
*/
bool js::temporal::CalendarDay(JSContext* cx, Handle<CalendarValue> calendar,
Handle<PlainMonthDayObject*> dateLike,
MutableHandle<Value> result) {
Rooted<CalendarRecord> calendarRec(cx);
if (!CreateCalendarMethodsRecord(cx, calendar,
{
CalendarMethod::Day,
},
&calendarRec)) {
return false;
}
return CalendarDay(cx, calendarRec, dateLike, ToPlainDate(dateLike), result);
}
/**
* CalendarDay ( calendarRec, dateLike )
*/
bool js::temporal::CalendarDay(JSContext* cx, Handle<CalendarRecord> calendar,
const PlainDate& date,
MutableHandle<Value> result) {
Rooted<PlainDateObject*> dateLike(
cx, CreateTemporalDate(cx, date, calendar.receiver()));
if (!dateLike) {
return false;
}
return ::CalendarDay(cx, calendar, dateLike, date, result);
}
/**
* CalendarDay ( calendarRec, dateLike )
*/
bool js::temporal::CalendarDay(JSContext* cx, Handle<CalendarRecord> calendar,
const PlainDateTime& dateTime,
MutableHandle<Value> result) {
Rooted<PlainDateTimeObject*> dateLike(
cx, CreateTemporalDateTime(cx, dateTime, calendar.receiver()));
if (!dateLike) {
return false;
}
return ::CalendarDay(cx, calendar, dateLike, dateTime.date, result);
}
/**
* Temporal.Calendar.prototype.dayOfWeek ( temporalDateLike )
*/
static bool BuiltinCalendarDayOfWeek(JSContext* cx, const PlainDate& date,
MutableHandle<Value> result) {
// Steps 1-4. (Not applicable.)
// Steps 5-9.
result.setInt32(ToISODayOfWeek(date));
return true;
}
static bool Calendar_dayOfWeek(JSContext* cx, unsigned argc, Value* vp);
/**
* CalendarDayOfWeek ( calendar, dateLike )
*/
static bool CalendarDayOfWeek(JSContext* cx, Handle<CalendarValue> calendar,
Handle<JSObject*> dateLike, const PlainDate& date,
MutableHandle<Value> result) {
// Steps 1-6.
return CallCalendarMethod<BuiltinCalendarDayOfWeek,
RequireIntegralPositiveNumber>(
cx, cx->names().dayOfWeek, Calendar_dayOfWeek, calendar, dateLike, date,
result);
}
/**
* CalendarDayOfWeek ( calendar, dateLike )
*/
bool js::temporal::CalendarDayOfWeek(JSContext* cx,
Handle<CalendarValue> calendar,
Handle<PlainDateObject*> dateLike,
MutableHandle<Value> result) {
return CalendarDayOfWeek(cx, calendar, dateLike, ToPlainDate(dateLike),
result);
}
/**
* CalendarDayOfWeek ( calendar, dateLike )
*/
bool js::temporal::CalendarDayOfWeek(JSContext* cx,
Handle<CalendarValue> calendar,
Handle<PlainDateTimeObject*> dateLike,
MutableHandle<Value> result) {
return CalendarDayOfWeek(cx, calendar, dateLike, ToPlainDate(dateLike),
result);
}
/**
* CalendarDayOfWeek ( calendar, dateLike )
*/
bool js::temporal::CalendarDayOfWeek(JSContext* cx,
Handle<CalendarValue> calendar,
const PlainDateTime& dateTime,
MutableHandle<Value> result) {
Rooted<PlainDateTimeObject*> dateLike(
cx, CreateTemporalDateTime(cx, dateTime, calendar));
if (!dateLike) {
return false;
}
return ::CalendarDayOfWeek(cx, calendar, dateLike, dateTime.date, result);
}
/**
* Temporal.Calendar.prototype.dayOfYear ( temporalDateLike )
*/
static bool BuiltinCalendarDayOfYear(JSContext* cx, const PlainDate& date,
MutableHandle<Value> result) {
// Steps 1-4. (Not applicable.)
// Steps 5-7.
result.setInt32(ToISODayOfYear(date));
return true;
}
static bool Calendar_dayOfYear(JSContext* cx, unsigned argc, Value* vp);
/**
* CalendarDayOfYear ( calendar, dateLike )
*/
static bool CalendarDayOfYear(JSContext* cx, Handle<CalendarValue> calendar,
Handle<JSObject*> dateLike, const PlainDate& date,
MutableHandle<Value> result) {
// Steps 1-6.
return CallCalendarMethod<BuiltinCalendarDayOfYear,
RequireIntegralPositiveNumber>(
cx, cx->names().dayOfYear, Calendar_dayOfYear, calendar, dateLike, date,
result);
}
/**
* CalendarDayOfYear ( calendar, dateLike )
*/
bool js::temporal::CalendarDayOfYear(JSContext* cx,
Handle<CalendarValue> calendar,
Handle<PlainDateObject*> dateLike,
MutableHandle<Value> result) {
return CalendarDayOfYear(cx, calendar, dateLike, ToPlainDate(dateLike),
result);
}
/**
* CalendarDayOfYear ( calendar, dateLike )
*/
bool js::temporal::CalendarDayOfYear(JSContext* cx,
Handle<CalendarValue> calendar,
Handle<PlainDateTimeObject*> dateLike,
MutableHandle<Value> result) {
return CalendarDayOfYear(cx, calendar, dateLike, ToPlainDate(dateLike),
result);
}
/**
* CalendarDayOfYear ( calendar, dateLike )
*/
bool js::temporal::CalendarDayOfYear(JSContext* cx,
Handle<CalendarValue> calendar,
const PlainDateTime& dateTime,
MutableHandle<Value> result) {
Rooted<PlainDateTimeObject*> dateLike(
cx, CreateTemporalDateTime(cx, dateTime, calendar));
if (!dateLike) {
return false;
}
return ::CalendarDayOfYear(cx, calendar, dateLike, dateTime.date, result);
}
/**
* Temporal.Calendar.prototype.weekOfYear ( temporalDateLike )
*/
static bool BuiltinCalendarWeekOfYear(JSContext* cx, const PlainDate& date,
MutableHandle<Value> result) {
// Steps 1-4. (Not applicable.)
// Steps 5-6.
result.setInt32(ToISOWeekOfYear(date).week);
return true;
}
static bool Calendar_weekOfYear(JSContext* cx, unsigned argc, Value* vp);
/**
* CalendarWeekOfYear ( calendar, dateLike )
*/
static bool CalendarWeekOfYear(JSContext* cx, Handle<CalendarValue> calendar,
Handle<JSObject*> dateLike,
const PlainDate& date,
MutableHandle<Value> result) {
// Steps 1-6.
return CallCalendarMethod<BuiltinCalendarWeekOfYear,
RequireIntegralPositiveNumber>(
cx, cx->names().weekOfYear, Calendar_weekOfYear, calendar, dateLike, date,
result);
}
/**
* CalendarWeekOfYear ( calendar, dateLike )
*/
bool js::temporal::CalendarWeekOfYear(JSContext* cx,
Handle<CalendarValue> calendar,
Handle<PlainDateObject*> dateLike,
MutableHandle<Value> result) {
return CalendarWeekOfYear(cx, calendar, dateLike, ToPlainDate(dateLike),
result);
}
/**
* CalendarWeekOfYear ( calendar, dateLike )
*/
bool js::temporal::CalendarWeekOfYear(JSContext* cx,
Handle<CalendarValue> calendar,
Handle<PlainDateTimeObject*> dateLike,
MutableHandle<Value> result) {
return CalendarWeekOfYear(cx, calendar, dateLike, ToPlainDate(dateLike),
result);
}
/**
* CalendarWeekOfYear ( calendar, dateLike )
*/
bool js::temporal::CalendarWeekOfYear(JSContext* cx,
Handle<CalendarValue> calendar,
const PlainDateTime& dateTime,
MutableHandle<Value> result) {
Rooted<PlainDateTimeObject*> dateLike(
cx, CreateTemporalDateTime(cx, dateTime, calendar));
if (!dateLike) {
return false;
}
return ::CalendarWeekOfYear(cx, calendar, dateLike, dateTime.date, result);
}
/**
* Temporal.Calendar.prototype.yearOfWeek ( temporalDateLike )
*/
static bool BuiltinCalendarYearOfWeek(JSContext* cx, const PlainDate& date,
MutableHandle<Value> result) {
// Steps 1-4. (Not applicable.)
// Steps 5-6.
result.setInt32(ToISOWeekOfYear(date).year);
return true;
}
static bool Calendar_yearOfWeek(JSContext* cx, unsigned argc, Value* vp);
/**
* CalendarYearOfWeek ( calendar, dateLike )
*/
static bool CalendarYearOfWeek(JSContext* cx, Handle<CalendarValue> calendar,
Handle<JSObject*> dateLike,
const PlainDate& date,
MutableHandle<Value> result) {
// Steps 1-5.
return CallCalendarMethod<BuiltinCalendarYearOfWeek, RequireIntegralNumber>(
cx, cx->names().yearOfWeek, Calendar_yearOfWeek, calendar, dateLike, date,
result);
}
/**
* CalendarYearOfWeek ( calendar, dateLike )
*/
bool js::temporal::CalendarYearOfWeek(JSContext* cx,
Handle<CalendarValue> calendar,
Handle<PlainDateObject*> dateLike,
MutableHandle<Value> result) {
return CalendarYearOfWeek(cx, calendar, dateLike, ToPlainDate(dateLike),
result);
}
/**
* CalendarYearOfWeek ( calendar, dateLike )
*/
bool js::temporal::CalendarYearOfWeek(JSContext* cx,
Handle<CalendarValue> calendar,
Handle<PlainDateTimeObject*> dateLike,
MutableHandle<Value> result) {
return CalendarYearOfWeek(cx, calendar, dateLike, ToPlainDate(dateLike),
result);
}
/**
* CalendarYearOfWeek ( calendar, dateLike )
*/
bool js::temporal::CalendarYearOfWeek(JSContext* cx,
Handle<CalendarValue> calendar,
const PlainDateTime& dateTime,
MutableHandle<Value> result) {
Rooted<PlainDateTimeObject*> dateLike(
cx, CreateTemporalDateTime(cx, dateTime, calendar));
if (!dateLike) {
return false;
}
return ::CalendarYearOfWeek(cx, calendar, dateLike, dateTime.date, result);
}
/**
* Temporal.Calendar.prototype.daysInWeek ( temporalDateLike )
*/
static bool BuiltinCalendarDaysInWeek(JSContext* cx, const PlainDate& date,
MutableHandle<Value> result) {
// Steps 1-4. (Not applicable.)
// Step 5.
result.setInt32(7);
return true;
}
static bool Calendar_daysInWeek(JSContext* cx, unsigned argc, Value* vp);
/**
* CalendarDaysInWeek ( calendar, dateLike )
*/
static bool CalendarDaysInWeek(JSContext* cx, Handle<CalendarValue> calendar,
Handle<JSObject*> dateLike,
const PlainDate& date,
MutableHandle<Value> result) {
// Steps 1-6.
return CallCalendarMethod<BuiltinCalendarDaysInWeek,
RequireIntegralPositiveNumber>(
cx, cx->names().daysInWeek, Calendar_daysInWeek, calendar, dateLike, date,
result);
}
/**
* CalendarDaysInWeek ( calendar, dateLike )
*/
bool js::temporal::CalendarDaysInWeek(JSContext* cx,
Handle<CalendarValue> calendar,
Handle<PlainDateObject*> dateLike,
MutableHandle<Value> result) {
return CalendarDaysInWeek(cx, calendar, dateLike, ToPlainDate(dateLike),
result);
}
/**
* CalendarDaysInWeek ( calendar, dateLike )
*/
bool js::temporal::CalendarDaysInWeek(JSContext* cx,
Handle<CalendarValue> calendar,
Handle<PlainDateTimeObject*> dateLike,
MutableHandle<Value> result) {
return CalendarDaysInWeek(cx, calendar, dateLike, ToPlainDate(dateLike),
result);
}
/**
* CalendarDaysInWeek ( calendar, dateLike )
*/
bool js::temporal::CalendarDaysInWeek(JSContext* cx,
Handle<CalendarValue> calendar,
const PlainDateTime& dateTime,
MutableHandle<Value> result) {
Rooted<PlainDateTimeObject*> dateLike(
cx, CreateTemporalDateTime(cx, dateTime, calendar));
if (!dateLike) {
return false;
}
return ::CalendarDaysInWeek(cx, calendar, dateLike, dateTime.date, result);
}
/**
* Temporal.Calendar.prototype.daysInMonth ( temporalDateLike )
*/
static bool BuiltinCalendarDaysInMonth(JSContext* cx, const PlainDate& date,
MutableHandle<Value> result) {
// Steps 1-4. (Not applicable.)
// Step 5.
result.setInt32(::ISODaysInMonth(date.year, date.month));
return true;
}
static bool Calendar_daysInMonth(JSContext* cx, unsigned argc, Value* vp);
/**
* CalendarDaysInMonth ( calendar, dateLike )
*/
static bool CalendarDaysInMonth(JSContext* cx, Handle<CalendarValue> calendar,
Handle<JSObject*> dateLike,
const PlainDate& date,
MutableHandle<Value> result) {
// Step 1-6.
return CallCalendarMethod<BuiltinCalendarDaysInMonth,
RequireIntegralPositiveNumber>(
cx, cx->names().daysInMonth, Calendar_daysInMonth, calendar, dateLike,
date, result);
}
/**
* CalendarDaysInMonth ( calendar, dateLike )
*/
bool js::temporal::CalendarDaysInMonth(JSContext* cx,
Handle<CalendarValue> calendar,
Handle<PlainDateObject*> dateLike,
MutableHandle<Value> result) {
return CalendarDaysInMonth(cx, calendar, dateLike, ToPlainDate(dateLike),
result);
}
/**
* CalendarDaysInMonth ( calendar, dateLike )
*/
bool js::temporal::CalendarDaysInMonth(JSContext* cx,
Handle<CalendarValue> calendar,
Handle<PlainDateTimeObject*> dateLike,
MutableHandle<Value> result) {
return CalendarDaysInMonth(cx, calendar, dateLike, ToPlainDate(dateLike),
result);
}
/**
* CalendarDaysInMonth ( calendar, dateLike )
*/
bool js::temporal::CalendarDaysInMonth(JSContext* cx,
Handle<CalendarValue> calendar,
Handle<PlainYearMonthObject*> dateLike,
MutableHandle<Value> result) {
return CalendarDaysInMonth(cx, calendar, dateLike, ToPlainDate(dateLike),
result);
}
/**
* CalendarDaysInMonth ( calendar, dateLike )
*/
bool js::temporal::CalendarDaysInMonth(JSContext* cx,
Handle<CalendarValue> calendar,
const PlainDateTime& dateTime,
MutableHandle<Value> result) {
Rooted<PlainDateTimeObject*> dateLike(
cx, CreateTemporalDateTime(cx, dateTime, calendar));
if (!dateLike) {
return false;
}
return ::CalendarDaysInMonth(cx, calendar, dateLike, dateTime.date, result);
}
/**
* Temporal.Calendar.prototype.daysInYear ( temporalDateLike )
*/
static bool BuiltinCalendarDaysInYear(JSContext* cx, const PlainDate& date,
MutableHandle<Value> result) {
// Steps 1-4. (Not applicable.)
// Step 5.
result.setInt32(ISODaysInYear(date.year));
return true;
}
static bool Calendar_daysInYear(JSContext* cx, unsigned argc, Value* vp);
/**
* CalendarDaysInYear ( calendar, dateLike )
*/
static bool CalendarDaysInYear(JSContext* cx, Handle<CalendarValue> calendar,
Handle<JSObject*> dateLike,
const PlainDate& date,
MutableHandle<Value> result) {
// Step 1-6.
return CallCalendarMethod<BuiltinCalendarDaysInYear,
RequireIntegralPositiveNumber>(
cx, cx->names().daysInYear, Calendar_daysInYear, calendar, dateLike, date,
result);
}
/**
* CalendarDaysInYear ( calendar, dateLike )
*/
bool js::temporal::CalendarDaysInYear(JSContext* cx,
Handle<CalendarValue> calendar,
Handle<PlainDateObject*> dateLike,
MutableHandle<Value> result) {
return CalendarDaysInYear(cx, calendar, dateLike, ToPlainDate(dateLike),
result);
}
/**
* CalendarDaysInYear ( calendar, dateLike )
*/
bool js::temporal::CalendarDaysInYear(JSContext* cx,
Handle<CalendarValue> calendar,
Handle<PlainDateTimeObject*> dateLike,
MutableHandle<Value> result) {
return CalendarDaysInYear(cx, calendar, dateLike, ToPlainDate(dateLike),
result);
}
/**
* CalendarDaysInYear ( calendar, dateLike )
*/
bool js::temporal::CalendarDaysInYear(JSContext* cx,
Handle<CalendarValue> calendar,
Handle<PlainYearMonthObject*> dateLike,
MutableHandle<Value> result) {
return CalendarDaysInYear(cx, calendar, dateLike, ToPlainDate(dateLike),
result);
}
/**
* CalendarDaysInYear ( calendar, dateLike )
*/
bool js::temporal::CalendarDaysInYear(JSContext* cx,
Handle<CalendarValue> calendar,
const PlainDateTime& dateTime,
MutableHandle<Value> result) {
Rooted<PlainDateTimeObject*> dateLike(
cx, CreateTemporalDateTime(cx, dateTime, calendar));
if (!dateLike) {
return false;
}
return ::CalendarDaysInYear(cx, calendar, dateLike, dateTime.date, result);
}
/**
* Temporal.Calendar.prototype.monthsInYear ( temporalDateLike )
*/
static bool BuiltinCalendarMonthsInYear(JSContext* cx, const PlainDate& date,
MutableHandle<Value> result) {
// Steps 1-4. (Not applicable.)
// Step 5.
result.setInt32(12);
return true;
}
static bool Calendar_monthsInYear(JSContext* cx, unsigned argc, Value* vp);
/**
* CalendarMonthsInYear ( calendar, dateLike )
*/
static bool CalendarMonthsInYear(JSContext* cx, Handle<CalendarValue> calendar,
Handle<JSObject*> dateLike,
const PlainDate& date,
MutableHandle<Value> result) {
// Step 1-6.
return CallCalendarMethod<BuiltinCalendarMonthsInYear,
RequireIntegralPositiveNumber>(
cx, cx->names().monthsInYear, Calendar_monthsInYear, calendar, dateLike,
date, result);
}