From 3147be47c476d2a8356418ca84033a899751342d Mon Sep 17 00:00:00 2001 From: =?utf8?q?Andr=C3=A9=20Bargull?= Date: Mon, 15 Apr 2024 18:27:32 +0000 Subject: [PATCH] Bug 1884553: Correctly handle modified array iterator state. r=mgaudet Differential Revision: https://phabricator.services.mozilla.com/D204403 --- js/src/builtin/temporal/Calendar.cpp | 176 +++++++++++---------- js/src/tests/non262/Temporal/PlainDate/browser.js | 0 .../from-with-modified-array-iterator-state.js | 22 +++ js/src/tests/non262/Temporal/PlainDate/shell.js | 0 4 files changed, 118 insertions(+), 80 deletions(-) create mode 100644 js/src/tests/non262/Temporal/PlainDate/browser.js create mode 100644 js/src/tests/non262/Temporal/PlainDate/from-with-modified-array-iterator-state.js create mode 100644 js/src/tests/non262/Temporal/PlainDate/shell.js diff --git a/js/src/builtin/temporal/Calendar.cpp b/js/src/builtin/temporal/Calendar.cpp index c4bbee240dd4..f9ccf323a36b 100644 --- a/js/src/builtin/temporal/Calendar.cpp +++ b/js/src/builtin/temporal/Calendar.cpp @@ -1057,6 +1057,88 @@ static bool BuiltinCalendarFields( return true; } +/** + * Temporal.Calendar.prototype.fields ( fields ) + */ +static bool BuiltinCalendarFields(JSContext* cx, Handle fields, + MutableHandle result) { + // Step 4. + JS::ForOfIterator iterator(cx); + if (!iterator.init(fields)) { + return false; + } + + // Step 5. + JS::RootedVector fieldNames(cx); + mozilla::EnumSet seen; + + // Step 6. + Rooted nextValue(cx); + Rooted 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 fieldNames) { return std::is_sorted(fieldNames.begin(), fieldNames.end(), @@ -1118,15 +1200,22 @@ bool js::temporal::CalendarFields( array->initDenseElement(i, StringValue(name)); } - Rooted fieldsFn(cx, ObjectValue(*fields)); - auto thisv = calendar.receiver().toValue(); Rooted fieldsArray(cx, ObjectValue(*array)); - if (!Call(cx, fieldsFn, thisv, fieldsArray, &fieldsArray)) { - return false; + Rooted calendarFieldNames(cx); + if (fields) { + Rooted 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 4-5. - if (!IterableToListOfStrings(cx, fieldsArray, result)) { + if (!IterableToListOfStrings(cx, calendarFieldNames, result)) { return false; } @@ -4546,81 +4635,8 @@ static bool Calendar_fields(JSContext* cx, const CallArgs& args) { // Step 3. MOZ_ASSERT(IsISO8601Calendar(&args.thisv().toObject().as())); - // Step 4. - JS::ForOfIterator iterator(cx); - if (!iterator.init(args.get(0))) { - return false; - } - - // Step 5. - JS::RootedVector fieldNames(cx); - mozilla::EnumSet seen; - - // Step 6. - Rooted nextValue(cx); - Rooted 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; - } - - args.rval().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; - } + // Steps 4-6. + return BuiltinCalendarFields(cx, args.get(0), args.rval()); } /** diff --git a/js/src/tests/non262/Temporal/PlainDate/browser.js b/js/src/tests/non262/Temporal/PlainDate/browser.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/js/src/tests/non262/Temporal/PlainDate/from-with-modified-array-iterator-state.js b/js/src/tests/non262/Temporal/PlainDate/from-with-modified-array-iterator-state.js new file mode 100644 index 000000000000..ac0febc1e644 --- /dev/null +++ b/js/src/tests/non262/Temporal/PlainDate/from-with-modified-array-iterator-state.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty("Temporal")) + +const ArrayIteratorPrototype = Object.getPrototypeOf([][Symbol.iterator]()); + +// Modify the ArrayIteratorPrototype prototype chain to disable optimisations. +Object.setPrototypeOf(ArrayIteratorPrototype, {}); + +let calendar = new Temporal.Calendar("iso8601"); + +let dateLike = { + calendar, + day: 1, + month: 1, + year: 0, +}; + +let result = Temporal.PlainDate.from(dateLike); + +assertEq(result.toString(), "0000-01-01"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/Temporal/PlainDate/shell.js b/js/src/tests/non262/Temporal/PlainDate/shell.js new file mode 100644 index 000000000000..e69de29bb2d1 -- 2.11.4.GIT