summaryrefslogtreecommitdiffstats
path: root/js/src/builtin/temporal/PlainMonthDay.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/builtin/temporal/PlainMonthDay.cpp')
-rw-r--r--js/src/builtin/temporal/PlainMonthDay.cpp1000
1 files changed, 1000 insertions, 0 deletions
diff --git a/js/src/builtin/temporal/PlainMonthDay.cpp b/js/src/builtin/temporal/PlainMonthDay.cpp
new file mode 100644
index 0000000000..f97b7ad68c
--- /dev/null
+++ b/js/src/builtin/temporal/PlainMonthDay.cpp
@@ -0,0 +1,1000 @@
+/* -*- 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/PlainMonthDay.h"
+
+#include "mozilla/Assertions.h"
+
+#include <utility>
+
+#include "jsnum.h"
+#include "jspubtd.h"
+#include "NamespaceImports.h"
+
+#include "builtin/temporal/Calendar.h"
+#include "builtin/temporal/PlainDate.h"
+#include "builtin/temporal/PlainDateTime.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/ToString.h"
+#include "builtin/temporal/Wrapped.h"
+#include "builtin/temporal/ZonedDateTime.h"
+#include "ds/IdValuePair.h"
+#include "gc/AllocKind.h"
+#include "gc/Barrier.h"
+#include "js/AllocPolicy.h"
+#include "js/CallArgs.h"
+#include "js/CallNonGenericMethod.h"
+#include "js/Class.h"
+#include "js/ErrorReport.h"
+#include "js/friend/ErrorMessages.h"
+#include "js/GCVector.h"
+#include "js/Id.h"
+#include "js/PropertyDescriptor.h"
+#include "js/PropertySpec.h"
+#include "js/RootingAPI.h"
+#include "js/TypeDecls.h"
+#include "js/Value.h"
+#include "vm/BytecodeUtil.h"
+#include "vm/GlobalObject.h"
+#include "vm/JSAtomState.h"
+#include "vm/JSContext.h"
+#include "vm/JSObject.h"
+#include "vm/PlainObject.h"
+#include "vm/StringType.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 IsPlainMonthDay(Handle<Value> v) {
+ return v.isObject() && v.toObject().is<PlainMonthDayObject>();
+}
+
+/**
+ * CreateTemporalMonthDay ( isoMonth, isoDay, calendar, referenceISOYear [ ,
+ * newTarget ] )
+ */
+static PlainMonthDayObject* CreateTemporalMonthDay(
+ JSContext* cx, const CallArgs& args, double isoYear, double isoMonth,
+ double isoDay, Handle<CalendarValue> calendar) {
+ MOZ_ASSERT(IsInteger(isoYear));
+ MOZ_ASSERT(IsInteger(isoMonth));
+ MOZ_ASSERT(IsInteger(isoDay));
+
+ // Step 1.
+ if (!ThrowIfInvalidISODate(cx, isoYear, isoMonth, isoDay)) {
+ return nullptr;
+ }
+
+ // Step 2.
+ if (!ISODateTimeWithinLimits(isoYear, isoMonth, isoDay)) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_TEMPORAL_PLAIN_MONTH_DAY_INVALID);
+ return nullptr;
+ }
+
+ // Steps 3-4.
+ Rooted<JSObject*> proto(cx);
+ if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_PlainMonthDay,
+ &proto)) {
+ return nullptr;
+ }
+
+ auto* obj = NewObjectWithClassProto<PlainMonthDayObject>(cx, proto);
+ if (!obj) {
+ return nullptr;
+ }
+
+ // Step 5.
+ obj->setFixedSlot(PlainMonthDayObject::ISO_MONTH_SLOT, Int32Value(isoMonth));
+
+ // Step 6.
+ obj->setFixedSlot(PlainMonthDayObject::ISO_DAY_SLOT, Int32Value(isoDay));
+
+ // Step 7.
+ obj->setFixedSlot(PlainMonthDayObject::CALENDAR_SLOT, calendar.toValue());
+
+ // Step 8.
+ obj->setFixedSlot(PlainMonthDayObject::ISO_YEAR_SLOT, Int32Value(isoYear));
+
+ // Step 9.
+ return obj;
+}
+
+/**
+ * CreateTemporalMonthDay ( isoMonth, isoDay, calendar, referenceISOYear [ ,
+ * newTarget ] )
+ */
+PlainMonthDayObject* js::temporal::CreateTemporalMonthDay(
+ JSContext* cx, const PlainDate& date, Handle<CalendarValue> calendar) {
+ auto& [isoYear, isoMonth, isoDay] = date;
+
+ // Step 1.
+ if (!ThrowIfInvalidISODate(cx, date)) {
+ return nullptr;
+ }
+
+ // Step 2.
+ if (!ISODateTimeWithinLimits(date)) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_TEMPORAL_PLAIN_MONTH_DAY_INVALID);
+ return nullptr;
+ }
+
+ // Steps 3-4.
+ auto* obj = NewBuiltinClassInstance<PlainMonthDayObject>(cx);
+ if (!obj) {
+ return nullptr;
+ }
+
+ // Step 5.
+ obj->setFixedSlot(PlainMonthDayObject::ISO_MONTH_SLOT, Int32Value(isoMonth));
+
+ // Step 6.
+ obj->setFixedSlot(PlainMonthDayObject::ISO_DAY_SLOT, Int32Value(isoDay));
+
+ // Step 7.
+ obj->setFixedSlot(PlainMonthDayObject::CALENDAR_SLOT, calendar.toValue());
+
+ // Step 8.
+ obj->setFixedSlot(PlainMonthDayObject::ISO_YEAR_SLOT, Int32Value(isoYear));
+
+ // Step 9.
+ return obj;
+}
+
+template <typename T, typename... Ts>
+static bool ToTemporalCalendarForMonthDay(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 ToTemporalCalendarForMonthDay<Ts...>(cx, object, result);
+ }
+
+ result.set(CalendarValue());
+ return true;
+}
+
+/**
+ * ToTemporalMonthDay ( item [ , options ] )
+ */
+static Wrapped<PlainMonthDayObject*> ToTemporalMonthDay(
+ JSContext* cx, Handle<Value> item,
+ Handle<JSObject*> maybeOptions = nullptr) {
+ // Step 1. (Not applicable in our implementation.)
+
+ // Step 2.
+ Rooted<PlainObject*> maybeResolvedOptions(cx);
+ if (maybeOptions) {
+ maybeResolvedOptions = SnapshotOwnProperties(cx, maybeOptions);
+ if (!maybeResolvedOptions) {
+ return nullptr;
+ }
+ }
+
+ // Step 3.
+ if (item.isObject()) {
+ Rooted<JSObject*> itemObj(cx, &item.toObject());
+
+ // Step 3.a.
+ if (itemObj->canUnwrapAs<PlainMonthDayObject>()) {
+ return itemObj;
+ }
+
+ // Steps 3.b-c.
+ Rooted<CalendarValue> calendarValue(cx);
+ if (!::ToTemporalCalendarForMonthDay<PlainDateObject, PlainDateTimeObject,
+ PlainYearMonthObject,
+ ZonedDateTimeObject>(cx, itemObj,
+ &calendarValue)) {
+ return nullptr;
+ }
+ if (!calendarValue) {
+ // Step 3.c.i.
+ Rooted<Value> calendarLike(cx);
+ if (!GetProperty(cx, itemObj, itemObj, cx->names().calendar,
+ &calendarLike)) {
+ return nullptr;
+ }
+
+ // Step 3.c.ii.
+ if (!ToTemporalCalendarWithISODefault(cx, calendarLike, &calendarValue)) {
+ return nullptr;
+ }
+ }
+
+ // Step 3.d.
+ Rooted<CalendarRecord> calendar(cx);
+ if (!CreateCalendarMethodsRecord(cx, calendarValue,
+ {
+ CalendarMethod::Fields,
+ CalendarMethod::MonthDayFromFields,
+ },
+ &calendar)) {
+ return nullptr;
+ }
+
+ // Step 3.e.
+ JS::RootedVector<PropertyKey> fieldNames(cx);
+ if (!CalendarFields(cx, calendar,
+ {CalendarField::Day, CalendarField::Month,
+ CalendarField::MonthCode, CalendarField::Year},
+ &fieldNames)) {
+ return nullptr;
+ }
+
+ // Step 3.f.
+ Rooted<PlainObject*> fields(cx,
+ PrepareTemporalFields(cx, itemObj, fieldNames));
+ if (!fields) {
+ return nullptr;
+ }
+
+ // Step 3.g.
+ if (maybeResolvedOptions) {
+ return js::temporal::CalendarMonthDayFromFields(cx, calendar, fields,
+ maybeResolvedOptions);
+ }
+ return js::temporal::CalendarMonthDayFromFields(cx, calendar, fields);
+ }
+
+ // Step 4.
+ if (!item.isString()) {
+ ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, item,
+ nullptr, "not a string");
+ return nullptr;
+ }
+ Rooted<JSString*> string(cx, item.toString());
+
+ // Step 5.
+ PlainDate result;
+ bool hasYear;
+ Rooted<JSString*> calendarString(cx);
+ if (!ParseTemporalMonthDayString(cx, string, &result, &hasYear,
+ &calendarString)) {
+ return nullptr;
+ }
+
+ // Steps 6-9.
+ Rooted<CalendarValue> calendarValue(cx, CalendarValue(cx->names().iso8601));
+ if (calendarString) {
+ if (!ToBuiltinCalendar(cx, calendarString, &calendarValue)) {
+ return nullptr;
+ }
+ }
+
+ // Step 10.
+ if (maybeResolvedOptions) {
+ TemporalOverflow ignored;
+ if (!ToTemporalOverflow(cx, maybeResolvedOptions, &ignored)) {
+ return nullptr;
+ }
+ }
+
+ // Step 11.
+ if (!hasYear) {
+ // Step 11.a.
+ MOZ_ASSERT(calendarValue.isString() &&
+ EqualStrings(calendarValue.toString(), cx->names().iso8601));
+
+ // Step 11.b.
+ constexpr int32_t referenceISOYear = 1972;
+
+ // Step 11.a.
+ return CreateTemporalMonthDay(
+ cx, {referenceISOYear, result.month, result.day}, calendarValue);
+ }
+
+ // Step 12.
+ Rooted<PlainMonthDayObject*> obj(
+ cx, CreateTemporalMonthDay(cx, result, calendarValue));
+ if (!obj) {
+ return nullptr;
+ }
+
+ // FIXME: spec bug - missing call to CreateCalendarMethodsRecord
+
+ // Step 13.
+ Rooted<CalendarRecord> calendar(cx);
+ if (!CreateCalendarMethodsRecord(cx, calendarValue,
+ {
+ CalendarMethod::MonthDayFromFields,
+ },
+ &calendar)) {
+ return nullptr;
+ }
+
+ // Steps 14-15.
+ return CalendarMonthDayFromFields(cx, calendar, obj);
+}
+
+/**
+ * ToTemporalMonthDay ( item [ , options ] )
+ */
+static bool ToTemporalMonthDay(JSContext* cx, Handle<Value> item,
+ PlainDate* result,
+ MutableHandle<CalendarValue> calendar) {
+ auto* obj = ToTemporalMonthDay(cx, item).unwrapOrNull();
+ if (!obj) {
+ return false;
+ }
+
+ *result = ToPlainDate(obj);
+ calendar.set(obj->calendar());
+ return calendar.wrap(cx);
+}
+
+/**
+ * Temporal.PlainMonthDay ( isoMonth, isoDay [ , calendarLike [ ,
+ * referenceISOYear ] ] )
+ */
+static bool PlainMonthDayConstructor(JSContext* cx, unsigned argc, Value* vp) {
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ // Step 1.
+ if (!ThrowIfNotConstructing(cx, args, "Temporal.PlainMonthDay")) {
+ return false;
+ }
+
+ // Step 3.
+ double isoMonth;
+ if (!ToIntegerWithTruncation(cx, args.get(0), "month", &isoMonth)) {
+ return false;
+ }
+
+ // Step 4.
+ double isoDay;
+ if (!ToIntegerWithTruncation(cx, args.get(1), "day", &isoDay)) {
+ return false;
+ }
+
+ // Step 5.
+ Rooted<CalendarValue> calendar(cx);
+ if (!ToTemporalCalendarWithISODefault(cx, args.get(2), &calendar)) {
+ return false;
+ }
+
+ // Steps 2 and 6.
+ double isoYear = 1972;
+ if (args.hasDefined(3)) {
+ if (!ToIntegerWithTruncation(cx, args[3], "year", &isoYear)) {
+ return false;
+ }
+ }
+
+ // Step 7.
+ auto* monthDay =
+ CreateTemporalMonthDay(cx, args, isoYear, isoMonth, isoDay, calendar);
+ if (!monthDay) {
+ return false;
+ }
+
+ args.rval().setObject(*monthDay);
+ return true;
+}
+
+/**
+ * Temporal.PlainMonthDay.from ( item [ , options ] )
+ */
+static bool PlainMonthDay_from(JSContext* cx, unsigned argc, Value* vp) {
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ // Step 1.
+ Rooted<JSObject*> options(cx);
+ if (args.hasDefined(1)) {
+ options = RequireObjectArg(cx, "options", "from", args[1]);
+ if (!options) {
+ return false;
+ }
+ }
+
+ // Step 2.
+ if (args.get(0).isObject()) {
+ JSObject* item = &args[0].toObject();
+
+ if (auto* monthDay = item->maybeUnwrapIf<PlainMonthDayObject>()) {
+ auto date = ToPlainDate(monthDay);
+
+ Rooted<CalendarValue> calendar(cx, monthDay->calendar());
+ if (!calendar.wrap(cx)) {
+ return false;
+ }
+
+ if (options) {
+ // Step 2.a.
+ TemporalOverflow ignored;
+ if (!ToTemporalOverflow(cx, options, &ignored)) {
+ return false;
+ }
+ }
+
+ // Step 2.b.
+ auto* obj = CreateTemporalMonthDay(cx, date, calendar);
+ if (!obj) {
+ return false;
+ }
+
+ args.rval().setObject(*obj);
+ return true;
+ }
+ }
+
+ // Step 3.
+ auto obj = ToTemporalMonthDay(cx, args.get(0), options);
+ if (!obj) {
+ return false;
+ }
+
+ args.rval().setObject(*obj);
+ return true;
+}
+
+/**
+ * get Temporal.PlainMonthDay.prototype.calendarId
+ */
+static bool PlainMonthDay_calendarId(JSContext* cx, const CallArgs& args) {
+ auto* monthDay = &args.thisv().toObject().as<PlainMonthDayObject>();
+
+ // Step 3.
+ Rooted<CalendarValue> calendar(cx, monthDay->calendar());
+ auto* calendarId = ToTemporalCalendarIdentifier(cx, calendar);
+ if (!calendarId) {
+ return false;
+ }
+
+ args.rval().setString(calendarId);
+ return true;
+}
+
+/**
+ * get Temporal.PlainMonthDay.prototype.calendarId
+ */
+static bool PlainMonthDay_calendarId(JSContext* cx, unsigned argc, Value* vp) {
+ // Steps 1-2.
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<IsPlainMonthDay, PlainMonthDay_calendarId>(cx,
+ args);
+}
+
+/**
+ * get Temporal.PlainMonthDay.prototype.monthCode
+ */
+static bool PlainMonthDay_monthCode(JSContext* cx, const CallArgs& args) {
+ // Step 3.
+ Rooted<PlainMonthDayObject*> monthDay(
+ cx, &args.thisv().toObject().as<PlainMonthDayObject>());
+ Rooted<CalendarValue> calendar(cx, monthDay->calendar());
+
+ // Step 4.
+ return CalendarMonthCode(cx, calendar, monthDay, args.rval());
+}
+
+/**
+ * get Temporal.PlainMonthDay.prototype.monthCode
+ */
+static bool PlainMonthDay_monthCode(JSContext* cx, unsigned argc, Value* vp) {
+ // Steps 1-2.
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<IsPlainMonthDay, PlainMonthDay_monthCode>(cx,
+ args);
+}
+
+/**
+ * get Temporal.PlainMonthDay.prototype.day
+ */
+static bool PlainMonthDay_day(JSContext* cx, const CallArgs& args) {
+ // Step 3.
+ Rooted<PlainMonthDayObject*> monthDay(
+ cx, &args.thisv().toObject().as<PlainMonthDayObject>());
+ Rooted<CalendarValue> calendar(cx, monthDay->calendar());
+
+ // Step 4.
+ return CalendarDay(cx, calendar, monthDay, args.rval());
+}
+
+/**
+ * get Temporal.PlainMonthDay.prototype.day
+ */
+static bool PlainMonthDay_day(JSContext* cx, unsigned argc, Value* vp) {
+ // Steps 1-2.
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<IsPlainMonthDay, PlainMonthDay_day>(cx, args);
+}
+
+/**
+ * Temporal.PlainMonthDay.prototype.with ( temporalMonthDayLike [ , options ] )
+ */
+static bool PlainMonthDay_with(JSContext* cx, const CallArgs& args) {
+ Rooted<PlainMonthDayObject*> monthDay(
+ cx, &args.thisv().toObject().as<PlainMonthDayObject>());
+ Rooted<CalendarValue> calendarValue(cx, monthDay->calendar());
+
+ // Step 3.
+ Rooted<JSObject*> temporalMonthDayLike(
+ cx, RequireObjectArg(cx, "temporalMonthDayLike", "with", args.get(0)));
+ if (!temporalMonthDayLike) {
+ return false;
+ }
+
+ // Step 4.
+ if (!RejectTemporalLikeObject(cx, temporalMonthDayLike)) {
+ return false;
+ }
+
+ // Step 5.
+ Rooted<PlainObject*> resolvedOptions(cx);
+ if (args.hasDefined(1)) {
+ Rooted<JSObject*> options(cx,
+ RequireObjectArg(cx, "options", "with", args[1]));
+ if (!options) {
+ return false;
+ }
+ resolvedOptions = SnapshotOwnProperties(cx, options);
+ } else {
+ resolvedOptions = NewPlainObjectWithProto(cx, nullptr);
+ }
+ if (!resolvedOptions) {
+ return false;
+ }
+
+ // Step 6.
+ Rooted<CalendarRecord> calendar(cx);
+ if (!CreateCalendarMethodsRecord(cx, calendarValue,
+ {
+ CalendarMethod::Fields,
+ CalendarMethod::MergeFields,
+ CalendarMethod::MonthDayFromFields,
+ },
+ &calendar)) {
+ return false;
+ }
+
+ // Step 7.
+ JS::RootedVector<PropertyKey> fieldNames(cx);
+ if (!CalendarFields(cx, calendar,
+ {CalendarField::Day, CalendarField::Month,
+ CalendarField::MonthCode, CalendarField::Year},
+ &fieldNames)) {
+ return false;
+ }
+
+ // Step 8.
+ Rooted<PlainObject*> fields(cx,
+ PrepareTemporalFields(cx, monthDay, fieldNames));
+ if (!fields) {
+ return false;
+ }
+
+ // Step 9.
+ Rooted<PlainObject*> partialMonthDay(
+ cx, PreparePartialTemporalFields(cx, temporalMonthDayLike, fieldNames));
+ if (!partialMonthDay) {
+ return false;
+ }
+
+ // Step 10.
+ Rooted<JSObject*> mergedFields(
+ cx, CalendarMergeFields(cx, calendar, fields, partialMonthDay));
+ if (!mergedFields) {
+ return false;
+ }
+
+ // Step 11.
+ fields = PrepareTemporalFields(cx, mergedFields, fieldNames);
+ if (!fields) {
+ return false;
+ }
+
+ // Step 12.
+ auto obj = js::temporal::CalendarMonthDayFromFields(cx, calendar, fields,
+ resolvedOptions);
+ if (!obj) {
+ return false;
+ }
+
+ args.rval().setObject(*obj);
+ return true;
+}
+
+/**
+ * Temporal.PlainMonthDay.prototype.with ( temporalMonthDayLike [ , options ] )
+ */
+static bool PlainMonthDay_with(JSContext* cx, unsigned argc, Value* vp) {
+ // Steps 1-2.
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<IsPlainMonthDay, PlainMonthDay_with>(cx, args);
+}
+
+/**
+ * Temporal.PlainMonthDay.prototype.equals ( other )
+ */
+static bool PlainMonthDay_equals(JSContext* cx, const CallArgs& args) {
+ auto* monthDay = &args.thisv().toObject().as<PlainMonthDayObject>();
+ auto date = ToPlainDate(monthDay);
+ Rooted<CalendarValue> calendar(cx, monthDay->calendar());
+
+ // Step 3.
+ PlainDate other;
+ Rooted<CalendarValue> otherCalendar(cx);
+ if (!ToTemporalMonthDay(cx, args.get(0), &other, &otherCalendar)) {
+ return false;
+ }
+
+ // Steps 4-7.
+ bool equals = date == other;
+ if (equals && !CalendarEquals(cx, calendar, otherCalendar, &equals)) {
+ return false;
+ }
+
+ args.rval().setBoolean(equals);
+ return true;
+}
+
+/**
+ * Temporal.PlainMonthDay.prototype.equals ( other )
+ */
+static bool PlainMonthDay_equals(JSContext* cx, unsigned argc, Value* vp) {
+ // Steps 1-2.
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<IsPlainMonthDay, PlainMonthDay_equals>(cx, args);
+}
+
+/**
+ * Temporal.PlainMonthDay.prototype.toString ( [ options ] )
+ */
+static bool PlainMonthDay_toString(JSContext* cx, const CallArgs& args) {
+ Rooted<PlainMonthDayObject*> monthDay(
+ cx, &args.thisv().toObject().as<PlainMonthDayObject>());
+
+ auto showCalendar = CalendarOption::Auto;
+ if (args.hasDefined(0)) {
+ // Step 3.
+ Rooted<JSObject*> options(
+ cx, RequireObjectArg(cx, "options", "toString", args[0]));
+ if (!options) {
+ return false;
+ }
+
+ // Step 4.
+ if (!ToCalendarNameOption(cx, options, &showCalendar)) {
+ return false;
+ }
+ }
+
+ // Step 5.
+ JSString* str = TemporalMonthDayToString(cx, monthDay, showCalendar);
+ if (!str) {
+ return false;
+ }
+
+ args.rval().setString(str);
+ return true;
+}
+
+/**
+ * Temporal.PlainMonthDay.prototype.toString ( [ options ] )
+ */
+static bool PlainMonthDay_toString(JSContext* cx, unsigned argc, Value* vp) {
+ // Steps 1-2.
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<IsPlainMonthDay, PlainMonthDay_toString>(cx,
+ args);
+}
+
+/**
+ * Temporal.PlainMonthDay.prototype.toLocaleString ( [ locales [ , options ] ] )
+ */
+static bool PlainMonthDay_toLocaleString(JSContext* cx, const CallArgs& args) {
+ Rooted<PlainMonthDayObject*> monthDay(
+ cx, &args.thisv().toObject().as<PlainMonthDayObject>());
+
+ // Step 3.
+ JSString* str = TemporalMonthDayToString(cx, monthDay, CalendarOption::Auto);
+ if (!str) {
+ return false;
+ }
+
+ args.rval().setString(str);
+ return true;
+}
+
+/**
+ * Temporal.PlainMonthDay.prototype.toLocaleString ( [ locales [ , options ] ] )
+ */
+static bool PlainMonthDay_toLocaleString(JSContext* cx, unsigned argc,
+ Value* vp) {
+ // Steps 1-2.
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<IsPlainMonthDay, PlainMonthDay_toLocaleString>(
+ cx, args);
+}
+
+/**
+ * Temporal.PlainMonthDay.prototype.toJSON ( )
+ */
+static bool PlainMonthDay_toJSON(JSContext* cx, const CallArgs& args) {
+ Rooted<PlainMonthDayObject*> monthDay(
+ cx, &args.thisv().toObject().as<PlainMonthDayObject>());
+
+ // Step 3.
+ JSString* str = TemporalMonthDayToString(cx, monthDay, CalendarOption::Auto);
+ if (!str) {
+ return false;
+ }
+
+ args.rval().setString(str);
+ return true;
+}
+
+/**
+ * Temporal.PlainMonthDay.prototype.toJSON ( )
+ */
+static bool PlainMonthDay_toJSON(JSContext* cx, unsigned argc, Value* vp) {
+ // Steps 1-2.
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<IsPlainMonthDay, PlainMonthDay_toJSON>(cx, args);
+}
+
+/**
+ * Temporal.PlainMonthDay.prototype.valueOf ( )
+ */
+static bool PlainMonthDay_valueOf(JSContext* cx, unsigned argc, Value* vp) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
+ "PlainMonthDay", "primitive type");
+ return false;
+}
+
+/**
+ * Temporal.PlainMonthDay.prototype.toPlainDate ( item )
+ */
+static bool PlainMonthDay_toPlainDate(JSContext* cx, const CallArgs& args) {
+ Rooted<PlainMonthDayObject*> monthDay(
+ cx, &args.thisv().toObject().as<PlainMonthDayObject>());
+
+ // Step 3.
+ Rooted<JSObject*> item(
+ cx, RequireObjectArg(cx, "item", "toPlainDate", args.get(0)));
+ if (!item) {
+ return false;
+ }
+
+ // Step 4.
+ Rooted<CalendarValue> calendarValue(cx, monthDay->calendar());
+ Rooted<CalendarRecord> calendar(cx);
+ if (!CreateCalendarMethodsRecord(cx, calendarValue,
+ {
+ CalendarMethod::DateFromFields,
+ CalendarMethod::Fields,
+ CalendarMethod::MergeFields,
+ },
+ &calendar)) {
+ return false;
+ }
+
+ // Step 5.
+ JS::RootedVector<PropertyKey> receiverFieldNames(cx);
+ if (!CalendarFields(cx, calendar,
+ {CalendarField::Day, CalendarField::MonthCode},
+ &receiverFieldNames)) {
+ return false;
+ }
+
+ // Step 6.
+ Rooted<PlainObject*> fields(
+ cx, PrepareTemporalFields(cx, monthDay, receiverFieldNames));
+ if (!fields) {
+ return false;
+ }
+
+ // Step 7.
+ JS::RootedVector<PropertyKey> inputFieldNames(cx);
+ if (!CalendarFields(cx, calendar, {CalendarField::Year}, &inputFieldNames)) {
+ return false;
+ }
+
+ // Step 8.
+ Rooted<PlainObject*> inputFields(
+ cx, PrepareTemporalFields(cx, item, inputFieldNames));
+ if (!inputFields) {
+ return false;
+ }
+
+ // Step 9.
+ Rooted<JSObject*> mergedFields(
+ cx, CalendarMergeFields(cx, calendar, fields, inputFields));
+ if (!mergedFields) {
+ return false;
+ }
+
+ // Step 10.
+ JS::RootedVector<PropertyKey> concatenatedFieldNames(cx);
+ if (!ConcatTemporalFieldNames(receiverFieldNames, inputFieldNames,
+ concatenatedFieldNames.get())) {
+ return false;
+ }
+
+ // Step 11.
+ Rooted<PlainObject*> mergedFromConcatenatedFields(
+ cx, PrepareTemporalFields(cx, mergedFields, concatenatedFieldNames));
+ if (!mergedFromConcatenatedFields) {
+ return false;
+ }
+
+ // Step 12.
+ Rooted<PlainObject*> options(cx, NewPlainObjectWithProto(cx, nullptr));
+ if (!options) {
+ return false;
+ }
+
+ // Step 13.
+ Rooted<Value> overflow(cx, StringValue(cx->names().constrain));
+ if (!DefineDataProperty(cx, options, cx->names().overflow, overflow)) {
+ return false;
+ }
+
+ // Step 14.
+ auto obj = js::temporal::CalendarDateFromFields(
+ cx, calendar, mergedFromConcatenatedFields, options);
+ if (!obj) {
+ return false;
+ }
+
+ args.rval().setObject(*obj);
+ return true;
+}
+
+/**
+ * Temporal.PlainMonthDay.prototype.toPlainDate ( item )
+ */
+static bool PlainMonthDay_toPlainDate(JSContext* cx, unsigned argc, Value* vp) {
+ // Steps 1-2.
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<IsPlainMonthDay, PlainMonthDay_toPlainDate>(cx,
+ args);
+}
+
+/**
+ * Temporal.PlainMonthDay.prototype.getISOFields ( )
+ */
+static bool PlainMonthDay_getISOFields(JSContext* cx, const CallArgs& args) {
+ Rooted<PlainMonthDayObject*> monthDay(
+ cx, &args.thisv().toObject().as<PlainMonthDayObject>());
+
+ // Step 3.
+ Rooted<IdValueVector> fields(cx, IdValueVector(cx));
+
+ // Step 4.
+ if (!fields.emplaceBack(NameToId(cx->names().calendar),
+ monthDay->calendar().toValue())) {
+ return false;
+ }
+
+ // Step 5.
+ if (!fields.emplaceBack(NameToId(cx->names().isoDay),
+ Int32Value(monthDay->isoDay()))) {
+ return false;
+ }
+
+ // Step 6.
+ if (!fields.emplaceBack(NameToId(cx->names().isoMonth),
+ Int32Value(monthDay->isoMonth()))) {
+ return false;
+ }
+
+ // Step 7.
+ if (!fields.emplaceBack(NameToId(cx->names().isoYear),
+ Int32Value(monthDay->isoYear()))) {
+ return false;
+ }
+
+ // Step 8.
+ auto* obj =
+ NewPlainObjectWithUniqueNames(cx, fields.begin(), fields.length());
+ if (!obj) {
+ return false;
+ }
+
+ args.rval().setObject(*obj);
+ return true;
+}
+
+/**
+ * Temporal.PlainMonthDay.prototype.getISOFields ( )
+ */
+static bool PlainMonthDay_getISOFields(JSContext* cx, unsigned argc,
+ Value* vp) {
+ // Steps 1-2.
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<IsPlainMonthDay, PlainMonthDay_getISOFields>(
+ cx, args);
+}
+
+/**
+ * Temporal.PlainMonthDay.prototype.getCalendar ( )
+ */
+static bool PlainMonthDay_getCalendar(JSContext* cx, const CallArgs& args) {
+ auto* monthDay = &args.thisv().toObject().as<PlainMonthDayObject>();
+ Rooted<CalendarValue> calendar(cx, monthDay->calendar());
+
+ // Step 3.
+ auto* obj = ToTemporalCalendarObject(cx, calendar);
+ if (!obj) {
+ return false;
+ }
+
+ args.rval().setObject(*obj);
+ return true;
+}
+
+/**
+ * Temporal.PlainMonthDay.prototype.getCalendar ( )
+ */
+static bool PlainMonthDay_getCalendar(JSContext* cx, unsigned argc, Value* vp) {
+ // Steps 1-2.
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<IsPlainMonthDay, PlainMonthDay_getCalendar>(cx,
+ args);
+}
+
+const JSClass PlainMonthDayObject::class_ = {
+ "Temporal.PlainMonthDay",
+ JSCLASS_HAS_RESERVED_SLOTS(PlainMonthDayObject::SLOT_COUNT) |
+ JSCLASS_HAS_CACHED_PROTO(JSProto_PlainMonthDay),
+ JS_NULL_CLASS_OPS,
+ &PlainMonthDayObject::classSpec_,
+};
+
+const JSClass& PlainMonthDayObject::protoClass_ = PlainObject::class_;
+
+static const JSFunctionSpec PlainMonthDay_methods[] = {
+ JS_FN("from", PlainMonthDay_from, 1, 0),
+ JS_FS_END,
+};
+
+static const JSFunctionSpec PlainMonthDay_prototype_methods[] = {
+ JS_FN("with", PlainMonthDay_with, 1, 0),
+ JS_FN("equals", PlainMonthDay_equals, 1, 0),
+ JS_FN("toString", PlainMonthDay_toString, 0, 0),
+ JS_FN("toLocaleString", PlainMonthDay_toLocaleString, 0, 0),
+ JS_FN("toJSON", PlainMonthDay_toJSON, 0, 0),
+ JS_FN("valueOf", PlainMonthDay_valueOf, 0, 0),
+ JS_FN("toPlainDate", PlainMonthDay_toPlainDate, 1, 0),
+ JS_FN("getISOFields", PlainMonthDay_getISOFields, 0, 0),
+ JS_FN("getCalendar", PlainMonthDay_getCalendar, 0, 0),
+ JS_FS_END,
+};
+
+static const JSPropertySpec PlainMonthDay_prototype_properties[] = {
+ JS_PSG("calendarId", PlainMonthDay_calendarId, 0),
+ JS_PSG("monthCode", PlainMonthDay_monthCode, 0),
+ JS_PSG("day", PlainMonthDay_day, 0),
+ JS_STRING_SYM_PS(toStringTag, "Temporal.PlainMonthDay", JSPROP_READONLY),
+ JS_PS_END,
+};
+
+const ClassSpec PlainMonthDayObject::classSpec_ = {
+ GenericCreateConstructor<PlainMonthDayConstructor, 2,
+ gc::AllocKind::FUNCTION>,
+ GenericCreatePrototype<PlainMonthDayObject>,
+ PlainMonthDay_methods,
+ nullptr,
+ PlainMonthDay_prototype_methods,
+ PlainMonthDay_prototype_properties,
+ nullptr,
+ ClassSpec::DontDefineConstructor,
+};