diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:43:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:43:14 +0000 |
commit | 8dd16259287f58f9273002717ec4d27e97127719 (patch) | |
tree | 3863e62a53829a84037444beab3abd4ed9dfc7d0 /js/src/builtin/temporal/Calendar.cpp | |
parent | Releasing progress-linux version 126.0.1-1~progress7.99u1. (diff) | |
download | firefox-8dd16259287f58f9273002717ec4d27e97127719.tar.xz firefox-8dd16259287f58f9273002717ec4d27e97127719.zip |
Merging upstream version 127.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/builtin/temporal/Calendar.cpp')
-rw-r--r-- | js/src/builtin/temporal/Calendar.cpp | 559 |
1 files changed, 374 insertions, 185 deletions
diff --git a/js/src/builtin/temporal/Calendar.cpp b/js/src/builtin/temporal/Calendar.cpp index 8a06877093..573a1740d2 100644 --- a/js/src/builtin/temporal/Calendar.cpp +++ b/js/src/builtin/temporal/Calendar.cpp @@ -137,18 +137,22 @@ static bool IterableToListOfStrings(JSContext* cx, Handle<Value> items, // Step 1. (Not applicable in our implementation.) - // Steps 2-3. + // Step 2. Rooted<Value> nextValue(cx); Rooted<PropertyKey> value(cx); while (true) { + // Step 2.a. bool done; if (!iterator.next(&nextValue, &done)) { return false; } + + // Step 2.b. if (done) { - break; + return true; } + // Step 2.d. (Reordered) if (nextValue.isString()) { if (!PrimitiveValueToId<CanGC>(cx, nextValue, &value)) { return false; @@ -159,15 +163,14 @@ static bool IterableToListOfStrings(JSContext* cx, Handle<Value> items, continue; } + // Step 2.c.1. ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, nextValue, nullptr, "not a string"); + // Step 2.c.2. iterator.closeThrow(); return false; } - - // Step 4. - return true; } /** @@ -312,7 +315,7 @@ int32_t js::temporal::ToISODayOfYear(const PlainDate& date) { MOZ_ASSERT(ISODateTimeWithinLimits(date)); // Steps 1-5. - auto& [year, month, day] = date; + const auto& [year, month, day] = date; return ::ToISODayOfYear(year, month, day); } @@ -423,7 +426,7 @@ struct YearWeek final { static YearWeek ToISOWeekOfYear(const PlainDate& date) { MOZ_ASSERT(ISODateTimeWithinLimits(date)); - auto& [year, month, day] = date; + const auto& [year, month, day] = date; // TODO: https://en.wikipedia.org/wiki/Week#The_ISO_week_date_system // TODO: https://en.wikipedia.org/wiki/ISO_week_date#Algorithms @@ -1054,6 +1057,88 @@ static bool BuiltinCalendarFields( return true; } +/** + * Temporal.Calendar.prototype.fields ( fields ) + */ +static bool BuiltinCalendarFields(JSContext* cx, Handle<Value> fields, + MutableHandle<Value> result) { + // Step 4. + JS::ForOfIterator iterator(cx); + if (!iterator.init(fields)) { + return false; + } + + // Step 5. + JS::RootedVector<Value> fieldNames(cx); + mozilla::EnumSet<CalendarField> seen; + + // Step 6. + Rooted<Value> nextValue(cx); + Rooted<JSLinearString*> linear(cx); + while (true) { + // Step 6.a. + bool done; + if (!iterator.next(&nextValue, &done)) { + return false; + } + + // Step 6.b. + if (done) { + auto* array = + NewDenseCopiedArray(cx, fieldNames.length(), fieldNames.begin()); + if (!array) { + return false; + } + + result.setObject(*array); + return true; + } + + // Step 6.c. + if (!nextValue.isString()) { + // Step 6.c.1. + ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, nextValue, + nullptr, "not a string"); + + // Step 6.c.2. + iterator.closeThrow(); + return false; + } + + linear = nextValue.toString()->ensureLinear(cx); + if (!linear) { + return false; + } + + // Step 6.e. (Reordered) + CalendarField field; + if (!ToCalendarField(cx, linear, &field)) { + iterator.closeThrow(); + return false; + } + + // Step 6.d. + if (seen.contains(field)) { + // Step 6.d.1. + if (auto chars = QuoteString(cx, linear, '"')) { + JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, + JSMSG_TEMPORAL_CALENDAR_DUPLICATE_FIELD, + chars.get()); + } + + // Step 6.d.2. + iterator.closeThrow(); + return false; + } + + // Step 6.f. + if (!fieldNames.append(nextValue)) { + return false; + } + seen += field; + } +} + #ifdef DEBUG static bool IsSorted(std::initializer_list<CalendarField> fieldNames) { return std::is_sorted(fieldNames.begin(), fieldNames.end(), @@ -1075,15 +1160,12 @@ bool js::temporal::CalendarFields( MOZ_ASSERT( CalendarMethodsRecordHasLookedUp(calendar, CalendarMethod::Fields)); - // FIXME: spec issue - the input is already sorted, let's assert this, too. + // Step 1. 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. + // Step 2. auto fields = calendar.fields(); if (!fields) { bool arrayIterationSane; @@ -1098,16 +1180,14 @@ bool js::temporal::CalendarFields( } if (arrayIterationSane) { - // Steps 1.a-b. (Not applicable in our implementation.) - - // Step 1.c. + // Steps 2.a-b. return BuiltinCalendarFields(cx, fieldNames, result.get()); - // Steps 1.d-f. (Not applicable in our implementation.) + // Steps 2.c-e. (Not applicable in our implementation.) } } - // Step 2. (Inlined call to CalendarMethodsRecordCall.) + // Step 3. (Inlined call to CalendarMethodsRecordCall.) auto* array = NewDenseFullyAllocatedArray(cx, fieldNames.size()); if (!array) { @@ -1120,15 +1200,22 @@ bool js::temporal::CalendarFields( 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; + Rooted<Value> calendarFieldNames(cx); + if (fields) { + Rooted<Value> fieldsFn(cx, ObjectValue(*fields)); + auto thisv = calendar.receiver().toValue(); + if (!Call(cx, fieldsFn, thisv, fieldsArray, &calendarFieldNames)) { + return false; + } + } else { + if (!BuiltinCalendarFields(cx, fieldsArray, &calendarFieldNames)) { + return false; + } } - // Steps 3-4. - if (!IterableToListOfStrings(cx, fieldsArray, result)) { + // Steps 4-5. + if (!IterableToListOfStrings(cx, calendarFieldNames, result)) { return false; } @@ -1184,6 +1271,26 @@ static bool RequireIntegralPositiveNumber(JSContext* cx, Handle<Value> value, return true; } +static bool RequireIntegralNumberOrUndefined(JSContext* cx, Handle<Value> value, + Handle<PropertyName*> name, + MutableHandle<Value> result) { + if (value.isUndefined()) { + result.setUndefined(); + return true; + } + return RequireIntegralNumber(cx, value, name, result); +} + +static bool RequireIntegralPositiveNumberOrUndefined( + JSContext* cx, Handle<Value> value, Handle<PropertyName*> name, + MutableHandle<Value> result) { + if (value.isUndefined()) { + result.setUndefined(); + return true; + } + return RequireIntegralPositiveNumber(cx, value, name, result); +} + static bool RequireString(JSContext* cx, Handle<Value> value, Handle<PropertyName*> name, MutableHandle<Value> result) { @@ -1756,7 +1863,7 @@ static bool CalendarWeekOfYear(JSContext* cx, Handle<CalendarValue> calendar, MutableHandle<Value> result) { // Steps 1-6. return CallCalendarMethod<BuiltinCalendarWeekOfYear, - RequireIntegralPositiveNumber>( + RequireIntegralPositiveNumberOrUndefined>( cx, cx->names().weekOfYear, Calendar_weekOfYear, calendar, dateLike, date, result); } @@ -1821,7 +1928,8 @@ static bool CalendarYearOfWeek(JSContext* cx, Handle<CalendarValue> calendar, const PlainDate& date, MutableHandle<Value> result) { // Steps 1-5. - return CallCalendarMethod<BuiltinCalendarYearOfWeek, RequireIntegralNumber>( + return CallCalendarMethod<BuiltinCalendarYearOfWeek, + RequireIntegralNumberOrUndefined>( cx, cx->names().yearOfWeek, Calendar_yearOfWeek, calendar, dateLike, date, result); } @@ -2482,8 +2590,8 @@ Wrapped<PlainDateObject*> js::temporal::CalendarDateFromFields( } struct RegulatedISOYearMonth final { - double year; - int32_t month; + double year = 0; + int32_t month = 0; }; /** @@ -2492,33 +2600,30 @@ struct RegulatedISOYearMonth final { static bool RegulateISOYearMonth(JSContext* cx, double year, double month, TemporalOverflow overflow, RegulatedISOYearMonth* result) { - // Step 1. MOZ_ASSERT(IsInteger(year)); MOZ_ASSERT(IsInteger(month)); - // Step 2. (Not applicable in our implementation.) - - // Step 3. + // Step 1. if (overflow == TemporalOverflow::Constrain) { - // Step 3.a. + // Step 1.a. month = std::clamp(month, 1.0, 12.0); - // Step 3.b. + // Step 1.b. *result = {year, int32_t(month)}; return true; } - // Step 4.a. + // Step 2.a. MOZ_ASSERT(overflow == TemporalOverflow::Reject); - // Step 4.b. + // Step 2.b. if (month < 1 || month > 12) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TEMPORAL_PLAIN_YEAR_MONTH_INVALID); return false; } - // Step 4.c. + // Step 2.c. *result = {year, int32_t(month)}; return true; } @@ -2850,7 +2955,7 @@ static bool ISOFieldKeysToIgnore(JSContext* cx, const PropertyVector& keys, // Step 2. bool seenMonthOrMonthCode = false; - for (auto& key : keys) { + for (const auto& key : keys) { // Reorder the substeps in order to use |putNew| instead of |put|, because // the former is slightly faster. @@ -2874,7 +2979,7 @@ static bool ISOFieldKeysToIgnore(JSContext* cx, const PropertyVector& keys, } } - // Step 3. + // Steps 3-4. return true; } @@ -3060,7 +3165,7 @@ JSObject* js::temporal::CalendarMergeFields( * Temporal.Calendar.prototype.dateAdd ( date, duration [ , options ] ) */ static bool BuiltinCalendarAdd(JSContext* cx, const PlainDate& date, - const Duration& duration, + const NormalizedDuration& duration, Handle<JSObject*> options, PlainDate* result) { MOZ_ASSERT(IsValidISODate(date)); MOZ_ASSERT(IsValidDuration(duration)); @@ -3076,32 +3181,71 @@ static bool BuiltinCalendarAdd(JSContext* cx, const PlainDate& date, } // Step 8. - TimeDuration balanceResult; - if (!BalanceTimeDuration(cx, duration, TemporalUnit::Day, &balanceResult)) { - return false; - } + const auto& timeDuration = duration.time; // Step 9. - Duration addDuration = {duration.years, duration.months, duration.weeks, - balanceResult.days}; + auto balanceResult = BalanceTimeDuration(timeDuration, TemporalUnit::Day); + + // Step 10. + auto addDuration = DateDuration{ + duration.date.years, + duration.date.months, + duration.date.weeks, + duration.date.days + balanceResult.days, + }; return AddISODate(cx, date, addDuration, overflow, result); } /** * Temporal.Calendar.prototype.dateAdd ( date, duration [ , options ] ) */ +static bool BuiltinCalendarAdd(JSContext* cx, const PlainDate& date, + const DateDuration& duration, + Handle<JSObject*> options, PlainDate* result) { + // Steps 1-6. (Not applicable) + + // Step 8. (Reordered) + auto normalized = CreateNormalizedDurationRecord(duration, {}); + + // Steps 7-10. + return BuiltinCalendarAdd(cx, date, normalized, options, result); +} + +/** + * Temporal.Calendar.prototype.dateAdd ( date, duration [ , options ] ) + */ +static PlainDateObject* BuiltinCalendarAdd(JSContext* cx, const PlainDate& date, + const DateDuration& duration, + Handle<JSObject*> options) { + // Steps 1-10. + PlainDate result; + if (!BuiltinCalendarAdd(cx, date, duration, options, &result)) { + return nullptr; + } + + // Step 11. + Rooted<CalendarValue> calendar(cx, CalendarValue(cx->names().iso8601)); + return CreateTemporalDate(cx, result, calendar); +} + +/** + * Temporal.Calendar.prototype.dateAdd ( date, duration [ , options ] ) + */ static PlainDateObject* BuiltinCalendarAdd(JSContext* cx, const PlainDate& date, const Duration& duration, Handle<JSObject*> options) { // Steps 1-6. (Not applicable) - // Steps 7-9. + // Step 8. (Reordered) + auto normalized = CreateNormalizedDurationRecord(duration); + + // Steps 7-10. PlainDate result; - if (!BuiltinCalendarAdd(cx, date, duration, options, &result)) { + if (!BuiltinCalendarAdd(cx, date, normalized, options, &result)) { return nullptr; } - // Step 10. + // Step 11. Rooted<CalendarValue> calendar(cx, CalendarValue(cx->names().iso8601)); return CreateTemporalDate(cx, result, calendar); } @@ -3188,6 +3332,38 @@ static Wrapped<PlainDateObject*> CalendarDateAdd( */ static Wrapped<PlainDateObject*> CalendarDateAdd( JSContext* cx, Handle<CalendarRecord> calendar, + Handle<Wrapped<PlainDateObject*>> date, const DateDuration& duration, + Handle<JSObject*> options) { + MOZ_ASSERT( + CalendarMethodsRecordHasLookedUp(calendar, CalendarMethod::DateAdd)); + + // Step 1. (Not applicable). + + // Step 3. (Reordered) + if (!calendar.dateAdd()) { + auto* unwrappedDate = date.unwrap(cx); + if (!unwrappedDate) { + return nullptr; + } + auto date = ToPlainDate(unwrappedDate); + + return BuiltinCalendarAdd(cx, date, duration, options); + } + + // Steps 2 and 4-5. + Rooted<DurationObject*> durationObj( + cx, CreateTemporalDuration(cx, duration.toDuration())); + if (!durationObj) { + return nullptr; + } + return CalendarDateAddSlow(cx, calendar, date, durationObj, options); +} + +/** + * CalendarDateAdd ( calendarRec, date, duration [ , options ] ) + */ +static Wrapped<PlainDateObject*> CalendarDateAdd( + JSContext* cx, Handle<CalendarRecord> calendar, Handle<Wrapped<PlainDateObject*>> date, Handle<Wrapped<DurationObject*>> duration, Handle<JSObject*> options) { MOZ_ASSERT( @@ -3221,8 +3397,8 @@ static Wrapped<PlainDateObject*> CalendarDateAdd( */ static bool CalendarDateAdd(JSContext* cx, Handle<CalendarRecord> calendar, Handle<Wrapped<PlainDateObject*>> date, - const Duration& duration, Handle<JSObject*> options, - PlainDate* result) { + const DateDuration& duration, + Handle<JSObject*> options, PlainDate* result) { MOZ_ASSERT( CalendarMethodsRecordHasLookedUp(calendar, CalendarMethod::DateAdd)); @@ -3241,7 +3417,8 @@ static bool CalendarDateAdd(JSContext* cx, Handle<CalendarRecord> calendar, // Steps 2 and 4-5. - Rooted<DurationObject*> durationObj(cx, CreateTemporalDuration(cx, duration)); + Rooted<DurationObject*> durationObj( + cx, CreateTemporalDuration(cx, duration.toDuration())); if (!durationObj) { return false; } @@ -3259,7 +3436,7 @@ static bool CalendarDateAdd(JSContext* cx, Handle<CalendarRecord> calendar, * CalendarDateAdd ( calendarRec, date, duration [ , options ] ) */ static bool CalendarDateAdd(JSContext* cx, Handle<CalendarRecord> calendar, - const PlainDate& date, const Duration& duration, + const PlainDate& date, const DateDuration& duration, Handle<JSObject*> options, PlainDate* result) { MOZ_ASSERT( CalendarMethodsRecordHasLookedUp(calendar, CalendarMethod::DateAdd)); @@ -3279,7 +3456,8 @@ static bool CalendarDateAdd(JSContext* cx, Handle<CalendarRecord> calendar, return false; } - Rooted<DurationObject*> durationObj(cx, CreateTemporalDuration(cx, duration)); + Rooted<DurationObject*> durationObj( + cx, CreateTemporalDuration(cx, duration.toDuration())); if (!durationObj) { return false; } @@ -3311,7 +3489,7 @@ Wrapped<PlainDateObject*> js::temporal::CalendarDateAdd( */ Wrapped<PlainDateObject*> js::temporal::CalendarDateAdd( JSContext* cx, Handle<CalendarRecord> calendar, - Handle<Wrapped<PlainDateObject*>> date, const Duration& duration) { + Handle<Wrapped<PlainDateObject*>> date, const DateDuration& duration) { // Step 1. Handle<JSObject*> options = nullptr; @@ -3352,7 +3530,7 @@ Wrapped<PlainDateObject*> js::temporal::CalendarDateAdd( bool js::temporal::CalendarDateAdd(JSContext* cx, Handle<CalendarRecord> calendar, const PlainDate& date, - const Duration& duration, + const DateDuration& duration, PlainDate* result) { // Step 1. Handle<JSObject*> options = nullptr; @@ -3364,9 +3542,12 @@ bool js::temporal::CalendarDateAdd(JSContext* cx, /** * CalendarDateAdd ( calendarRec, date, duration [ , options ] ) */ -bool js::temporal::CalendarDateAdd( - JSContext* cx, Handle<CalendarRecord> calendar, const PlainDate& date, - const Duration& duration, Handle<JSObject*> options, PlainDate* result) { +bool js::temporal::CalendarDateAdd(JSContext* cx, + Handle<CalendarRecord> calendar, + const PlainDate& date, + const DateDuration& duration, + Handle<JSObject*> options, + PlainDate* result) { // Step 1. (Not applicable) // Steps 2-5. @@ -3379,7 +3560,7 @@ bool js::temporal::CalendarDateAdd( bool js::temporal::CalendarDateAdd(JSContext* cx, Handle<CalendarRecord> calendar, Handle<Wrapped<PlainDateObject*>> date, - const Duration& duration, + const DateDuration& duration, PlainDate* result) { // Step 1. Handle<JSObject*> options = nullptr; @@ -3391,18 +3572,15 @@ bool js::temporal::CalendarDateAdd(JSContext* cx, /** * Temporal.Calendar.prototype.dateUntil ( one, two [ , options ] ) */ -static Duration BuiltinCalendarDateUntil(const PlainDate& one, - const PlainDate& two, - TemporalUnit largestUnit) { +static DateDuration BuiltinCalendarDateUntil(const PlainDate& one, + const PlainDate& two, + TemporalUnit largestUnit) { // Steps 1-3. (Not applicable) // Steps 4-8. (Not applicable) - // Step 9. - auto difference = DifferenceISODate(one, two, largestUnit); - - // Step 10. - return difference.toDuration(); + // Steps 9-10. + return DifferenceISODate(one, two, largestUnit); } /** @@ -3412,7 +3590,7 @@ static bool BuiltinCalendarDateUntil(JSContext* cx, Handle<Wrapped<PlainDateObject*>> one, Handle<Wrapped<PlainDateObject*>> two, TemporalUnit largestUnit, - Duration* result) { + DateDuration* result) { MOZ_ASSERT(largestUnit <= TemporalUnit::Day); auto* unwrappedOne = one.unwrap(cx); @@ -3432,37 +3610,31 @@ static bool BuiltinCalendarDateUntil(JSContext* cx, return true; } -/** - * Temporal.Calendar.prototype.dateUntil ( one, two [ , options ] ) - */ -static bool BuiltinCalendarDateUntil(JSContext* cx, - Handle<Wrapped<PlainDateObject*>> one, - Handle<Wrapped<PlainDateObject*>> two, - Handle<JSObject*> options, - Duration* result) { - // Steps 1-6. (Not applicable) - - // Steps 7-8. - auto largestUnit = TemporalUnit::Day; - if (!GetTemporalUnit(cx, options, TemporalUnitKey::LargestUnit, - TemporalUnitGroup::Date, &largestUnit)) { - return false; - } - - // Steps 9-10. - return BuiltinCalendarDateUntil(cx, one, two, largestUnit, result); -} - static bool CalendarDateUntilSlow(JSContext* cx, Handle<CalendarRecord> calendar, Handle<Wrapped<PlainDateObject*>> one, Handle<Wrapped<PlainDateObject*>> two, - Handle<JSObject*> options, Duration* result) { + TemporalUnit largestUnit, + Handle<JSObject*> maybeOptions, + DateDuration* result) { MOZ_ASSERT( CalendarMethodsRecordHasLookedUp(calendar, CalendarMethod::DateUntil)); MOZ_ASSERT(calendar.receiver().isObject()); MOZ_ASSERT(calendar.dateUntil()); + Rooted<JSObject*> options(cx, maybeOptions); + if (!options) { + options = NewPlainObjectWithProto(cx, nullptr); + if (!options) { + return false; + } + } + + Rooted<Value> value(cx, StringValue(TemporalUnitToString(cx, largestUnit))); + if (!DefineDataProperty(cx, options, cx->names().largestUnit, value)) { + return false; + } + // Step 1. (Inlined call to CalendarMethodsRecordCall.) Rooted<JS::Value> dateUntil(cx, ObjectValue(*calendar.dateUntil())); auto thisv = calendar.receiver().toValue(); @@ -3488,29 +3660,90 @@ static bool CalendarDateUntilSlow(JSContext* cx, } // Step 4. - *result = ToDuration(&rval.toObject().unwrapAs<DurationObject>()); + auto duration = ToDuration(&rval.toObject().unwrapAs<DurationObject>()); + *result = duration.toDateDuration(); return true; } +static bool CalendarDateUntilSlow(JSContext* cx, + Handle<CalendarRecord> calendar, + const PlainDate& one, const PlainDate& two, + TemporalUnit largestUnit, + Handle<JSObject*> maybeOptions, + DateDuration* result) { + Rooted<PlainDateObject*> date1( + cx, CreateTemporalDate(cx, one, calendar.receiver())); + if (!date1) { + return false; + } + + Rooted<PlainDateObject*> date2( + cx, CreateTemporalDate(cx, two, calendar.receiver())); + if (!date2) { + return false; + } + + return CalendarDateUntilSlow(cx, calendar, date1, date2, largestUnit, + maybeOptions, result); +} + /** * CalendarDateUntil ( calendarRec, one, two, options ) */ bool js::temporal::CalendarDateUntil(JSContext* cx, Handle<CalendarRecord> calendar, - Handle<Wrapped<PlainDateObject*>> one, - Handle<Wrapped<PlainDateObject*>> two, + const PlainDate& one, const PlainDate& two, + TemporalUnit largestUnit, + DateDuration* result) { + MOZ_ASSERT( + CalendarMethodsRecordHasLookedUp(calendar, CalendarMethod::DateUntil)); + MOZ_ASSERT(largestUnit <= TemporalUnit::Day); + + // Step 2. (Reordered) + if (!calendar.dateUntil()) { + *result = BuiltinCalendarDateUntil(one, two, largestUnit); + return true; + } + + // Steps 1 and 3-4. + return CalendarDateUntilSlow(cx, calendar, one, two, largestUnit, nullptr, + result); +} + +/** + * CalendarDateUntil ( calendarRec, one, two, options ) + */ +bool js::temporal::CalendarDateUntil(JSContext* cx, + Handle<CalendarRecord> calendar, + const PlainDate& one, const PlainDate& two, + TemporalUnit largestUnit, Handle<PlainObject*> options, - Duration* result) { + DateDuration* result) { MOZ_ASSERT( CalendarMethodsRecordHasLookedUp(calendar, CalendarMethod::DateUntil)); + // As an optimization, our implementation only adds |largestUnit| to the + // options object when taking the slow-path. +#ifdef DEBUG + // The object must be extensible, otherwise we'd need to throw an error when + // attempting to add the "largestUnit" property to a non-extensible object. + MOZ_ASSERT(options->isExtensible()); + + // Similarily, if there's an existing "largestUnit" property, this property + // must be configurable. + auto largestUnitProp = options->lookupPure(cx->names().largestUnit); + MOZ_ASSERT_IF(largestUnitProp, largestUnitProp->configurable()); +#endif + // Step 2. (Reordered) if (!calendar.dateUntil()) { - return BuiltinCalendarDateUntil(cx, one, two, options, result); + *result = BuiltinCalendarDateUntil(one, two, largestUnit); + return true; } // Steps 1 and 3-4. - return CalendarDateUntilSlow(cx, calendar, one, two, options, result); + return CalendarDateUntilSlow(cx, calendar, one, two, largestUnit, options, + result); } /** @@ -3521,7 +3754,7 @@ bool js::temporal::CalendarDateUntil(JSContext* cx, Handle<Wrapped<PlainDateObject*>> one, Handle<Wrapped<PlainDateObject*>> two, TemporalUnit largestUnit, - Duration* result) { + DateDuration* result) { MOZ_ASSERT( CalendarMethodsRecordHasLookedUp(calendar, CalendarMethod::DateUntil)); MOZ_ASSERT(largestUnit <= TemporalUnit::Day); @@ -3531,18 +3764,45 @@ bool js::temporal::CalendarDateUntil(JSContext* cx, return BuiltinCalendarDateUntil(cx, one, two, largestUnit, result); } - Rooted<PlainObject*> untilOptions(cx, NewPlainObjectWithProto(cx, nullptr)); - if (!untilOptions) { - return false; - } + // Steps 1 and 3-4. + return CalendarDateUntilSlow(cx, calendar, one, two, largestUnit, nullptr, + result); +} - Rooted<Value> value(cx, StringValue(TemporalUnitToString(cx, largestUnit))); - if (!DefineDataProperty(cx, untilOptions, cx->names().largestUnit, value)) { - return false; +/** + * CalendarDateUntil ( calendarRec, one, two, options ) + */ +bool js::temporal::CalendarDateUntil(JSContext* cx, + Handle<CalendarRecord> calendar, + Handle<Wrapped<PlainDateObject*>> one, + Handle<Wrapped<PlainDateObject*>> two, + TemporalUnit largestUnit, + Handle<PlainObject*> options, + DateDuration* result) { + MOZ_ASSERT( + CalendarMethodsRecordHasLookedUp(calendar, CalendarMethod::DateUntil)); + + // As an optimization, our implementation only adds |largestUnit| to the + // options object when taking the slow-path. +#ifdef DEBUG + // The object must be extensible, otherwise we'd need to throw an error when + // attempting to add the "largestUnit" property to a non-extensible object. + MOZ_ASSERT(options->isExtensible()); + + // Similarily, if there's an existing "largestUnit" property, this property + // must be configurable. + auto largestUnitProp = options->lookupPure(cx->names().largestUnit); + MOZ_ASSERT_IF(largestUnitProp, largestUnitProp->configurable()); +#endif + + // Step 2. (Reordered) + if (!calendar.dateUntil()) { + return BuiltinCalendarDateUntil(cx, one, two, largestUnit, result); } // Steps 1 and 3-4. - return CalendarDateUntilSlow(cx, calendar, one, two, untilOptions, result); + return CalendarDateUntilSlow(cx, calendar, one, two, largestUnit, options, + result); } /** @@ -3925,7 +4185,7 @@ static bool Calendar_dateAdd(JSContext* cx, const CallArgs& args) { } } - // Steps 7-10. + // Steps 7-11. auto* obj = BuiltinCalendarAdd(cx, date, duration, options); if (!obj) { return false; @@ -3979,10 +4239,11 @@ static bool Calendar_dateUntil(JSContext* cx, const CallArgs& args) { } } - // Steps 9-10. + // Step 9. auto duration = BuiltinCalendarDateUntil(one, two, largestUnit); - auto* obj = CreateTemporalDuration(cx, duration); + // Step 10. + auto* obj = CreateTemporalDuration(cx, duration.toDuration()); if (!obj) { return false; } @@ -4363,76 +4624,8 @@ static bool Calendar_fields(JSContext* cx, const CallArgs& args) { // Step 3. MOZ_ASSERT(IsISO8601Calendar(&args.thisv().toObject().as<CalendarObject>())); - // Step 4. - JS::ForOfIterator iterator(cx); - if (!iterator.init(args.get(0))) { - return false; - } - - // Step 5. - JS::RootedVector<Value> fieldNames(cx); - mozilla::EnumSet<CalendarField> seen; - - // Steps 6-7. - Rooted<Value> nextValue(cx); - Rooted<JSLinearString*> linear(cx); - while (true) { - // Steps 7.a and 7.b.i. - bool done; - if (!iterator.next(&nextValue, &done)) { - return false; - } - if (done) { - break; - } - - // Step 7.b.ii. - if (!nextValue.isString()) { - ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_IGNORE_STACK, nextValue, - nullptr, "not a string"); - iterator.closeThrow(); - return false; - } - - linear = nextValue.toString()->ensureLinear(cx); - if (!linear) { - return false; - } - - // Step 7.b.iv. (Reordered) - CalendarField field; - if (!ToCalendarField(cx, linear, &field)) { - iterator.closeThrow(); - return false; - } - - // Step 7.b.iii. - if (seen.contains(field)) { - if (auto chars = QuoteString(cx, linear, '"')) { - JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, - JSMSG_TEMPORAL_CALENDAR_DUPLICATE_FIELD, - chars.get()); - } - iterator.closeThrow(); - return false; - } - - // Step 7.b.v. - if (!fieldNames.append(nextValue)) { - return false; - } - seen += field; - } - - // Step 8. - auto* array = - NewDenseCopiedArray(cx, fieldNames.length(), fieldNames.begin()); - if (!array) { - return false; - } - - args.rval().setObject(*array); - return true; + // Steps 4-6. + return BuiltinCalendarFields(cx, args.get(0), args.rval()); } /** @@ -4512,18 +4705,14 @@ static bool Calendar_mergeFields(JSContext* cx, const CallArgs& args) { for (size_t i = 0; i < fieldsKeys.length(); i++) { Handle<PropertyKey> key = fieldsKeys[i]; - // Step 12.a. - // FIXME: spec issue - unnecessary initialisation - // https://github.com/tc39/proposal-temporal/issues/2549 - - // Steps 12.b-c. + // Steps 12.a-b. if (overriddenKeys.has(key)) { if (!GetProperty(cx, additionalFieldsCopy, additionalFieldsCopy, key, &propValue)) { return false; } - // Step 12.d. (Reordered) + // Step 12.c. (Reordered) if (propValue.isUndefined()) { // The property can be undefined if the key is "month" or "monthCode". MOZ_ASSERT(key.isAtom(cx->names().month) || @@ -4540,7 +4729,7 @@ static bool Calendar_mergeFields(JSContext* cx, const CallArgs& args) { MOZ_ASSERT(!propValue.isUndefined()); } - // Step 12.d. + // Step 12.c. if (!DefineDataProperty(cx, merged, key, propValue)) { return false; } |