summaryrefslogtreecommitdiffstats
path: root/js/src/tests/test262/staging
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /js/src/tests/test262/staging
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/browser.js0
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/access-out-of-bounds-typed-array-strict.js96
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/array-fill-parameter-conversion-resizes-strict.js113
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/array-sort-with-default-comparison-strict.js239
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/at-parameter-conversion-resizes-strict.js95
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/at-strict.js139
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/browser.js0
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/construct-from-typed-array-strict.js212
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/construct-invalid-strict.js74
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/copy-within-parameter-conversion-grows-strict.js118
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/copy-within-parameter-conversion-shrinks-strict.js143
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/destructuring-strict.js262
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/entries-keys-values-grow-mid-iteration-strict.js303
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/entries-keys-values-shrink-mid-iteration-strict.js248
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/entries-keys-values-strict.js509
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/enumerate-elements-strict.js59
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/every-grow-mid-iteration-strict.js153
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/every-shrink-mid-iteration-strict.js176
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/every-some-strict.js256
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/fill-parameter-conversion-resizes-strict.js97
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/filter-grow-mid-iteration-strict.js168
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/filter-shrink-mid-iteration-strict.js155
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/filter-strict.js209
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/find-find-index-find-last-find-last-index-strict.js332
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/find-grow-mid-iteration-strict.js153
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/find-index-grow-mid-iteration-strict.js153
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/find-index-shrink-mid-iteration-strict.js153
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/find-last-grow-mid-iteration-strict.js153
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/find-last-index-grow-mid-iteration-strict.js153
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/find-last-index-shrink-mid-iteration-strict.js167
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/find-last-shrink-mid-iteration-strict.js153
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/find-shrink-mid-iteration-strict.js153
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/for-each-reduce-reduce-right-grow-mid-iteration-strict.js277
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/for-each-reduce-reduce-right-shrink-mid-iteration-strict.js262
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/for-each-reduce-reduce-right-strict.js260
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/function-apply-strict.js160
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/includes-parameter-conversion-resizes-strict.js147
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/includes-special-values-strict.js39
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/includes-strict.js212
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/index-of-last-index-of-special-values-strict.js46
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/index-of-last-index-of-strict.js329
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/index-of-parameter-conversion-grows-strict.js123
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/index-of-parameter-conversion-shrinks-strict.js134
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-and-grow-just-before-iteration-would-end-strict.js126
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-and-grow-mid-iteration-strict.js139
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-and-shrink-mid-iteration-strict.js154
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-and-shrink-to-zero-mid-iteration-strict.js126
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-strict.js118
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/join-parameter-conversion-grows-strict.js89
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/join-parameter-conversion-shrinks-strict.js93
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/join-to-locale-string-strict.js223
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/last-index-of-parameter-conversion-grows-strict.js125
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/last-index-of-parameter-conversion-shrinks-strict.js134
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/length-tracking-1-strict.js96
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/length-tracking-2-strict.js116
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/map-grow-mid-iteration-strict.js150
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/map-shrink-mid-iteration-strict.js182
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/map-species-create-grows-strict.js135
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/map-species-create-shrinks-strict.js131
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/object-define-property-define-properties-strict.js279
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/object-define-property-parameter-conversion-grows-strict.js117
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/object-define-property-parameter-conversion-shrinks-strict.js89
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/object-freeze-strict.js91
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/oobbehaves-like-detached-strict.js35
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/out-of-bounds-typed-array-and-has-strict.js77
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/reverse-strict.js273
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/set-grow-target-mid-iteration-strict.js192
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/set-shrink-target-mid-iteration-strict.js172
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/set-source-length-getter-grows-target-strict.js137
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/set-source-length-getter-shrinks-target-strict.js219
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/set-with-resizable-source-strict.js292
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/set-with-resizable-target-strict.js580
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/shell.js0
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/slice-parameter-conversion-grows-strict.js107
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/slice-parameter-conversion-shrinks-strict.js108
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/slice-species-create-resizes-strict.js191
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/slice-strict.js206
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/some-grow-mid-iteration-strict.js154
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/some-shrink-mid-iteration-strict.js176
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/sort-callback-grows-strict.js143
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/sort-callback-shrinks-strict.js145
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/sort-with-custom-comparison-strict.js309
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/sort-with-default-comparison-strict.js245
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/subarray-parameter-conversion-grows-strict.js141
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/subarray-parameter-conversion-shrinks-strict.js223
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/subarray-strict.js234
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/test-copy-within-strict.js272
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/test-fill-strict.js243
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/test-map-strict.js247
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/to-locale-string-number-prototype-to-locale-string-grows-strict.js116
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/to-locale-string-number-prototype-to-locale-string-shrinks-strict.js117
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/typed-array-length-and-byte-length-strict.js83
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/typed-array-length-when-resized-out-of-bounds-1-strict.js81
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/typed-array-length-when-resized-out-of-bounds-2-strict.js87
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/resizable/typed-array-prototype-strict.js57
-rw-r--r--js/src/tests/test262/staging/ArrayBuffer/shell.js0
-rw-r--r--js/src/tests/test262/staging/Intl402/Temporal/browser.js0
-rw-r--r--js/src/tests/test262/staging/Intl402/Temporal/old/addition-across-lunisolar-leap-months.js96
-rw-r--r--js/src/tests/test262/staging/Intl402/Temporal/old/browser.js0
-rw-r--r--js/src/tests/test262/staging/Intl402/Temporal/old/date-time-format.js1668
-rw-r--r--js/src/tests/test262/staging/Intl402/Temporal/old/date-toLocaleString.js36
-rw-r--r--js/src/tests/test262/staging/Intl402/Temporal/old/datetime-toLocaleString.js54
-rw-r--r--js/src/tests/test262/staging/Intl402/Temporal/old/duration-arithmetic-dst.js76
-rw-r--r--js/src/tests/test262/staging/Intl402/Temporal/old/hebrew-leap-months.js63
-rw-r--r--js/src/tests/test262/staging/Intl402/Temporal/old/indian-calendar.js39
-rw-r--r--js/src/tests/test262/staging/Intl402/Temporal/old/instant-toLocaleString.js48
-rw-r--r--js/src/tests/test262/staging/Intl402/Temporal/old/islamic-calendars.js94
-rw-r--r--js/src/tests/test262/staging/Intl402/Temporal/old/japanese-era.js140
-rw-r--r--js/src/tests/test262/staging/Intl402/Temporal/old/monthday-toLocaleString.js31
-rw-r--r--js/src/tests/test262/staging/Intl402/Temporal/old/non-iso-calendars.js1380
-rw-r--r--js/src/tests/test262/staging/Intl402/Temporal/old/persian-calendar.js93
-rw-r--r--js/src/tests/test262/staging/Intl402/Temporal/old/shell.js329
-rw-r--r--js/src/tests/test262/staging/Intl402/Temporal/old/time-toLocaleString.js30
-rw-r--r--js/src/tests/test262/staging/Intl402/Temporal/old/timezone-america-la.js31
-rw-r--r--js/src/tests/test262/staging/Intl402/Temporal/old/tzdb-string-parsing.js101
-rw-r--r--js/src/tests/test262/staging/Intl402/Temporal/old/tzdb-transitions.js47
-rw-r--r--js/src/tests/test262/staging/Intl402/Temporal/old/yearmonth-toLocaleString.js42
-rw-r--r--js/src/tests/test262/staging/Intl402/Temporal/old/zoneddatetime-dst-corner-cases.js29
-rw-r--r--js/src/tests/test262/staging/Intl402/Temporal/shell.js0
-rw-r--r--js/src/tests/test262/staging/Intl402/browser.js0
-rw-r--r--js/src/tests/test262/staging/Intl402/shell.js0
-rw-r--r--js/src/tests/test262/staging/JSON/browser.js0
-rw-r--r--js/src/tests/test262/staging/JSON/json-parse-with-source-snapshot.js97
-rw-r--r--js/src/tests/test262/staging/JSON/json-parse-with-source.js292
-rw-r--r--js/src/tests/test262/staging/JSON/shell.js329
-rw-r--r--js/src/tests/test262/staging/Temporal/Duration/browser.js0
-rw-r--r--js/src/tests/test262/staging/Temporal/Duration/old/add.js140
-rw-r--r--js/src/tests/test262/staging/Temporal/Duration/old/browser.js0
-rw-r--r--js/src/tests/test262/staging/Temporal/Duration/old/compare-no-precision-loss.js16
-rw-r--r--js/src/tests/test262/staging/Temporal/Duration/old/limits.js37
-rw-r--r--js/src/tests/test262/staging/Temporal/Duration/old/round.js868
-rw-r--r--js/src/tests/test262/staging/Temporal/Duration/old/shell.js2158
-rw-r--r--js/src/tests/test262/staging/Temporal/Duration/old/subtract.js105
-rw-r--r--js/src/tests/test262/staging/Temporal/Duration/old/toString.js18
-rw-r--r--js/src/tests/test262/staging/Temporal/Duration/old/total.js453
-rw-r--r--js/src/tests/test262/staging/Temporal/Duration/shell.js0
-rw-r--r--js/src/tests/test262/staging/Temporal/Instant/browser.js0
-rw-r--r--js/src/tests/test262/staging/Temporal/Instant/old/add.js35
-rw-r--r--js/src/tests/test262/staging/Temporal/Instant/old/browser.js0
-rw-r--r--js/src/tests/test262/staging/Temporal/Instant/old/compare.js33
-rw-r--r--js/src/tests/test262/staging/Temporal/Instant/old/equals.js27
-rw-r--r--js/src/tests/test262/staging/Temporal/Instant/old/limits.js33
-rw-r--r--js/src/tests/test262/staging/Temporal/Instant/old/round.js131
-rw-r--r--js/src/tests/test262/staging/Temporal/Instant/old/shell.js0
-rw-r--r--js/src/tests/test262/staging/Temporal/Instant/old/since.js249
-rw-r--r--js/src/tests/test262/staging/Temporal/Instant/old/toZonedDateTime.js61
-rw-r--r--js/src/tests/test262/staging/Temporal/Instant/old/toZonedDateTimeISO.js28
-rw-r--r--js/src/tests/test262/staging/Temporal/Instant/old/until.js249
-rw-r--r--js/src/tests/test262/staging/Temporal/Instant/shell.js0
-rw-r--r--js/src/tests/test262/staging/Temporal/Regex/browser.js0
-rw-r--r--js/src/tests/test262/staging/Temporal/Regex/old/browser.js0
-rw-r--r--js/src/tests/test262/staging/Temporal/Regex/old/duration.js271
-rw-r--r--js/src/tests/test262/staging/Temporal/Regex/old/instant.js255
-rw-r--r--js/src/tests/test262/staging/Temporal/Regex/old/plaindate.js84
-rw-r--r--js/src/tests/test262/staging/Temporal/Regex/old/plaindatetime.js74
-rw-r--r--js/src/tests/test262/staging/Temporal/Regex/old/plainmonthday.js78
-rw-r--r--js/src/tests/test262/staging/Temporal/Regex/old/plaintime.js65
-rw-r--r--js/src/tests/test262/staging/Temporal/Regex/old/plainyearmonth.js109
-rw-r--r--js/src/tests/test262/staging/Temporal/Regex/old/shell.js0
-rw-r--r--js/src/tests/test262/staging/Temporal/Regex/old/timezone.js91
-rw-r--r--js/src/tests/test262/staging/Temporal/Regex/shell.js0
-rw-r--r--js/src/tests/test262/staging/Temporal/TimeZone/browser.js0
-rw-r--r--js/src/tests/test262/staging/Temporal/TimeZone/old/browser.js0
-rw-r--r--js/src/tests/test262/staging/Temporal/TimeZone/old/dst-change.js29
-rw-r--r--js/src/tests/test262/staging/Temporal/TimeZone/old/getInstantFor-disambiguation.js50
-rw-r--r--js/src/tests/test262/staging/Temporal/TimeZone/old/getInstantFor.js61
-rw-r--r--js/src/tests/test262/staging/Temporal/TimeZone/old/getNextTransition.js20
-rw-r--r--js/src/tests/test262/staging/Temporal/TimeZone/old/getPossibleInstantsFor.js46
-rw-r--r--js/src/tests/test262/staging/Temporal/TimeZone/old/getPreviousTransition.js20
-rw-r--r--js/src/tests/test262/staging/Temporal/TimeZone/old/shell.js2487
-rw-r--r--js/src/tests/test262/staging/Temporal/TimeZone/old/timezone-offset.js24
-rw-r--r--js/src/tests/test262/staging/Temporal/TimeZone/old/timezone-utc-offset.js21
-rw-r--r--js/src/tests/test262/staging/Temporal/TimeZone/shell.js0
-rw-r--r--js/src/tests/test262/staging/Temporal/UserCalendar/browser.js0
-rw-r--r--js/src/tests/test262/staging/Temporal/UserCalendar/old/browser.js0
-rw-r--r--js/src/tests/test262/staging/Temporal/UserCalendar/old/calendar-extra-fields.js160
-rw-r--r--js/src/tests/test262/staging/Temporal/UserCalendar/old/calendar-non-trivial-mergefields.js154
-rw-r--r--js/src/tests/test262/staging/Temporal/UserCalendar/old/shell.js0
-rw-r--r--js/src/tests/test262/staging/Temporal/UserCalendar/old/trivial-protocol-implementation.js231
-rw-r--r--js/src/tests/test262/staging/Temporal/UserCalendar/old/trivial-subclass.js167
-rw-r--r--js/src/tests/test262/staging/Temporal/UserCalendar/shell.js0
-rw-r--r--js/src/tests/test262/staging/Temporal/UserTimezone/browser.js0
-rw-r--r--js/src/tests/test262/staging/Temporal/UserTimezone/old/browser.js0
-rw-r--r--js/src/tests/test262/staging/Temporal/UserTimezone/old/shell.js0
-rw-r--r--js/src/tests/test262/staging/Temporal/UserTimezone/old/subminute-offset.js106
-rw-r--r--js/src/tests/test262/staging/Temporal/UserTimezone/old/trivial-protocol.js63
-rw-r--r--js/src/tests/test262/staging/Temporal/UserTimezone/old/trivial-subclass.js144
-rw-r--r--js/src/tests/test262/staging/Temporal/UserTimezone/shell.js0
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/browser.js0
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/old/add.js53
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/old/browser.js0
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/old/compare.js146
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/old/construction-and-properties.js117
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/old/date-time-hours-overflow.js26
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/old/dst-math.js279
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/old/dst-properties.js45
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/old/equals.js111
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/old/order-of-operations.js82
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/old/property-bags.js296
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/old/reversibility-of-differences.js40
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/old/round.js242
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/old/shell.js2158
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/old/since.js310
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/old/string-parsing.js71
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/old/subtract.js32
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toInstant.js36
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainDate.js47
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainMonthDay.js51
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainTime.js17
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainYearMonth.js51
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toString.js74
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/old/until.js314
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/old/with.js272
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withCalendar.js46
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withPlainDate.js98
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withPlainTime.js29
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withTimezone.js42
-rw-r--r--js/src/tests/test262/staging/Temporal/ZonedDateTime/shell.js0
-rw-r--r--js/src/tests/test262/staging/Temporal/browser.js0
-rw-r--r--js/src/tests/test262/staging/Temporal/shell.js0
-rw-r--r--js/src/tests/test262/staging/browser.js0
-rw-r--r--js/src/tests/test262/staging/set-is-subset-on-set-like.js27
-rw-r--r--js/src/tests/test262/staging/set-is-subset-table-receiver-cleared.js28
-rw-r--r--js/src/tests/test262/staging/set-is-subset-table-transition.js32
-rw-r--r--js/src/tests/test262/staging/set-methods/browser.js0
-rw-r--r--js/src/tests/test262/staging/set-methods/set-intersect-other-is-set-like.js34
-rw-r--r--js/src/tests/test262/staging/set-methods/set-intersection-other-is-map.js27
-rw-r--r--js/src/tests/test262/staging/set-methods/set-intersection-other-is-set.js27
-rw-r--r--js/src/tests/test262/staging/set-methods/shell.js0
-rw-r--r--js/src/tests/test262/staging/shell.js0
230 files changed, 35614 insertions, 0 deletions
diff --git a/js/src/tests/test262/staging/ArrayBuffer/browser.js b/js/src/tests/test262/staging/ArrayBuffer/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/browser.js
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/access-out-of-bounds-typed-array-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/access-out-of-bounds-typed-array-strict.js
new file mode 100644
index 0000000000..a5135998af
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/access-out-of-bounds-typed-array-strict.js
@@ -0,0 +1,96 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from AccessOutOfBoundsTypedArray test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+for (let ctor of ctors) {
+ if (ctor.BYTES_PER_ELEMENT != 1) {
+ continue;
+ }
+ const rab = CreateResizableArrayBuffer(16, 40);
+ const array = new ctor(rab, 0, 4);
+ // Initial values
+ for (let i = 0; i < 4; ++i) {
+ assert.sameValue(array[i], 0);
+ }
+ // Within-bounds write
+ for (let i = 0; i < 4; ++i) {
+ array[i] = i;
+ }
+ // Within-bounds read
+ for (let i = 0; i < 4; ++i) {
+ assert.sameValue(array[i], i);
+ }
+ rab.resize(2);
+ // OOB read. If the RAB isn't large enough to fit the entire TypedArray,
+ // the length of the TypedArray is treated as 0.
+ for (let i = 0; i < 4; ++i) {
+ assert.sameValue(array[i], undefined);
+ }
+ // OOB write (has no effect)
+ for (let i = 0; i < 4; ++i) {
+ array[i] = 10;
+ }
+ rab.resize(4);
+ // Within-bounds read
+ for (let i = 0; i < 2; ++i) {
+ assert.sameValue(array[i], i);
+ }
+ // The shrunk-and-regrown part got zeroed.
+ for (let i = 2; i < 4; ++i) {
+ assert.sameValue(array[i], 0);
+ }
+ rab.resize(40);
+ // Within-bounds read
+ for (let i = 0; i < 2; ++i) {
+ assert.sameValue(array[i], i);
+ }
+ for (let i = 2; i < 4; ++i) {
+ assert.sameValue(array[i], 0);
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/array-fill-parameter-conversion-resizes-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/array-fill-parameter-conversion-resizes-strict.js
new file mode 100644
index 0000000000..eb28c9d602
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/array-fill-parameter-conversion-resizes-strict.js
@@ -0,0 +1,113 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from ArrayFillParameterConversionResizes test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function ReadDataFromBuffer(ab, ctor) {
+ let result = [];
+ const ta = new ctor(ab, 0, ab.byteLength / ctor.BYTES_PER_ELEMENT);
+ for (let item of ta) {
+ result.push(Number(item));
+ }
+ return result;
+}
+
+function ArrayFillHelper(ta, n, start, end) {
+ if (ta instanceof BigInt64Array || ta instanceof BigUint64Array) {
+ Array.prototype.fill.call(ta, BigInt(n), start, end);
+ } else {
+ Array.prototype.fill.call(ta, n, start, end);
+ }
+}
+
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const evil = {
+ valueOf: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 3;
+ }
+ };
+ ArrayFillHelper(fixedLength, evil, 1, 2);
+ assert.compareArray(ReadDataFromBuffer(rab, ctor), [
+ 0,
+ 0
+ ]);
+}
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const evil = {
+ valueOf: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 1;
+ }
+ };
+ ArrayFillHelper(fixedLength, 3, evil, 2);
+ assert.compareArray(ReadDataFromBuffer(rab, ctor), [
+ 0,
+ 0
+ ]);
+}
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const evil = {
+ valueOf: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 2;
+ }
+ };
+ ArrayFillHelper(fixedLength, 3, 1, evil);
+ assert.compareArray(ReadDataFromBuffer(rab, ctor), [
+ 0,
+ 0
+ ]);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/array-sort-with-default-comparison-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/array-sort-with-default-comparison-strict.js
new file mode 100644
index 0000000000..486c3a96e7
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/array-sort-with-default-comparison-strict.js
@@ -0,0 +1,239 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from ArraySortWithDefaultComparison test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+// The default comparison function for Array.prototype.sort is the string sort.
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+const ArraySortHelper = (ta, ...rest) => {
+ Array.prototype.sort.call(ta, ...rest);
+};
+
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ const lengthTracking = new ctor(rab, 0);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ const taFull = new ctor(rab, 0);
+ function WriteUnsortedData() {
+ // Write some data into the array.
+ for (let i = 0; i < taFull.length; ++i) {
+ WriteToTypedArray(taFull, i, 10 - 2 * i);
+ }
+ }
+ // Orig. array: [10, 8, 6, 4]
+ // [10, 8, 6, 4] << fixedLength
+ // [6, 4] << fixedLengthWithOffset
+ // [10, 8, 6, 4, ...] << lengthTracking
+ // [6, 4, ...] << lengthTrackingWithOffset
+
+ WriteUnsortedData();
+ ArraySortHelper(fixedLength);
+ assert.compareArray(ToNumbers(taFull), [
+ 10,
+ 4,
+ 6,
+ 8
+ ]);
+ WriteUnsortedData();
+ ArraySortHelper(fixedLengthWithOffset);
+ assert.compareArray(ToNumbers(taFull), [
+ 10,
+ 8,
+ 4,
+ 6
+ ]);
+ WriteUnsortedData();
+ ArraySortHelper(lengthTracking);
+ assert.compareArray(ToNumbers(taFull), [
+ 10,
+ 4,
+ 6,
+ 8
+ ]);
+ WriteUnsortedData();
+ ArraySortHelper(lengthTrackingWithOffset);
+ assert.compareArray(ToNumbers(taFull), [
+ 10,
+ 8,
+ 4,
+ 6
+ ]);
+
+ // Shrink so that fixed length TAs go out of bounds.
+ rab.resize(3 * ctor.BYTES_PER_ELEMENT);
+
+ // Orig. array: [10, 8, 6]
+ // [10, 8, 6, ...] << lengthTracking
+ // [6, ...] << lengthTrackingWithOffset
+
+ WriteUnsortedData();
+ ArraySortHelper(fixedLength); // OOB -> NOOP
+ assert.compareArray(ToNumbers(taFull), [
+ 10,
+ 8,
+ 6
+ ]);
+ ArraySortHelper(fixedLengthWithOffset); // OOB -> NOOP
+ assert.compareArray(ToNumbers(taFull), [
+ 10,
+ 8,
+ 6
+ ]);
+ ArraySortHelper(lengthTracking);
+ assert.compareArray(ToNumbers(taFull), [
+ 10,
+ 6,
+ 8
+ ]);
+ WriteUnsortedData();
+ ArraySortHelper(lengthTrackingWithOffset);
+ assert.compareArray(ToNumbers(taFull), [
+ 10,
+ 8,
+ 6
+ ]);
+
+ // Shrink so that the TAs with offset go out of bounds.
+ rab.resize(1 * ctor.BYTES_PER_ELEMENT);
+ WriteUnsortedData();
+ ArraySortHelper(fixedLength); // OOB -> NOOP
+ assert.compareArray(ToNumbers(taFull), [10]);
+ ArraySortHelper(fixedLengthWithOffset); // OOB -> NOOP
+ assert.compareArray(ToNumbers(taFull), [10]);
+ ArraySortHelper(lengthTrackingWithOffset); // OOB -> NOOP
+ assert.compareArray(ToNumbers(taFull), [10]);
+ ArraySortHelper(lengthTracking);
+ assert.compareArray(ToNumbers(taFull), [10]);
+
+ // Shrink to zero.
+ rab.resize(0);
+ ArraySortHelper(fixedLength); // OOB -> NOOP
+ assert.compareArray(ToNumbers(taFull), []);
+ ArraySortHelper(fixedLengthWithOffset); // OOB -> NOOP
+ assert.compareArray(ToNumbers(taFull), []);
+ ArraySortHelper(lengthTrackingWithOffset); // OOB -> NOOP
+ assert.compareArray(ToNumbers(taFull), []);
+ ArraySortHelper(lengthTracking);
+ assert.compareArray(ToNumbers(taFull), []);
+
+ // Grow so that all TAs are back in-bounds.
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+
+ // Orig. array: [10, 8, 6, 4, 2, 0]
+ // [10, 8, 6, 4] << fixedLength
+ // [6, 4] << fixedLengthWithOffset
+ // [10, 8, 6, 4, 2, 0, ...] << lengthTracking
+ // [6, 4, 2, 0, ...] << lengthTrackingWithOffset
+
+ WriteUnsortedData();
+ ArraySortHelper(fixedLength);
+ assert.compareArray(ToNumbers(taFull), [
+ 10,
+ 4,
+ 6,
+ 8,
+ 2,
+ 0
+ ]);
+ WriteUnsortedData();
+ ArraySortHelper(fixedLengthWithOffset);
+ assert.compareArray(ToNumbers(taFull), [
+ 10,
+ 8,
+ 4,
+ 6,
+ 2,
+ 0
+ ]);
+ WriteUnsortedData();
+ ArraySortHelper(lengthTracking);
+ assert.compareArray(ToNumbers(taFull), [
+ 0,
+ 10,
+ 2,
+ 4,
+ 6,
+ 8
+ ]);
+ WriteUnsortedData();
+ ArraySortHelper(lengthTrackingWithOffset);
+ assert.compareArray(ToNumbers(taFull), [
+ 10,
+ 8,
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/at-parameter-conversion-resizes-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/at-parameter-conversion-resizes-strict.js
new file mode 100644
index 0000000000..6f7e006b38
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/at-parameter-conversion-resizes-strict.js
@@ -0,0 +1,95 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from AtParameterConversionResizes test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function TypedArrayAtHelper(ta, index) {
+ const result = ta.at(index);
+ return Convert(result);
+}
+
+function ArrayAtHelper(ta, index) {
+ const result = Array.prototype.at.call(ta, index);
+ return Convert(result);
+}
+
+function AtParameterConversionResizes(atHelper) {
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ let evil = {
+ valueOf: () => {
+ rab.resize(2);
+ return 0;
+ }
+ };
+ assert.sameValue(atHelper(fixedLength, evil), undefined);
+ }
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const lengthTracking = new ctor(rab);
+ let evil = {
+ valueOf: () => {
+ rab.resize(2);
+ return -1;
+ }
+ };
+ // The TypedArray is *not* out of bounds since it's length-tracking.
+ assert.sameValue(atHelper(lengthTracking, evil), undefined);
+ }
+}
+
+AtParameterConversionResizes(TypedArrayAtHelper);
+AtParameterConversionResizes(ArrayAtHelper);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/at-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/at-strict.js
new file mode 100644
index 0000000000..f7c0f112ab
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/at-strict.js
@@ -0,0 +1,139 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from At test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function TypedArrayAtHelper(ta, index) {
+ const result = ta.at(index);
+ return Convert(result);
+}
+
+function ArrayAtHelper(ta, index) {
+ const result = Array.prototype.at.call(ta, index);
+ return Convert(result);
+}
+
+function At(atHelper, oobThrows) {
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ const lengthTracking = new ctor(rab, 0);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+
+ // Write some data into the array.
+ let ta_write = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(ta_write, i, i);
+ }
+ assert.sameValue(atHelper(fixedLength, -1), 3);
+ assert.sameValue(atHelper(lengthTracking, -1), 3);
+ assert.sameValue(atHelper(fixedLengthWithOffset, -1), 3);
+ assert.sameValue(atHelper(lengthTrackingWithOffset, -1), 3);
+
+ // Shrink so that fixed length TAs go out of bounds.
+ rab.resize(3 * ctor.BYTES_PER_ELEMENT);
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ atHelper(fixedLength, -1);
+ });
+ assert.throws(TypeError, () => {
+ atHelper(fixedLengthWithOffset, -1);
+ });
+ } else {
+ assert.sameValue(atHelper(fixedLength, -1), undefined);
+ assert.sameValue(atHelper(fixedLengthWithOffset, -1), undefined);
+ }
+ assert.sameValue(atHelper(lengthTracking, -1), 2);
+ assert.sameValue(atHelper(lengthTrackingWithOffset, -1), 2);
+
+ // Shrink so that the TAs with offset go out of bounds.
+ rab.resize(1 * ctor.BYTES_PER_ELEMENT);
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ atHelper(fixedLength, -1);
+ });
+ assert.throws(TypeError, () => {
+ atHelper(fixedLengthWithOffset, -1);
+ });
+ assert.throws(TypeError, () => {
+ atHelper(lengthTrackingWithOffset, -1);
+ });
+ } else {
+ assert.sameValue(atHelper(fixedLength, -1), undefined);
+ assert.sameValue(atHelper(fixedLengthWithOffset, -1), undefined);
+ assert.sameValue(atHelper(lengthTrackingWithOffset, -1), undefined);
+ }
+ assert.sameValue(atHelper(lengthTracking, -1), 0);
+
+ // Grow so that all TAs are back in-bounds. New memory is zeroed.
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ assert.sameValue(atHelper(fixedLength, -1), 0);
+ assert.sameValue(atHelper(lengthTracking, -1), 0);
+ assert.sameValue(atHelper(fixedLengthWithOffset, -1), 0);
+ assert.sameValue(atHelper(lengthTrackingWithOffset, -1), 0);
+ }
+}
+
+At(TypedArrayAtHelper, true);
+At(ArrayAtHelper, false);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/browser.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/browser.js
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/construct-from-typed-array-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/construct-from-typed-array-strict.js
new file mode 100644
index 0000000000..ffc0364625
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/construct-from-typed-array-strict.js
@@ -0,0 +1,212 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from ConstructFromTypedArray test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function IsBigIntTypedArray(ta) {
+ return ta instanceof BigInt64Array || ta instanceof BigUint64Array;
+}
+
+function AllBigIntMatchedCtorCombinations(test) {
+ for (let targetCtor of ctors) {
+ for (let sourceCtor of ctors) {
+ if (IsBigIntTypedArray(new targetCtor()) != IsBigIntTypedArray(new sourceCtor())) {
+ continue;
+ }
+ test(targetCtor, sourceCtor);
+ }
+ }
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+AllBigIntMatchedCtorCombinations((targetCtor, sourceCtor) => {
+ const rab = CreateResizableArrayBuffer(4 * sourceCtor.BYTES_PER_ELEMENT, 8 * sourceCtor.BYTES_PER_ELEMENT);
+ const fixedLength = new sourceCtor(rab, 0, 4);
+ const fixedLengthWithOffset = new sourceCtor(rab, 2 * sourceCtor.BYTES_PER_ELEMENT, 2);
+ const lengthTracking = new sourceCtor(rab, 0);
+ const lengthTrackingWithOffset = new sourceCtor(rab, 2 * sourceCtor.BYTES_PER_ELEMENT);
+
+ // Write some data into the array.
+ const taFull = new sourceCtor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taFull, i, i + 1);
+ }
+
+ // Orig. array: [1, 2, 3, 4]
+ // [1, 2, 3, 4] << fixedLength
+ // [3, 4] << fixedLengthWithOffset
+ // [1, 2, 3, 4, ...] << lengthTracking
+ // [3, 4, ...] << lengthTrackingWithOffset
+
+ assert.compareArray(ToNumbers(new targetCtor(fixedLength)), [
+ 1,
+ 2,
+ 3,
+ 4
+ ]);
+ assert.compareArray(ToNumbers(new targetCtor(fixedLengthWithOffset)), [
+ 3,
+ 4
+ ]);
+ assert.compareArray(ToNumbers(new targetCtor(lengthTracking)), [
+ 1,
+ 2,
+ 3,
+ 4
+ ]);
+ assert.compareArray(ToNumbers(new targetCtor(lengthTrackingWithOffset)), [
+ 3,
+ 4
+ ]);
+
+ // Shrink so that fixed length TAs go out of bounds.
+ rab.resize(3 * sourceCtor.BYTES_PER_ELEMENT);
+
+ // Orig. array: [1, 2, 3]
+ // [1, 2, 3, ...] << lengthTracking
+ // [3, ...] << lengthTrackingWithOffset
+
+ assert.throws(TypeError, () => {
+ new targetCtor(fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ new targetCtor(fixedLengthWithOffset);
+ });
+ assert.compareArray(ToNumbers(new targetCtor(lengthTracking)), [
+ 1,
+ 2,
+ 3
+ ]);
+ assert.compareArray(ToNumbers(new targetCtor(lengthTrackingWithOffset)), [3]);
+
+ // Shrink so that the TAs with offset go out of bounds.
+ rab.resize(1 * sourceCtor.BYTES_PER_ELEMENT);
+ assert.throws(TypeError, () => {
+ new targetCtor(fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ new targetCtor(fixedLengthWithOffset);
+ });
+ assert.compareArray(ToNumbers(new targetCtor(lengthTracking)), [1]);
+ assert.throws(TypeError, () => {
+ new targetCtor(lengthTrackingWithOffset);
+ });
+
+ // Shrink to zero.
+ rab.resize(0);
+ assert.throws(TypeError, () => {
+ new targetCtor(fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ new targetCtor(fixedLengthWithOffset);
+ });
+ assert.compareArray(ToNumbers(new targetCtor(lengthTracking)), []);
+ assert.throws(TypeError, () => {
+ new targetCtor(lengthTrackingWithOffset);
+ });
+
+ // Grow so that all TAs are back in-bounds.
+ rab.resize(6 * sourceCtor.BYTES_PER_ELEMENT);
+ for (let i = 0; i < 6; ++i) {
+ WriteToTypedArray(taFull, i, i + 1);
+ }
+
+ // Orig. array: [1, 2, 3, 4, 5, 6]
+ // [1, 2, 3, 4] << fixedLength
+ // [3, 4] << fixedLengthWithOffset
+ // [1, 2, 3, 4, 5, 6, ...] << lengthTracking
+ // [3, 4, 5, 6, ...] << lengthTrackingWithOffset
+
+ assert.compareArray(ToNumbers(new targetCtor(fixedLength)), [
+ 1,
+ 2,
+ 3,
+ 4
+ ]);
+ assert.compareArray(ToNumbers(new targetCtor(fixedLengthWithOffset)), [
+ 3,
+ 4
+ ]);
+ assert.compareArray(ToNumbers(new targetCtor(lengthTracking)), [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6
+ ]);
+ assert.compareArray(ToNumbers(new targetCtor(lengthTrackingWithOffset)), [
+ 3,
+ 4,
+ 5,
+ 6
+ ]);
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/construct-invalid-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/construct-invalid-strict.js
new file mode 100644
index 0000000000..7ef2fa1295
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/construct-invalid-strict.js
@@ -0,0 +1,74 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from ConstructInvalid test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+const rab = CreateResizableArrayBuffer(40, 80);
+for (let ctor of ctors) {
+ // Length too big.
+ assert.throws(RangeError, () => {
+ new ctor(rab, 0, 40 / ctor.BYTES_PER_ELEMENT + 1);
+ });
+ // Offset too close to the end.
+ assert.throws(RangeError, () => {
+ new ctor(rab, 40 - ctor.BYTES_PER_ELEMENT, 2);
+ });
+ // Offset beyond end.
+ assert.throws(RangeError, () => {
+ new ctor(rab, 40, 1);
+ });
+ if (ctor.BYTES_PER_ELEMENT > 1) {
+ // Offset not a multiple of the byte size.
+ assert.throws(RangeError, () => {
+ new ctor(rab, 1, 1);
+ });
+ assert.throws(RangeError, () => {
+ new ctor(rab, 1);
+ });
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/copy-within-parameter-conversion-grows-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/copy-within-parameter-conversion-grows-strict.js
new file mode 100644
index 0000000000..d3b6cbe667
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/copy-within-parameter-conversion-grows-strict.js
@@ -0,0 +1,118 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from CopyWithinParameterConversionGrows test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const lengthTracking = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(lengthTracking, i, i);
+ }
+ const evil = {
+ valueOf: () => {
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ WriteToTypedArray(lengthTracking, 4, 4);
+ WriteToTypedArray(lengthTracking, 5, 5);
+ return 0;
+ }
+ };
+ // Orig. array: [0, 1, 2, 3] [4, 5]
+ // ^ ^ ^ new elements
+ // target start
+ lengthTracking.copyWithin(evil, 2);
+ assert.compareArray(ToNumbers(lengthTracking), [
+ 2,
+ 3,
+ 2,
+ 3,
+ 4,
+ 5
+ ]);
+ rab.resize(4 * ctor.BYTES_PER_ELEMENT);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(lengthTracking, i, i);
+ }
+
+ // Orig. array: [0, 1, 2, 3] [4, 5]
+ // ^ ^ ^ new elements
+ // start target
+ lengthTracking.copyWithin(2, evil);
+ assert.compareArray(ToNumbers(lengthTracking), [
+ 0,
+ 1,
+ 0,
+ 1,
+ 4,
+ 5
+ ]);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/copy-within-parameter-conversion-shrinks-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/copy-within-parameter-conversion-shrinks-strict.js
new file mode 100644
index 0000000000..53aedbf7b6
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/copy-within-parameter-conversion-shrinks-strict.js
@@ -0,0 +1,143 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from CopyWithinParameterConversionShrinks test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const evil = {
+ valueOf: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 2;
+ }
+ };
+ assert.throws(TypeError, () => {
+ fixedLength.copyWithin(evil, 0, 1);
+ });
+ rab.resize(4 * ctor.BYTES_PER_ELEMENT);
+ assert.throws(TypeError, () => {
+ fixedLength.copyWithin(0, evil, 3);
+ });
+ rab.resize(4 * ctor.BYTES_PER_ELEMENT);
+ assert.throws(TypeError, () => {
+ fixedLength.copyWithin(0, 1, evil);
+ });
+}
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const lengthTracking = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(lengthTracking, i, i);
+ }
+ // [0, 1, 2, 3]
+ // ^
+ // target
+ // ^
+ // start
+ const evil = {
+ valueOf: () => {
+ rab.resize(3 * ctor.BYTES_PER_ELEMENT);
+ return 2;
+ }
+ };
+ lengthTracking.copyWithin(evil, 0);
+ assert.compareArray(ToNumbers(lengthTracking), [
+ 0,
+ 1,
+ 0
+ ]);
+}
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const lengthTracking = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(lengthTracking, i, i);
+ }
+ // [0, 1, 2, 3]
+ // ^
+ // start
+ // ^
+ // target
+ const evil = {
+ valueOf: () => {
+ rab.resize(3 * ctor.BYTES_PER_ELEMENT);
+ return 2;
+ }
+ };
+ lengthTracking.copyWithin(0, evil);
+ assert.compareArray(ToNumbers(lengthTracking), [
+ 2,
+ 1,
+ 2
+ ]);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/destructuring-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/destructuring-strict.js
new file mode 100644
index 0000000000..b01425f6cc
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/destructuring-strict.js
@@ -0,0 +1,262 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from Destructuring test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ const lengthTracking = new ctor(rab, 0);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+
+ // Write some data into the array.
+ let ta_write = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(ta_write, i, i);
+ }
+ {
+ let [a, b, c, d, e] = fixedLength;
+ assert.compareArray(ToNumbers([
+ a,
+ b,
+ c,
+ d
+ ]), [
+ 0,
+ 1,
+ 2,
+ 3
+ ]);
+ assert.sameValue(e, undefined);
+ }
+ {
+ let [a, b, c] = fixedLengthWithOffset;
+ assert.compareArray(ToNumbers([
+ a,
+ b
+ ]), [
+ 2,
+ 3
+ ]);
+ assert.sameValue(c, undefined);
+ }
+ {
+ let [a, b, c, d, e] = lengthTracking;
+ assert.compareArray(ToNumbers([
+ a,
+ b,
+ c,
+ d
+ ]), [
+ 0,
+ 1,
+ 2,
+ 3
+ ]);
+ assert.sameValue(e, undefined);
+ }
+ {
+ let [a, b, c] = lengthTrackingWithOffset;
+ assert.compareArray(ToNumbers([
+ a,
+ b
+ ]), [
+ 2,
+ 3
+ ]);
+ assert.sameValue(c, undefined);
+ }
+
+ // Shrink so that fixed length TAs go out of bounds.
+ rab.resize(3 * ctor.BYTES_PER_ELEMENT);
+ assert.throws(TypeError, () => {
+ let [a, b, c] = fixedLength;
+ });
+ assert.throws(TypeError, () => {
+ let [a, b, c] = fixedLengthWithOffset;
+ });
+ {
+ let [a, b, c, d] = lengthTracking;
+ assert.compareArray(ToNumbers([
+ a,
+ b,
+ c
+ ]), [
+ 0,
+ 1,
+ 2
+ ]);
+ assert.sameValue(d, undefined);
+ }
+ {
+ let [a, b] = lengthTrackingWithOffset;
+ assert.compareArray(ToNumbers([a]), [2]);
+ assert.sameValue(b, undefined);
+ }
+
+ // Shrink so that the TAs with offset go out of bounds.
+ rab.resize(1 * ctor.BYTES_PER_ELEMENT);
+ assert.throws(TypeError, () => {
+ let [a, b, c] = fixedLength;
+ });
+ assert.throws(TypeError, () => {
+ let [a, b, c] = fixedLengthWithOffset;
+ });
+ assert.throws(TypeError, () => {
+ let [a, b, c] = lengthTrackingWithOffset;
+ });
+ {
+ let [a, b] = lengthTracking;
+ assert.compareArray(ToNumbers([a]), [0]);
+ assert.sameValue(b, undefined);
+ }
+
+ // Shrink to 0.
+ rab.resize(0);
+ assert.throws(TypeError, () => {
+ let [a, b, c] = fixedLength;
+ });
+ assert.throws(TypeError, () => {
+ let [a, b, c] = fixedLengthWithOffset;
+ });
+ assert.throws(TypeError, () => {
+ let [a, b, c] = lengthTrackingWithOffset;
+ });
+ {
+ let [a] = lengthTracking;
+ assert.sameValue(a, undefined);
+ }
+
+ // Grow so that all TAs are back in-bounds. The new memory is zeroed.
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ {
+ let [a, b, c, d, e] = fixedLength;
+ assert.compareArray(ToNumbers([
+ a,
+ b,
+ c,
+ d
+ ]), [
+ 0,
+ 0,
+ 0,
+ 0
+ ]);
+ assert.sameValue(e, undefined);
+ }
+ {
+ let [a, b, c] = fixedLengthWithOffset;
+ assert.compareArray(ToNumbers([
+ a,
+ b
+ ]), [
+ 0,
+ 0
+ ]);
+ assert.sameValue(c, undefined);
+ }
+ {
+ let [a, b, c, d, e, f, g] = lengthTracking;
+ assert.compareArray(ToNumbers([
+ a,
+ b,
+ c,
+ d,
+ e,
+ f
+ ]), [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]);
+ assert.sameValue(g, undefined);
+ }
+ {
+ let [a, b, c, d, e] = lengthTrackingWithOffset;
+ assert.compareArray(ToNumbers([
+ a,
+ b,
+ c,
+ d
+ ]), [
+ 0,
+ 0,
+ 0,
+ 0
+ ]);
+ assert.sameValue(e, undefined);
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/entries-keys-values-grow-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/entries-keys-values-grow-mid-iteration-strict.js
new file mode 100644
index 0000000000..dfe2aea4f8
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/entries-keys-values-grow-mid-iteration-strict.js
@@ -0,0 +1,303 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from EntriesKeysValuesGrowMidIteration test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+includes: [compareArray.js]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function TypedArrayEntriesHelper(ta) {
+ return ta.entries();
+}
+
+function ArrayEntriesHelper(ta) {
+ return Array.prototype.entries.call(ta);
+}
+
+function TypedArrayKeysHelper(ta) {
+ return ta.keys();
+}
+
+function ArrayKeysHelper(ta) {
+ return Array.prototype.keys.call(ta);
+}
+
+function TypedArrayValuesHelper(ta) {
+ return ta.values();
+}
+
+function ArrayValuesHelper(ta) {
+ return Array.prototype.values.call(ta);
+}
+
+function TestIterationAndResize(ta, expected, rab, resize_after, new_byte_length) {
+ let values = [];
+ let resized = false;
+ for (const value of ta) {
+ if (value instanceof Array) {
+ values.push([
+ value[0],
+ Number(value[1])
+ ]);
+ } else {
+ values.push(Number(value));
+ }
+ if (!resized && values.length == resize_after) {
+ rab.resize(new_byte_length);
+ resized = true;
+ }
+ }
+ assert.compareArray(values.flat(), expected.flat());
+ assert(resized);
+}
+
+function EntriesKeysValuesGrowMidIteration(entriesHelper, keysHelper, valuesHelper) {
+ // Orig. array: [0, 2, 4, 6]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, ...] << lengthTracking
+ // [4, 6, ...] << lengthTrackingWithOffset
+ function CreateRabForTest(ctor) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+ return rab;
+ }
+
+ // Iterating with entries() (the 4 loops below).
+ for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ // The fixed length array is not affected by resizing.
+ TestIterationAndResize(entriesHelper(fixedLength), [
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 2
+ ],
+ [
+ 2,
+ 4
+ ],
+ [
+ 3,
+ 6
+ ]
+ ], rab, 2, 6 * ctor.BYTES_PER_ELEMENT);
+ }
+ for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ // The fixed length array is not affected by resizing.
+ TestIterationAndResize(entriesHelper(fixedLengthWithOffset), [
+ [
+ 0,
+ 4
+ ],
+ [
+ 1,
+ 6
+ ]
+ ], rab, 2, 6 * ctor.BYTES_PER_ELEMENT);
+ }
+ for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ TestIterationAndResize(entriesHelper(lengthTracking), [
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 2
+ ],
+ [
+ 2,
+ 4
+ ],
+ [
+ 3,
+ 6
+ ],
+ [
+ 4,
+ 0
+ ],
+ [
+ 5,
+ 0
+ ]
+ ], rab, 2, 6 * ctor.BYTES_PER_ELEMENT);
+ }
+ for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ TestIterationAndResize(entriesHelper(lengthTrackingWithOffset), [
+ [
+ 0,
+ 4
+ ],
+ [
+ 1,
+ 6
+ ],
+ [
+ 2,
+ 0
+ ],
+ [
+ 3,
+ 0
+ ]
+ ], rab, 2, 6 * ctor.BYTES_PER_ELEMENT);
+ }
+
+ // Iterating with keys() (the 4 loops below).
+ for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ // The fixed length array is not affected by resizing.
+ TestIterationAndResize(keysHelper(fixedLength), [
+ 0,
+ 1,
+ 2,
+ 3
+ ], rab, 2, 6 * ctor.BYTES_PER_ELEMENT);
+ }
+ for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ // The fixed length array is not affected by resizing.
+ TestIterationAndResize(keysHelper(fixedLengthWithOffset), [
+ 0,
+ 1
+ ], rab, 2, 6 * ctor.BYTES_PER_ELEMENT);
+ }
+ for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ TestIterationAndResize(keysHelper(lengthTracking), [
+ 0,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5
+ ], rab, 2, 6 * ctor.BYTES_PER_ELEMENT);
+ }
+ for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ TestIterationAndResize(keysHelper(lengthTrackingWithOffset), [
+ 0,
+ 1,
+ 2,
+ 3
+ ], rab, 2, 6 * ctor.BYTES_PER_ELEMENT);
+ }
+
+ // Iterating with values() (the 4 loops below).
+ for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ // The fixed length array is not affected by resizing.
+ TestIterationAndResize(valuesHelper(fixedLength), [
+ 0,
+ 2,
+ 4,
+ 6
+ ], rab, 2, 6 * ctor.BYTES_PER_ELEMENT);
+ }
+ for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ // The fixed length array is not affected by resizing.
+ TestIterationAndResize(valuesHelper(fixedLengthWithOffset), [
+ 4,
+ 6
+ ], rab, 2, 6 * ctor.BYTES_PER_ELEMENT);
+ }
+ for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ TestIterationAndResize(valuesHelper(lengthTracking), [
+ 0,
+ 2,
+ 4,
+ 6,
+ 0,
+ 0
+ ], rab, 2, 6 * ctor.BYTES_PER_ELEMENT);
+ }
+ for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ TestIterationAndResize(valuesHelper(lengthTrackingWithOffset), [
+ 4,
+ 6,
+ 0,
+ 0
+ ], rab, 2, 6 * ctor.BYTES_PER_ELEMENT);
+ }
+}
+
+EntriesKeysValuesGrowMidIteration(TypedArrayEntriesHelper, TypedArrayKeysHelper, TypedArrayValuesHelper);
+EntriesKeysValuesGrowMidIteration(ArrayEntriesHelper, ArrayKeysHelper, ArrayValuesHelper);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/entries-keys-values-shrink-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/entries-keys-values-shrink-mid-iteration-strict.js
new file mode 100644
index 0000000000..08cfb4dd2c
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/entries-keys-values-shrink-mid-iteration-strict.js
@@ -0,0 +1,248 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from EntriesKeysValuesShrinkMidIteration test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+includes: [compareArray.js]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function TypedArrayEntriesHelper(ta) {
+ return ta.entries();
+}
+
+function ArrayEntriesHelper(ta) {
+ return Array.prototype.entries.call(ta);
+}
+
+function TypedArrayKeysHelper(ta) {
+ return ta.keys();
+}
+
+function ArrayKeysHelper(ta) {
+ return Array.prototype.keys.call(ta);
+}
+
+function TypedArrayValuesHelper(ta) {
+ return ta.values();
+}
+
+function ArrayValuesHelper(ta) {
+ return Array.prototype.values.call(ta);
+}
+
+function TestIterationAndResize(ta, expected, rab, resize_after, new_byte_length) {
+ let values = [];
+ let resized = false;
+ for (const value of ta) {
+ if (value instanceof Array) {
+ values.push([
+ value[0],
+ Number(value[1])
+ ]);
+ } else {
+ values.push(Number(value));
+ }
+ if (!resized && values.length == resize_after) {
+ rab.resize(new_byte_length);
+ resized = true;
+ }
+ }
+ assert.compareArray(values.flat(), expected.flat());
+ assert(resized);
+}
+
+function EntriesKeysValuesShrinkMidIteration(entriesHelper, keysHelper, valuesHelper) {
+ // Orig. array: [0, 2, 4, 6]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, ...] << lengthTracking
+ // [4, 6, ...] << lengthTrackingWithOffset
+ function CreateRabForTest(ctor) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+ return rab;
+ }
+
+ // Iterating with entries() (the 4 loops below).
+ for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+
+ // The fixed length array goes out of bounds when the RAB is resized.
+ assert.throws(TypeError, () => {
+ TestIterationAndResize(entriesHelper(fixedLength), null, rab, 2, 3 * ctor.BYTES_PER_ELEMENT);
+ });
+ }
+ for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+
+ // The fixed length array goes out of bounds when the RAB is resized.
+ assert.throws(TypeError, () => {
+ TestIterationAndResize(entriesHelper(fixedLengthWithOffset), null, rab, 1, 3 * ctor.BYTES_PER_ELEMENT);
+ });
+ }
+ for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ TestIterationAndResize(entriesHelper(lengthTracking), [
+ [
+ 0,
+ 0
+ ],
+ [
+ 1,
+ 2
+ ],
+ [
+ 2,
+ 4
+ ]
+ ], rab, 2, 3 * ctor.BYTES_PER_ELEMENT);
+ }
+ for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ TestIterationAndResize(entriesHelper(lengthTrackingWithOffset), [
+ [
+ 0,
+ 4
+ ],
+ [
+ 1,
+ 6
+ ]
+ ], rab, 2, 3 * ctor.BYTES_PER_ELEMENT);
+ }
+
+ // Iterating with keys() (the 4 loops below).
+ for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+
+ // The fixed length array goes out of bounds when the RAB is resized.
+ assert.throws(TypeError, () => {
+ TestIterationAndResize(keysHelper(fixedLength), null, rab, 2, 3 * ctor.BYTES_PER_ELEMENT);
+ });
+ }
+ for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+
+ // The fixed length array goes out of bounds when the RAB is resized.
+ assert.throws(TypeError, () => {
+ TestIterationAndResize(keysHelper(fixedLengthWithOffset), null, rab, 2, 3 * ctor.BYTES_PER_ELEMENT);
+ });
+ }
+ for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ TestIterationAndResize(keysHelper(lengthTracking), [
+ 0,
+ 1,
+ 2
+ ], rab, 2, 3 * ctor.BYTES_PER_ELEMENT);
+ }
+ for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ TestIterationAndResize(keysHelper(lengthTrackingWithOffset), [
+ 0,
+ 1
+ ], rab, 2, 3 * ctor.BYTES_PER_ELEMENT);
+ }
+
+ // Iterating with values() (the 4 loops below).
+ for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+
+ // The fixed length array goes out of bounds when the RAB is resized.
+ assert.throws(TypeError, () => {
+ TestIterationAndResize(valuesHelper(fixedLength), null, rab, 2, 3 * ctor.BYTES_PER_ELEMENT);
+ });
+ }
+ for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ assert.throws(TypeError, () => {
+ TestIterationAndResize(valuesHelper(fixedLengthWithOffset), null, rab, 2, 3 * ctor.BYTES_PER_ELEMENT);
+ });
+ }
+ for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ TestIterationAndResize(valuesHelper(lengthTracking), [
+ 0,
+ 2,
+ 4
+ ], rab, 2, 3 * ctor.BYTES_PER_ELEMENT);
+ }
+ for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+
+ // The fixed length array goes out of bounds when the RAB is resized.
+ TestIterationAndResize(valuesHelper(lengthTrackingWithOffset), [
+ 4,
+ 6
+ ], rab, 2, 3 * ctor.BYTES_PER_ELEMENT);
+ }
+}
+
+EntriesKeysValuesShrinkMidIteration(TypedArrayEntriesHelper, TypedArrayKeysHelper, TypedArrayValuesHelper);
+EntriesKeysValuesShrinkMidIteration(ArrayEntriesHelper, ArrayKeysHelper, ArrayValuesHelper);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/entries-keys-values-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/entries-keys-values-strict.js
new file mode 100644
index 0000000000..13d4cb1f89
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/entries-keys-values-strict.js
@@ -0,0 +1,509 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from EntriesKeysValues test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function TypedArrayEntriesHelper(ta) {
+ return ta.entries();
+}
+
+function ArrayEntriesHelper(ta) {
+ return Array.prototype.entries.call(ta);
+}
+
+function ValuesFromTypedArrayEntries(ta) {
+ let result = [];
+ let expectedKey = 0;
+ for (let [key, value] of ta.entries()) {
+ assert.sameValue(key, expectedKey);
+ ++expectedKey;
+ result.push(Number(value));
+ }
+ return result;
+}
+
+function ValuesFromArrayEntries(ta) {
+ let result = [];
+ let expectedKey = 0;
+ for (let [key, value] of Array.prototype.entries.call(ta)) {
+ assert.sameValue(key, expectedKey);
+ ++expectedKey;
+ result.push(Number(value));
+ }
+ return result;
+}
+
+function TypedArrayKeysHelper(ta) {
+ return ta.keys();
+}
+
+function ArrayKeysHelper(ta) {
+ return Array.prototype.keys.call(ta);
+}
+
+function TypedArrayValuesHelper(ta) {
+ return ta.values();
+}
+
+function ArrayValuesHelper(ta) {
+ return Array.prototype.values.call(ta);
+}
+
+function ValuesFromTypedArrayValues(ta) {
+ let result = [];
+ for (let value of ta.values()) {
+ result.push(Number(value));
+ }
+ return result;
+}
+
+function ValuesFromArrayValues(ta) {
+ const result = [];
+ for (let value of Array.prototype.values.call(ta)) {
+ result.push(Number(value));
+ }
+ return result;
+}
+
+function EntriesKeysValues(entriesHelper, keysHelper, valuesHelper, valuesFromEntries, valuesFromValues, oobThrows) {
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ const lengthTracking = new ctor(rab, 0);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+
+ // Orig. array: [0, 2, 4, 6]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, ...] << lengthTracking
+ // [4, 6, ...] << lengthTrackingWithOffset
+
+ assert.compareArray(valuesFromEntries(fixedLength), [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ assert.compareArray(valuesFromValues(fixedLength), [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ assert.compareArray(Array.from(keysHelper(fixedLength)), [
+ 0,
+ 1,
+ 2,
+ 3
+ ]);
+ assert.compareArray(valuesFromEntries(fixedLengthWithOffset), [
+ 4,
+ 6
+ ]);
+ assert.compareArray(valuesFromValues(fixedLengthWithOffset), [
+ 4,
+ 6
+ ]);
+ assert.compareArray(Array.from(keysHelper(fixedLengthWithOffset)), [
+ 0,
+ 1
+ ]);
+ assert.compareArray(valuesFromEntries(lengthTracking), [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ assert.compareArray(valuesFromValues(lengthTracking), [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ assert.compareArray(Array.from(keysHelper(lengthTracking)), [
+ 0,
+ 1,
+ 2,
+ 3
+ ]);
+ assert.compareArray(valuesFromEntries(lengthTrackingWithOffset), [
+ 4,
+ 6
+ ]);
+ assert.compareArray(valuesFromValues(lengthTrackingWithOffset), [
+ 4,
+ 6
+ ]);
+ assert.compareArray(Array.from(keysHelper(lengthTrackingWithOffset)), [
+ 0,
+ 1
+ ]);
+
+ // Shrink so that fixed length TAs go out of bounds.
+ rab.resize(3 * ctor.BYTES_PER_ELEMENT);
+
+ // Orig. array: [0, 2, 4]
+ // [0, 2, 4, ...] << lengthTracking
+ // [4, ...] << lengthTrackingWithOffset
+
+ // TypedArray.prototype.{entries, keys, values} throw right away when
+ // called. Array.prototype.{entries, keys, values} don't throw, but when
+ // we try to iterate the returned ArrayIterator, that throws.
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ entriesHelper(fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ valuesHelper(fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ keysHelper(fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ entriesHelper(fixedLengthWithOffset);
+ });
+ assert.throws(TypeError, () => {
+ valuesHelper(fixedLengthWithOffset);
+ });
+ assert.throws(TypeError, () => {
+ keysHelper(fixedLengthWithOffset);
+ });
+ } else {
+ entriesHelper(fixedLength);
+ valuesHelper(fixedLength);
+ keysHelper(fixedLength);
+ entriesHelper(fixedLengthWithOffset);
+ valuesHelper(fixedLengthWithOffset);
+ keysHelper(fixedLengthWithOffset);
+ }
+ assert.throws(TypeError, () => {
+ Array.from(entriesHelper(fixedLength));
+ });
+ assert.throws(TypeError, () => {
+ Array.from(valuesHelper(fixedLength));
+ });
+ assert.throws(TypeError, () => {
+ Array.from(keysHelper(fixedLength));
+ });
+ assert.throws(TypeError, () => {
+ Array.from(entriesHelper(fixedLengthWithOffset));
+ });
+ assert.throws(TypeError, () => {
+ Array.from(valuesHelper(fixedLengthWithOffset));
+ });
+ assert.throws(TypeError, () => {
+ Array.from(keysHelper(fixedLengthWithOffset));
+ });
+ assert.compareArray(valuesFromEntries(lengthTracking), [
+ 0,
+ 2,
+ 4
+ ]);
+ assert.compareArray(valuesFromValues(lengthTracking), [
+ 0,
+ 2,
+ 4
+ ]);
+ assert.compareArray(Array.from(keysHelper(lengthTracking)), [
+ 0,
+ 1,
+ 2
+ ]);
+ assert.compareArray(valuesFromEntries(lengthTrackingWithOffset), [4]);
+ assert.compareArray(valuesFromValues(lengthTrackingWithOffset), [4]);
+ assert.compareArray(Array.from(keysHelper(lengthTrackingWithOffset)), [0]);
+
+ // Shrink so that the TAs with offset go out of bounds.
+ rab.resize(1 * ctor.BYTES_PER_ELEMENT);
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ entriesHelper(fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ valuesHelper(fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ keysHelper(fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ entriesHelper(fixedLengthWithOffset);
+ });
+ assert.throws(TypeError, () => {
+ valuesHelper(fixedLengthWithOffset);
+ });
+ assert.throws(TypeError, () => {
+ keysHelper(fixedLengthWithOffset);
+ });
+ assert.throws(TypeError, () => {
+ entriesHelper(lengthTrackingWithOffset);
+ });
+ assert.throws(TypeError, () => {
+ valuesHelper(lengthTrackingWithOffset);
+ });
+ assert.throws(TypeError, () => {
+ keysHelper(lengthTrackingWithOffset);
+ });
+ } else {
+ entriesHelper(fixedLength);
+ valuesHelper(fixedLength);
+ keysHelper(fixedLength);
+ entriesHelper(fixedLengthWithOffset);
+ valuesHelper(fixedLengthWithOffset);
+ keysHelper(fixedLengthWithOffset);
+ entriesHelper(lengthTrackingWithOffset);
+ valuesHelper(lengthTrackingWithOffset);
+ keysHelper(lengthTrackingWithOffset);
+ }
+ assert.throws(TypeError, () => {
+ Array.from(entriesHelper(fixedLength));
+ });
+ assert.throws(TypeError, () => {
+ Array.from(valuesHelper(fixedLength));
+ });
+ assert.throws(TypeError, () => {
+ Array.from(keysHelper(fixedLength));
+ });
+ assert.throws(TypeError, () => {
+ Array.from(entriesHelper(fixedLengthWithOffset));
+ });
+ assert.throws(TypeError, () => {
+ Array.from(valuesHelper(fixedLengthWithOffset));
+ });
+ assert.throws(TypeError, () => {
+ Array.from(keysHelper(fixedLengthWithOffset));
+ });
+ assert.throws(TypeError, () => {
+ Array.from(entriesHelper(lengthTrackingWithOffset));
+ });
+ assert.throws(TypeError, () => {
+ Array.from(valuesHelper(lengthTrackingWithOffset));
+ });
+ assert.throws(TypeError, () => {
+ Array.from(keysHelper(lengthTrackingWithOffset));
+ });
+ assert.compareArray(valuesFromEntries(lengthTracking), [0]);
+ assert.compareArray(valuesFromValues(lengthTracking), [0]);
+ assert.compareArray(Array.from(keysHelper(lengthTracking)), [0]);
+
+ // Shrink to zero.
+ rab.resize(0);
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ entriesHelper(fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ valuesHelper(fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ keysHelper(fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ entriesHelper(fixedLengthWithOffset);
+ });
+ assert.throws(TypeError, () => {
+ valuesHelper(fixedLengthWithOffset);
+ });
+ assert.throws(TypeError, () => {
+ keysHelper(fixedLengthWithOffset);
+ });
+ assert.throws(TypeError, () => {
+ entriesHelper(lengthTrackingWithOffset);
+ });
+ assert.throws(TypeError, () => {
+ valuesHelper(lengthTrackingWithOffset);
+ });
+ assert.throws(TypeError, () => {
+ keysHelper(lengthTrackingWithOffset);
+ });
+ } else {
+ entriesHelper(fixedLength);
+ valuesHelper(fixedLength);
+ keysHelper(fixedLength);
+ entriesHelper(fixedLengthWithOffset);
+ valuesHelper(fixedLengthWithOffset);
+ keysHelper(fixedLengthWithOffset);
+ entriesHelper(lengthTrackingWithOffset);
+ valuesHelper(lengthTrackingWithOffset);
+ keysHelper(lengthTrackingWithOffset);
+ }
+ assert.throws(TypeError, () => {
+ Array.from(entriesHelper(fixedLength));
+ });
+ assert.throws(TypeError, () => {
+ Array.from(valuesHelper(fixedLength));
+ });
+ assert.throws(TypeError, () => {
+ Array.from(keysHelper(fixedLength));
+ });
+ assert.throws(TypeError, () => {
+ Array.from(entriesHelper(fixedLengthWithOffset));
+ });
+ assert.throws(TypeError, () => {
+ Array.from(valuesHelper(fixedLengthWithOffset));
+ });
+ assert.throws(TypeError, () => {
+ Array.from(keysHelper(fixedLengthWithOffset));
+ });
+ assert.throws(TypeError, () => {
+ Array.from(entriesHelper(lengthTrackingWithOffset));
+ });
+ assert.throws(TypeError, () => {
+ Array.from(valuesHelper(lengthTrackingWithOffset));
+ });
+ assert.throws(TypeError, () => {
+ Array.from(keysHelper(lengthTrackingWithOffset));
+ });
+ assert.compareArray(valuesFromEntries(lengthTracking), []);
+ assert.compareArray(valuesFromValues(lengthTracking), []);
+ assert.compareArray(Array.from(keysHelper(lengthTracking)), []);
+
+ // Grow so that all TAs are back in-bounds.
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ for (let i = 0; i < 6; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+
+ // Orig. array: [0, 2, 4, 6, 8, 10]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, 8, 10, ...] << lengthTracking
+ // [4, 6, 8, 10, ...] << lengthTrackingWithOffset
+
+ assert.compareArray(valuesFromEntries(fixedLength), [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ assert.compareArray(valuesFromValues(fixedLength), [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ assert.compareArray(Array.from(keysHelper(fixedLength)), [
+ 0,
+ 1,
+ 2,
+ 3
+ ]);
+ assert.compareArray(valuesFromEntries(fixedLengthWithOffset), [
+ 4,
+ 6
+ ]);
+ assert.compareArray(valuesFromValues(fixedLengthWithOffset), [
+ 4,
+ 6
+ ]);
+ assert.compareArray(Array.from(keysHelper(fixedLengthWithOffset)), [
+ 0,
+ 1
+ ]);
+ assert.compareArray(valuesFromEntries(lengthTracking), [
+ 0,
+ 2,
+ 4,
+ 6,
+ 8,
+ 10
+ ]);
+ assert.compareArray(valuesFromValues(lengthTracking), [
+ 0,
+ 2,
+ 4,
+ 6,
+ 8,
+ 10
+ ]);
+ assert.compareArray(Array.from(keysHelper(lengthTracking)), [
+ 0,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5
+ ]);
+ assert.compareArray(valuesFromEntries(lengthTrackingWithOffset), [
+ 4,
+ 6,
+ 8,
+ 10
+ ]);
+ assert.compareArray(valuesFromValues(lengthTrackingWithOffset), [
+ 4,
+ 6,
+ 8,
+ 10
+ ]);
+ assert.compareArray(Array.from(keysHelper(lengthTrackingWithOffset)), [
+ 0,
+ 1,
+ 2,
+ 3
+ ]);
+ }
+}
+
+EntriesKeysValues(TypedArrayEntriesHelper, TypedArrayKeysHelper, TypedArrayValuesHelper, ValuesFromTypedArrayEntries, ValuesFromTypedArrayValues, true);
+EntriesKeysValues(ArrayEntriesHelper, ArrayKeysHelper, ArrayValuesHelper, ValuesFromArrayEntries, ValuesFromArrayValues, false);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/enumerate-elements-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/enumerate-elements-strict.js
new file mode 100644
index 0000000000..12da5209c5
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/enumerate-elements-strict.js
@@ -0,0 +1,59 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from EnumerateElements test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+let rab = CreateResizableArrayBuffer(100, 200);
+for (let ctor of ctors) {
+ const ta = new ctor(rab, 0, 3);
+ let keys = '';
+ for (const key in ta) {
+ keys += key;
+ }
+ assert.sameValue(keys, '012');
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/every-grow-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/every-grow-mid-iteration-strict.js
new file mode 100644
index 0000000000..5594faf89c
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/every-grow-mid-iteration-strict.js
@@ -0,0 +1,153 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from EveryGrowMidIteration test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+const TypedArrayEveryHelper = (ta, ...rest) => {
+ return ta.every(...rest);
+};
+
+const ArrayEveryHelper = (ta, ...rest) => {
+ return Array.prototype.every.call(ta, ...rest);
+};
+
+function EveryGrowMidIteration(everyHelper) {
+ // Orig. array: [0, 2, 4, 6]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, ...] << lengthTracking
+ // [4, 6, ...] << lengthTrackingWithOffset
+ function CreateRabForTest(ctor) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+ return rab;
+ }
+ let values;
+ let rab;
+ let resizeAfter;
+ let resizeTo;
+ function CollectValuesAndResize(n) {
+ if (typeof n == 'bigint') {
+ values.push(Number(n));
+ } else {
+ values.push(n);
+ }
+ if (values.length == resizeAfter) {
+ rab.resize(resizeTo);
+ }
+ return true;
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ values = [];
+ resizeAfter = 2;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert(everyHelper(fixedLength, CollectValuesAndResize));
+ assert.compareArray(values, [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ values = [];
+ resizeAfter = 1;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert(everyHelper(fixedLengthWithOffset, CollectValuesAndResize));
+ assert.compareArray(values, [
+ 4,
+ 6
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ values = [];
+ resizeAfter = 2;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert(everyHelper(lengthTracking, CollectValuesAndResize));
+ assert.compareArray(values, [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ values = [];
+ resizeAfter = 1;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert(everyHelper(lengthTrackingWithOffset, CollectValuesAndResize));
+ assert.compareArray(values, [
+ 4,
+ 6
+ ]);
+ }
+}
+
+EveryGrowMidIteration(TypedArrayEveryHelper);
+EveryGrowMidIteration(ArrayEveryHelper);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/every-shrink-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/every-shrink-mid-iteration-strict.js
new file mode 100644
index 0000000000..acddb44be2
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/every-shrink-mid-iteration-strict.js
@@ -0,0 +1,176 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from EveryShrinkMidIteration test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+const TypedArrayEveryHelper = (ta, ...rest) => {
+ return ta.every(...rest);
+};
+
+const ArrayEveryHelper = (ta, ...rest) => {
+ return Array.prototype.every.call(ta, ...rest);
+};
+
+function EveryShrinkMidIteration(everyHelper, hasUndefined) {
+ // Orig. array: [0, 2, 4, 6]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, ...] << lengthTracking
+ // [4, 6, ...] << lengthTrackingWithOffset
+ function CreateRabForTest(ctor) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+ return rab;
+ }
+ let values;
+ let rab;
+ let resizeAfter;
+ let resizeTo;
+ function CollectValuesAndResize(n) {
+ if (typeof n == 'bigint') {
+ values.push(Number(n));
+ } else {
+ values.push(n);
+ }
+ if (values.length == resizeAfter) {
+ rab.resize(resizeTo);
+ }
+ return true;
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ values = [];
+ resizeAfter = 2;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert(everyHelper(fixedLength, CollectValuesAndResize));
+ if (hasUndefined) {
+ assert.compareArray(values, [
+ 0,
+ 2,
+ undefined,
+ undefined
+ ]);
+ } else {
+ assert.compareArray(values, [
+ 0,
+ 2
+ ]);
+ }
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ values = [];
+ resizeAfter = 1;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert(everyHelper(fixedLengthWithOffset, CollectValuesAndResize));
+ if (hasUndefined) {
+ assert.compareArray(values, [
+ 4,
+ undefined
+ ]);
+ } else {
+ assert.compareArray(values, [4]);
+ }
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ values = [];
+ resizeAfter = 2;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert(everyHelper(lengthTracking, CollectValuesAndResize));
+ if (hasUndefined) {
+ assert.compareArray(values, [
+ 0,
+ 2,
+ 4,
+ undefined
+ ]);
+ } else {
+ assert.compareArray(values, [
+ 0,
+ 2,
+ 4
+ ]);
+ }
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ values = [];
+ resizeAfter = 1;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert(everyHelper(lengthTrackingWithOffset, CollectValuesAndResize));
+ if (hasUndefined) {
+ assert.compareArray(values, [
+ 4,
+ undefined
+ ]);
+ } else {
+ assert.compareArray(values, [4]);
+ }
+ }
+}
+
+EveryShrinkMidIteration(TypedArrayEveryHelper, true);
+EveryShrinkMidIteration(ArrayEveryHelper, false);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/every-some-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/every-some-strict.js
new file mode 100644
index 0000000000..0454e1583e
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/every-some-strict.js
@@ -0,0 +1,256 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from EverySome test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+const TypedArrayEveryHelper = (ta, ...rest) => {
+ return ta.every(...rest);
+};
+
+const ArrayEveryHelper = (ta, ...rest) => {
+ return Array.prototype.every.call(ta, ...rest);
+};
+
+const TypedArraySomeHelper = (ta, ...rest) => {
+ return ta.some(...rest);
+};
+
+const ArraySomeHelper = (ta, ...rest) => {
+ return Array.prototype.some.call(ta, ...rest);
+};
+
+function EverySome(everyHelper, someHelper, oobThrows) {
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ const lengthTracking = new ctor(rab, 0);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+
+ // Orig. array: [0, 2, 4, 6]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, ...] << lengthTracking
+ // [4, 6, ...] << lengthTrackingWithOffset
+
+ function div3(n) {
+ return Number(n) % 3 == 0;
+ }
+ function even(n) {
+ return Number(n) % 2 == 0;
+ }
+ function over10(n) {
+ return Number(n) > 10;
+ }
+ assert(!everyHelper(fixedLength, div3));
+ assert(everyHelper(fixedLength, even));
+ assert(someHelper(fixedLength, div3));
+ assert(!someHelper(fixedLength, over10));
+ assert(!everyHelper(fixedLengthWithOffset, div3));
+ assert(everyHelper(fixedLengthWithOffset, even));
+ assert(someHelper(fixedLengthWithOffset, div3));
+ assert(!someHelper(fixedLengthWithOffset, over10));
+ assert(!everyHelper(lengthTracking, div3));
+ assert(everyHelper(lengthTracking, even));
+ assert(someHelper(lengthTracking, div3));
+ assert(!someHelper(lengthTracking, over10));
+ assert(!everyHelper(lengthTrackingWithOffset, div3));
+ assert(everyHelper(lengthTrackingWithOffset, even));
+ assert(someHelper(lengthTrackingWithOffset, div3));
+ assert(!someHelper(lengthTrackingWithOffset, over10));
+
+ // Shrink so that fixed length TAs go out of bounds.
+ rab.resize(3 * ctor.BYTES_PER_ELEMENT);
+
+ // Orig. array: [0, 2, 4]
+ // [0, 2, 4, ...] << lengthTracking
+ // [4, ...] << lengthTrackingWithOffset
+
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ everyHelper(fixedLength, div3);
+ });
+ assert.throws(TypeError, () => {
+ someHelper(fixedLength, div3);
+ });
+ assert.throws(TypeError, () => {
+ everyHelper(fixedLengthWithOffset, div3);
+ });
+ assert.throws(TypeError, () => {
+ someHelper(fixedLengthWithOffset, div3);
+ });
+ } else {
+ assert(everyHelper(fixedLength, div3));
+ assert(!someHelper(fixedLength, div3));
+ assert(everyHelper(fixedLengthWithOffset, div3));
+ assert(!someHelper(fixedLengthWithOffset, div3));
+ }
+ assert(!everyHelper(lengthTracking, div3));
+ assert(everyHelper(lengthTracking, even));
+ assert(someHelper(lengthTracking, div3));
+ assert(!someHelper(lengthTracking, over10));
+ assert(!everyHelper(lengthTrackingWithOffset, div3));
+ assert(everyHelper(lengthTrackingWithOffset, even));
+ assert(!someHelper(lengthTrackingWithOffset, div3));
+ assert(!someHelper(lengthTrackingWithOffset, over10));
+
+ // Shrink so that the TAs with offset go out of bounds.
+ rab.resize(1 * ctor.BYTES_PER_ELEMENT);
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ everyHelper(fixedLength, div3);
+ });
+ assert.throws(TypeError, () => {
+ someHelper(fixedLength, div3);
+ });
+ assert.throws(TypeError, () => {
+ everyHelper(fixedLengthWithOffset, div3);
+ });
+ assert.throws(TypeError, () => {
+ someHelper(fixedLengthWithOffset, div3);
+ });
+ assert.throws(TypeError, () => {
+ everyHelper(lengthTrackingWithOffset, div3);
+ });
+ assert.throws(TypeError, () => {
+ someHelper(lengthTrackingWithOffset, div3);
+ });
+ } else {
+ assert(everyHelper(fixedLength, div3));
+ assert(!someHelper(fixedLength, div3));
+ assert(everyHelper(fixedLengthWithOffset, div3));
+ assert(!someHelper(fixedLengthWithOffset, div3));
+ assert(everyHelper(lengthTrackingWithOffset, div3));
+ assert(!someHelper(lengthTrackingWithOffset, div3));
+ }
+ assert(everyHelper(lengthTracking, div3));
+ assert(everyHelper(lengthTracking, even));
+ assert(someHelper(lengthTracking, div3));
+ assert(!someHelper(lengthTracking, over10));
+
+ // Shrink to zero.
+ rab.resize(0);
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ everyHelper(fixedLength, div3);
+ });
+ assert.throws(TypeError, () => {
+ someHelper(fixedLength, div3);
+ });
+ assert.throws(TypeError, () => {
+ everyHelper(fixedLengthWithOffset, div3);
+ });
+ assert.throws(TypeError, () => {
+ someHelper(fixedLengthWithOffset, div3);
+ });
+ assert.throws(TypeError, () => {
+ everyHelper(lengthTrackingWithOffset, div3);
+ });
+ assert.throws(TypeError, () => {
+ someHelper(lengthTrackingWithOffset, div3);
+ });
+ } else {
+ assert(everyHelper(fixedLength, div3));
+ assert(!someHelper(fixedLength, div3));
+ assert(everyHelper(fixedLengthWithOffset, div3));
+ assert(!someHelper(fixedLengthWithOffset, div3));
+ assert(everyHelper(lengthTrackingWithOffset, div3));
+ assert(!someHelper(lengthTrackingWithOffset, div3));
+ }
+ assert(everyHelper(lengthTracking, div3));
+ assert(everyHelper(lengthTracking, even));
+ assert(!someHelper(lengthTracking, div3));
+ assert(!someHelper(lengthTracking, over10));
+
+ // Grow so that all TAs are back in-bounds.
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ for (let i = 0; i < 6; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+
+ // Orig. array: [0, 2, 4, 6, 8, 10]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, 8, 10, ...] << lengthTracking
+ // [4, 6, 8, 10, ...] << lengthTrackingWithOffset
+
+ assert(!everyHelper(fixedLength, div3));
+ assert(everyHelper(fixedLength, even));
+ assert(someHelper(fixedLength, div3));
+ assert(!someHelper(fixedLength, over10));
+ assert(!everyHelper(fixedLengthWithOffset, div3));
+ assert(everyHelper(fixedLengthWithOffset, even));
+ assert(someHelper(fixedLengthWithOffset, div3));
+ assert(!someHelper(fixedLengthWithOffset, over10));
+ assert(!everyHelper(lengthTracking, div3));
+ assert(everyHelper(lengthTracking, even));
+ assert(someHelper(lengthTracking, div3));
+ assert(!someHelper(lengthTracking, over10));
+ assert(!everyHelper(lengthTrackingWithOffset, div3));
+ assert(everyHelper(lengthTrackingWithOffset, even));
+ assert(someHelper(lengthTrackingWithOffset, div3));
+ assert(!someHelper(lengthTrackingWithOffset, over10));
+ }
+}
+
+EverySome(TypedArrayEveryHelper, TypedArraySomeHelper, true);
+EverySome(ArrayEveryHelper, ArraySomeHelper, false);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/fill-parameter-conversion-resizes-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/fill-parameter-conversion-resizes-strict.js
new file mode 100644
index 0000000000..3544bc20ed
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/fill-parameter-conversion-resizes-strict.js
@@ -0,0 +1,97 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from FillParameterConversionResizes test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function TypedArrayFillHelper(ta, n, start, end) {
+ if (ta instanceof BigInt64Array || ta instanceof BigUint64Array) {
+ ta.fill(BigInt(n), start, end);
+ } else {
+ ta.fill(n, start, end);
+ }
+}
+
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const evil = {
+ valueOf: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 3;
+ }
+ };
+ assert.throws(TypeError, () => {
+ TypedArrayFillHelper(fixedLength, evil, 1, 2);
+ });
+}
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const evil = {
+ valueOf: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 1;
+ }
+ };
+ assert.throws(TypeError, () => {
+ TypedArrayFillHelper(fixedLength, 3, evil, 2);
+ });
+}
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const evil = {
+ valueOf: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 2;
+ }
+ };
+ assert.throws(TypeError, () => {
+ TypedArrayFillHelper(fixedLength, 3, 1, evil);
+ });
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/filter-grow-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/filter-grow-mid-iteration-strict.js
new file mode 100644
index 0000000000..87ad8440a8
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/filter-grow-mid-iteration-strict.js
@@ -0,0 +1,168 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from FilterGrowMidIteration test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+const TypedArrayFilterHelper = (ta, ...rest) => {
+ return ta.filter(...rest);
+};
+
+const ArrayFilterHelper = (ta, ...rest) => {
+ return Array.prototype.filter.call(ta, ...rest);
+};
+
+function FilterGrowMidIteration(filterHelper) {
+ // Orig. array: [0, 2, 4, 6]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, ...] << lengthTracking
+ // [4, 6, ...] << lengthTrackingWithOffset
+ function CreateRabForTest(ctor) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+ return rab;
+ }
+ let values;
+ let rab;
+ let resizeAfter;
+ let resizeTo;
+ function CollectValuesAndResize(n) {
+ if (typeof n == 'bigint') {
+ values.push(Number(n));
+ } else {
+ values.push(n);
+ }
+ if (values.length == resizeAfter) {
+ rab.resize(resizeTo);
+ }
+ return false;
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ values = [];
+ resizeAfter = 2;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ToNumbers(filterHelper(fixedLength, CollectValuesAndResize)), []);
+ assert.compareArray(values, [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ values = [];
+ resizeAfter = 1;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ToNumbers(filterHelper(fixedLengthWithOffset, CollectValuesAndResize)), []);
+ assert.compareArray(values, [
+ 4,
+ 6
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ values = [];
+ resizeAfter = 2;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ToNumbers(filterHelper(lengthTracking, CollectValuesAndResize)), []);
+ assert.compareArray(values, [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ values = [];
+ resizeAfter = 1;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ToNumbers(filterHelper(lengthTrackingWithOffset, CollectValuesAndResize)), []);
+ assert.compareArray(values, [
+ 4,
+ 6
+ ]);
+ }
+}
+
+FilterGrowMidIteration(TypedArrayFilterHelper);
+FilterGrowMidIteration(ArrayFilterHelper);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/filter-shrink-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/filter-shrink-mid-iteration-strict.js
new file mode 100644
index 0000000000..d08f29ca4f
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/filter-shrink-mid-iteration-strict.js
@@ -0,0 +1,155 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from FilterShrinkMidIteration test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+// Orig. array: [0, 2, 4, 6]
+// [0, 2, 4, 6] << fixedLength
+// [4, 6] << fixedLengthWithOffset
+// [0, 2, 4, 6, ...] << lengthTracking
+// [4, 6, ...] << lengthTrackingWithOffset
+function CreateRabForTest(ctor) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+ return rab;
+}
+let values;
+let rab;
+let resizeAfter;
+let resizeTo;
+function CollectValuesAndResize(n) {
+ if (typeof n == 'bigint') {
+ values.push(Number(n));
+ } else {
+ values.push(n);
+ }
+ if (values.length == resizeAfter) {
+ rab.resize(resizeTo);
+ }
+ return false;
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ values = [];
+ resizeAfter = 2;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ToNumbers(fixedLength.filter(CollectValuesAndResize)), []);
+ assert.compareArray(values, [
+ 0,
+ 2,
+ undefined,
+ undefined
+ ]);
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ values = [];
+ resizeAfter = 1;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ToNumbers(fixedLengthWithOffset.filter(CollectValuesAndResize)), []);
+ assert.compareArray(values, [
+ 4,
+ undefined
+ ]);
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ values = [];
+ resizeAfter = 2;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ToNumbers(lengthTracking.filter(CollectValuesAndResize)), []);
+ assert.compareArray(values, [
+ 0,
+ 2,
+ 4,
+ undefined
+ ]);
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ values = [];
+ resizeAfter = 1;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ToNumbers(lengthTrackingWithOffset.filter(CollectValuesAndResize)), []);
+ assert.compareArray(values, [
+ 4,
+ undefined
+ ]);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/filter-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/filter-strict.js
new file mode 100644
index 0000000000..c2b5f183dc
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/filter-strict.js
@@ -0,0 +1,209 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from Filter test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+const TypedArrayFilterHelper = (ta, ...rest) => {
+ return ta.filter(...rest);
+};
+
+const ArrayFilterHelper = (ta, ...rest) => {
+ return Array.prototype.filter.call(ta, ...rest);
+};
+
+function Filter(filterHelper, oobThrows) {
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ const lengthTracking = new ctor(rab, 0);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, i);
+ }
+
+ // Orig. array: [0, 1, 2, 3]
+ // [0, 1, 2, 3] << fixedLength
+ // [2, 3] << fixedLengthWithOffset
+ // [0, 1, 2, 3, ...] << lengthTracking
+ // [2, 3, ...] << lengthTrackingWithOffset
+
+ function isEven(n) {
+ return n != undefined && Number(n) % 2 == 0;
+ }
+ assert.compareArray(ToNumbers(filterHelper(fixedLength, isEven)), [
+ 0,
+ 2
+ ]);
+ assert.compareArray(ToNumbers(filterHelper(fixedLengthWithOffset, isEven)), [2]);
+ assert.compareArray(ToNumbers(filterHelper(lengthTracking, isEven)), [
+ 0,
+ 2
+ ]);
+ assert.compareArray(ToNumbers(filterHelper(lengthTrackingWithOffset, isEven)), [2]);
+
+ // Shrink so that fixed length TAs go out of bounds.
+ rab.resize(3 * ctor.BYTES_PER_ELEMENT);
+
+ // Orig. array: [0, 1, 2]
+ // [0, 1, 2, ...] << lengthTracking
+ // [2, ...] << lengthTrackingWithOffset
+
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ filterHelper(fixedLength, isEven);
+ });
+ assert.throws(TypeError, () => {
+ filterHelper(fixedLengthWithOffset, isEven);
+ });
+ } else {
+ assert.compareArray(filterHelper(fixedLength, isEven), []);
+ assert.compareArray(filterHelper(fixedLengthWithOffset, isEven), []);
+ }
+ assert.compareArray(ToNumbers(filterHelper(lengthTracking, isEven)), [
+ 0,
+ 2
+ ]);
+ assert.compareArray(ToNumbers(filterHelper(lengthTrackingWithOffset, isEven)), [2]);
+
+ // Shrink so that the TAs with offset go out of bounds.
+ rab.resize(1 * ctor.BYTES_PER_ELEMENT);
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ filterHelper(fixedLength, isEven);
+ });
+ assert.throws(TypeError, () => {
+ filterHelper(fixedLengthWithOffset, isEven);
+ });
+ assert.throws(TypeError, () => {
+ filterHelper(lengthTrackingWithOffset, isEven);
+ });
+ } else {
+ assert.compareArray(filterHelper(fixedLength, isEven), []);
+ assert.compareArray(filterHelper(fixedLengthWithOffset, isEven), []);
+ assert.compareArray(filterHelper(lengthTrackingWithOffset, isEven), []);
+ }
+ assert.compareArray(ToNumbers(filterHelper(lengthTracking, isEven)), [0]);
+
+ // Shrink to zero.
+ rab.resize(0);
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ filterHelper(fixedLength, isEven);
+ });
+ assert.throws(TypeError, () => {
+ filterHelper(fixedLengthWithOffset, isEven);
+ });
+ assert.throws(TypeError, () => {
+ filterHelper(lengthTrackingWithOffset, isEven);
+ });
+ } else {
+ assert.compareArray(filterHelper(fixedLength, isEven), []);
+ assert.compareArray(filterHelper(fixedLengthWithOffset, isEven), []);
+ assert.compareArray(filterHelper(lengthTrackingWithOffset, isEven), []);
+ }
+ assert.compareArray(ToNumbers(filterHelper(lengthTracking, isEven)), []);
+
+ // Grow so that all TAs are back in-bounds.
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ for (let i = 0; i < 6; ++i) {
+ WriteToTypedArray(taWrite, i, i);
+ }
+
+ // Orig. array: [0, 1, 2, 3, 4, 5]
+ // [0, 1, 2, 3] << fixedLength
+ // [2, 3] << fixedLengthWithOffset
+ // [0, 1, 2, 3, 4, 5, ...] << lengthTracking
+ // [2, 3, 4, 5, ...] << lengthTrackingWithOffset
+
+ assert.compareArray(ToNumbers(filterHelper(fixedLength, isEven)), [
+ 0,
+ 2
+ ]);
+ assert.compareArray(ToNumbers(filterHelper(fixedLengthWithOffset, isEven)), [2]);
+ assert.compareArray(ToNumbers(filterHelper(lengthTracking, isEven)), [
+ 0,
+ 2,
+ 4
+ ]);
+ assert.compareArray(ToNumbers(filterHelper(lengthTrackingWithOffset, isEven)), [
+ 2,
+ 4
+ ]);
+ }
+}
+
+Filter(TypedArrayFilterHelper, true);
+Filter(ArrayFilterHelper, false);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/find-find-index-find-last-find-last-index-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-find-index-find-last-find-last-index-strict.js
new file mode 100644
index 0000000000..6d66d11e29
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-find-index-find-last-find-last-index-strict.js
@@ -0,0 +1,332 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from FindFindIndexFindLastFindLastIndex test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function TypedArrayFindHelper(ta, p) {
+ return ta.find(p);
+}
+
+function ArrayFindHelper(ta, p) {
+ return Array.prototype.find.call(ta, p);
+}
+
+function TypedArrayFindIndexHelper(ta, p) {
+ return ta.findIndex(p);
+}
+
+function ArrayFindIndexHelper(ta, p) {
+ return Array.prototype.findIndex.call(ta, p);
+}
+
+function TypedArrayFindLastHelper(ta, p) {
+ return ta.findLast(p);
+}
+
+function ArrayFindLastHelper(ta, p) {
+ return Array.prototype.findLast.call(ta, p);
+}
+
+function TypedArrayFindLastIndexHelper(ta, p) {
+ return ta.findLastIndex(p);
+}
+
+function ArrayFindLastIndexHelper(ta, p) {
+ return Array.prototype.findLastIndex.call(ta, p);
+}
+
+function FindFindIndexFindLastFindLastIndex(findHelper, findIndexHelper, findLastHelper, findLastIndexHelper, oobThrows) {
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ const lengthTracking = new ctor(rab, 0);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+
+ // Orig. array: [0, 2, 4, 6]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, ...] << lengthTracking
+ // [4, 6, ...] << lengthTrackingWithOffset
+
+ function isTwoOrFour(n) {
+ return n == 2 || n == 4;
+ }
+ assert.sameValue(Number(findHelper(fixedLength, isTwoOrFour)), 2);
+ assert.sameValue(Number(findHelper(fixedLengthWithOffset, isTwoOrFour)), 4);
+ assert.sameValue(Number(findHelper(lengthTracking, isTwoOrFour)), 2);
+ assert.sameValue(Number(findHelper(lengthTrackingWithOffset, isTwoOrFour)), 4);
+ assert.sameValue(findIndexHelper(fixedLength, isTwoOrFour), 1);
+ assert.sameValue(findIndexHelper(fixedLengthWithOffset, isTwoOrFour), 0);
+ assert.sameValue(findIndexHelper(lengthTracking, isTwoOrFour), 1);
+ assert.sameValue(findIndexHelper(lengthTrackingWithOffset, isTwoOrFour), 0);
+ assert.sameValue(Number(findLastHelper(fixedLength, isTwoOrFour)), 4);
+ assert.sameValue(Number(findLastHelper(fixedLengthWithOffset, isTwoOrFour)), 4);
+ assert.sameValue(Number(findLastHelper(lengthTracking, isTwoOrFour)), 4);
+ assert.sameValue(Number(findLastHelper(lengthTrackingWithOffset, isTwoOrFour)), 4);
+ assert.sameValue(findLastIndexHelper(fixedLength, isTwoOrFour), 2);
+ assert.sameValue(findLastIndexHelper(fixedLengthWithOffset, isTwoOrFour), 0);
+ assert.sameValue(findLastIndexHelper(lengthTracking, isTwoOrFour), 2);
+ assert.sameValue(findLastIndexHelper(lengthTrackingWithOffset, isTwoOrFour), 0);
+
+ // Shrink so that fixed length TAs go out of bounds.
+ rab.resize(3 * ctor.BYTES_PER_ELEMENT);
+
+ // Orig. array: [0, 2, 4]
+ // [0, 2, 4, ...] << lengthTracking
+ // [4, ...] << lengthTrackingWithOffset
+
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ findHelper(fixedLength, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findIndexHelper(fixedLength, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findLastHelper(fixedLength, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findLastIndexHelper(fixedLength, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findHelper(fixedLengthWithOffset, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findIndexHelper(fixedLengthWithOffset, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findLastHelper(fixedLengthWithOffset, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findLastIndexHelper(fixedLengthWithOffset, isTwoOrFour);
+ });
+ } else {
+ assert.sameValue(findHelper(fixedLength, isTwoOrFour), undefined);
+ assert.sameValue(findIndexHelper(fixedLength, isTwoOrFour), -1);
+ assert.sameValue(findLastHelper(fixedLength, isTwoOrFour), undefined);
+ assert.sameValue(findLastIndexHelper(fixedLength, isTwoOrFour), -1);
+ assert.sameValue(findHelper(fixedLengthWithOffset, isTwoOrFour), undefined);
+ assert.sameValue(findIndexHelper(fixedLengthWithOffset, isTwoOrFour), -1);
+ assert.sameValue(findLastHelper(fixedLengthWithOffset, isTwoOrFour), undefined);
+ assert.sameValue(findLastIndexHelper(fixedLengthWithOffset, isTwoOrFour), -1);
+ }
+ assert.sameValue(Number(findHelper(lengthTracking, isTwoOrFour)), 2);
+ assert.sameValue(Number(findHelper(lengthTrackingWithOffset, isTwoOrFour)), 4);
+ assert.sameValue(findIndexHelper(lengthTracking, isTwoOrFour), 1);
+ assert.sameValue(findIndexHelper(lengthTrackingWithOffset, isTwoOrFour), 0);
+ assert.sameValue(Number(findLastHelper(lengthTracking, isTwoOrFour)), 4);
+ assert.sameValue(Number(findLastHelper(lengthTrackingWithOffset, isTwoOrFour)), 4);
+ assert.sameValue(findLastIndexHelper(lengthTracking, isTwoOrFour), 2);
+ assert.sameValue(findLastIndexHelper(lengthTrackingWithOffset, isTwoOrFour), 0);
+
+ // Shrink so that the TAs with offset go out of bounds.
+ rab.resize(1 * ctor.BYTES_PER_ELEMENT);
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ findHelper(fixedLength, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findIndexHelper(fixedLength, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findLastHelper(fixedLength, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findLastIndexHelper(fixedLength, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findHelper(fixedLengthWithOffset, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findIndexHelper(fixedLengthWithOffset, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findLastHelper(fixedLengthWithOffset, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findLastIndexHelper(fixedLengthWithOffset, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findHelper(lengthTrackingWithOffset, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findIndexHelper(lengthTrackingWithOffset, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findLastHelper(lengthTrackingWithOffset, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findLastIndexHelper(lengthTrackingWithOffset, isTwoOrFour);
+ });
+ } else {
+ assert.sameValue(findHelper(fixedLength, isTwoOrFour), undefined);
+ assert.sameValue(findIndexHelper(fixedLength, isTwoOrFour), -1);
+ assert.sameValue(findLastHelper(fixedLength, isTwoOrFour), undefined);
+ assert.sameValue(findLastIndexHelper(fixedLength, isTwoOrFour), -1);
+ assert.sameValue(findHelper(fixedLengthWithOffset, isTwoOrFour), undefined);
+ assert.sameValue(findIndexHelper(fixedLengthWithOffset, isTwoOrFour), -1);
+ assert.sameValue(findLastHelper(fixedLengthWithOffset, isTwoOrFour), undefined);
+ assert.sameValue(findLastIndexHelper(fixedLengthWithOffset, isTwoOrFour), -1);
+ assert.sameValue(findHelper(lengthTrackingWithOffset, isTwoOrFour), undefined);
+ assert.sameValue(findIndexHelper(lengthTrackingWithOffset, isTwoOrFour), -1);
+ assert.sameValue(findLastHelper(lengthTrackingWithOffset, isTwoOrFour), undefined);
+ assert.sameValue(findLastIndexHelper(lengthTrackingWithOffset, isTwoOrFour), -1);
+ }
+ assert.sameValue(findHelper(lengthTracking, isTwoOrFour), undefined);
+ assert.sameValue(findIndexHelper(lengthTracking, isTwoOrFour), -1);
+ assert.sameValue(findLastHelper(lengthTracking, isTwoOrFour), undefined);
+ assert.sameValue(findLastIndexHelper(lengthTracking, isTwoOrFour), -1);
+
+ // Shrink to zero.
+ rab.resize(0);
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ findHelper(fixedLength, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findIndexHelper(fixedLength, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findLastHelper(fixedLength, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findLastIndexHelper(fixedLength, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findHelper(fixedLengthWithOffset, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findIndexHelper(fixedLengthWithOffset, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findLastHelper(fixedLengthWithOffset, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findLastIndexHelper(fixedLengthWithOffset, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findHelper(lengthTrackingWithOffset, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findIndexHelper(lengthTrackingWithOffset, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findLastHelper(lengthTrackingWithOffset, isTwoOrFour);
+ });
+ assert.throws(TypeError, () => {
+ findLastIndexHelper(lengthTrackingWithOffset, isTwoOrFour);
+ });
+ } else {
+ assert.sameValue(findHelper(fixedLength, isTwoOrFour), undefined);
+ assert.sameValue(findIndexHelper(fixedLength, isTwoOrFour), -1);
+ assert.sameValue(findLastHelper(fixedLength, isTwoOrFour), undefined);
+ assert.sameValue(findLastIndexHelper(fixedLength, isTwoOrFour), -1);
+ assert.sameValue(findHelper(fixedLengthWithOffset, isTwoOrFour), undefined);
+ assert.sameValue(findIndexHelper(fixedLengthWithOffset, isTwoOrFour), -1);
+ assert.sameValue(findLastHelper(fixedLengthWithOffset, isTwoOrFour), undefined);
+ assert.sameValue(findLastIndexHelper(fixedLengthWithOffset, isTwoOrFour), -1);
+ assert.sameValue(findHelper(lengthTrackingWithOffset, isTwoOrFour), undefined);
+ assert.sameValue(findIndexHelper(lengthTrackingWithOffset, isTwoOrFour), -1);
+ assert.sameValue(findLastHelper(lengthTrackingWithOffset, isTwoOrFour), undefined);
+ assert.sameValue(findLastIndexHelper(lengthTrackingWithOffset, isTwoOrFour), -1);
+ }
+ assert.sameValue(findHelper(lengthTracking, isTwoOrFour), undefined);
+ assert.sameValue(findIndexHelper(lengthTracking, isTwoOrFour), -1);
+ assert.sameValue(findLastHelper(lengthTracking, isTwoOrFour), undefined);
+ assert.sameValue(findLastIndexHelper(lengthTracking, isTwoOrFour), -1);
+
+ // Grow so that all TAs are back in-bounds.
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 0);
+ }
+ WriteToTypedArray(taWrite, 4, 2);
+ WriteToTypedArray(taWrite, 5, 4);
+
+ // Orig. array: [0, 0, 0, 0, 2, 4]
+ // [0, 0, 0, 0] << fixedLength
+ // [0, 0] << fixedLengthWithOffset
+ // [0, 0, 0, 0, 2, 4, ...] << lengthTracking
+ // [0, 0, 2, 4, ...] << lengthTrackingWithOffset
+
+ assert.sameValue(findHelper(fixedLength, isTwoOrFour), undefined);
+ assert.sameValue(findHelper(fixedLengthWithOffset, isTwoOrFour), undefined);
+ assert.sameValue(Number(findHelper(lengthTracking, isTwoOrFour)), 2);
+ assert.sameValue(Number(findHelper(lengthTrackingWithOffset, isTwoOrFour)), 2);
+ assert.sameValue(findIndexHelper(fixedLength, isTwoOrFour), -1);
+ assert.sameValue(findIndexHelper(fixedLengthWithOffset, isTwoOrFour), -1);
+ assert.sameValue(findIndexHelper(lengthTracking, isTwoOrFour), 4);
+ assert.sameValue(findIndexHelper(lengthTrackingWithOffset, isTwoOrFour), 2);
+ assert.sameValue(findLastHelper(fixedLength, isTwoOrFour), undefined);
+ assert.sameValue(findLastHelper(fixedLengthWithOffset, isTwoOrFour), undefined);
+ assert.sameValue(Number(findLastHelper(lengthTracking, isTwoOrFour)), 4);
+ assert.sameValue(Number(findLastHelper(lengthTrackingWithOffset, isTwoOrFour)), 4);
+ assert.sameValue(findLastIndexHelper(fixedLength, isTwoOrFour), -1);
+ assert.sameValue(findLastIndexHelper(fixedLengthWithOffset, isTwoOrFour), -1);
+ assert.sameValue(findLastIndexHelper(lengthTracking, isTwoOrFour), 5);
+ assert.sameValue(findLastIndexHelper(lengthTrackingWithOffset, isTwoOrFour), 3);
+ }
+}
+
+FindFindIndexFindLastFindLastIndex(TypedArrayFindHelper, TypedArrayFindIndexHelper, TypedArrayFindLastHelper, TypedArrayFindLastIndexHelper, true);
+FindFindIndexFindLastFindLastIndex(ArrayFindHelper, ArrayFindIndexHelper, ArrayFindLastHelper, ArrayFindLastIndexHelper, false);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/find-grow-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-grow-mid-iteration-strict.js
new file mode 100644
index 0000000000..ec36398067
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-grow-mid-iteration-strict.js
@@ -0,0 +1,153 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from FindGrowMidIteration test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function TypedArrayFindHelper(ta, p) {
+ return ta.find(p);
+}
+
+function ArrayFindHelper(ta, p) {
+ return Array.prototype.find.call(ta, p);
+}
+
+function FindGrowMidIteration(findHelper) {
+ // Orig. array: [0, 2, 4, 6]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, ...] << lengthTracking
+ // [4, 6, ...] << lengthTrackingWithOffset
+ function CreateRabForTest(ctor) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+ return rab;
+ }
+ let values;
+ let rab;
+ let resizeAfter;
+ let resizeTo;
+ function CollectValuesAndResize(n) {
+ if (typeof n == 'bigint') {
+ values.push(Number(n));
+ } else {
+ values.push(n);
+ }
+ if (values.length == resizeAfter) {
+ rab.resize(resizeTo);
+ }
+ return false;
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ values = [];
+ resizeAfter = 2;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findHelper(fixedLength, CollectValuesAndResize), undefined);
+ assert.compareArray(values, [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ values = [];
+ resizeAfter = 1;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findHelper(fixedLengthWithOffset, CollectValuesAndResize), undefined);
+ assert.compareArray(values, [
+ 4,
+ 6
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ values = [];
+ resizeAfter = 2;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findHelper(lengthTracking, CollectValuesAndResize), undefined);
+ assert.compareArray(values, [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ values = [];
+ resizeAfter = 1;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findHelper(lengthTrackingWithOffset, CollectValuesAndResize), undefined);
+ assert.compareArray(values, [
+ 4,
+ 6
+ ]);
+ }
+}
+
+FindGrowMidIteration(TypedArrayFindHelper);
+FindGrowMidIteration(ArrayFindHelper);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/find-index-grow-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-index-grow-mid-iteration-strict.js
new file mode 100644
index 0000000000..d11967b305
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-index-grow-mid-iteration-strict.js
@@ -0,0 +1,153 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from FindIndexGrowMidIteration test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function TypedArrayFindIndexHelper(ta, p) {
+ return ta.findIndex(p);
+}
+
+function ArrayFindIndexHelper(ta, p) {
+ return Array.prototype.findIndex.call(ta, p);
+}
+
+function FindIndexGrowMidIteration(findIndexHelper) {
+ // Orig. array: [0, 2, 4, 6]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, ...] << lengthTracking
+ // [4, 6, ...] << lengthTrackingWithOffset
+ function CreateRabForTest(ctor) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+ return rab;
+ }
+ let values;
+ let rab;
+ let resizeAfter;
+ let resizeTo;
+ function CollectValuesAndResize(n) {
+ if (typeof n == 'bigint') {
+ values.push(Number(n));
+ } else {
+ values.push(n);
+ }
+ if (values.length == resizeAfter) {
+ rab.resize(resizeTo);
+ }
+ return false;
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ values = [];
+ resizeAfter = 2;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findIndexHelper(fixedLength, CollectValuesAndResize), -1);
+ assert.compareArray(values, [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ values = [];
+ resizeAfter = 1;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findIndexHelper(fixedLengthWithOffset, CollectValuesAndResize), -1);
+ assert.compareArray(values, [
+ 4,
+ 6
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ values = [];
+ resizeAfter = 2;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findIndexHelper(lengthTracking, CollectValuesAndResize), -1);
+ assert.compareArray(values, [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ values = [];
+ resizeAfter = 1;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findIndexHelper(lengthTrackingWithOffset, CollectValuesAndResize), -1);
+ assert.compareArray(values, [
+ 4,
+ 6
+ ]);
+ }
+}
+
+FindIndexGrowMidIteration(TypedArrayFindIndexHelper);
+FindIndexGrowMidIteration(ArrayFindIndexHelper);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/find-index-shrink-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-index-shrink-mid-iteration-strict.js
new file mode 100644
index 0000000000..3331a9aef3
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-index-shrink-mid-iteration-strict.js
@@ -0,0 +1,153 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from FindIndexShrinkMidIteration test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function TypedArrayFindIndexHelper(ta, p) {
+ return ta.findIndex(p);
+}
+
+function ArrayFindIndexHelper(ta, p) {
+ return Array.prototype.findIndex.call(ta, p);
+}
+
+function FindIndexShrinkMidIteration(findIndexHelper) {
+ // Orig. array: [0, 2, 4, 6]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, ...] << lengthTracking
+ // [4, 6, ...] << lengthTrackingWithOffset
+ function CreateRabForTest(ctor) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+ return rab;
+ }
+ let values;
+ let rab;
+ let resizeAfter;
+ let resizeTo;
+ function CollectValuesAndResize(n) {
+ if (typeof n == 'bigint') {
+ values.push(Number(n));
+ } else {
+ values.push(n);
+ }
+ if (values.length == resizeAfter) {
+ rab.resize(resizeTo);
+ }
+ return false;
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ values = [];
+ resizeAfter = 2;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findIndexHelper(fixedLength, CollectValuesAndResize), -1);
+ assert.compareArray(values, [
+ 0,
+ 2,
+ undefined,
+ undefined
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ values = [];
+ resizeAfter = 1;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findIndexHelper(fixedLengthWithOffset, CollectValuesAndResize), -1);
+ assert.compareArray(values, [
+ 4,
+ undefined
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ values = [];
+ resizeAfter = 2;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findIndexHelper(lengthTracking, CollectValuesAndResize), -1);
+ assert.compareArray(values, [
+ 0,
+ 2,
+ 4,
+ undefined
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ values = [];
+ resizeAfter = 1;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findIndexHelper(lengthTrackingWithOffset, CollectValuesAndResize), -1);
+ assert.compareArray(values, [
+ 4,
+ undefined
+ ]);
+ }
+}
+
+FindIndexShrinkMidIteration(TypedArrayFindIndexHelper);
+FindIndexShrinkMidIteration(ArrayFindIndexHelper);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/find-last-grow-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-last-grow-mid-iteration-strict.js
new file mode 100644
index 0000000000..8e1ee4edc5
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-last-grow-mid-iteration-strict.js
@@ -0,0 +1,153 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from FindLastGrowMidIteration test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function TypedArrayFindLastHelper(ta, p) {
+ return ta.findLast(p);
+}
+
+function ArrayFindLastHelper(ta, p) {
+ return Array.prototype.findLast.call(ta, p);
+}
+
+function FindLastGrowMidIteration(findLastHelper) {
+ // Orig. array: [0, 2, 4, 6]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, ...] << lengthTracking
+ // [4, 6, ...] << lengthTrackingWithOffset
+ function CreateRabForTest(ctor) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+ return rab;
+ }
+ let values;
+ let rab;
+ let resizeAfter;
+ let resizeTo;
+ function CollectValuesAndResize(n) {
+ if (typeof n == 'bigint') {
+ values.push(Number(n));
+ } else {
+ values.push(n);
+ }
+ if (values.length == resizeAfter) {
+ rab.resize(resizeTo);
+ }
+ return false;
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ values = [];
+ resizeAfter = 2;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findLastHelper(fixedLength, CollectValuesAndResize), undefined);
+ assert.compareArray(values, [
+ 6,
+ 4,
+ 2,
+ 0
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ values = [];
+ resizeAfter = 1;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findLastHelper(fixedLengthWithOffset, CollectValuesAndResize), undefined);
+ assert.compareArray(values, [
+ 6,
+ 4
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ values = [];
+ resizeAfter = 2;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findLastHelper(lengthTracking, CollectValuesAndResize), undefined);
+ assert.compareArray(values, [
+ 6,
+ 4,
+ 2,
+ 0
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ values = [];
+ resizeAfter = 1;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findLastHelper(lengthTrackingWithOffset, CollectValuesAndResize), undefined);
+ assert.compareArray(values, [
+ 6,
+ 4
+ ]);
+ }
+}
+
+FindLastGrowMidIteration(TypedArrayFindLastHelper);
+FindLastGrowMidIteration(ArrayFindLastHelper);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/find-last-index-grow-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-last-index-grow-mid-iteration-strict.js
new file mode 100644
index 0000000000..639d3d5767
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-last-index-grow-mid-iteration-strict.js
@@ -0,0 +1,153 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from FindLastIndexGrowMidIteration test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function TypedArrayFindLastIndexHelper(ta, p) {
+ return ta.findLastIndex(p);
+}
+
+function ArrayFindLastIndexHelper(ta, p) {
+ return Array.prototype.findLastIndex.call(ta, p);
+}
+
+function FindLastIndexGrowMidIteration(findLastIndexHelper) {
+ // Orig. array: [0, 2, 4, 6]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, ...] << lengthTracking
+ // [4, 6, ...] << lengthTrackingWithOffset
+ function CreateRabForTest(ctor) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+ return rab;
+ }
+ let values;
+ let rab;
+ let resizeAfter;
+ let resizeTo;
+ function CollectValuesAndResize(n) {
+ if (typeof n == 'bigint') {
+ values.push(Number(n));
+ } else {
+ values.push(n);
+ }
+ if (values.length == resizeAfter) {
+ rab.resize(resizeTo);
+ }
+ return false;
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ values = [];
+ resizeAfter = 2;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findLastIndexHelper(fixedLength, CollectValuesAndResize), -1);
+ assert.compareArray(values, [
+ 6,
+ 4,
+ 2,
+ 0
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ values = [];
+ resizeAfter = 1;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findLastIndexHelper(fixedLengthWithOffset, CollectValuesAndResize), -1);
+ assert.compareArray(values, [
+ 6,
+ 4
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ values = [];
+ resizeAfter = 2;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findLastIndexHelper(lengthTracking, CollectValuesAndResize), -1);
+ assert.compareArray(values, [
+ 6,
+ 4,
+ 2,
+ 0
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ values = [];
+ resizeAfter = 1;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findLastIndexHelper(lengthTrackingWithOffset, CollectValuesAndResize), -1);
+ assert.compareArray(values, [
+ 6,
+ 4
+ ]);
+ }
+}
+
+FindLastIndexGrowMidIteration(TypedArrayFindLastIndexHelper);
+FindLastIndexGrowMidIteration(ArrayFindLastIndexHelper);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/find-last-index-shrink-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-last-index-shrink-mid-iteration-strict.js
new file mode 100644
index 0000000000..e489835d75
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-last-index-shrink-mid-iteration-strict.js
@@ -0,0 +1,167 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from FindLastIndexShrinkMidIteration test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function TypedArrayFindLastIndexHelper(ta, p) {
+ return ta.findLastIndex(p);
+}
+
+function ArrayFindLastIndexHelper(ta, p) {
+ return Array.prototype.findLastIndex.call(ta, p);
+}
+
+function FindLastIndexShrinkMidIteration(findLastIndexHelper) {
+ // Orig. array: [0, 2, 4, 6]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, ...] << lengthTracking
+ // [4, 6, ...] << lengthTrackingWithOffset
+ function CreateRabForTest(ctor) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+ return rab;
+ }
+ let values;
+ let rab;
+ let resizeAfter;
+ let resizeTo;
+ function CollectValuesAndResize(n) {
+ if (typeof n == 'bigint') {
+ values.push(Number(n));
+ } else {
+ values.push(n);
+ }
+ if (values.length == resizeAfter) {
+ rab.resize(resizeTo);
+ }
+ return false;
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ values = [];
+ resizeAfter = 2;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findLastIndexHelper(fixedLength, CollectValuesAndResize), -1);
+ assert.compareArray(values, [
+ 6,
+ 4,
+ undefined,
+ undefined
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ values = [];
+ resizeAfter = 1;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findLastIndexHelper(fixedLengthWithOffset, CollectValuesAndResize), -1);
+ assert.compareArray(values, [
+ 6,
+ undefined
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ values = [];
+ resizeAfter = 2;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findLastIndexHelper(lengthTracking, CollectValuesAndResize), -1);
+ assert.compareArray(values, [
+ 6,
+ 4,
+ 2,
+ 0
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ values = [];
+ resizeAfter = 1;
+ resizeTo = 2 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findLastIndexHelper(lengthTracking, CollectValuesAndResize), -1);
+ assert.compareArray(values, [
+ 6,
+ undefined,
+ 2,
+ 0
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ values = [];
+ resizeAfter = 1;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findLastIndexHelper(lengthTrackingWithOffset, CollectValuesAndResize), -1);
+ assert.compareArray(values, [
+ 6,
+ 4
+ ]);
+ }
+}
+
+FindLastIndexShrinkMidIteration(TypedArrayFindLastIndexHelper);
+FindLastIndexShrinkMidIteration(ArrayFindLastIndexHelper);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/find-last-shrink-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-last-shrink-mid-iteration-strict.js
new file mode 100644
index 0000000000..c4fd72479c
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-last-shrink-mid-iteration-strict.js
@@ -0,0 +1,153 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from FindLastShrinkMidIteration test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function TypedArrayFindLastHelper(ta, p) {
+ return ta.findLast(p);
+}
+
+function ArrayFindLastHelper(ta, p) {
+ return Array.prototype.findLast.call(ta, p);
+}
+
+function FindLastShrinkMidIteration(findLastHelper) {
+ // Orig. array: [0, 2, 4, 6]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, ...] << lengthTracking
+ // [4, 6, ...] << lengthTrackingWithOffset
+ function CreateRabForTest(ctor) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+ return rab;
+ }
+ let values;
+ let rab;
+ let resizeAfter;
+ let resizeTo;
+ function CollectValuesAndResize(n) {
+ if (typeof n == 'bigint') {
+ values.push(Number(n));
+ } else {
+ values.push(n);
+ }
+ if (values.length == resizeAfter) {
+ rab.resize(resizeTo);
+ }
+ return false;
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ values = [];
+ resizeAfter = 2;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findLastHelper(fixedLength, CollectValuesAndResize), undefined);
+ assert.compareArray(values, [
+ 6,
+ 4,
+ undefined,
+ undefined
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ values = [];
+ resizeAfter = 1;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findLastHelper(fixedLengthWithOffset, CollectValuesAndResize), undefined);
+ assert.compareArray(values, [
+ 6,
+ undefined
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ values = [];
+ resizeAfter = 2;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findLastHelper(lengthTracking, CollectValuesAndResize), undefined);
+ assert.compareArray(values, [
+ 6,
+ 4,
+ 2,
+ 0
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ values = [];
+ resizeAfter = 1;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findLastHelper(lengthTrackingWithOffset, CollectValuesAndResize), undefined);
+ assert.compareArray(values, [
+ 6,
+ 4
+ ]);
+ }
+}
+
+FindLastShrinkMidIteration(TypedArrayFindLastHelper);
+FindLastShrinkMidIteration(ArrayFindLastHelper);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/find-shrink-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-shrink-mid-iteration-strict.js
new file mode 100644
index 0000000000..de3096e7e5
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/find-shrink-mid-iteration-strict.js
@@ -0,0 +1,153 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from FindShrinkMidIteration test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function TypedArrayFindHelper(ta, p) {
+ return ta.find(p);
+}
+
+function ArrayFindHelper(ta, p) {
+ return Array.prototype.find.call(ta, p);
+}
+
+function FindShrinkMidIteration(findHelper) {
+ // Orig. array: [0, 2, 4, 6]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, ...] << lengthTracking
+ // [4, 6, ...] << lengthTrackingWithOffset
+ function CreateRabForTest(ctor) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+ return rab;
+ }
+ let values;
+ let rab;
+ let resizeAfter;
+ let resizeTo;
+ function CollectValuesAndResize(n) {
+ if (typeof n == 'bigint') {
+ values.push(Number(n));
+ } else {
+ values.push(n);
+ }
+ if (values.length == resizeAfter) {
+ rab.resize(resizeTo);
+ }
+ return false;
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ values = [];
+ resizeAfter = 2;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findHelper(fixedLength, CollectValuesAndResize), undefined);
+ assert.compareArray(values, [
+ 0,
+ 2,
+ undefined,
+ undefined
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ values = [];
+ resizeAfter = 1;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findHelper(fixedLengthWithOffset, CollectValuesAndResize), undefined);
+ assert.compareArray(values, [
+ 4,
+ undefined
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ values = [];
+ resizeAfter = 2;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findHelper(lengthTracking, CollectValuesAndResize), undefined);
+ assert.compareArray(values, [
+ 0,
+ 2,
+ 4,
+ undefined
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ values = [];
+ resizeAfter = 1;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.sameValue(findHelper(lengthTrackingWithOffset, CollectValuesAndResize), undefined);
+ assert.compareArray(values, [
+ 4,
+ undefined
+ ]);
+ }
+}
+
+FindShrinkMidIteration(TypedArrayFindHelper);
+FindShrinkMidIteration(ArrayFindHelper);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/for-each-reduce-reduce-right-grow-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/for-each-reduce-reduce-right-grow-mid-iteration-strict.js
new file mode 100644
index 0000000000..97e2409e3c
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/for-each-reduce-reduce-right-grow-mid-iteration-strict.js
@@ -0,0 +1,277 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from ForEachReduceReduceRightGrowMidIteration test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+const TypedArrayForEachHelper = (ta, ...rest) => {
+ return ta.forEach(...rest);
+};
+
+const ArrayForEachHelper = (ta, ...rest) => {
+ return Array.prototype.forEach.call(ta, ...rest);
+};
+
+const TypedArrayReduceHelper = (ta, ...rest) => {
+ return ta.reduce(...rest);
+};
+
+const ArrayReduceHelper = (ta, ...rest) => {
+ return Array.prototype.reduce.call(ta, ...rest);
+};
+
+const TypedArrayReduceRightHelper = (ta, ...rest) => {
+ return ta.reduceRight(...rest);
+};
+
+const ArrayReduceRightHelper = (ta, ...rest) => {
+ return Array.prototype.reduceRight.call(ta, ...rest);
+};
+
+function ForEachReduceReduceRightGrowMidIteration(forEachHelper, reduceHelper, reduceRightHelper) {
+ // Orig. array: [0, 2, 4, 6]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, ...] << lengthTracking
+ // [4, 6, ...] << lengthTrackingWithOffset
+ function CreateRabForTest(ctor) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+ return rab;
+ }
+ let values;
+ let rab;
+ let resizeAfter;
+ let resizeTo;
+ function CollectValuesAndResize(n) {
+ if (typeof n == 'bigint') {
+ values.push(Number(n));
+ } else {
+ values.push(n);
+ }
+ if (values.length == resizeAfter) {
+ rab.resize(resizeTo);
+ }
+ return true;
+ }
+ function ForEachHelper(array) {
+ values = [];
+ forEachHelper(array, CollectValuesAndResize);
+ return values;
+ }
+ function ReduceHelper(array) {
+ values = [];
+ reduceHelper(array, (acc, n) => {
+ CollectValuesAndResize(n);
+ }, 'initial value');
+ return values;
+ }
+ function ReduceRightHelper(array) {
+ values = [];
+ reduceRightHelper(array, (acc, n) => {
+ CollectValuesAndResize(n);
+ }, 'initial value');
+ return values;
+ }
+
+ // Test for forEach.
+
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ resizeAfter = 2;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ForEachHelper(fixedLength), [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ resizeAfter = 1;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ForEachHelper(fixedLengthWithOffset), [
+ 4,
+ 6
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ resizeAfter = 2;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ForEachHelper(lengthTracking), [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ resizeAfter = 1;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ForEachHelper(lengthTrackingWithOffset), [
+ 4,
+ 6
+ ]);
+ }
+
+ // Test for reduce.
+
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ resizeAfter = 2;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ReduceHelper(fixedLength), [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ resizeAfter = 1;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ReduceHelper(fixedLengthWithOffset), [
+ 4,
+ 6
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ resizeAfter = 2;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ReduceHelper(lengthTracking), [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ resizeAfter = 1;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ReduceHelper(lengthTrackingWithOffset), [
+ 4,
+ 6
+ ]);
+ }
+
+ // Test for reduceRight.
+
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ resizeAfter = 2;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ReduceRightHelper(fixedLength), [
+ 6,
+ 4,
+ 2,
+ 0
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ resizeAfter = 1;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ReduceRightHelper(fixedLengthWithOffset), [
+ 6,
+ 4
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ resizeAfter = 2;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ReduceRightHelper(lengthTracking), [
+ 6,
+ 4,
+ 2,
+ 0
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ resizeAfter = 1;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ReduceRightHelper(lengthTrackingWithOffset), [
+ 6,
+ 4
+ ]);
+ }
+}
+
+ForEachReduceReduceRightGrowMidIteration(TypedArrayForEachHelper, TypedArrayReduceHelper, TypedArrayReduceRightHelper);
+ForEachReduceReduceRightGrowMidIteration(ArrayForEachHelper, ArrayReduceHelper, ArrayReduceRightHelper);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/for-each-reduce-reduce-right-shrink-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/for-each-reduce-reduce-right-shrink-mid-iteration-strict.js
new file mode 100644
index 0000000000..bce655454e
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/for-each-reduce-reduce-right-shrink-mid-iteration-strict.js
@@ -0,0 +1,262 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from ForEachReduceReduceRightShrinkMidIteration test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+// Orig. array: [0, 2, 4, 6]
+// [0, 2, 4, 6] << fixedLength
+// [4, 6] << fixedLengthWithOffset
+// [0, 2, 4, 6, ...] << lengthTracking
+// [4, 6, ...] << lengthTrackingWithOffset
+function CreateRabForTest(ctor) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+ return rab;
+}
+let values;
+let rab;
+let resizeAfter;
+let resizeTo;
+function CollectValuesAndResize(n) {
+ if (typeof n == 'bigint') {
+ values.push(Number(n));
+ } else {
+ values.push(n);
+ }
+ if (values.length == resizeAfter) {
+ rab.resize(resizeTo);
+ }
+ return true;
+}
+function ForEachHelper(array) {
+ values = [];
+ array.forEach(CollectValuesAndResize);
+ return values;
+}
+function ReduceHelper(array) {
+ values = [];
+ array.reduce((acc, n) => {
+ CollectValuesAndResize(n);
+ }, 'initial value');
+ return values;
+}
+function ReduceRightHelper(array) {
+ values = [];
+ array.reduceRight((acc, n) => {
+ CollectValuesAndResize(n);
+ }, 'initial value');
+ return values;
+}
+
+// Test for forEach.
+
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ resizeAfter = 2;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ForEachHelper(fixedLength), [
+ 0,
+ 2,
+ undefined,
+ undefined
+ ]);
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ resizeAfter = 1;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ForEachHelper(fixedLengthWithOffset), [
+ 4,
+ undefined
+ ]);
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ resizeAfter = 2;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ForEachHelper(lengthTracking), [
+ 0,
+ 2,
+ 4,
+ undefined
+ ]);
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ resizeAfter = 1;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ForEachHelper(lengthTrackingWithOffset), [
+ 4,
+ undefined
+ ]);
+}
+
+// Tests for reduce.
+
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ resizeAfter = 2;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ReduceHelper(fixedLength), [
+ 0,
+ 2,
+ undefined,
+ undefined
+ ]);
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ resizeAfter = 1;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ReduceHelper(fixedLengthWithOffset), [
+ 4,
+ undefined
+ ]);
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ resizeAfter = 2;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ReduceHelper(lengthTracking), [
+ 0,
+ 2,
+ 4,
+ undefined
+ ]);
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ resizeAfter = 1;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ReduceHelper(lengthTrackingWithOffset), [
+ 4,
+ undefined
+ ]);
+}
+
+// Tests for reduceRight.
+
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ resizeAfter = 2;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ReduceRightHelper(fixedLength), [
+ 6,
+ 4,
+ undefined,
+ undefined
+ ]);
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ resizeAfter = 1;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ReduceRightHelper(fixedLengthWithOffset), [
+ 6,
+ undefined
+ ]);
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ resizeAfter = 2;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ // Unaffected by the shrinking, since we've already iterated past the point.
+ assert.compareArray(ReduceRightHelper(lengthTracking), [
+ 6,
+ 4,
+ 2,
+ 0
+ ]);
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ resizeAfter = 1;
+ resizeTo = 2 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(ReduceRightHelper(lengthTracking), [
+ 6,
+ undefined,
+ 2,
+ 0
+ ]);
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ resizeAfter = 1;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ // Unaffected by the shrinking, since we've already iterated past the point.
+ assert.compareArray(ReduceRightHelper(lengthTrackingWithOffset), [
+ 6,
+ 4
+ ]);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/for-each-reduce-reduce-right-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/for-each-reduce-reduce-right-strict.js
new file mode 100644
index 0000000000..39b5b6318d
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/for-each-reduce-reduce-right-strict.js
@@ -0,0 +1,260 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from ForEachReduceReduceRight test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+const TypedArrayForEachHelper = (ta, ...rest) => {
+ return ta.forEach(...rest);
+};
+
+const ArrayForEachHelper = (ta, ...rest) => {
+ return Array.prototype.forEach.call(ta, ...rest);
+};
+
+const TypedArrayReduceHelper = (ta, ...rest) => {
+ return ta.reduce(...rest);
+};
+
+const ArrayReduceHelper = (ta, ...rest) => {
+ return Array.prototype.reduce.call(ta, ...rest);
+};
+
+const TypedArrayReduceRightHelper = (ta, ...rest) => {
+ return ta.reduceRight(...rest);
+};
+
+const ArrayReduceRightHelper = (ta, ...rest) => {
+ return Array.prototype.reduceRight.call(ta, ...rest);
+};
+
+function ForEachReduceReduceRight(forEachHelper, reduceHelper, reduceRightHelper, oobThrows) {
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ const lengthTracking = new ctor(rab, 0);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+
+ // Orig. array: [0, 2, 4, 6]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, ...] << lengthTracking
+ // [4, 6, ...] << lengthTrackingWithOffset
+
+ function Helper(array) {
+ const forEachValues = [];
+ const reduceValues = [];
+ const reduceRightValues = [];
+ forEachHelper(array, n => {
+ forEachValues.push(n);
+ });
+ reduceHelper(array, (acc, n) => {
+ reduceValues.push(n);
+ }, 'initial value');
+ reduceRightHelper(array, (acc, n) => {
+ reduceRightValues.push(n);
+ }, 'initial value');
+ assert.compareArray(forEachValues, reduceValues);
+ reduceRightValues.reverse();
+ assert.compareArray(reduceRightValues, reduceValues);
+ return ToNumbers(forEachValues);
+ }
+ assert.compareArray(Helper(fixedLength), [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ assert.compareArray(Helper(fixedLengthWithOffset), [
+ 4,
+ 6
+ ]);
+ assert.compareArray(Helper(lengthTracking), [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ assert.compareArray(Helper(lengthTrackingWithOffset), [
+ 4,
+ 6
+ ]);
+
+ // Shrink so that fixed length TAs go out of bounds.
+ rab.resize(3 * ctor.BYTES_PER_ELEMENT);
+
+ // Orig. array: [0, 2, 4]
+ // [0, 2, 4, ...] << lengthTracking
+ // [4, ...] << lengthTrackingWithOffset
+
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ Helper(fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ Helper(fixedLengthWithOffset);
+ });
+ } else {
+ assert.compareArray(Helper(fixedLength), []);
+ assert.compareArray(Helper(fixedLengthWithOffset), []);
+ }
+ assert.compareArray(Helper(lengthTracking), [
+ 0,
+ 2,
+ 4
+ ]);
+ assert.compareArray(Helper(lengthTrackingWithOffset), [4]);
+
+ // Shrink so that the TAs with offset go out of bounds.
+ rab.resize(1 * ctor.BYTES_PER_ELEMENT);
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ Helper(fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ Helper(fixedLengthWithOffset);
+ });
+ assert.throws(TypeError, () => {
+ Helper(lengthTrackingWithOffset);
+ });
+ } else {
+ assert.compareArray(Helper(fixedLength), []);
+ assert.compareArray(Helper(fixedLengthWithOffset), []);
+ }
+ assert.compareArray(Helper(lengthTracking), [0]);
+
+ // Shrink to zero.
+ rab.resize(0);
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ Helper(fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ Helper(fixedLengthWithOffset);
+ });
+ assert.throws(TypeError, () => {
+ Helper(lengthTrackingWithOffset);
+ });
+ } else {
+ assert.compareArray(Helper(fixedLength), []);
+ assert.compareArray(Helper(fixedLengthWithOffset), []);
+ assert.compareArray(Helper(lengthTrackingWithOffset), []);
+ }
+ assert.compareArray(Helper(lengthTracking), []);
+
+ // Grow so that all TAs are back in-bounds.
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ for (let i = 0; i < 6; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+
+ // Orig. array: [0, 2, 4, 6, 8, 10]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, 8, 10, ...] << lengthTracking
+ // [4, 6, 8, 10, ...] << lengthTrackingWithOffset
+
+ assert.compareArray(Helper(fixedLength), [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ assert.compareArray(Helper(fixedLengthWithOffset), [
+ 4,
+ 6
+ ]);
+ assert.compareArray(Helper(lengthTracking), [
+ 0,
+ 2,
+ 4,
+ 6,
+ 8,
+ 10
+ ]);
+ assert.compareArray(Helper(lengthTrackingWithOffset), [
+ 4,
+ 6,
+ 8,
+ 10
+ ]);
+ }
+}
+
+ForEachReduceReduceRight(TypedArrayForEachHelper, TypedArrayReduceHelper, TypedArrayReduceRightHelper, true);
+ForEachReduceReduceRight(ArrayForEachHelper, ArrayReduceHelper, ArrayReduceRightHelper, false);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/function-apply-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/function-apply-strict.js
new file mode 100644
index 0000000000..1da01f4826
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/function-apply-strict.js
@@ -0,0 +1,160 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from FunctionApply test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ const lengthTracking = new ctor(rab, 0);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, i);
+ }
+ function func(...args) {
+ return [...args];
+ }
+ assert.compareArray(ToNumbers(func.apply(null, fixedLength)), [
+ 0,
+ 1,
+ 2,
+ 3
+ ]);
+ assert.compareArray(ToNumbers(func.apply(null, fixedLengthWithOffset)), [
+ 2,
+ 3
+ ]);
+ assert.compareArray(ToNumbers(func.apply(null, lengthTracking)), [
+ 0,
+ 1,
+ 2,
+ 3
+ ]);
+ assert.compareArray(ToNumbers(func.apply(null, lengthTrackingWithOffset)), [
+ 2,
+ 3
+ ]);
+
+ // Shrink so that fixed length TAs go out of bounds.
+ rab.resize(3 * ctor.BYTES_PER_ELEMENT);
+ assert.compareArray(ToNumbers(func.apply(null, fixedLength)), []);
+ assert.compareArray(ToNumbers(func.apply(null, fixedLengthWithOffset)), []);
+ assert.compareArray(ToNumbers(func.apply(null, lengthTracking)), [
+ 0,
+ 1,
+ 2
+ ]);
+ assert.compareArray(ToNumbers(func.apply(null, lengthTrackingWithOffset)), [2]);
+
+ // Shrink so that the TAs with offset go out of bounds.
+ rab.resize(1 * ctor.BYTES_PER_ELEMENT);
+ assert.compareArray(ToNumbers(func.apply(null, fixedLength)), []);
+ assert.compareArray(ToNumbers(func.apply(null, fixedLengthWithOffset)), []);
+ assert.compareArray(ToNumbers(func.apply(null, lengthTracking)), [0]);
+ assert.compareArray(ToNumbers(func.apply(null, lengthTrackingWithOffset)), []);
+
+ // Shrink to zero.
+ rab.resize(0);
+ assert.compareArray(ToNumbers(func.apply(null, fixedLength)), []);
+ assert.compareArray(ToNumbers(func.apply(null, fixedLengthWithOffset)), []);
+ assert.compareArray(ToNumbers(func.apply(null, lengthTracking)), []);
+ assert.compareArray(ToNumbers(func.apply(null, lengthTrackingWithOffset)), []);
+
+ // Grow so that all TAs are back in-bounds. New memory is zeroed.
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ assert.compareArray(ToNumbers(func.apply(null, fixedLength)), [
+ 0,
+ 0,
+ 0,
+ 0
+ ]);
+ assert.compareArray(ToNumbers(func.apply(null, fixedLengthWithOffset)), [
+ 0,
+ 0
+ ]);
+ assert.compareArray(ToNumbers(func.apply(null, lengthTracking)), [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]);
+ assert.compareArray(ToNumbers(func.apply(null, lengthTrackingWithOffset)), [
+ 0,
+ 0,
+ 0,
+ 0
+ ]);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/includes-parameter-conversion-resizes-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/includes-parameter-conversion-resizes-strict.js
new file mode 100644
index 0000000000..19fd438ed8
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/includes-parameter-conversion-resizes-strict.js
@@ -0,0 +1,147 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from IncludesParameterConversionResizes test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer, Array.prototype.includes]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function TypedArrayIncludesHelper(array, n, fromIndex) {
+ if (typeof n == 'number' && (array instanceof BigInt64Array || array instanceof BigUint64Array)) {
+ return array.includes(BigInt(n), fromIndex);
+ }
+ return array.includes(n, fromIndex);
+}
+
+function ArrayIncludesHelper(array, n, fromIndex) {
+ if (typeof n == 'number' && (array instanceof BigInt64Array || array instanceof BigUint64Array)) {
+ return Array.prototype.includes.call(array, BigInt(n), fromIndex);
+ }
+ return Array.prototype.includes.call(array, n, fromIndex);
+}
+
+function IncludesParameterConversionResizes(helper) {
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ let evil = {
+ valueOf: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 0;
+ }
+ };
+ assert(!helper(fixedLength, undefined));
+ // The TA is OOB so it includes only "undefined".
+ assert(helper(fixedLength, undefined, evil));
+ }
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ let evil = {
+ valueOf: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 0;
+ }
+ };
+ assert(helper(fixedLength, 0));
+ // The TA is OOB so it includes only "undefined".
+ assert(!helper(fixedLength, 0, evil));
+ }
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const lengthTracking = new ctor(rab);
+ let evil = {
+ valueOf: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 0;
+ }
+ };
+ assert(!helper(lengthTracking, undefined));
+ // "includes" iterates until the original length and sees "undefined"s.
+ assert(helper(lengthTracking, undefined, evil));
+ }
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const lengthTracking = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(lengthTracking, i, 1);
+ }
+ let evil = {
+ valueOf: () => {
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ return 0;
+ }
+ };
+ assert(!helper(lengthTracking, 0));
+ // The TA grew but we only look at the data until the original length.
+ assert(!helper(lengthTracking, 0, evil));
+ }
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const lengthTracking = new ctor(rab);
+ WriteToTypedArray(lengthTracking, 0, 1);
+ let evil = {
+ valueOf: () => {
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ return -4;
+ }
+ };
+ assert(helper(lengthTracking, 1, -4));
+ // The TA grew but the start index conversion is done based on the original
+ // length.
+ assert(helper(lengthTracking, 1, evil));
+ }
+}
+
+IncludesParameterConversionResizes(TypedArrayIncludesHelper);
+IncludesParameterConversionResizes(ArrayIncludesHelper);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/includes-special-values-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/includes-special-values-strict.js
new file mode 100644
index 0000000000..1581e2369e
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/includes-special-values-strict.js
@@ -0,0 +1,39 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from IncludesSpecialValues test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyFloat32Array extends Float32Array {
+}
+
+const floatCtors = [
+ Float32Array,
+ Float64Array,
+ MyFloat32Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+for (let ctor of floatCtors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const lengthTracking = new ctor(rab);
+ lengthTracking[0] = -Infinity;
+ lengthTracking[1] = Infinity;
+ lengthTracking[2] = NaN;
+ assert(lengthTracking.includes(-Infinity));
+ assert(lengthTracking.includes(Infinity));
+ assert(lengthTracking.includes(NaN));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/includes-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/includes-strict.js
new file mode 100644
index 0000000000..cc14e2d783
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/includes-strict.js
@@ -0,0 +1,212 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from Includes test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer, Array.prototype.includes]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function TypedArrayIncludesHelper(array, n, fromIndex) {
+ if (typeof n == 'number' && (array instanceof BigInt64Array || array instanceof BigUint64Array)) {
+ return array.includes(BigInt(n), fromIndex);
+ }
+ return array.includes(n, fromIndex);
+}
+
+function ArrayIncludesHelper(array, n, fromIndex) {
+ if (typeof n == 'number' && (array instanceof BigInt64Array || array instanceof BigUint64Array)) {
+ return Array.prototype.includes.call(array, BigInt(n), fromIndex);
+ }
+ return Array.prototype.includes.call(array, n, fromIndex);
+}
+
+function Includes(helper, oobThrows) {
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ const lengthTracking = new ctor(rab, 0);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+
+ // Orig. array: [0, 2, 4, 6]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, ...] << lengthTracking
+ // [4, 6, ...] << lengthTrackingWithOffset
+
+ assert(helper(fixedLength, 2));
+ assert(!helper(fixedLength, undefined));
+ assert(helper(fixedLength, 2, 1));
+ assert(!helper(fixedLength, 2, 2));
+ assert(helper(fixedLength, 2, -3));
+ assert(!helper(fixedLength, 2, -2));
+ assert(!helper(fixedLengthWithOffset, 2));
+ assert(helper(fixedLengthWithOffset, 4));
+ assert(!helper(fixedLengthWithOffset, undefined));
+ assert(helper(fixedLengthWithOffset, 4, 0));
+ assert(!helper(fixedLengthWithOffset, 4, 1));
+ assert(helper(fixedLengthWithOffset, 4, -2));
+ assert(!helper(fixedLengthWithOffset, 4, -1));
+ assert(helper(lengthTracking, 2));
+ assert(!helper(lengthTracking, undefined));
+ assert(helper(lengthTracking, 2, 1));
+ assert(!helper(lengthTracking, 2, 2));
+ assert(helper(lengthTracking, 2, -3));
+ assert(!helper(lengthTracking, 2, -2));
+ assert(!helper(lengthTrackingWithOffset, 2));
+ assert(helper(lengthTrackingWithOffset, 4));
+ assert(!helper(lengthTrackingWithOffset, undefined));
+ assert(helper(lengthTrackingWithOffset, 4, 0));
+ assert(!helper(lengthTrackingWithOffset, 4, 1));
+ assert(helper(lengthTrackingWithOffset, 4, -2));
+ assert(!helper(lengthTrackingWithOffset, 4, -1));
+
+ // Shrink so that fixed length TAs go out of bounds.
+ rab.resize(3 * ctor.BYTES_PER_ELEMENT);
+
+ // Orig. array: [0, 2, 4]
+ // [0, 2, 4, ...] << lengthTracking
+ // [4, ...] << lengthTrackingWithOffset
+
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ helper(fixedLength, 2);
+ });
+ assert.throws(TypeError, () => {
+ helper(fixedLengthWithOffset, 2);
+ });
+ } else {
+ assert(!helper(fixedLength, 2));
+ assert(!helper(fixedLengthWithOffset, 2));
+ }
+ assert(helper(lengthTracking, 2));
+ assert(!helper(lengthTracking, undefined));
+ assert(!helper(lengthTrackingWithOffset, 2));
+ assert(helper(lengthTrackingWithOffset, 4));
+ assert(!helper(lengthTrackingWithOffset, undefined));
+
+ // Shrink so that the TAs with offset go out of bounds.
+ rab.resize(1 * ctor.BYTES_PER_ELEMENT);
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ helper(fixedLength, 2);
+ });
+ assert.throws(TypeError, () => {
+ helper(fixedLengthWithOffset, 2);
+ });
+ assert.throws(TypeError, () => {
+ helper(lengthTrackingWithOffset, 2);
+ });
+ } else {
+ assert(!helper(fixedLength, 2));
+ assert(!helper(fixedLengthWithOffset, 2));
+ assert(!helper(lengthTrackingWithOffset, 2));
+ }
+
+ // Shrink to zero.
+ rab.resize(0);
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ helper(fixedLength, 2);
+ });
+ assert.throws(TypeError, () => {
+ helper(fixedLengthWithOffset, 2);
+ });
+ assert.throws(TypeError, () => {
+ helper(lengthTrackingWithOffset, 2);
+ });
+ } else {
+ assert(!helper(fixedLength, 2));
+ assert(!helper(fixedLengthWithOffset, 2));
+ assert(!helper(lengthTrackingWithOffset, 2));
+ }
+ assert(!helper(lengthTracking, 2));
+ assert(!helper(lengthTracking, undefined));
+
+ // Grow so that all TAs are back in-bounds.
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ for (let i = 0; i < 6; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+
+ // Orig. array: [0, 2, 4, 6, 8, 10]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, 8, 10, ...] << lengthTracking
+ // [4, 6, 8, 10, ...] << lengthTrackingWithOffset
+
+ assert(helper(fixedLength, 2));
+ assert(!helper(fixedLength, undefined));
+ assert(!helper(fixedLength, 8));
+ assert(!helper(fixedLengthWithOffset, 2));
+ assert(helper(fixedLengthWithOffset, 4));
+ assert(!helper(fixedLengthWithOffset, undefined));
+ assert(!helper(fixedLengthWithOffset, 8));
+ assert(helper(lengthTracking, 2));
+ assert(!helper(lengthTracking, undefined));
+ assert(helper(lengthTracking, 8));
+ assert(!helper(lengthTrackingWithOffset, 2));
+ assert(helper(lengthTrackingWithOffset, 4));
+ assert(!helper(lengthTrackingWithOffset, undefined));
+ assert(helper(lengthTrackingWithOffset, 8));
+ }
+}
+
+Includes(TypedArrayIncludesHelper, true);
+Includes(ArrayIncludesHelper, false);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/index-of-last-index-of-special-values-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/index-of-last-index-of-special-values-strict.js
new file mode 100644
index 0000000000..35b408cced
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/index-of-last-index-of-special-values-strict.js
@@ -0,0 +1,46 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from IndexOfLastIndexOfSpecialValues test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyFloat32Array extends Float32Array {
+}
+
+const floatCtors = [
+ Float32Array,
+ Float64Array,
+ MyFloat32Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+for (let ctor of floatCtors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const lengthTracking = new ctor(rab);
+ lengthTracking[0] = -Infinity;
+ lengthTracking[1] = -Infinity;
+ lengthTracking[2] = Infinity;
+ lengthTracking[3] = Infinity;
+ lengthTracking[4] = NaN;
+ lengthTracking[5] = NaN;
+ assert.sameValue(lengthTracking.indexOf(-Infinity), 0);
+ assert.sameValue(lengthTracking.lastIndexOf(-Infinity), 1);
+ assert.sameValue(lengthTracking.indexOf(Infinity), 2);
+ assert.sameValue(lengthTracking.lastIndexOf(Infinity), 3);
+ // NaN is never found.
+ assert.sameValue(lengthTracking.indexOf(NaN), -1);
+ assert.sameValue(lengthTracking.lastIndexOf(NaN), -1);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/index-of-last-index-of-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/index-of-last-index-of-strict.js
new file mode 100644
index 0000000000..6269766782
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/index-of-last-index-of-strict.js
@@ -0,0 +1,329 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from IndexOfLastIndexOf test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function TypedArrayIndexOfHelper(ta, n, fromIndex) {
+ if (typeof n == 'number' && (ta instanceof BigInt64Array || ta instanceof BigUint64Array)) {
+ if (fromIndex == undefined) {
+ return ta.indexOf(BigInt(n));
+ }
+ return ta.indexOf(BigInt(n), fromIndex);
+ }
+ if (fromIndex == undefined) {
+ return ta.indexOf(n);
+ }
+ return ta.indexOf(n, fromIndex);
+}
+
+function ArrayIndexOfHelper(ta, n, fromIndex) {
+ if (typeof n == 'number' && (ta instanceof BigInt64Array || ta instanceof BigUint64Array)) {
+ if (fromIndex == undefined) {
+ return Array.prototype.indexOf.call(ta, BigInt(n));
+ }
+ return Array.prototype.indexOf.call(ta, BigInt(n), fromIndex);
+ }
+ if (fromIndex == undefined) {
+ return Array.prototype.indexOf.call(ta, n);
+ }
+ return Array.prototype.indexOf.call(ta, n, fromIndex);
+}
+
+function TypedArrayLastIndexOfHelper(ta, n, fromIndex) {
+ if (typeof n == 'number' && (ta instanceof BigInt64Array || ta instanceof BigUint64Array)) {
+ if (fromIndex == undefined) {
+ return ta.lastIndexOf(BigInt(n));
+ }
+ return ta.lastIndexOf(BigInt(n), fromIndex);
+ }
+ if (fromIndex == undefined) {
+ return ta.lastIndexOf(n);
+ }
+ return ta.lastIndexOf(n, fromIndex);
+}
+
+function ArrayLastIndexOfHelper(ta, n, fromIndex) {
+ if (typeof n == 'number' && (ta instanceof BigInt64Array || ta instanceof BigUint64Array)) {
+ if (fromIndex == undefined) {
+ return Array.prototype.lastIndexOf.call(ta, BigInt(n));
+ }
+ return Array.prototype.lastIndexOf.call(ta, BigInt(n), fromIndex);
+ }
+ if (fromIndex == undefined) {
+ return Array.prototype.lastIndexOf.call(ta, n);
+ }
+ return Array.prototype.lastIndexOf.call(ta, n, fromIndex);
+}
+
+function IndexOfLastIndexOf(indexOfHelper, lastIndexOfHelper, oobThrows) {
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ const lengthTracking = new ctor(rab, 0);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, Math.floor(i / 2));
+ }
+
+ // Orig. array: [0, 0, 1, 1]
+ // [0, 0, 1, 1] << fixedLength
+ // [1, 1] << fixedLengthWithOffset
+ // [0, 0, 1, 1, ...] << lengthTracking
+ // [1, 1, ...] << lengthTrackingWithOffset
+
+ assert.sameValue(indexOfHelper(fixedLength, 0), 0);
+ assert.sameValue(indexOfHelper(fixedLength, 0, 1), 1);
+ assert.sameValue(indexOfHelper(fixedLength, 0, 2), -1);
+ assert.sameValue(indexOfHelper(fixedLength, 0, -2), -1);
+ assert.sameValue(indexOfHelper(fixedLength, 0, -3), 1);
+ assert.sameValue(indexOfHelper(fixedLength, 1, 1), 2);
+ assert.sameValue(indexOfHelper(fixedLength, 1, -3), 2);
+ assert.sameValue(indexOfHelper(fixedLength, 1, -2), 2);
+ assert.sameValue(indexOfHelper(fixedLength, undefined), -1);
+ assert.sameValue(lastIndexOfHelper(fixedLength, 0), 1);
+ assert.sameValue(lastIndexOfHelper(fixedLength, 0, 1), 1);
+ assert.sameValue(lastIndexOfHelper(fixedLength, 0, 2), 1);
+ assert.sameValue(lastIndexOfHelper(fixedLength, 0, -2), 1);
+ assert.sameValue(lastIndexOfHelper(fixedLength, 0, -3), 1);
+ assert.sameValue(lastIndexOfHelper(fixedLength, 1, 1), -1);
+ assert.sameValue(lastIndexOfHelper(fixedLength, 1, -2), 2);
+ assert.sameValue(lastIndexOfHelper(fixedLength, 1, -3), -1);
+ assert.sameValue(lastIndexOfHelper(fixedLength, undefined), -1);
+ assert.sameValue(indexOfHelper(fixedLengthWithOffset, 0), -1);
+ assert.sameValue(indexOfHelper(fixedLengthWithOffset, 1), 0);
+ assert.sameValue(indexOfHelper(fixedLengthWithOffset, 1, -2), 0);
+ assert.sameValue(indexOfHelper(fixedLengthWithOffset, 1, -1), 1);
+ assert.sameValue(indexOfHelper(fixedLengthWithOffset, undefined), -1);
+ assert.sameValue(lastIndexOfHelper(fixedLengthWithOffset, 0), -1);
+ assert.sameValue(lastIndexOfHelper(fixedLengthWithOffset, 1), 1);
+ assert.sameValue(lastIndexOfHelper(fixedLengthWithOffset, 1, -2), 0);
+ assert.sameValue(lastIndexOfHelper(fixedLengthWithOffset, 1, -1), 1);
+ assert.sameValue(lastIndexOfHelper(fixedLengthWithOffset, undefined), -1);
+ assert.sameValue(indexOfHelper(lengthTracking, 0), 0);
+ assert.sameValue(indexOfHelper(lengthTracking, 0, 2), -1);
+ assert.sameValue(indexOfHelper(lengthTracking, 1, -3), 2);
+ assert.sameValue(indexOfHelper(lengthTracking, undefined), -1);
+ assert.sameValue(lastIndexOfHelper(lengthTracking, 0), 1);
+ assert.sameValue(lastIndexOfHelper(lengthTracking, 0, 2), 1);
+ assert.sameValue(lastIndexOfHelper(lengthTracking, 0, -3), 1);
+ assert.sameValue(lastIndexOfHelper(lengthTracking, 1, 1), -1);
+ assert.sameValue(lastIndexOfHelper(lengthTracking, 1, 2), 2);
+ assert.sameValue(lastIndexOfHelper(lengthTracking, 1, -3), -1);
+ assert.sameValue(lastIndexOfHelper(lengthTracking, undefined), -1);
+ assert.sameValue(indexOfHelper(lengthTrackingWithOffset, 0), -1);
+ assert.sameValue(indexOfHelper(lengthTrackingWithOffset, 1), 0);
+ assert.sameValue(indexOfHelper(lengthTrackingWithOffset, 1, 1), 1);
+ assert.sameValue(indexOfHelper(lengthTrackingWithOffset, 1, -2), 0);
+ assert.sameValue(indexOfHelper(lengthTrackingWithOffset, undefined), -1);
+ assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, 0), -1);
+ assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, 1), 1);
+ assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, 1, 1), 1);
+ assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, 1, -2), 0);
+ assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, 1, -1), 1);
+ assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, undefined), -1);
+
+ // Shrink so that fixed length TAs go out of bounds.
+ rab.resize(3 * ctor.BYTES_PER_ELEMENT);
+
+ // Orig. array: [0, 0, 1]
+ // [0, 0, 1, ...] << lengthTracking
+ // [1, ...] << lengthTrackingWithOffset
+
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ indexOfHelper(fixedLength, 1);
+ });
+ assert.throws(TypeError, () => {
+ indexOfHelper(fixedLengthWithOffset, 1);
+ });
+ assert.throws(TypeError, () => {
+ lastIndexOfHelper(fixedLength, 1);
+ });
+ assert.throws(TypeError, () => {
+ lastIndexOfHelper(fixedLengthWithOffset, 1);
+ });
+ } else {
+ assert.sameValue(indexOfHelper(fixedLength, 1), -1);
+ assert.sameValue(indexOfHelper(fixedLengthWithOffset, 1), -1);
+ assert.sameValue(lastIndexOfHelper(fixedLength, 1), -1);
+ assert.sameValue(lastIndexOfHelper(fixedLengthWithOffset, 1), -1);
+ }
+ assert.sameValue(indexOfHelper(lengthTracking, 1), 2);
+ assert.sameValue(indexOfHelper(lengthTracking, undefined), -1);
+ assert.sameValue(lastIndexOfHelper(lengthTracking, 0), 1);
+ assert.sameValue(lastIndexOfHelper(lengthTracking, undefined), -1);
+ assert.sameValue(indexOfHelper(lengthTrackingWithOffset, 0), -1);
+ assert.sameValue(indexOfHelper(lengthTrackingWithOffset, 1), 0);
+ assert.sameValue(indexOfHelper(lengthTrackingWithOffset, undefined), -1);
+ assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, 0), -1);
+ assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, 1), 0);
+ assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, undefined), -1);
+
+ // Shrink so that the TAs with offset go out of bounds.
+ rab.resize(1 * ctor.BYTES_PER_ELEMENT);
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ indexOfHelper(fixedLength, 0);
+ });
+ assert.throws(TypeError, () => {
+ indexOfHelper(fixedLengthWithOffset, 0);
+ });
+ assert.throws(TypeError, () => {
+ indexOfHelper(lengthTrackingWithOffset, 0);
+ });
+ assert.throws(TypeError, () => {
+ lastIndexOfHelper(fixedLength, 0);
+ });
+ assert.throws(TypeError, () => {
+ lastIndexOfHelper(fixedLengthWithOffset, 0);
+ });
+ assert.throws(TypeError, () => {
+ lastIndexOfHelper(lengthTrackingWithOffset, 0);
+ });
+ } else {
+ assert.sameValue(indexOfHelper(fixedLength, 0), -1);
+ assert.sameValue(indexOfHelper(fixedLengthWithOffset, 0), -1);
+ assert.sameValue(indexOfHelper(lengthTrackingWithOffset, 0), -1);
+ assert.sameValue(lastIndexOfHelper(fixedLength, 0), -1);
+ assert.sameValue(lastIndexOfHelper(fixedLengthWithOffset, 0), -1);
+ assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, 0), -1);
+ }
+ assert.sameValue(indexOfHelper(lengthTracking, 0), 0);
+ assert.sameValue(lastIndexOfHelper(lengthTracking, 0), 0);
+
+ // Shrink to zero.
+ rab.resize(0);
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ indexOfHelper(fixedLength, 0);
+ });
+ assert.throws(TypeError, () => {
+ indexOfHelper(fixedLengthWithOffset, 0);
+ });
+ assert.throws(TypeError, () => {
+ indexOfHelper(lengthTrackingWithOffset, 0);
+ });
+ assert.throws(TypeError, () => {
+ lastIndexOfHelper(fixedLength, 0);
+ });
+ assert.throws(TypeError, () => {
+ lastIndexOfHelper(fixedLengthWithOffset, 0);
+ });
+ assert.throws(TypeError, () => {
+ lastIndexOfHelper(lengthTrackingWithOffset, 0);
+ });
+ } else {
+ assert.sameValue(indexOfHelper(fixedLength, 0), -1);
+ assert.sameValue(indexOfHelper(fixedLengthWithOffset, 0), -1);
+ assert.sameValue(indexOfHelper(lengthTrackingWithOffset, 0), -1);
+ assert.sameValue(lastIndexOfHelper(fixedLength, 0), -1);
+ assert.sameValue(lastIndexOfHelper(fixedLengthWithOffset, 0), -1);
+ assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, 0), -1);
+ }
+ assert.sameValue(indexOfHelper(lengthTracking, 0), -1);
+ assert.sameValue(indexOfHelper(lengthTracking, undefined), -1);
+ assert.sameValue(lastIndexOfHelper(lengthTracking, 0), -1);
+ assert.sameValue(lastIndexOfHelper(lengthTracking, undefined), -1);
+
+ // Grow so that all TAs are back in-bounds.
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ for (let i = 0; i < 6; ++i) {
+ WriteToTypedArray(taWrite, i, Math.floor(i / 2));
+ }
+
+ // Orig. array: [0, 0, 1, 1, 2, 2]
+ // [0, 0, 1, 1] << fixedLength
+ // [1, 1] << fixedLengthWithOffset
+ // [0, 0, 1, 1, 2, 2, ...] << lengthTracking
+ // [1, 1, 2, 2, ...] << lengthTrackingWithOffset
+
+ assert.sameValue(indexOfHelper(fixedLength, 1), 2);
+ assert.sameValue(indexOfHelper(fixedLength, 2), -1);
+ assert.sameValue(indexOfHelper(fixedLength, undefined), -1);
+ assert.sameValue(lastIndexOfHelper(fixedLength, 1), 3);
+ assert.sameValue(lastIndexOfHelper(fixedLength, 2), -1);
+ assert.sameValue(lastIndexOfHelper(fixedLength, undefined), -1);
+ assert.sameValue(indexOfHelper(fixedLengthWithOffset, 0), -1);
+ assert.sameValue(indexOfHelper(fixedLengthWithOffset, 1), 0);
+ assert.sameValue(indexOfHelper(fixedLengthWithOffset, 2), -1);
+ assert.sameValue(indexOfHelper(fixedLengthWithOffset, undefined), -1);
+ assert.sameValue(lastIndexOfHelper(fixedLengthWithOffset, 0), -1);
+ assert.sameValue(lastIndexOfHelper(fixedLengthWithOffset, 1), 1);
+ assert.sameValue(lastIndexOfHelper(fixedLengthWithOffset, 2), -1);
+ assert.sameValue(lastIndexOfHelper(fixedLengthWithOffset, undefined), -1);
+ assert.sameValue(indexOfHelper(lengthTracking, 1), 2);
+ assert.sameValue(indexOfHelper(lengthTracking, 2), 4);
+ assert.sameValue(indexOfHelper(lengthTracking, undefined), -1);
+ assert.sameValue(lastIndexOfHelper(lengthTracking, 1), 3);
+ assert.sameValue(lastIndexOfHelper(lengthTracking, 2), 5);
+ assert.sameValue(lastIndexOfHelper(lengthTracking, undefined), -1);
+ assert.sameValue(indexOfHelper(lengthTrackingWithOffset, 0), -1);
+ assert.sameValue(indexOfHelper(lengthTrackingWithOffset, 1), 0);
+ assert.sameValue(indexOfHelper(lengthTrackingWithOffset, 2), 2);
+ assert.sameValue(indexOfHelper(lengthTrackingWithOffset, undefined), -1);
+ assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, 0), -1);
+ assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, 1), 1);
+ assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, 2), 3);
+ assert.sameValue(lastIndexOfHelper(lengthTrackingWithOffset, undefined), -1);
+ }
+}
+
+IndexOfLastIndexOf(TypedArrayIndexOfHelper, TypedArrayLastIndexOfHelper, true);
+IndexOfLastIndexOf(ArrayIndexOfHelper, ArrayLastIndexOfHelper, false);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/index-of-parameter-conversion-grows-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/index-of-parameter-conversion-grows-strict.js
new file mode 100644
index 0000000000..676e4ebe64
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/index-of-parameter-conversion-grows-strict.js
@@ -0,0 +1,123 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from IndexOfParameterConversionGrows test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function TypedArrayIndexOfHelper(ta, n, fromIndex) {
+ if (typeof n == 'number' && (ta instanceof BigInt64Array || ta instanceof BigUint64Array)) {
+ if (fromIndex == undefined) {
+ return ta.indexOf(BigInt(n));
+ }
+ return ta.indexOf(BigInt(n), fromIndex);
+ }
+ if (fromIndex == undefined) {
+ return ta.indexOf(n);
+ }
+ return ta.indexOf(n, fromIndex);
+}
+
+function ArrayIndexOfHelper(ta, n, fromIndex) {
+ if (typeof n == 'number' && (ta instanceof BigInt64Array || ta instanceof BigUint64Array)) {
+ if (fromIndex == undefined) {
+ return Array.prototype.indexOf.call(ta, BigInt(n));
+ }
+ return Array.prototype.indexOf.call(ta, BigInt(n), fromIndex);
+ }
+ if (fromIndex == undefined) {
+ return Array.prototype.indexOf.call(ta, n);
+ }
+ return Array.prototype.indexOf.call(ta, n, fromIndex);
+}
+
+function IndexOfParameterConversionGrows(indexOfHelper) {
+ // Growing + length-tracking TA.
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const lengthTracking = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(lengthTracking, i, 1);
+ }
+ let evil = {
+ valueOf: () => {
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ return 0;
+ }
+ };
+ assert.sameValue(indexOfHelper(lengthTracking, 0), -1);
+ // The TA grew but we only look at the data until the original length.
+ assert.sameValue(indexOfHelper(lengthTracking, 0, evil), -1);
+ }
+
+ // Growing + length-tracking TA, index conversion.
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const lengthTracking = new ctor(rab);
+ WriteToTypedArray(lengthTracking, 0, 1);
+ let evil = {
+ valueOf: () => {
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ return -4;
+ }
+ };
+ assert.sameValue(indexOfHelper(lengthTracking, 1, -4), 0);
+ // The TA grew but the start index conversion is done based on the original
+ // length.
+ assert.sameValue(indexOfHelper(lengthTracking, 1, evil), 0);
+ }
+}
+
+IndexOfParameterConversionGrows(TypedArrayIndexOfHelper);
+IndexOfParameterConversionGrows(ArrayIndexOfHelper);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/index-of-parameter-conversion-shrinks-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/index-of-parameter-conversion-shrinks-strict.js
new file mode 100644
index 0000000000..fb2d146f08
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/index-of-parameter-conversion-shrinks-strict.js
@@ -0,0 +1,134 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from IndexOfParameterConversionShrinks test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function TypedArrayIndexOfHelper(ta, n, fromIndex) {
+ if (typeof n == 'number' && (ta instanceof BigInt64Array || ta instanceof BigUint64Array)) {
+ if (fromIndex == undefined) {
+ return ta.indexOf(BigInt(n));
+ }
+ return ta.indexOf(BigInt(n), fromIndex);
+ }
+ if (fromIndex == undefined) {
+ return ta.indexOf(n);
+ }
+ return ta.indexOf(n, fromIndex);
+}
+
+function ArrayIndexOfHelper(ta, n, fromIndex) {
+ if (typeof n == 'number' && (ta instanceof BigInt64Array || ta instanceof BigUint64Array)) {
+ if (fromIndex == undefined) {
+ return Array.prototype.indexOf.call(ta, BigInt(n));
+ }
+ return Array.prototype.indexOf.call(ta, BigInt(n), fromIndex);
+ }
+ if (fromIndex == undefined) {
+ return Array.prototype.indexOf.call(ta, n);
+ }
+ return Array.prototype.indexOf.call(ta, n, fromIndex);
+}
+
+function IndexOfParameterConversionShrinks(indexOfHelper, lastIndexOfHelper) {
+ // Shrinking + fixed-length TA.
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ let evil = {
+ valueOf: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 0;
+ }
+ };
+ assert.sameValue(indexOfHelper(fixedLength, 0), 0);
+ // The TA is OOB so indexOf returns -1.
+ assert.sameValue(indexOfHelper(fixedLength, 0, evil), -1);
+ }
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ let evil = {
+ valueOf: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 0;
+ }
+ };
+ assert.sameValue(indexOfHelper(fixedLength, 0), 0);
+ // The TA is OOB so indexOf returns -1, also for undefined).
+ assert.sameValue(indexOfHelper(fixedLength, undefined, evil), -1);
+ }
+
+ // Shrinking + length-tracking TA.
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const lengthTracking = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(lengthTracking, i, i);
+ }
+ let evil = {
+ valueOf: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 0;
+ }
+ };
+ assert.sameValue(indexOfHelper(lengthTracking, 2), 2);
+ // 2 no longer found.
+ assert.sameValue(indexOfHelper(lengthTracking, 2, evil), -1);
+ }
+}
+
+IndexOfParameterConversionShrinks(TypedArrayIndexOfHelper);
+IndexOfParameterConversionShrinks(ArrayIndexOfHelper);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-and-grow-just-before-iteration-would-end-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-and-grow-just-before-iteration-would-end-strict.js
new file mode 100644
index 0000000000..f32949dc76
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-and-grow-just-before-iteration-would-end-strict.js
@@ -0,0 +1,126 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from IterateTypedArrayAndGrowJustBeforeIterationWouldEnd test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+includes: [compareArray.js]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function CreateRab(buffer_byte_length, ctor) {
+ const rab = CreateResizableArrayBuffer(buffer_byte_length, 2 * buffer_byte_length);
+ let ta_write = new ctor(rab);
+ for (let i = 0; i < buffer_byte_length / ctor.BYTES_PER_ELEMENT; ++i) {
+ WriteToTypedArray(ta_write, i, i % 128);
+ }
+ return rab;
+}
+
+function TestIterationAndResize(ta, expected, rab, resize_after, new_byte_length) {
+ let values = [];
+ let resized = false;
+ for (const value of ta) {
+ if (value instanceof Array) {
+ values.push([
+ value[0],
+ Number(value[1])
+ ]);
+ } else {
+ values.push(Number(value));
+ }
+ if (!resized && values.length == resize_after) {
+ rab.resize(new_byte_length);
+ resized = true;
+ }
+ }
+ assert.compareArray(values, expected);
+ assert(resized);
+}
+
+const no_elements = 10;
+const offset = 2;
+
+// We need to recreate the RAB between all TA tests, since we grow it.
+for (let ctor of ctors) {
+ const buffer_byte_length = no_elements * ctor.BYTES_PER_ELEMENT;
+ const byte_offset = offset * ctor.BYTES_PER_ELEMENT;
+
+ // Create various different styles of TypedArrays with the RAB as the
+ // backing store and iterate them.
+
+ let rab = CreateRab(buffer_byte_length, ctor);
+ const length_tracking_ta = new ctor(rab);
+ {
+ let expected = [];
+ for (let i = 0; i < no_elements; ++i) {
+ expected.push(i % 128);
+ }
+ // After resizing, the new memory contains zeros.
+ for (let i = 0; i < no_elements; ++i) {
+ expected.push(0);
+ }
+ TestIterationAndResize(length_tracking_ta, expected, rab, no_elements, buffer_byte_length * 2);
+ }
+ rab = CreateRab(buffer_byte_length, ctor);
+ const length_tracking_ta_with_offset = new ctor(rab, byte_offset);
+ {
+ let expected = [];
+ for (let i = offset; i < no_elements; ++i) {
+ expected.push(i % 128);
+ }
+ for (let i = 0; i < no_elements; ++i) {
+ expected.push(0);
+ }
+ TestIterationAndResize(length_tracking_ta_with_offset, expected, rab, no_elements - offset, buffer_byte_length * 2);
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-and-grow-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-and-grow-mid-iteration-strict.js
new file mode 100644
index 0000000000..9c281202fa
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-and-grow-mid-iteration-strict.js
@@ -0,0 +1,139 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from IterateTypedArrayAndGrowMidIteration test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+includes: [compareArray.js]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function CreateRab(buffer_byte_length, ctor) {
+ const rab = CreateResizableArrayBuffer(buffer_byte_length, 2 * buffer_byte_length);
+ let ta_write = new ctor(rab);
+ for (let i = 0; i < buffer_byte_length / ctor.BYTES_PER_ELEMENT; ++i) {
+ WriteToTypedArray(ta_write, i, i % 128);
+ }
+ return rab;
+}
+
+function TestIterationAndResize(ta, expected, rab, resize_after, new_byte_length) {
+ let values = [];
+ let resized = false;
+ for (const value of ta) {
+ if (value instanceof Array) {
+ values.push([
+ value[0],
+ Number(value[1])
+ ]);
+ } else {
+ values.push(Number(value));
+ }
+ if (!resized && values.length == resize_after) {
+ rab.resize(new_byte_length);
+ resized = true;
+ }
+ }
+ assert.compareArray(values, expected);
+ assert(resized);
+}
+
+const no_elements = 10;
+const offset = 2;
+for (let ctor of ctors) {
+ const buffer_byte_length = no_elements * ctor.BYTES_PER_ELEMENT;
+ const byte_offset = offset * ctor.BYTES_PER_ELEMENT;
+
+ // Create various different styles of TypedArrays with the RAB as the
+ // backing store and iterate them.
+
+ // Fixed-length TAs aren't affected by resizing.
+ let rab = CreateRab(buffer_byte_length, ctor);
+ const ta = new ctor(rab, 0, 3);
+ TestIterationAndResize(ta, [
+ 0,
+ 1,
+ 2
+ ], rab, 2, buffer_byte_length * 2);
+ rab = CreateRab(buffer_byte_length, ctor);
+ const ta_with_offset = new ctor(rab, byte_offset, 3);
+ TestIterationAndResize(ta_with_offset, [
+ 2,
+ 3,
+ 4
+ ], rab, 2, buffer_byte_length * 2);
+ rab = CreateRab(buffer_byte_length, ctor);
+ const length_tracking_ta = new ctor(rab);
+ {
+ let expected = [];
+ for (let i = 0; i < no_elements; ++i) {
+ expected.push(i % 128);
+ }
+ for (let i = 0; i < no_elements; ++i) {
+ // After resizing, the new memory contains zeros.
+ expected.push(0);
+ }
+ TestIterationAndResize(length_tracking_ta, expected, rab, 2, buffer_byte_length * 2);
+ }
+ rab = CreateRab(buffer_byte_length, ctor);
+ const length_tracking_ta_with_offset = new ctor(rab, byte_offset);
+ {
+ let expected = [];
+ for (let i = offset; i < no_elements; ++i) {
+ expected.push(i % 128);
+ }
+ for (let i = 0; i < no_elements; ++i) {
+ expected.push(0);
+ }
+ TestIterationAndResize(length_tracking_ta_with_offset, expected, rab, 2, buffer_byte_length * 2);
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-and-shrink-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-and-shrink-mid-iteration-strict.js
new file mode 100644
index 0000000000..2227212b6d
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-and-shrink-mid-iteration-strict.js
@@ -0,0 +1,154 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from IterateTypedArrayAndShrinkMidIteration test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+includes: [compareArray.js]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function CreateRab(buffer_byte_length, ctor) {
+ const rab = CreateResizableArrayBuffer(buffer_byte_length, 2 * buffer_byte_length);
+ let ta_write = new ctor(rab);
+ for (let i = 0; i < buffer_byte_length / ctor.BYTES_PER_ELEMENT; ++i) {
+ WriteToTypedArray(ta_write, i, i % 128);
+ }
+ return rab;
+}
+
+function TestIterationAndResize(ta, expected, rab, resize_after, new_byte_length) {
+ let values = [];
+ let resized = false;
+ for (const value of ta) {
+ if (value instanceof Array) {
+ values.push([
+ value[0],
+ Number(value[1])
+ ]);
+ } else {
+ values.push(Number(value));
+ }
+ if (!resized && values.length == resize_after) {
+ rab.resize(new_byte_length);
+ resized = true;
+ }
+ }
+ assert.compareArray(values, expected);
+ assert(resized);
+}
+
+const no_elements = 10;
+const offset = 2;
+for (let ctor of ctors) {
+ const buffer_byte_length = no_elements * ctor.BYTES_PER_ELEMENT;
+ const byte_offset = offset * ctor.BYTES_PER_ELEMENT;
+
+ // Create various different styles of TypedArrays with the RAB as the
+ // backing store and iterate them.
+
+ // Fixed-length TAs aren't affected by shrinking if they stay in-bounds.
+ // They appear detached after shrinking out of bounds.
+ let rab = CreateRab(buffer_byte_length, ctor);
+ const ta1 = new ctor(rab, 0, 3);
+ TestIterationAndResize(ta1, [
+ 0,
+ 1,
+ 2
+ ], rab, 2, buffer_byte_length / 2);
+ rab = CreateRab(buffer_byte_length, ctor);
+ const ta2 = new ctor(rab, 0, 3);
+ assert.throws(TypeError, () => {
+ TestIterationAndResize(ta2, null, rab, 2, 1);
+ });
+ rab = CreateRab(buffer_byte_length, ctor);
+ const ta_with_offset1 = new ctor(rab, byte_offset, 3);
+ TestIterationAndResize(ta_with_offset1, [
+ 2,
+ 3,
+ 4
+ ], rab, 2, buffer_byte_length / 2);
+ rab = CreateRab(buffer_byte_length, ctor);
+ const ta_with_offset2 = new ctor(rab, byte_offset, 3);
+ assert.throws(TypeError, () => {
+ TestIterationAndResize(ta_with_offset2, null, rab, 2, 0);
+ });
+
+ // Length-tracking TA with offset 0 doesn't throw, but its length gracefully
+ // reduces too.
+ rab = CreateRab(buffer_byte_length, ctor);
+ const length_tracking_ta = new ctor(rab);
+ TestIterationAndResize(length_tracking_ta, [
+ 0,
+ 1,
+ 2,
+ 3,
+ 4
+ ], rab, 2, buffer_byte_length / 2);
+
+ // Length-tracking TA appears detached when the buffer is resized beyond the
+ // offset.
+ rab = CreateRab(buffer_byte_length, ctor);
+ const length_tracking_ta_with_offset = new ctor(rab, byte_offset);
+ assert.throws(TypeError, () => {
+ TestIterationAndResize(length_tracking_ta_with_offset, null, rab, 2, byte_offset / 2);
+ });
+
+ // Length-tracking TA reduces its length gracefully when the buffer is
+ // resized to barely cover the offset.
+ rab = CreateRab(buffer_byte_length, ctor);
+ const length_tracking_ta_with_offset2 = new ctor(rab, byte_offset);
+ TestIterationAndResize(length_tracking_ta_with_offset2, [
+ 2,
+ 3
+ ], rab, 2, byte_offset);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-and-shrink-to-zero-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-and-shrink-to-zero-mid-iteration-strict.js
new file mode 100644
index 0000000000..1a6c154188
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-and-shrink-to-zero-mid-iteration-strict.js
@@ -0,0 +1,126 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from IterateTypedArrayAndShrinkToZeroMidIteration test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+includes: [compareArray.js]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function CreateRab(buffer_byte_length, ctor) {
+ const rab = CreateResizableArrayBuffer(buffer_byte_length, 2 * buffer_byte_length);
+ let ta_write = new ctor(rab);
+ for (let i = 0; i < buffer_byte_length / ctor.BYTES_PER_ELEMENT; ++i) {
+ WriteToTypedArray(ta_write, i, i % 128);
+ }
+ return rab;
+}
+
+function TestIterationAndResize(ta, expected, rab, resize_after, new_byte_length) {
+ let values = [];
+ let resized = false;
+ for (const value of ta) {
+ if (value instanceof Array) {
+ values.push([
+ value[0],
+ Number(value[1])
+ ]);
+ } else {
+ values.push(Number(value));
+ }
+ if (!resized && values.length == resize_after) {
+ rab.resize(new_byte_length);
+ resized = true;
+ }
+ }
+ assert.compareArray(values, expected);
+ assert(resized);
+}
+
+const no_elements = 10;
+const offset = 2;
+for (let ctor of ctors) {
+ const buffer_byte_length = no_elements * ctor.BYTES_PER_ELEMENT;
+ const byte_offset = offset * ctor.BYTES_PER_ELEMENT;
+
+ // Create various different styles of TypedArrays with the RAB as the
+ // backing store and iterate them.
+
+ // Fixed-length TAs appear detached after shrinking out of bounds.
+ let rab = CreateRab(buffer_byte_length, ctor);
+ const ta = new ctor(rab, 0, 3);
+ assert.throws(TypeError, () => {
+ TestIterationAndResize(ta, null, rab, 2, 0);
+ });
+ rab = CreateRab(buffer_byte_length, ctor);
+ const ta_with_offset = new ctor(rab, byte_offset, 3);
+ assert.throws(TypeError, () => {
+ TestIterationAndResize(ta_with_offset, null, rab, 2, 0);
+ });
+
+ // Length-tracking TA with offset 0 doesn't throw, but its length gracefully
+ // goes to zero too.
+ rab = CreateRab(buffer_byte_length, ctor);
+ const length_tracking_ta = new ctor(rab);
+ TestIterationAndResize(length_tracking_ta, [
+ 0,
+ 1
+ ], rab, 2, 0);
+
+ // Length-tracking TA which is resized beyond the offset appars detached.
+ rab = CreateRab(buffer_byte_length, ctor);
+ const length_tracking_ta_with_offset = new ctor(rab, byte_offset);
+ assert.throws(TypeError, () => {
+ TestIterationAndResize(length_tracking_ta_with_offset, null, rab, 2, 0);
+ });
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-strict.js
new file mode 100644
index 0000000000..c485044559
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/iterate-typed-array-strict.js
@@ -0,0 +1,118 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from IterateTypedArray test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+includes: [compareArray.js]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+const no_elements = 10;
+const offset = 2;
+function TestIteration(ta, expected) {
+ let values = [];
+ for (const value of ta) {
+ values.push(Number(value));
+ }
+ assert.compareArray(values, expected);
+}
+for (let ctor of ctors) {
+ const buffer_byte_length = no_elements * ctor.BYTES_PER_ELEMENT;
+ // We can use the same RAB for all the TAs below, since we won't modify it
+ // after writing the initial values.
+ const rab = CreateResizableArrayBuffer(buffer_byte_length, 2 * buffer_byte_length);
+ const byte_offset = offset * ctor.BYTES_PER_ELEMENT;
+
+ // Write some data into the array.
+ let ta_write = new ctor(rab);
+ for (let i = 0; i < no_elements; ++i) {
+ WriteToTypedArray(ta_write, i, i % 128);
+ }
+
+ // Create various different styles of TypedArrays with the RAB as the
+ // backing store and iterate them.
+ const ta = new ctor(rab, 0, 3);
+ TestIteration(ta, [
+ 0,
+ 1,
+ 2
+ ]);
+ const empty_ta = new ctor(rab, 0, 0);
+ TestIteration(empty_ta, []);
+ const ta_with_offset = new ctor(rab, byte_offset, 3);
+ TestIteration(ta_with_offset, [
+ 2,
+ 3,
+ 4
+ ]);
+ const empty_ta_with_offset = new ctor(rab, byte_offset, 0);
+ TestIteration(empty_ta_with_offset, []);
+ const length_tracking_ta = new ctor(rab);
+ {
+ let expected = [];
+ for (let i = 0; i < no_elements; ++i) {
+ expected.push(i % 128);
+ }
+ TestIteration(length_tracking_ta, expected);
+ }
+ const length_tracking_ta_with_offset = new ctor(rab, byte_offset);
+ {
+ let expected = [];
+ for (let i = offset; i < no_elements; ++i) {
+ expected.push(i % 128);
+ }
+ TestIteration(length_tracking_ta_with_offset, expected);
+ }
+ const empty_length_tracking_ta_with_offset = new ctor(rab, buffer_byte_length);
+ TestIteration(empty_length_tracking_ta_with_offset, []);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/join-parameter-conversion-grows-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/join-parameter-conversion-grows-strict.js
new file mode 100644
index 0000000000..2fcffb813b
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/join-parameter-conversion-grows-strict.js
@@ -0,0 +1,89 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from JoinParameterConversionGrows test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+const TypedArrayJoinHelper = (ta, ...rest) => {
+ return ta.join(...rest);
+};
+
+const ArrayJoinHelper = (ta, ...rest) => {
+ return Array.prototype.join.call(ta, ...rest);
+};
+
+function JoinParameterConversionGrows(joinHelper) {
+ // Growing + fixed-length TA.
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ let evil = {
+ toString: () => {
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ return '.';
+ }
+ };
+ assert.sameValue(joinHelper(fixedLength, evil), '0.0.0.0');
+ }
+
+ // Growing + length-tracking TA.
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const lengthTracking = new ctor(rab);
+ let evil = {
+ toString: () => {
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ return '.';
+ }
+ };
+ // We iterate 4 elements, since it was the starting length.
+ assert.sameValue(joinHelper(lengthTracking, evil), '0.0.0.0');
+ }
+}
+
+JoinParameterConversionGrows(TypedArrayJoinHelper);
+JoinParameterConversionGrows(ArrayJoinHelper);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/join-parameter-conversion-shrinks-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/join-parameter-conversion-shrinks-strict.js
new file mode 100644
index 0000000000..ab090558cc
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/join-parameter-conversion-shrinks-strict.js
@@ -0,0 +1,93 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from JoinParameterConversionShrinks test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+const TypedArrayJoinHelper = (ta, ...rest) => {
+ return ta.join(...rest);
+};
+
+const ArrayJoinHelper = (ta, ...rest) => {
+ return Array.prototype.join.call(ta, ...rest);
+};
+
+function JoinParameterConversionShrinks(joinHelper) {
+ // Shrinking + fixed-length TA.
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ let evil = {
+ toString: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return '.';
+ }
+ };
+ // We iterate 4 elements, since it was the starting length, but the TA is
+ // OOB right after parameter conversion, so all elements are converted to
+ // the empty string.
+ assert.sameValue(joinHelper(fixedLength, evil), '...');
+ }
+
+ // Shrinking + length-tracking TA.
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const lengthTracking = new ctor(rab);
+ let evil = {
+ toString: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return '.';
+ }
+ };
+ // We iterate 4 elements, since it was the starting length. Elements beyond
+ // the new length are converted to the empty string.
+ assert.sameValue(joinHelper(lengthTracking, evil), '0.0..');
+ }
+}
+
+JoinParameterConversionShrinks(TypedArrayJoinHelper);
+JoinParameterConversionShrinks(ArrayJoinHelper);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/join-to-locale-string-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/join-to-locale-string-strict.js
new file mode 100644
index 0000000000..0867bb6d26
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/join-to-locale-string-strict.js
@@ -0,0 +1,223 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from JoinToLocaleString test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+const TypedArrayJoinHelper = (ta, ...rest) => {
+ return ta.join(...rest);
+};
+
+const ArrayJoinHelper = (ta, ...rest) => {
+ return Array.prototype.join.call(ta, ...rest);
+};
+
+const TypedArrayToLocaleStringHelper = (ta, ...rest) => {
+ return ta.toLocaleString(...rest);
+};
+
+const ArrayToLocaleStringHelper = (ta, ...rest) => {
+ return Array.prototype.toLocaleString.call(ta, ...rest);
+};
+
+function JoinToLocaleString(joinHelper, toLocaleStringHelper, oobThrows) {
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ const lengthTracking = new ctor(rab, 0);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ const taWrite = new ctor(rab);
+
+ // Write some data into the array.
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+
+ // Orig. array: [0, 2, 4, 6]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, ...] << lengthTracking
+ // [4, 6, ...] << lengthTrackingWithOffset
+
+ assert.sameValue(joinHelper(fixedLength), '0,2,4,6');
+ assert.sameValue(toLocaleStringHelper(fixedLength), '0,2,4,6');
+ assert.sameValue(joinHelper(fixedLengthWithOffset), '4,6');
+ assert.sameValue(toLocaleStringHelper(fixedLengthWithOffset), '4,6');
+ assert.sameValue(joinHelper(lengthTracking), '0,2,4,6');
+ assert.sameValue(toLocaleStringHelper(lengthTracking), '0,2,4,6');
+ assert.sameValue(joinHelper(lengthTrackingWithOffset), '4,6');
+ assert.sameValue(toLocaleStringHelper(lengthTrackingWithOffset), '4,6');
+
+ // Shrink so that fixed length TAs go out of bounds.
+ rab.resize(3 * ctor.BYTES_PER_ELEMENT);
+
+ // Orig. array: [0, 2, 4]
+ // [0, 2, 4, ...] << lengthTracking
+ // [4, ...] << lengthTrackingWithOffset
+
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ joinHelper(fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ toLocaleStringHelper(fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ joinHelper(fixedLengthWithOffset);
+ });
+ assert.throws(TypeError, () => {
+ toLocaleStringHelper(fixedLengthWithOffset);
+ });
+ } else {
+ assert.sameValue(joinHelper(fixedLength), '');
+ assert.sameValue(toLocaleStringHelper(fixedLength), '');
+ assert.sameValue(joinHelper(fixedLengthWithOffset), '');
+ assert.sameValue(toLocaleStringHelper(fixedLengthWithOffset), '');
+ }
+ assert.sameValue(joinHelper(lengthTracking), '0,2,4');
+ assert.sameValue(toLocaleStringHelper(lengthTracking), '0,2,4');
+ assert.sameValue(joinHelper(lengthTrackingWithOffset), '4');
+ assert.sameValue(toLocaleStringHelper(lengthTrackingWithOffset), '4');
+
+ // Shrink so that the TAs with offset go out of bounds.
+ rab.resize(1 * ctor.BYTES_PER_ELEMENT);
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ joinHelper(fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ toLocaleStringHelper(fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ joinHelper(fixedLengthWithOffset);
+ });
+ assert.throws(TypeError, () => {
+ toLocaleStringHelper(fixedLengthWithOffset);
+ });
+ assert.throws(TypeError, () => {
+ joinHelper(lengthTrackingWithOffset);
+ });
+ assert.throws(TypeError, () => {
+ toLocaleStringHelper(lengthTrackingWithOffset);
+ });
+ } else {
+ assert.sameValue(joinHelper(fixedLength), '');
+ assert.sameValue(toLocaleStringHelper(fixedLength), '');
+ assert.sameValue(joinHelper(fixedLengthWithOffset), '');
+ assert.sameValue(toLocaleStringHelper(fixedLengthWithOffset), '');
+ assert.sameValue(joinHelper(lengthTrackingWithOffset), '');
+ assert.sameValue(toLocaleStringHelper(lengthTrackingWithOffset), '');
+ }
+ assert.sameValue(joinHelper(lengthTracking), '0');
+ assert.sameValue(toLocaleStringHelper(lengthTracking), '0');
+
+ // Shrink to zero.
+ rab.resize(0);
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ joinHelper(fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ toLocaleStringHelper(fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ joinHelper(fixedLengthWithOffset);
+ });
+ assert.throws(TypeError, () => {
+ toLocaleStringHelper(fixedLengthWithOffset);
+ });
+ assert.throws(TypeError, () => {
+ joinHelper(lengthTrackingWithOffset);
+ });
+ assert.throws(TypeError, () => {
+ toLocaleStringHelper(lengthTrackingWithOffset);
+ });
+ } else {
+ assert.sameValue(joinHelper(fixedLength), '');
+ assert.sameValue(toLocaleStringHelper(fixedLength), '');
+ assert.sameValue(joinHelper(fixedLengthWithOffset), '');
+ assert.sameValue(toLocaleStringHelper(fixedLengthWithOffset), '');
+ assert.sameValue(joinHelper(lengthTrackingWithOffset), '');
+ assert.sameValue(toLocaleStringHelper(lengthTrackingWithOffset), '');
+ }
+ assert.sameValue(joinHelper(lengthTracking), '');
+ assert.sameValue(toLocaleStringHelper(lengthTracking), '');
+
+ // Grow so that all TAs are back in-bounds.
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ for (let i = 0; i < 6; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+
+ // Orig. array: [0, 2, 4, 6, 8, 10]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, 8, 10, ...] << lengthTracking
+ // [4, 6, 8, 10, ...] << lengthTrackingWithOffset
+
+ assert.sameValue(joinHelper(fixedLength), '0,2,4,6');
+ assert.sameValue(toLocaleStringHelper(fixedLength), '0,2,4,6');
+ assert.sameValue(joinHelper(fixedLengthWithOffset), '4,6');
+ assert.sameValue(toLocaleStringHelper(fixedLengthWithOffset), '4,6');
+ assert.sameValue(joinHelper(lengthTracking), '0,2,4,6,8,10');
+ assert.sameValue(toLocaleStringHelper(lengthTracking), '0,2,4,6,8,10');
+ assert.sameValue(joinHelper(lengthTrackingWithOffset), '4,6,8,10');
+ assert.sameValue(toLocaleStringHelper(lengthTrackingWithOffset), '4,6,8,10');
+ }
+}
+
+JoinToLocaleString(TypedArrayJoinHelper, TypedArrayToLocaleStringHelper, true);
+JoinToLocaleString(ArrayJoinHelper, ArrayToLocaleStringHelper, false);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/last-index-of-parameter-conversion-grows-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/last-index-of-parameter-conversion-grows-strict.js
new file mode 100644
index 0000000000..242853fab6
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/last-index-of-parameter-conversion-grows-strict.js
@@ -0,0 +1,125 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from LastIndexOfParameterConversionGrows test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function TypedArrayLastIndexOfHelper(ta, n, fromIndex) {
+ if (typeof n == 'number' && (ta instanceof BigInt64Array || ta instanceof BigUint64Array)) {
+ if (fromIndex == undefined) {
+ return ta.lastIndexOf(BigInt(n));
+ }
+ return ta.lastIndexOf(BigInt(n), fromIndex);
+ }
+ if (fromIndex == undefined) {
+ return ta.lastIndexOf(n);
+ }
+ return ta.lastIndexOf(n, fromIndex);
+}
+
+function ArrayLastIndexOfHelper(ta, n, fromIndex) {
+ if (typeof n == 'number' && (ta instanceof BigInt64Array || ta instanceof BigUint64Array)) {
+ if (fromIndex == undefined) {
+ return Array.prototype.lastIndexOf.call(ta, BigInt(n));
+ }
+ return Array.prototype.lastIndexOf.call(ta, BigInt(n), fromIndex);
+ }
+ if (fromIndex == undefined) {
+ return Array.prototype.lastIndexOf.call(ta, n);
+ }
+ return Array.prototype.lastIndexOf.call(ta, n, fromIndex);
+}
+
+function LastIndexOfParameterConversionGrows(lastIndexOfHelper) {
+ // Growing + length-tracking TA.
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const lengthTracking = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(lengthTracking, i, 1);
+ }
+ let evil = {
+ valueOf: () => {
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ return -1;
+ }
+ };
+ assert.sameValue(lastIndexOfHelper(lengthTracking, 0), -1);
+ // Because lastIndexOf iterates from the given index downwards, it's not
+ // possible to test that "we only look at the data until the original
+ // length" without also testing that the index conversion happening with the
+ // original length.
+ assert.sameValue(lastIndexOfHelper(lengthTracking, 0, evil), -1);
+ }
+
+ // Growing + length-tracking TA, index conversion.
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const lengthTracking = new ctor(rab);
+ let evil = {
+ valueOf: () => {
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ return -4;
+ }
+ };
+ assert.sameValue(lastIndexOfHelper(lengthTracking, 0, -4), 0);
+ // The TA grew but the start index conversion is done based on the original
+ // length.
+ assert.sameValue(lastIndexOfHelper(lengthTracking, 0, evil), 0);
+ }
+}
+
+LastIndexOfParameterConversionGrows(TypedArrayLastIndexOfHelper);
+LastIndexOfParameterConversionGrows(ArrayLastIndexOfHelper);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/last-index-of-parameter-conversion-shrinks-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/last-index-of-parameter-conversion-shrinks-strict.js
new file mode 100644
index 0000000000..3ebc0b2329
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/last-index-of-parameter-conversion-shrinks-strict.js
@@ -0,0 +1,134 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from LastIndexOfParameterConversionShrinks test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function TypedArrayLastIndexOfHelper(ta, n, fromIndex) {
+ if (typeof n == 'number' && (ta instanceof BigInt64Array || ta instanceof BigUint64Array)) {
+ if (fromIndex == undefined) {
+ return ta.lastIndexOf(BigInt(n));
+ }
+ return ta.lastIndexOf(BigInt(n), fromIndex);
+ }
+ if (fromIndex == undefined) {
+ return ta.lastIndexOf(n);
+ }
+ return ta.lastIndexOf(n, fromIndex);
+}
+
+function ArrayLastIndexOfHelper(ta, n, fromIndex) {
+ if (typeof n == 'number' && (ta instanceof BigInt64Array || ta instanceof BigUint64Array)) {
+ if (fromIndex == undefined) {
+ return Array.prototype.lastIndexOf.call(ta, BigInt(n));
+ }
+ return Array.prototype.lastIndexOf.call(ta, BigInt(n), fromIndex);
+ }
+ if (fromIndex == undefined) {
+ return Array.prototype.lastIndexOf.call(ta, n);
+ }
+ return Array.prototype.lastIndexOf.call(ta, n, fromIndex);
+}
+
+function LastIndexOfParameterConversionShrinks(lastIndexOfHelper) {
+ // Shrinking + fixed-length TA.
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ let evil = {
+ valueOf: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 2;
+ }
+ };
+ assert.sameValue(lastIndexOfHelper(fixedLength, 0), 3);
+ // The TA is OOB so lastIndexOf returns -1.
+ assert.sameValue(lastIndexOfHelper(fixedLength, 0, evil), -1);
+ }
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ let evil = {
+ valueOf: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 2;
+ }
+ };
+ assert.sameValue(lastIndexOfHelper(fixedLength, 0), 3);
+ // The TA is OOB so lastIndexOf returns -1, also for undefined).
+ assert.sameValue(lastIndexOfHelper(fixedLength, undefined, evil), -1);
+ }
+
+ // Shrinking + length-tracking TA.
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const lengthTracking = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(lengthTracking, i, i);
+ }
+ let evil = {
+ valueOf: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 2;
+ }
+ };
+ assert.sameValue(lastIndexOfHelper(lengthTracking, 2), 2);
+ // 2 no longer found.
+ assert.sameValue(lastIndexOfHelper(lengthTracking, 2, evil), -1);
+ }
+}
+
+LastIndexOfParameterConversionShrinks(TypedArrayLastIndexOfHelper);
+LastIndexOfParameterConversionShrinks(ArrayLastIndexOfHelper);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/length-tracking-1-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/length-tracking-1-strict.js
new file mode 100644
index 0000000000..86e436ad15
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/length-tracking-1-strict.js
@@ -0,0 +1,96 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from LengthTracking1 test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+const rab = CreateResizableArrayBuffer(16, 40);
+let tas = [];
+for (let ctor of ctors) {
+ tas.push(new ctor(rab));
+}
+for (let ta of tas) {
+ assert.sameValue(ta.length, 16 / ta.BYTES_PER_ELEMENT);
+ assert.sameValue(ta.byteLength, 16);
+}
+rab.resize(40);
+for (let ta of tas) {
+ assert.sameValue(ta.length, 40 / ta.BYTES_PER_ELEMENT);
+ assert.sameValue(ta.byteLength, 40);
+}
+// Resize to a number which is not a multiple of all byte_lengths.
+rab.resize(19);
+for (let ta of tas) {
+ const expected_length = Math.floor(19 / ta.BYTES_PER_ELEMENT);
+ assert.sameValue(ta.length, expected_length);
+ assert.sameValue(ta.byteLength, expected_length * ta.BYTES_PER_ELEMENT);
+}
+rab.resize(1);
+for (let ta of tas) {
+ if (ta.BYTES_PER_ELEMENT == 1) {
+ assert.sameValue(ta.length, 1);
+ assert.sameValue(ta.byteLength, 1);
+ } else {
+ assert.sameValue(ta.length, 0);
+ assert.sameValue(ta.byteLength, 0);
+ }
+}
+rab.resize(0);
+for (let ta of tas) {
+ assert.sameValue(ta.length, 0);
+ assert.sameValue(ta.byteLength, 0);
+}
+rab.resize(8);
+for (let ta of tas) {
+ assert.sameValue(ta.length, 8 / ta.BYTES_PER_ELEMENT);
+ assert.sameValue(ta.byteLength, 8);
+}
+rab.resize(40);
+for (let ta of tas) {
+ assert.sameValue(ta.length, 40 / ta.BYTES_PER_ELEMENT);
+ assert.sameValue(ta.byteLength, 40);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/length-tracking-2-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/length-tracking-2-strict.js
new file mode 100644
index 0000000000..cf4dbc2a7c
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/length-tracking-2-strict.js
@@ -0,0 +1,116 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from LengthTracking2 test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+// length-tracking-1 but with offsets.
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+const rab = CreateResizableArrayBuffer(16, 40);
+const offset = 8;
+let tas = [];
+for (let ctor of ctors) {
+ tas.push(new ctor(rab, offset));
+}
+for (let ta of tas) {
+ assert.sameValue(ta.length, (16 - offset) / ta.BYTES_PER_ELEMENT);
+ assert.sameValue(ta.byteLength, 16 - offset);
+ assert.sameValue(ta.byteOffset, offset);
+}
+rab.resize(40);
+for (let ta of tas) {
+ assert.sameValue(ta.length, (40 - offset) / ta.BYTES_PER_ELEMENT);
+ assert.sameValue(ta.byteLength, 40 - offset);
+ assert.sameValue(ta.byteOffset, offset);
+}
+// Resize to a number which is not a multiple of all byte_lengths.
+rab.resize(20);
+for (let ta of tas) {
+ const expected_length = Math.floor((20 - offset) / ta.BYTES_PER_ELEMENT);
+ assert.sameValue(ta.length, expected_length);
+ assert.sameValue(ta.byteLength, expected_length * ta.BYTES_PER_ELEMENT);
+ assert.sameValue(ta.byteOffset, offset);
+}
+// Resize so that all TypedArrays go out of bounds (because of the offset).
+rab.resize(7);
+for (let ta of tas) {
+ assert.sameValue(ta.length, 0);
+ assert.sameValue(ta.byteLength, 0);
+ assert.sameValue(ta.byteOffset, 0);
+}
+rab.resize(0);
+for (let ta of tas) {
+ assert.sameValue(ta.length, 0);
+ assert.sameValue(ta.byteLength, 0);
+ assert.sameValue(ta.byteOffset, 0);
+}
+rab.resize(8);
+for (let ta of tas) {
+ assert.sameValue(ta.length, 0);
+ assert.sameValue(ta.byteLength, 0);
+ assert.sameValue(ta.byteOffset, offset);
+}
+// Resize so that the TypedArrays which have element size > 1 go out of bounds
+// (because less than 1 full element would fit).
+rab.resize(offset + 1);
+for (let ta of tas) {
+ if (ta.BYTES_PER_ELEMENT == 1) {
+ assert.sameValue(ta.length, 1);
+ assert.sameValue(ta.byteLength, 1);
+ assert.sameValue(ta.byteOffset, offset);
+ } else {
+ assert.sameValue(ta.length, 0);
+ assert.sameValue(ta.byteLength, 0);
+ assert.sameValue(ta.byteOffset, offset);
+ }
+}
+rab.resize(40);
+for (let ta of tas) {
+ assert.sameValue(ta.length, (40 - offset) / ta.BYTES_PER_ELEMENT);
+ assert.sameValue(ta.byteLength, 40 - offset);
+ assert.sameValue(ta.byteOffset, offset);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/map-grow-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/map-grow-mid-iteration-strict.js
new file mode 100644
index 0000000000..b98d8faddd
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/map-grow-mid-iteration-strict.js
@@ -0,0 +1,150 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from MapGrowMidIteration test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+const TypedArrayMapHelper = (ta, ...rest) => {
+ return ta.map(...rest);
+};
+
+const ArrayMapHelper = (ta, ...rest) => {
+ return Array.prototype.map.call(ta, ...rest);
+};
+
+function MapGrowMidIteration(mapHelper) {
+ // Orig. array: [0, 2, 4, 6]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, ...] << lengthTracking
+ // [4, 6, ...] << lengthTrackingWithOffset
+ function CreateRabForTest(ctor) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+ return rab;
+ }
+ let values;
+ let rab;
+ let resizeAfter;
+ let resizeTo;
+ function CollectValuesAndResize(n) {
+ if (typeof n == 'bigint') {
+ values.push(Number(n));
+ } else {
+ values.push(n);
+ }
+ if (values.length == resizeAfter) {
+ rab.resize(resizeTo);
+ }
+ return n;
+ }
+ function Helper(array) {
+ values = [];
+ mapHelper(array, CollectValuesAndResize);
+ return values;
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ resizeAfter = 2;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(Helper(fixedLength), [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ resizeAfter = 1;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(Helper(fixedLengthWithOffset), [
+ 4,
+ 6
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ resizeAfter = 2;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(Helper(lengthTracking), [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ resizeAfter = 1;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert.compareArray(Helper(lengthTrackingWithOffset), [
+ 4,
+ 6
+ ]);
+ }
+}
+
+MapGrowMidIteration(TypedArrayMapHelper);
+MapGrowMidIteration(ArrayMapHelper);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/map-shrink-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/map-shrink-mid-iteration-strict.js
new file mode 100644
index 0000000000..535b3d04f9
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/map-shrink-mid-iteration-strict.js
@@ -0,0 +1,182 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from MapShrinkMidIteration test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function IsBigIntTypedArray(ta) {
+ return ta instanceof BigInt64Array || ta instanceof BigUint64Array;
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+const TypedArrayMapHelper = (ta, ...rest) => {
+ return ta.map(...rest);
+};
+
+const ArrayMapHelper = (ta, ...rest) => {
+ return Array.prototype.map.call(ta, ...rest);
+};
+
+function MapShrinkMidIteration(mapHelper, hasUndefined) {
+ // Orig. array: [0, 2, 4, 6]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, ...] << lengthTracking
+ // [4, 6, ...] << lengthTrackingWithOffset
+ function CreateRabForTest(ctor) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+ return rab;
+ }
+ let values;
+ let rab;
+ let resizeAfter;
+ let resizeTo;
+ function CollectValuesAndResize(n, ix, ta) {
+ if (typeof n == 'bigint') {
+ values.push(Number(n));
+ } else {
+ values.push(n);
+ }
+ if (values.length == resizeAfter) {
+ rab.resize(resizeTo);
+ }
+ // We still need to return a valid BigInt / non-BigInt, even if
+ // n is `undefined`.
+ if (IsBigIntTypedArray(ta)) {
+ return 0n;
+ }
+ return 0;
+ }
+ function Helper(array) {
+ values = [];
+ mapHelper(array, CollectValuesAndResize);
+ return values;
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ resizeAfter = 2;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ if (hasUndefined) {
+ assert.compareArray(Helper(fixedLength), [
+ 0,
+ 2,
+ undefined,
+ undefined
+ ]);
+ } else {
+ assert.compareArray(Helper(fixedLength), [
+ 0,
+ 2
+ ]);
+ }
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ resizeAfter = 1;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ if (hasUndefined) {
+ assert.compareArray(Helper(fixedLengthWithOffset), [
+ 4,
+ undefined
+ ]);
+ } else {
+ assert.compareArray(Helper(fixedLengthWithOffset), [4]);
+ }
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ resizeAfter = 2;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ if (hasUndefined) {
+ assert.compareArray(Helper(lengthTracking), [
+ 0,
+ 2,
+ 4,
+ undefined
+ ]);
+ } else {
+ assert.compareArray(Helper(lengthTracking), [
+ 0,
+ 2,
+ 4
+ ]);
+ }
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ resizeAfter = 1;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ if (hasUndefined) {
+ assert.compareArray(Helper(lengthTrackingWithOffset), [
+ 4,
+ undefined
+ ]);
+ } else {
+ assert.compareArray(Helper(lengthTrackingWithOffset), [4]);
+ }
+ }
+}
+
+MapShrinkMidIteration(TypedArrayMapHelper, true);
+MapShrinkMidIteration(ArrayMapHelper, false);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/map-species-create-grows-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/map-species-create-grows-strict.js
new file mode 100644
index 0000000000..e1780d8cc7
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/map-species-create-grows-strict.js
@@ -0,0 +1,135 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from MapSpeciesCreateGrows test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function IsBigIntTypedArray(ta) {
+ return ta instanceof BigInt64Array || ta instanceof BigUint64Array;
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+let values;
+let rab;
+function CollectValues(n, ix, ta) {
+ if (typeof n == 'bigint') {
+ values.push(Number(n));
+ } else {
+ values.push(n);
+ }
+ // We still need to return a valid BigInt / non-BigInt, even if
+ // n is `undefined`.
+ if (IsBigIntTypedArray(ta)) {
+ return 0n;
+ }
+ return 0;
+}
+function Helper(array) {
+ values = [];
+ array.map(CollectValues);
+ return values;
+}
+for (let ctor of ctors) {
+ rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, i);
+ }
+ let resizeWhenConstructorCalled = false;
+ class MyArray extends ctor {
+ constructor(...params) {
+ super(...params);
+ if (resizeWhenConstructorCalled) {
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ }
+ }
+ }
+ ;
+ const fixedLength = new MyArray(rab, 0, 4);
+ resizeWhenConstructorCalled = true;
+ assert.compareArray(Helper(fixedLength), [
+ 0,
+ 1,
+ 2,
+ 3
+ ]);
+ assert.sameValue(rab.byteLength, 6 * ctor.BYTES_PER_ELEMENT);
+}
+for (let ctor of ctors) {
+ rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, i);
+ }
+ let resizeWhenConstructorCalled = false;
+ class MyArray extends ctor {
+ constructor(...params) {
+ super(...params);
+ if (resizeWhenConstructorCalled) {
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ }
+ }
+ }
+ ;
+ const lengthTracking = new MyArray(rab);
+ resizeWhenConstructorCalled = true;
+ assert.compareArray(Helper(lengthTracking), [
+ 0,
+ 1,
+ 2,
+ 3
+ ]);
+ assert.sameValue(rab.byteLength, 6 * ctor.BYTES_PER_ELEMENT);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/map-species-create-shrinks-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/map-species-create-shrinks-strict.js
new file mode 100644
index 0000000000..46fd48b006
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/map-species-create-shrinks-strict.js
@@ -0,0 +1,131 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from MapSpeciesCreateShrinks test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function IsBigIntTypedArray(ta) {
+ return ta instanceof BigInt64Array || ta instanceof BigUint64Array;
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+let values;
+let rab;
+function CollectValues(n, ix, ta) {
+ if (typeof n == 'bigint') {
+ values.push(Number(n));
+ } else {
+ values.push(n);
+ }
+ if (IsBigIntTypedArray(ta)) {
+ // We still need to return a valid BigInt / non-BigInt, even if
+ // n is `undefined`.
+ return 0n;
+ }
+ return 0;
+}
+function Helper(array) {
+ values = [];
+ array.map(CollectValues);
+ return values;
+}
+for (let ctor of ctors) {
+ rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ let resizeWhenConstructorCalled = false;
+ class MyArray extends ctor {
+ constructor(...params) {
+ super(...params);
+ if (resizeWhenConstructorCalled) {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ }
+ }
+ }
+ ;
+ const fixedLength = new MyArray(rab, 0, 4);
+ resizeWhenConstructorCalled = true;
+ assert.compareArray(Helper(fixedLength), [
+ undefined,
+ undefined,
+ undefined,
+ undefined
+ ]);
+ assert.sameValue(rab.byteLength, 2 * ctor.BYTES_PER_ELEMENT);
+}
+for (let ctor of ctors) {
+ rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, i);
+ }
+ let resizeWhenConstructorCalled = false;
+ class MyArray extends ctor {
+ constructor(...params) {
+ super(...params);
+ if (resizeWhenConstructorCalled) {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ }
+ }
+ }
+ ;
+ const lengthTracking = new MyArray(rab);
+ resizeWhenConstructorCalled = true;
+ assert.compareArray(Helper(lengthTracking), [
+ 0,
+ 1,
+ undefined,
+ undefined
+ ]);
+ assert.sameValue(rab.byteLength, 2 * ctor.BYTES_PER_ELEMENT);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/object-define-property-define-properties-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/object-define-property-define-properties-strict.js
new file mode 100644
index 0000000000..011c23b98f
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/object-define-property-define-properties-strict.js
@@ -0,0 +1,279 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from ObjectDefinePropertyDefineProperties test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+function ObjectDefinePropertyHelper(ta, index, value) {
+ if (ta instanceof BigInt64Array || ta instanceof BigUint64Array) {
+ Object.defineProperty(ta, index, { value: BigInt(value) });
+ } else {
+ Object.defineProperty(ta, index, { value: value });
+ }
+}
+
+function ObjectDefinePropertiesHelper(ta, index, value) {
+ const values = {};
+ if (ta instanceof BigInt64Array || ta instanceof BigUint64Array) {
+ values[index] = { value: BigInt(value) };
+ } else {
+ values[index] = { value: value };
+ }
+ Object.defineProperties(ta, values);
+}
+
+for (let helper of [
+ ObjectDefinePropertyHelper,
+ ObjectDefinePropertiesHelper
+ ]) {
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ const lengthTracking = new ctor(rab, 0);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ const taFull = new ctor(rab, 0);
+
+ // Orig. array: [0, 0, 0, 0]
+ // [0, 0, 0, 0] << fixedLength
+ // [0, 0] << fixedLengthWithOffset
+ // [0, 0, 0, 0, ...] << lengthTracking
+ // [0, 0, ...] << lengthTrackingWithOffset
+
+ helper(fixedLength, 0, 1);
+ assert.compareArray(ToNumbers(taFull), [
+ 1,
+ 0,
+ 0,
+ 0
+ ]);
+ helper(fixedLengthWithOffset, 0, 2);
+ assert.compareArray(ToNumbers(taFull), [
+ 1,
+ 0,
+ 2,
+ 0
+ ]);
+ helper(lengthTracking, 1, 3);
+ assert.compareArray(ToNumbers(taFull), [
+ 1,
+ 3,
+ 2,
+ 0
+ ]);
+ helper(lengthTrackingWithOffset, 1, 4);
+ assert.compareArray(ToNumbers(taFull), [
+ 1,
+ 3,
+ 2,
+ 4
+ ]);
+ assert.throws(TypeError, () => {
+ helper(fixedLength, 4, 8);
+ });
+ assert.throws(TypeError, () => {
+ helper(fixedLengthWithOffset, 2, 8);
+ });
+ assert.throws(TypeError, () => {
+ helper(lengthTracking, 4, 8);
+ });
+ assert.throws(TypeError, () => {
+ helper(lengthTrackingWithOffset, 2, 8);
+ });
+
+ // Shrink so that fixed length TAs go out of bounds.
+ rab.resize(3 * ctor.BYTES_PER_ELEMENT);
+
+ // Orig. array: [1, 3, 2]
+ // [1, 3, 2, ...] << lengthTracking
+ // [2, ...] << lengthTrackingWithOffset
+
+ assert.throws(TypeError, () => {
+ helper(fixedLength, 0, 8);
+ });
+ assert.throws(TypeError, () => {
+ helper(fixedLengthWithOffset, 0, 8);
+ });
+ assert.compareArray(ToNumbers(taFull), [
+ 1,
+ 3,
+ 2
+ ]);
+ helper(lengthTracking, 0, 5);
+ assert.compareArray(ToNumbers(taFull), [
+ 5,
+ 3,
+ 2
+ ]);
+ helper(lengthTrackingWithOffset, 0, 6);
+ assert.compareArray(ToNumbers(taFull), [
+ 5,
+ 3,
+ 6
+ ]);
+
+ // Shrink so that the TAs with offset go out of bounds.
+ rab.resize(1 * ctor.BYTES_PER_ELEMENT);
+ assert.throws(TypeError, () => {
+ helper(fixedLength, 0, 8);
+ });
+ assert.throws(TypeError, () => {
+ helper(fixedLengthWithOffset, 0, 8);
+ });
+ assert.throws(TypeError, () => {
+ helper(lengthTrackingWithOffset, 0, 8);
+ });
+ assert.compareArray(ToNumbers(taFull), [5]);
+ helper(lengthTracking, 0, 7);
+ assert.compareArray(ToNumbers(taFull), [7]);
+
+ // Shrink to zero.
+ rab.resize(0);
+ assert.throws(TypeError, () => {
+ helper(fixedLength, 0, 8);
+ });
+ assert.throws(TypeError, () => {
+ helper(fixedLengthWithOffset, 0, 8);
+ });
+ assert.throws(TypeError, () => {
+ helper(lengthTracking, 0, 8);
+ });
+ assert.throws(TypeError, () => {
+ helper(lengthTrackingWithOffset, 0, 8);
+ });
+ assert.compareArray(ToNumbers(taFull), []);
+
+ // Grow so that all TAs are back in-bounds.
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ helper(fixedLength, 0, 9);
+ assert.compareArray(ToNumbers(taFull), [
+ 9,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]);
+ helper(fixedLengthWithOffset, 0, 10);
+ assert.compareArray(ToNumbers(taFull), [
+ 9,
+ 0,
+ 10,
+ 0,
+ 0,
+ 0
+ ]);
+ helper(lengthTracking, 1, 11);
+ assert.compareArray(ToNumbers(taFull), [
+ 9,
+ 11,
+ 10,
+ 0,
+ 0,
+ 0
+ ]);
+ helper(lengthTrackingWithOffset, 2, 12);
+ assert.compareArray(ToNumbers(taFull), [
+ 9,
+ 11,
+ 10,
+ 0,
+ 12,
+ 0
+ ]);
+
+ // Trying to define properties out of the fixed-length bounds throws.
+ assert.throws(TypeError, () => {
+ helper(fixedLength, 5, 13);
+ });
+ assert.throws(TypeError, () => {
+ helper(fixedLengthWithOffset, 3, 13);
+ });
+ assert.compareArray(ToNumbers(taFull), [
+ 9,
+ 11,
+ 10,
+ 0,
+ 12,
+ 0
+ ]);
+ helper(lengthTracking, 4, 14);
+ assert.compareArray(ToNumbers(taFull), [
+ 9,
+ 11,
+ 10,
+ 0,
+ 14,
+ 0
+ ]);
+ helper(lengthTrackingWithOffset, 3, 15);
+ assert.compareArray(ToNumbers(taFull), [
+ 9,
+ 11,
+ 10,
+ 0,
+ 14,
+ 15
+ ]);
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/object-define-property-parameter-conversion-grows-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/object-define-property-parameter-conversion-grows-strict.js
new file mode 100644
index 0000000000..36375f0a15
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/object-define-property-parameter-conversion-grows-strict.js
@@ -0,0 +1,117 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from ObjectDefinePropertyParameterConversionGrows test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+function ObjectDefinePropertyHelper(ta, index, value) {
+ if (ta instanceof BigInt64Array || ta instanceof BigUint64Array) {
+ Object.defineProperty(ta, index, { value: BigInt(value) });
+ } else {
+ Object.defineProperty(ta, index, { value: value });
+ }
+}
+
+const helper = ObjectDefinePropertyHelper;
+
+// Fixed length.
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ // Make fixedLength go OOB.
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ const evil = {
+ toString: () => {
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ return 0;
+ }
+ };
+ helper(fixedLength, evil, 8);
+ assert.compareArray(ToNumbers(fixedLength), [
+ 8,
+ 0,
+ 0,
+ 0
+ ]);
+}
+
+// Length tracking.
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const lengthTracking = new ctor(rab, 0);
+ const evil = {
+ toString: () => {
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ return 4; // Index valid after resize.
+ }
+ };
+ helper(lengthTracking, evil, 8);
+ assert.compareArray(ToNumbers(lengthTracking), [
+ 0,
+ 0,
+ 0,
+ 0,
+ 8,
+ 0
+ ]);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/object-define-property-parameter-conversion-shrinks-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/object-define-property-parameter-conversion-shrinks-strict.js
new file mode 100644
index 0000000000..b8eae3741b
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/object-define-property-parameter-conversion-shrinks-strict.js
@@ -0,0 +1,89 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from ObjectDefinePropertyParameterConversionShrinks test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function ObjectDefinePropertyHelper(ta, index, value) {
+ if (ta instanceof BigInt64Array || ta instanceof BigUint64Array) {
+ Object.defineProperty(ta, index, { value: BigInt(value) });
+ } else {
+ Object.defineProperty(ta, index, { value: value });
+ }
+}
+
+const helper = ObjectDefinePropertyHelper;
+
+// Fixed length.
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const evil = {
+ toString: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 0;
+ }
+ };
+ assert.throws(TypeError, () => {
+ helper(fixedLength, evil, 8);
+ });
+}
+
+// Length tracking.
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const lengthTracking = new ctor(rab, 0);
+ const evil = {
+ toString: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 3; // Index too large after resize.
+ }
+ };
+ assert.throws(TypeError, () => {
+ helper(lengthTracking, evil, 8);
+ });
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/object-freeze-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/object-freeze-strict.js
new file mode 100644
index 0000000000..f6a1a19945
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/object-freeze-strict.js
@@ -0,0 +1,91 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from ObjectFreeze test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+// Freezing non-OOB non-zero-length TAs throws.
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ const lengthTracking = new ctor(rab, 0);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ assert.throws(TypeError, () => {
+ Object.freeze(fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ Object.freeze(fixedLengthWithOffset);
+ });
+ assert.throws(TypeError, () => {
+ Object.freeze(lengthTracking);
+ });
+ assert.throws(TypeError, () => {
+ Object.freeze(lengthTrackingWithOffset);
+ });
+}
+// Freezing zero-length TAs doesn't throw.
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 0);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 0);
+ const lengthTrackingWithOffset = new ctor(rab, 4 * ctor.BYTES_PER_ELEMENT);
+ Object.freeze(fixedLength);
+ Object.freeze(fixedLengthWithOffset);
+ Object.freeze(lengthTrackingWithOffset);
+}
+// If the buffer has been resized to make length-tracking TAs zero-length,
+// freezing them also doesn't throw.
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const lengthTracking = new ctor(rab);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ Object.freeze(lengthTrackingWithOffset);
+ rab.resize(0 * ctor.BYTES_PER_ELEMENT);
+ Object.freeze(lengthTracking);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/oobbehaves-like-detached-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/oobbehaves-like-detached-strict.js
new file mode 100644
index 0000000000..58d7d9cdb3
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/oobbehaves-like-detached-strict.js
@@ -0,0 +1,35 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from OOBBehavesLikeDetached test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function ReadElement2(ta) {
+ return ta[2];
+}
+function HasElement2(ta) {
+ return 2 in ta;
+}
+const rab = CreateResizableArrayBuffer(16, 40);
+const i8a = new Int8Array(rab, 0, 4);
+i8a.__proto__ = { 2: 'wrong value' };
+i8a[2] = 10;
+assert.sameValue(ReadElement2(i8a), 10);
+assert(HasElement2(i8a));
+rab.resize(0);
+assert.sameValue(ReadElement2(i8a), undefined);
+assert(!HasElement2(i8a));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/out-of-bounds-typed-array-and-has-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/out-of-bounds-typed-array-and-has-strict.js
new file mode 100644
index 0000000000..41a553b8d5
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/out-of-bounds-typed-array-and-has-strict.js
@@ -0,0 +1,77 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from OutOfBoundsTypedArrayAndHas test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+for (let ctor of ctors) {
+ if (ctor.BYTES_PER_ELEMENT != 1) {
+ continue;
+ }
+ const rab = CreateResizableArrayBuffer(16, 40);
+ const array = new ctor(rab, 0, 4);
+ // Within-bounds read
+ for (let i = 0; i < 4; ++i) {
+ assert(i in array);
+ }
+ rab.resize(2);
+ // OOB read. If the RAB isn't large enough to fit the entire TypedArray,
+ // the length of the TypedArray is treated as 0.
+ for (let i = 0; i < 4; ++i) {
+ assert(!(i in array));
+ }
+ rab.resize(4);
+ // Within-bounds read
+ for (let i = 0; i < 4; ++i) {
+ assert(i in array);
+ }
+ rab.resize(40);
+ // Within-bounds read
+ for (let i = 0; i < 4; ++i) {
+ assert(i in array);
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/reverse-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/reverse-strict.js
new file mode 100644
index 0000000000..9e6ff827a1
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/reverse-strict.js
@@ -0,0 +1,273 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from Reverse test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+const TypedArrayReverseHelper = ta => {
+ ta.reverse();
+};
+
+const ArrayReverseHelper = ta => {
+ Array.prototype.reverse.call(ta);
+};
+
+function Reverse(reverseHelper, oobThrows) {
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ const lengthTracking = new ctor(rab, 0);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ const wholeArrayView = new ctor(rab);
+ function WriteData() {
+ // Write some data into the array.
+ for (let i = 0; i < wholeArrayView.length; ++i) {
+ WriteToTypedArray(wholeArrayView, i, 2 * i);
+ }
+ }
+ WriteData();
+
+ // Orig. array: [0, 2, 4, 6]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, ...] << lengthTracking
+ // [4, 6, ...] << lengthTrackingWithOffset
+
+ reverseHelper(fixedLength);
+ assert.compareArray(ToNumbers(wholeArrayView), [
+ 6,
+ 4,
+ 2,
+ 0
+ ]);
+ reverseHelper(fixedLengthWithOffset);
+ assert.compareArray(ToNumbers(wholeArrayView), [
+ 6,
+ 4,
+ 0,
+ 2
+ ]);
+ reverseHelper(lengthTracking);
+ assert.compareArray(ToNumbers(wholeArrayView), [
+ 2,
+ 0,
+ 4,
+ 6
+ ]);
+ reverseHelper(lengthTrackingWithOffset);
+ assert.compareArray(ToNumbers(wholeArrayView), [
+ 2,
+ 0,
+ 6,
+ 4
+ ]);
+
+ // Shrink so that fixed length TAs go out of bounds.
+ rab.resize(3 * ctor.BYTES_PER_ELEMENT);
+ WriteData();
+
+ // Orig. array: [0, 2, 4]
+ // [0, 2, 4, ...] << lengthTracking
+ // [4, ...] << lengthTrackingWithOffset
+
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ reverseHelper(fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ reverseHelper(fixedLengthWithOffset);
+ });
+ } else {
+ reverseHelper(fixedLength);
+ assert.compareArray(ToNumbers(wholeArrayView), [
+ 0,
+ 2,
+ 4
+ ]);
+ reverseHelper(fixedLengthWithOffset);
+ assert.compareArray(ToNumbers(wholeArrayView), [
+ 0,
+ 2,
+ 4
+ ]);
+ }
+ reverseHelper(lengthTracking);
+ assert.compareArray(ToNumbers(wholeArrayView), [
+ 4,
+ 2,
+ 0
+ ]);
+ reverseHelper(lengthTrackingWithOffset);
+ assert.compareArray(ToNumbers(wholeArrayView), [
+ 4,
+ 2,
+ 0
+ ]);
+
+ // Shrink so that the TAs with offset go out of bounds.
+ rab.resize(1 * ctor.BYTES_PER_ELEMENT);
+ WriteData();
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ reverseHelper(fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ reverseHelper(fixedLengthWithOffset);
+ });
+ assert.throws(TypeError, () => {
+ reverseHelper(lengthTrackingWithOffset);
+ });
+ } else {
+ reverseHelper(fixedLength);
+ assert.compareArray(ToNumbers(wholeArrayView), [0]);
+ reverseHelper(fixedLengthWithOffset);
+ assert.compareArray(ToNumbers(wholeArrayView), [0]);
+ reverseHelper(lengthTrackingWithOffset);
+ assert.compareArray(ToNumbers(wholeArrayView), [0]);
+ }
+ reverseHelper(lengthTracking);
+ assert.compareArray(ToNumbers(wholeArrayView), [0]);
+
+ // Shrink to zero.
+ rab.resize(0);
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ reverseHelper(fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ reverseHelper(fixedLengthWithOffset);
+ });
+ assert.throws(TypeError, () => {
+ reverseHelper(lengthTrackingWithOffset);
+ });
+ } else {
+ reverseHelper(fixedLength);
+ assert.compareArray(ToNumbers(wholeArrayView), []);
+ reverseHelper(fixedLengthWithOffset);
+ assert.compareArray(ToNumbers(wholeArrayView), []);
+ reverseHelper(lengthTrackingWithOffset);
+ assert.compareArray(ToNumbers(wholeArrayView), []);
+ }
+ reverseHelper(lengthTracking);
+ assert.compareArray(ToNumbers(wholeArrayView), []);
+
+ // Grow so that all TAs are back in-bounds.
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ WriteData();
+
+ // Orig. array: [0, 2, 4, 6, 8, 10]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, 8, 10, ...] << lengthTracking
+ // [4, 6, 8, 10, ...] << lengthTrackingWithOffset
+
+ reverseHelper(fixedLength);
+ assert.compareArray(ToNumbers(wholeArrayView), [
+ 6,
+ 4,
+ 2,
+ 0,
+ 8,
+ 10
+ ]);
+ reverseHelper(fixedLengthWithOffset);
+ assert.compareArray(ToNumbers(wholeArrayView), [
+ 6,
+ 4,
+ 0,
+ 2,
+ 8,
+ 10
+ ]);
+ reverseHelper(lengthTracking);
+ assert.compareArray(ToNumbers(wholeArrayView), [
+ 10,
+ 8,
+ 2,
+ 0,
+ 4,
+ 6
+ ]);
+ reverseHelper(lengthTrackingWithOffset);
+ assert.compareArray(ToNumbers(wholeArrayView), [
+ 10,
+ 8,
+ 6,
+ 4,
+ 0,
+ 2
+ ]);
+ }
+}
+
+Reverse(TypedArrayReverseHelper, true);
+Reverse(ArrayReverseHelper, false);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/set-grow-target-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/set-grow-target-mid-iteration-strict.js
new file mode 100644
index 0000000000..6be7a34e88
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/set-grow-target-mid-iteration-strict.js
@@ -0,0 +1,192 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from SetGrowTargetMidIteration test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+// Orig. array: [0, 2, 4, 6]
+// [0, 2, 4, 6] << fixedLength
+// [4, 6] << fixedLengthWithOffset
+// [0, 2, 4, 6, ...] << lengthTracking
+// [4, 6, ...] << lengthTrackingWithOffset
+function CreateRabForTest(ctor) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+ return rab;
+}
+let rab;
+// Resizing will happen when we're calling Get for the `resizeAt`:th data
+// element, but we haven't yet written it to the target.
+let resizeAt;
+let resizeTo;
+function CreateSourceProxy(length) {
+ let requestedIndices = [];
+ return new Proxy({}, {
+ get(target, prop, receiver) {
+ if (prop == 'length') {
+ return length;
+ }
+ requestedIndices.push(prop);
+ if (requestedIndices.length == resizeAt) {
+ rab.resize(resizeTo);
+ }
+ return true; // Can be converted to both BigInt and Number.
+ }
+ });
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ resizeAt = 2;
+ resizeTo = 6 * ctor.BYTES_PER_ELEMENT;
+ fixedLength.set(CreateSourceProxy(4));
+ assert.compareArray(ToNumbers(fixedLength), [
+ 1,
+ 1,
+ 1,
+ 1
+ ]);
+ assert.compareArray(ToNumbers(new ctor(rab)), [
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0
+ ]);
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ resizeAt = 1;
+ resizeTo = 6 * ctor.BYTES_PER_ELEMENT;
+ fixedLengthWithOffset.set(CreateSourceProxy(2));
+ assert.compareArray(ToNumbers(fixedLengthWithOffset), [
+ 1,
+ 1
+ ]);
+ assert.compareArray(ToNumbers(new ctor(rab)), [
+ 0,
+ 2,
+ 1,
+ 1,
+ 0,
+ 0
+ ]);
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ resizeAt = 2;
+ resizeTo = 6 * ctor.BYTES_PER_ELEMENT;
+ lengthTracking.set(CreateSourceProxy(2));
+ assert.compareArray(ToNumbers(lengthTracking), [
+ 1,
+ 1,
+ 4,
+ 6,
+ 0,
+ 0
+ ]);
+ assert.compareArray(ToNumbers(new ctor(rab)), [
+ 1,
+ 1,
+ 4,
+ 6,
+ 0,
+ 0
+ ]);
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ resizeAt = 1;
+ resizeTo = 6 * ctor.BYTES_PER_ELEMENT;
+ lengthTrackingWithOffset.set(CreateSourceProxy(2));
+ assert.compareArray(ToNumbers(lengthTrackingWithOffset), [
+ 1,
+ 1,
+ 0,
+ 0
+ ]);
+ assert.compareArray(ToNumbers(new ctor(rab)), [
+ 0,
+ 2,
+ 1,
+ 1,
+ 0,
+ 0
+ ]);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/set-shrink-target-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/set-shrink-target-mid-iteration-strict.js
new file mode 100644
index 0000000000..547606ee0b
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/set-shrink-target-mid-iteration-strict.js
@@ -0,0 +1,172 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from SetShrinkTargetMidIteration test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+// Orig. array: [0, 2, 4, 6]
+// [0, 2, 4, 6] << fixedLength
+// [4, 6] << fixedLengthWithOffset
+// [0, 2, 4, 6, ...] << lengthTracking
+// [4, 6, ...] << lengthTrackingWithOffset
+function CreateRabForTest(ctor) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+ return rab;
+}
+let rab;
+// Resizing will happen when we're calling Get for the `resizeAt`:th data
+// element, but we haven't yet written it to the target.
+let resizeAt;
+let resizeTo;
+function CreateSourceProxy(length) {
+ let requestedIndices = [];
+ return new Proxy({}, {
+ get(target, prop, receiver) {
+ if (prop == 'length') {
+ return length;
+ }
+ requestedIndices.push(prop);
+ if (requestedIndices.length == resizeAt) {
+ rab.resize(resizeTo);
+ }
+ return true; // Can be converted to both BigInt and Number.
+ }
+ });
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ resizeAt = 2;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ fixedLength.set(CreateSourceProxy(4));
+ assert.compareArray(ToNumbers(new ctor(rab)), [
+ 1,
+ 2,
+ 4
+ ]);
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ resizeAt = 2;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ fixedLengthWithOffset.set(CreateSourceProxy(2));
+ assert.compareArray(ToNumbers(new ctor(rab)), [
+ 0,
+ 2,
+ 1
+ ]);
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ resizeAt = 2;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ lengthTracking.set(CreateSourceProxy(2));
+ assert.compareArray(ToNumbers(lengthTracking), [
+ 1,
+ 1,
+ 4
+ ]);
+ assert.compareArray(ToNumbers(new ctor(rab)), [
+ 1,
+ 1,
+ 4
+ ]);
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ resizeAt = 2;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ lengthTrackingWithOffset.set(CreateSourceProxy(2));
+ assert.compareArray(ToNumbers(lengthTrackingWithOffset), [1]);
+ assert.compareArray(ToNumbers(new ctor(rab)), [
+ 0,
+ 2,
+ 1
+ ]);
+}
+
+// Length-tracking TA goes OOB because of the offset.
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ resizeAt = 1;
+ resizeTo = 1 * ctor.BYTES_PER_ELEMENT;
+ lengthTrackingWithOffset.set(CreateSourceProxy(2));
+ assert.compareArray(ToNumbers(new ctor(rab)), [0]);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/set-source-length-getter-grows-target-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/set-source-length-getter-grows-target-strict.js
new file mode 100644
index 0000000000..71bcc27ad4
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/set-source-length-getter-grows-target-strict.js
@@ -0,0 +1,137 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from SetSourceLengthGetterGrowsTarget test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+// Orig. array: [0, 2, 4, 6]
+// [0, 2, 4, 6] << fixedLength
+// [4, 6] << fixedLengthWithOffset
+// [0, 2, 4, 6, ...] << lengthTracking
+// [4, 6, ...] << lengthTrackingWithOffset
+function CreateRabForTest(ctor) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+ return rab;
+}
+let rab;
+let resizeTo;
+function CreateSourceProxy(length) {
+ return new Proxy({}, {
+ get(target, prop, receiver) {
+ if (prop == 'length') {
+ rab.resize(resizeTo);
+ return length;
+ }
+ return true; // Can be converted to both BigInt and Number.
+ }
+ });
+}
+
+// Test that we still throw for lengthTracking TAs if the source length is
+// too large, even though we resized in the length getter (we check against
+// the original length).
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ resizeTo = 6 * ctor.BYTES_PER_ELEMENT;
+ assert.throws(RangeError, () => {
+ lengthTracking.set(CreateSourceProxy(6));
+ });
+ assert.compareArray(ToNumbers(new ctor(rab)), [
+ 0,
+ 2,
+ 4,
+ 6,
+ 0,
+ 0
+ ]);
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ resizeTo = 6 * ctor.BYTES_PER_ELEMENT;
+ assert.throws(RangeError, () => {
+ lengthTrackingWithOffset.set(CreateSourceProxy(4));
+ });
+ assert.compareArray(ToNumbers(new ctor(rab)), [
+ 0,
+ 2,
+ 4,
+ 6,
+ 0,
+ 0
+ ]);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/set-source-length-getter-shrinks-target-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/set-source-length-getter-shrinks-target-strict.js
new file mode 100644
index 0000000000..8a06215b30
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/set-source-length-getter-shrinks-target-strict.js
@@ -0,0 +1,219 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from SetSourceLengthGetterShrinksTarget test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+// Orig. array: [0, 2, 4, 6]
+// [0, 2, 4, 6] << fixedLength
+// [4, 6] << fixedLengthWithOffset
+// [0, 2, 4, 6, ...] << lengthTracking
+// [4, 6, ...] << lengthTrackingWithOffset
+function CreateRabForTest(ctor) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+ return rab;
+}
+let rab;
+let resizeTo;
+function CreateSourceProxy(length) {
+ return new Proxy({}, {
+ get(target, prop, receiver) {
+ if (prop == 'length') {
+ rab.resize(resizeTo);
+ return length;
+ }
+ return true; // Can be converted to both BigInt and Number.
+ }
+ });
+}
+
+// Tests where the length getter returns a non-zero value -> these are nop if
+// the TA went OOB.
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ fixedLength.set(CreateSourceProxy(1));
+ assert.compareArray(ToNumbers(new ctor(rab)), [
+ 0,
+ 2,
+ 4
+ ]);
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ fixedLengthWithOffset.set(CreateSourceProxy(1));
+ assert.compareArray(ToNumbers(new ctor(rab)), [
+ 0,
+ 2,
+ 4
+ ]);
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ lengthTracking.set(CreateSourceProxy(1));
+ assert.compareArray(ToNumbers(lengthTracking), [
+ 1,
+ 2,
+ 4
+ ]);
+ assert.compareArray(ToNumbers(new ctor(rab)), [
+ 1,
+ 2,
+ 4
+ ]);
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ lengthTrackingWithOffset.set(CreateSourceProxy(1));
+ assert.compareArray(ToNumbers(lengthTrackingWithOffset), [1]);
+ assert.compareArray(ToNumbers(new ctor(rab)), [
+ 0,
+ 2,
+ 1
+ ]);
+}
+
+// Length-tracking TA goes OOB because of the offset.
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ resizeTo = 1 * ctor.BYTES_PER_ELEMENT;
+ lengthTrackingWithOffset.set(CreateSourceProxy(1));
+ assert.compareArray(ToNumbers(new ctor(rab)), [0]);
+}
+
+// Tests where the length getter returns a zero -> these don't throw even if
+// the TA went OOB.
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ fixedLength.set(CreateSourceProxy(0));
+ assert.compareArray(ToNumbers(new ctor(rab)), [
+ 0,
+ 2,
+ 4
+ ]);
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ fixedLengthWithOffset.set(CreateSourceProxy(0));
+ assert.compareArray(ToNumbers(new ctor(rab)), [
+ 0,
+ 2,
+ 4
+ ]);
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ lengthTracking.set(CreateSourceProxy(0));
+ assert.compareArray(ToNumbers(new ctor(rab)), [
+ 0,
+ 2,
+ 4
+ ]);
+}
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ lengthTrackingWithOffset.set(CreateSourceProxy(0));
+ assert.compareArray(ToNumbers(new ctor(rab)), [
+ 0,
+ 2,
+ 4
+ ]);
+}
+
+// Length-tracking TA goes OOB because of the offset.
+for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ resizeTo = 1 * ctor.BYTES_PER_ELEMENT;
+ lengthTrackingWithOffset.set(CreateSourceProxy(0));
+ assert.compareArray(ToNumbers(new ctor(rab)), [0]);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/set-with-resizable-source-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/set-with-resizable-source-strict.js
new file mode 100644
index 0000000000..f56a1e86f4
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/set-with-resizable-source-strict.js
@@ -0,0 +1,292 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from SetWithResizableSource test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function IsBigIntTypedArray(ta) {
+ return ta instanceof BigInt64Array || ta instanceof BigUint64Array;
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+function SetHelper(target, source, offset) {
+ if (target instanceof BigInt64Array || target instanceof BigUint64Array) {
+ const bigIntSource = [];
+ for (const s of source) {
+ bigIntSource.push(BigInt(s));
+ }
+ source = bigIntSource;
+ }
+ if (offset == undefined) {
+ return target.set(source);
+ }
+ return target.set(source, offset);
+}
+
+for (let targetIsResizable of [
+ false,
+ true
+ ]) {
+ for (let targetCtor of ctors) {
+ for (let sourceCtor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * sourceCtor.BYTES_PER_ELEMENT, 8 * sourceCtor.BYTES_PER_ELEMENT);
+ const fixedLength = new sourceCtor(rab, 0, 4);
+ const fixedLengthWithOffset = new sourceCtor(rab, 2 * sourceCtor.BYTES_PER_ELEMENT, 2);
+ const lengthTracking = new sourceCtor(rab, 0);
+ const lengthTrackingWithOffset = new sourceCtor(rab, 2 * sourceCtor.BYTES_PER_ELEMENT);
+
+ // Write some data into the array.
+ const taFull = new sourceCtor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taFull, i, i + 1);
+ }
+
+ // Orig. array: [1, 2, 3, 4]
+ // [1, 2, 3, 4] << fixedLength
+ // [3, 4] << fixedLengthWithOffset
+ // [1, 2, 3, 4, ...] << lengthTracking
+ // [3, 4, ...] << lengthTrackingWithOffset
+
+ const targetAb = targetIsResizable ? new ArrayBuffer(6 * targetCtor.BYTES_PER_ELEMENT) : new ArrayBuffer(6 * targetCtor.BYTES_PER_ELEMENT, { maxByteLength: 8 * targetCtor.BYTES_PER_ELEMENT });
+ const target = new targetCtor(targetAb);
+ if (IsBigIntTypedArray(target) != IsBigIntTypedArray(taFull)) {
+ // Can't mix BigInt and non-BigInt types.
+ continue;
+ }
+ SetHelper(target, fixedLength);
+ assert.compareArray(ToNumbers(target), [
+ 1,
+ 2,
+ 3,
+ 4,
+ 0,
+ 0
+ ]);
+ SetHelper(target, fixedLengthWithOffset);
+ assert.compareArray(ToNumbers(target), [
+ 3,
+ 4,
+ 3,
+ 4,
+ 0,
+ 0
+ ]);
+ SetHelper(target, lengthTracking, 1);
+ assert.compareArray(ToNumbers(target), [
+ 3,
+ 1,
+ 2,
+ 3,
+ 4,
+ 0
+ ]);
+ SetHelper(target, lengthTrackingWithOffset, 1);
+ assert.compareArray(ToNumbers(target), [
+ 3,
+ 3,
+ 4,
+ 3,
+ 4,
+ 0
+ ]);
+
+ // Shrink so that fixed length TAs go out of bounds.
+ rab.resize(3 * sourceCtor.BYTES_PER_ELEMENT);
+
+ // Orig. array: [1, 2, 3]
+ // [1, 2, 3, ...] << lengthTracking
+ // [3, ...] << lengthTrackingWithOffset
+
+ assert.throws(TypeError, () => {
+ SetHelper(target, fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ SetHelper(target, fixedLengthWithOffset);
+ });
+ assert.compareArray(ToNumbers(target), [
+ 3,
+ 3,
+ 4,
+ 3,
+ 4,
+ 0
+ ]);
+ SetHelper(target, lengthTracking);
+ assert.compareArray(ToNumbers(target), [
+ 1,
+ 2,
+ 3,
+ 3,
+ 4,
+ 0
+ ]);
+ SetHelper(target, lengthTrackingWithOffset);
+ assert.compareArray(ToNumbers(target), [
+ 3,
+ 2,
+ 3,
+ 3,
+ 4,
+ 0
+ ]);
+
+ // Shrink so that the TAs with offset go out of bounds.
+ rab.resize(1 * sourceCtor.BYTES_PER_ELEMENT);
+ assert.throws(TypeError, () => {
+ SetHelper(target, fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ SetHelper(target, fixedLengthWithOffset);
+ });
+ assert.throws(TypeError, () => {
+ SetHelper(target, lengthTrackingWithOffset);
+ });
+ SetHelper(target, lengthTracking, 3);
+ assert.compareArray(ToNumbers(target), [
+ 3,
+ 2,
+ 3,
+ 1,
+ 4,
+ 0
+ ]);
+
+ // Shrink to zero.
+ rab.resize(0);
+ assert.throws(TypeError, () => {
+ SetHelper(target, fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ SetHelper(target, fixedLengthWithOffset);
+ });
+ assert.throws(TypeError, () => {
+ SetHelper(target, lengthTrackingWithOffset);
+ });
+ SetHelper(target, lengthTracking, 4);
+ assert.compareArray(ToNumbers(target), [
+ 3,
+ 2,
+ 3,
+ 1,
+ 4,
+ 0
+ ]);
+
+ // Grow so that all TAs are back in-bounds.
+ rab.resize(6 * sourceCtor.BYTES_PER_ELEMENT);
+ for (let i = 0; i < 6; ++i) {
+ WriteToTypedArray(taFull, i, i + 1);
+ }
+
+ // Orig. array: [1, 2, 3, 4, 5, 6]
+ // [1, 2, 3, 4] << fixedLength
+ // [3, 4] << fixedLengthWithOffset
+ // [1, 2, 3, 4, 5, 6, ...] << lengthTracking
+ // [3, 4, 5, 6, ...] << lengthTrackingWithOffset
+
+ SetHelper(target, fixedLength);
+ assert.compareArray(ToNumbers(target), [
+ 1,
+ 2,
+ 3,
+ 4,
+ 4,
+ 0
+ ]);
+ SetHelper(target, fixedLengthWithOffset);
+ assert.compareArray(ToNumbers(target), [
+ 3,
+ 4,
+ 3,
+ 4,
+ 4,
+ 0
+ ]);
+ SetHelper(target, lengthTracking, 0);
+ assert.compareArray(ToNumbers(target), [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6
+ ]);
+ SetHelper(target, lengthTrackingWithOffset, 1);
+ assert.compareArray(ToNumbers(target), [
+ 1,
+ 3,
+ 4,
+ 5,
+ 6,
+ 6
+ ]);
+ }
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/set-with-resizable-target-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/set-with-resizable-target-strict.js
new file mode 100644
index 0000000000..3bacca2986
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/set-with-resizable-target-strict.js
@@ -0,0 +1,580 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from SetWithResizableTarget test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+function SetHelper(target, source, offset) {
+ if (target instanceof BigInt64Array || target instanceof BigUint64Array) {
+ const bigIntSource = [];
+ for (const s of source) {
+ bigIntSource.push(BigInt(s));
+ }
+ source = bigIntSource;
+ }
+ if (offset == undefined) {
+ return target.set(source);
+ }
+ return target.set(source, offset);
+}
+
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ const lengthTracking = new ctor(rab, 0);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ const taFull = new ctor(rab);
+
+ // Orig. array: [0, 0, 0, 0]
+ // [0, 0, 0, 0] << fixedLength
+ // [0, 0] << fixedLengthWithOffset
+ // [0, 0, 0, 0, ...] << lengthTracking
+ // [0, 0, ...] << lengthTrackingWithOffset
+
+ // For making sure we're not calling the source length or element getters
+ // if the target is OOB.
+ const throwingProxy = new Proxy({}, {
+ get(target, prop, receiver) {
+ throw new Error('Called getter for ' + prop);
+ }
+ });
+ SetHelper(fixedLength, [
+ 1,
+ 2
+ ]);
+ assert.compareArray(ToNumbers(taFull), [
+ 1,
+ 2,
+ 0,
+ 0
+ ]);
+ SetHelper(fixedLength, [
+ 3,
+ 4
+ ], 1);
+ assert.compareArray(ToNumbers(taFull), [
+ 1,
+ 3,
+ 4,
+ 0
+ ]);
+ assert.throws(RangeError, () => {
+ SetHelper(fixedLength, [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]);
+ });
+ assert.throws(RangeError, () => {
+ SetHelper(fixedLength, [
+ 0,
+ 0,
+ 0,
+ 0
+ ], 1);
+ });
+ assert.compareArray(ToNumbers(taFull), [
+ 1,
+ 3,
+ 4,
+ 0
+ ]);
+ SetHelper(fixedLengthWithOffset, [
+ 5,
+ 6
+ ]);
+ assert.compareArray(ToNumbers(taFull), [
+ 1,
+ 3,
+ 5,
+ 6
+ ]);
+ SetHelper(fixedLengthWithOffset, [7], 1);
+ assert.compareArray(ToNumbers(taFull), [
+ 1,
+ 3,
+ 5,
+ 7
+ ]);
+ assert.throws(RangeError, () => {
+ SetHelper(fixedLengthWithOffset, [
+ 0,
+ 0,
+ 0
+ ]);
+ });
+ assert.throws(RangeError, () => {
+ SetHelper(fixedLengthWithOffset, [
+ 0,
+ 0
+ ], 1);
+ });
+ assert.compareArray(ToNumbers(taFull), [
+ 1,
+ 3,
+ 5,
+ 7
+ ]);
+ SetHelper(lengthTracking, [
+ 8,
+ 9
+ ]);
+ assert.compareArray(ToNumbers(taFull), [
+ 8,
+ 9,
+ 5,
+ 7
+ ]);
+ SetHelper(lengthTracking, [
+ 10,
+ 11
+ ], 1);
+ assert.compareArray(ToNumbers(taFull), [
+ 8,
+ 10,
+ 11,
+ 7
+ ]);
+ assert.throws(RangeError, () => {
+ SetHelper(lengthTracking, [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]);
+ });
+ assert.throws(RangeError, () => {
+ SetHelper(lengthTracking, [
+ 0,
+ 0,
+ 0,
+ 0
+ ], 1);
+ });
+ assert.compareArray(ToNumbers(taFull), [
+ 8,
+ 10,
+ 11,
+ 7
+ ]);
+ SetHelper(lengthTrackingWithOffset, [
+ 12,
+ 13
+ ]);
+ assert.compareArray(ToNumbers(taFull), [
+ 8,
+ 10,
+ 12,
+ 13
+ ]);
+ SetHelper(lengthTrackingWithOffset, [14], 1);
+ assert.compareArray(ToNumbers(taFull), [
+ 8,
+ 10,
+ 12,
+ 14
+ ]);
+ assert.throws(RangeError, () => {
+ SetHelper(lengthTrackingWithOffset, [
+ 0,
+ 0,
+ 0
+ ]);
+ });
+ assert.throws(RangeError, () => {
+ SetHelper(lengthTrackingWithOffset, [
+ 0,
+ 0
+ ], 1);
+ });
+ assert.compareArray(ToNumbers(taFull), [
+ 8,
+ 10,
+ 12,
+ 14
+ ]);
+
+ // Shrink so that fixed length TAs go out of bounds.
+ rab.resize(3 * ctor.BYTES_PER_ELEMENT);
+
+ // Orig. array: [8, 10, 12]
+ // [8, 10, 12, ...] << lengthTracking
+ // [12, ...] << lengthTrackingWithOffset
+
+ assert.throws(TypeError, () => {
+ SetHelper(fixedLength, throwingProxy);
+ });
+ assert.throws(TypeError, () => {
+ SetHelper(fixedLengthWithOffset, throwingProxy);
+ });
+ assert.compareArray(ToNumbers(taFull), [
+ 8,
+ 10,
+ 12
+ ]);
+ SetHelper(lengthTracking, [
+ 15,
+ 16
+ ]);
+ assert.compareArray(ToNumbers(taFull), [
+ 15,
+ 16,
+ 12
+ ]);
+ SetHelper(lengthTracking, [
+ 17,
+ 18
+ ], 1);
+ assert.compareArray(ToNumbers(taFull), [
+ 15,
+ 17,
+ 18
+ ]);
+ assert.throws(RangeError, () => {
+ SetHelper(lengthTracking, [
+ 0,
+ 0,
+ 0,
+ 0
+ ]);
+ });
+ assert.throws(RangeError, () => {
+ SetHelper(lengthTracking, [
+ 0,
+ 0,
+ 0
+ ], 1);
+ });
+ assert.compareArray(ToNumbers(taFull), [
+ 15,
+ 17,
+ 18
+ ]);
+ SetHelper(lengthTrackingWithOffset, [19]);
+ assert.compareArray(ToNumbers(taFull), [
+ 15,
+ 17,
+ 19
+ ]);
+ assert.throws(RangeError, () => {
+ SetHelper(lengthTrackingWithOffset, [
+ 0,
+ 0
+ ]);
+ });
+ assert.throws(RangeError, () => {
+ SetHelper(lengthTrackingWithOffset, [0], 1);
+ });
+ assert.compareArray(ToNumbers(taFull), [
+ 15,
+ 17,
+ 19
+ ]);
+
+ // Shrink so that the TAs with offset go out of bounds.
+ rab.resize(1 * ctor.BYTES_PER_ELEMENT);
+ assert.throws(TypeError, () => {
+ SetHelper(fixedLength, throwingProxy);
+ });
+ assert.throws(TypeError, () => {
+ SetHelper(fixedLengthWithOffset, throwingProxy);
+ });
+ assert.throws(TypeError, () => {
+ SetHelper(lengthTrackingWithOffset, throwingProxy);
+ });
+ assert.compareArray(ToNumbers(taFull), [15]);
+ SetHelper(lengthTracking, [20]);
+ assert.compareArray(ToNumbers(taFull), [20]);
+
+ // Shrink to zero.
+ rab.resize(0);
+ assert.throws(TypeError, () => {
+ SetHelper(fixedLength, throwingProxy);
+ });
+ assert.throws(TypeError, () => {
+ SetHelper(fixedLengthWithOffset, throwingProxy);
+ });
+ assert.throws(TypeError, () => {
+ SetHelper(lengthTrackingWithOffset, throwingProxy);
+ });
+ assert.throws(RangeError, () => {
+ SetHelper(lengthTracking, [0]);
+ });
+ assert.compareArray(ToNumbers(taFull), []);
+
+ // Grow so that all TAs are back in-bounds.
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+
+ // Orig. array: [0, 0, 0, 0, 0, 0]
+ // [0, 0, 0, 0] << fixedLength
+ // [0, 0] << fixedLengthWithOffset
+ // [0, 0, 0, 0, 0, 0, ...] << lengthTracking
+ // [0, 0, 0, 0, ...] << lengthTrackingWithOffset
+ SetHelper(fixedLength, [
+ 21,
+ 22
+ ]);
+ assert.compareArray(ToNumbers(taFull), [
+ 21,
+ 22,
+ 0,
+ 0,
+ 0,
+ 0
+ ]);
+ SetHelper(fixedLength, [
+ 23,
+ 24
+ ], 1);
+ assert.compareArray(ToNumbers(taFull), [
+ 21,
+ 23,
+ 24,
+ 0,
+ 0,
+ 0
+ ]);
+ assert.throws(RangeError, () => {
+ SetHelper(fixedLength, [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]);
+ });
+ assert.throws(RangeError, () => {
+ SetHelper(fixedLength, [
+ 0,
+ 0,
+ 0,
+ 0
+ ], 1);
+ });
+ assert.compareArray(ToNumbers(taFull), [
+ 21,
+ 23,
+ 24,
+ 0,
+ 0,
+ 0
+ ]);
+ SetHelper(fixedLengthWithOffset, [
+ 25,
+ 26
+ ]);
+ assert.compareArray(ToNumbers(taFull), [
+ 21,
+ 23,
+ 25,
+ 26,
+ 0,
+ 0
+ ]);
+ SetHelper(fixedLengthWithOffset, [27], 1);
+ assert.compareArray(ToNumbers(taFull), [
+ 21,
+ 23,
+ 25,
+ 27,
+ 0,
+ 0
+ ]);
+ assert.throws(RangeError, () => {
+ SetHelper(fixedLengthWithOffset, [
+ 0,
+ 0,
+ 0
+ ]);
+ });
+ assert.throws(RangeError, () => {
+ SetHelper(fixedLengthWithOffset, [
+ 0,
+ 0
+ ], 1);
+ });
+ assert.compareArray(ToNumbers(taFull), [
+ 21,
+ 23,
+ 25,
+ 27,
+ 0,
+ 0
+ ]);
+ SetHelper(lengthTracking, [
+ 28,
+ 29,
+ 30,
+ 31,
+ 32,
+ 33
+ ]);
+ assert.compareArray(ToNumbers(taFull), [
+ 28,
+ 29,
+ 30,
+ 31,
+ 32,
+ 33
+ ]);
+ SetHelper(lengthTracking, [
+ 34,
+ 35,
+ 36,
+ 37,
+ 38
+ ], 1);
+ assert.compareArray(ToNumbers(taFull), [
+ 28,
+ 34,
+ 35,
+ 36,
+ 37,
+ 38
+ ]);
+ assert.throws(RangeError, () => {
+ SetHelper(lengthTracking, [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]);
+ });
+ assert.throws(RangeError, () => {
+ SetHelper(lengthTracking, [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ], 1);
+ });
+ assert.compareArray(ToNumbers(taFull), [
+ 28,
+ 34,
+ 35,
+ 36,
+ 37,
+ 38
+ ]);
+ SetHelper(lengthTrackingWithOffset, [
+ 39,
+ 40,
+ 41,
+ 42
+ ]);
+ assert.compareArray(ToNumbers(taFull), [
+ 28,
+ 34,
+ 39,
+ 40,
+ 41,
+ 42
+ ]);
+ SetHelper(lengthTrackingWithOffset, [
+ 43,
+ 44,
+ 45
+ ], 1);
+ assert.compareArray(ToNumbers(taFull), [
+ 28,
+ 34,
+ 39,
+ 43,
+ 44,
+ 45
+ ]);
+ assert.throws(RangeError, () => {
+ SetHelper(lengthTrackingWithOffset, [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]);
+ });
+ assert.throws(RangeError, () => {
+ SetHelper(lengthTrackingWithOffset, [
+ 0,
+ 0,
+ 0,
+ 0
+ ], 1);
+ });
+ assert.compareArray(ToNumbers(taFull), [
+ 28,
+ 34,
+ 39,
+ 43,
+ 44,
+ 45
+ ]);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/shell.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/shell.js
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/slice-parameter-conversion-grows-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/slice-parameter-conversion-grows-strict.js
new file mode 100644
index 0000000000..c3fff45e27
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/slice-parameter-conversion-grows-strict.js
@@ -0,0 +1,107 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from SliceParameterConversionGrows test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+const TypedArraySliceHelper = (ta, ...rest) => {
+ return ta.slice(...rest);
+};
+
+const ArraySliceHelper = (ta, ...rest) => {
+ return Array.prototype.slice.call(ta, ...rest);
+};
+
+function SliceParameterConversionGrows(sliceHelper) {
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const lengthTracking = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(lengthTracking, i, i + 1);
+ }
+ const evil = {
+ valueOf: () => {
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ return 0;
+ }
+ };
+ assert.compareArray(ToNumbers(sliceHelper(lengthTracking, evil)), [
+ 1,
+ 2,
+ 3,
+ 4
+ ]);
+ assert.sameValue(rab.byteLength, 6 * ctor.BYTES_PER_ELEMENT);
+ }
+}
+
+SliceParameterConversionGrows(TypedArraySliceHelper);
+SliceParameterConversionGrows(ArraySliceHelper);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/slice-parameter-conversion-shrinks-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/slice-parameter-conversion-shrinks-strict.js
new file mode 100644
index 0000000000..9f46f9e17e
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/slice-parameter-conversion-shrinks-strict.js
@@ -0,0 +1,108 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from SliceParameterConversionShrinks test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const evil = {
+ valueOf: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 0;
+ }
+ };
+ assert.throws(TypeError, () => {
+ fixedLength.slice(evil);
+ });
+ assert.sameValue(rab.byteLength, 2 * ctor.BYTES_PER_ELEMENT);
+}
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const lengthTracking = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(lengthTracking, i, i + 1);
+ }
+ const evil = {
+ valueOf: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 0;
+ }
+ };
+ assert.compareArray(ToNumbers(lengthTracking.slice(evil)), [
+ 1,
+ 2,
+ 0,
+ 0
+ ]);
+ assert.sameValue(rab.byteLength, 2 * ctor.BYTES_PER_ELEMENT);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/slice-species-create-resizes-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/slice-species-create-resizes-strict.js
new file mode 100644
index 0000000000..d71dd89186
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/slice-species-create-resizes-strict.js
@@ -0,0 +1,191 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from SliceSpeciesCreateResizes test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+// The corresponding test for Array.prototype.slice is not possible, since it
+// doesn't call the species constructor if the "original array" is not an Array.
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ let resizeWhenConstructorCalled = false;
+ class MyArray extends ctor {
+ constructor(...params) {
+ super(...params);
+ if (resizeWhenConstructorCalled) {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ }
+ }
+ }
+ ;
+ const fixedLength = new MyArray(rab, 0, 4);
+ resizeWhenConstructorCalled = true;
+ assert.throws(TypeError, () => {
+ fixedLength.slice();
+ });
+ assert.sameValue(rab.byteLength, 2 * ctor.BYTES_PER_ELEMENT);
+}
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 1);
+ }
+ let resizeWhenConstructorCalled = false;
+ class MyArray extends ctor {
+ constructor(...params) {
+ super(...params);
+ if (resizeWhenConstructorCalled) {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ }
+ }
+ }
+ ;
+ const lengthTracking = new MyArray(rab);
+ resizeWhenConstructorCalled = true;
+ const a = lengthTracking.slice();
+ assert.sameValue(rab.byteLength, 2 * ctor.BYTES_PER_ELEMENT);
+ // The length of the resulting TypedArray is determined before
+ // TypedArraySpeciesCreate is called, and it doesn't change.
+ assert.sameValue(a.length, 4);
+ assert.compareArray(ToNumbers(a), [
+ 1,
+ 1,
+ 0,
+ 0
+ ]);
+}
+
+// Test that the (start, end) parameters are computed based on the original
+// length.
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 1);
+ }
+ let resizeWhenConstructorCalled = false;
+ class MyArray extends ctor {
+ constructor(...params) {
+ super(...params);
+ if (resizeWhenConstructorCalled) {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ }
+ }
+ }
+ ;
+ const lengthTracking = new MyArray(rab);
+ resizeWhenConstructorCalled = true;
+ const a = lengthTracking.slice(-3, -1);
+ assert.sameValue(rab.byteLength, 2 * ctor.BYTES_PER_ELEMENT);
+ // The length of the resulting TypedArray is determined before
+ // TypedArraySpeciesCreate is called, and it doesn't change.
+ assert.sameValue(a.length, 2);
+ assert.compareArray(ToNumbers(a), [
+ 1,
+ 0
+ ]);
+}
+
+// Test where the buffer gets resized "between elements".
+{
+ const rab = CreateResizableArrayBuffer(8, 16);
+ const taWrite = new Uint8Array(rab);
+ for (let i = 0; i < 8; ++i) {
+ WriteToTypedArray(taWrite, i, 255);
+ }
+ let resizeWhenConstructorCalled = false;
+ class MyArray extends Uint16Array {
+ constructor(...params) {
+ super(...params);
+ if (resizeWhenConstructorCalled) {
+ rab.resize(5);
+ }
+ }
+ }
+ ;
+ const lengthTracking = new MyArray(rab);
+ assert.compareArray(ToNumbers(lengthTracking), [
+ 65535,
+ 65535,
+ 65535,
+ 65535
+ ]);
+ resizeWhenConstructorCalled = true;
+ const a = lengthTracking.slice();
+ assert.sameValue(rab.byteLength, 5);
+ assert.sameValue(a.length, 4);
+ assert.sameValue(a[0], 65535);
+ assert.sameValue(a[1], 65535);
+ assert.sameValue(a[2], 0);
+ assert.sameValue(a[3], 0);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/slice-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/slice-strict.js
new file mode 100644
index 0000000000..17e8f7551d
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/slice-strict.js
@@ -0,0 +1,206 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from Slice test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ const lengthTracking = new ctor(rab, 0);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, i);
+ }
+ const fixedLengthSlice = fixedLength.slice();
+ assert.compareArray(ToNumbers(fixedLengthSlice), [
+ 0,
+ 1,
+ 2,
+ 3
+ ]);
+ assert(!fixedLengthSlice.buffer.resizable);
+ const fixedLengthWithOffsetSlice = fixedLengthWithOffset.slice();
+ assert.compareArray(ToNumbers(fixedLengthWithOffsetSlice), [
+ 2,
+ 3
+ ]);
+ assert(!fixedLengthWithOffsetSlice.buffer.resizable);
+ const lengthTrackingSlice = lengthTracking.slice();
+ assert.compareArray(ToNumbers(lengthTrackingSlice), [
+ 0,
+ 1,
+ 2,
+ 3
+ ]);
+ assert(!lengthTrackingSlice.buffer.resizable);
+ const lengthTrackingWithOffsetSlice = lengthTrackingWithOffset.slice();
+ assert.compareArray(ToNumbers(lengthTrackingWithOffsetSlice), [
+ 2,
+ 3
+ ]);
+ assert(!lengthTrackingWithOffsetSlice.buffer.resizable);
+
+ // Shrink so that fixed length TAs go out of bounds.
+ rab.resize(3 * ctor.BYTES_PER_ELEMENT);
+ assert.throws(TypeError, () => {
+ fixedLength.slice();
+ });
+ assert.throws(TypeError, () => {
+ fixedLengthWithOffset.slice();
+ });
+ assert.compareArray(ToNumbers(lengthTracking.slice()), [
+ 0,
+ 1,
+ 2
+ ]);
+ assert.compareArray(ToNumbers(lengthTrackingWithOffset.slice()), [2]);
+
+ // Shrink so that the TAs with offset go out of bounds.
+ rab.resize(1 * ctor.BYTES_PER_ELEMENT);
+ assert.throws(TypeError, () => {
+ fixedLength.slice();
+ });
+ assert.throws(TypeError, () => {
+ fixedLengthWithOffset.slice();
+ });
+ assert.compareArray(ToNumbers(lengthTracking.slice()), [0]);
+ assert.throws(TypeError, () => {
+ lengthTrackingWithOffset.slice();
+ });
+
+ // Shrink to zero.
+ rab.resize(0);
+ assert.throws(TypeError, () => {
+ fixedLength.slice();
+ });
+ assert.throws(TypeError, () => {
+ fixedLengthWithOffset.slice();
+ });
+ assert.compareArray(ToNumbers(lengthTracking.slice()), []);
+ assert.throws(TypeError, () => {
+ lengthTrackingWithOffset.slice();
+ });
+
+ // Verify that the previously created slices aren't affected by the
+ // shrinking.
+ assert.compareArray(ToNumbers(fixedLengthSlice), [
+ 0,
+ 1,
+ 2,
+ 3
+ ]);
+ assert.compareArray(ToNumbers(fixedLengthWithOffsetSlice), [
+ 2,
+ 3
+ ]);
+ assert.compareArray(ToNumbers(lengthTrackingSlice), [
+ 0,
+ 1,
+ 2,
+ 3
+ ]);
+ assert.compareArray(ToNumbers(lengthTrackingWithOffsetSlice), [
+ 2,
+ 3
+ ]);
+
+ // Grow so that all TAs are back in-bounds. New memory is zeroed.
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ assert.compareArray(ToNumbers(fixedLength.slice()), [
+ 0,
+ 0,
+ 0,
+ 0
+ ]);
+ assert.compareArray(ToNumbers(fixedLengthWithOffset.slice()), [
+ 0,
+ 0
+ ]);
+ assert.compareArray(ToNumbers(lengthTracking.slice()), [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]);
+ assert.compareArray(ToNumbers(lengthTrackingWithOffset.slice()), [
+ 0,
+ 0,
+ 0,
+ 0
+ ]);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/some-grow-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/some-grow-mid-iteration-strict.js
new file mode 100644
index 0000000000..38e9aaca7a
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/some-grow-mid-iteration-strict.js
@@ -0,0 +1,154 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from SomeGrowMidIteration test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+const TypedArraySomeHelper = (ta, ...rest) => {
+ return ta.some(...rest);
+};
+
+const ArraySomeHelper = (ta, ...rest) => {
+ return Array.prototype.some.call(ta, ...rest);
+};
+
+function SomeGrowMidIteration(someHelper) {
+ // Orig. array: [0, 2, 4, 6]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, ...] << lengthTracking
+ // [4, 6, ...] << lengthTrackingWithOffset
+ function CreateRabForTest(ctor) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+ return rab;
+ }
+ let values = [];
+ let rab;
+ let resizeAfter;
+ let resizeTo;
+ function CollectValuesAndResize(n) {
+ if (typeof n == 'bigint') {
+ values.push(Number(n));
+ } else {
+ values.push(n);
+ }
+ if (values.length == resizeAfter) {
+ rab.resize(resizeTo);
+ }
+ return false;
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ values = [];
+ resizeAfter = 2;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert(!someHelper(fixedLength, CollectValuesAndResize));
+ assert.compareArray(values, [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ values = [];
+ resizeAfter = 1;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert(!someHelper(fixedLengthWithOffset, CollectValuesAndResize));
+ assert.compareArray(values, [
+ 4,
+ 6
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ values = [];
+ resizeAfter = 2;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert(!someHelper(lengthTracking, CollectValuesAndResize));
+ assert.compareArray(values, [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ values = [];
+ rab = rab;
+ resizeAfter = 1;
+ resizeTo = 5 * ctor.BYTES_PER_ELEMENT;
+ assert(!someHelper(lengthTrackingWithOffset, CollectValuesAndResize));
+ assert.compareArray(values, [
+ 4,
+ 6
+ ]);
+ }
+}
+
+SomeGrowMidIteration(TypedArraySomeHelper);
+SomeGrowMidIteration(ArraySomeHelper);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/some-shrink-mid-iteration-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/some-shrink-mid-iteration-strict.js
new file mode 100644
index 0000000000..990e32c561
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/some-shrink-mid-iteration-strict.js
@@ -0,0 +1,176 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from SomeShrinkMidIteration test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+const TypedArraySomeHelper = (ta, ...rest) => {
+ return ta.some(...rest);
+};
+
+const ArraySomeHelper = (ta, ...rest) => {
+ return Array.prototype.some.call(ta, ...rest);
+};
+
+function SomeShrinkMidIteration(someHelper, hasUndefined) {
+ // Orig. array: [0, 2, 4, 6]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, ...] << lengthTracking
+ // [4, 6, ...] << lengthTrackingWithOffset
+ function CreateRabForTest(ctor) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+ return rab;
+ }
+ let values;
+ let rab;
+ let resizeAfter;
+ let resizeTo;
+ function CollectValuesAndResize(n) {
+ if (typeof n == 'bigint') {
+ values.push(Number(n));
+ } else {
+ values.push(n);
+ }
+ if (values.length == resizeAfter) {
+ rab.resize(resizeTo);
+ }
+ return false;
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ values = [];
+ resizeAfter = 2;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert(!someHelper(fixedLength, CollectValuesAndResize));
+ if (hasUndefined) {
+ assert.compareArray(values, [
+ 0,
+ 2,
+ undefined,
+ undefined
+ ]);
+ } else {
+ assert.compareArray(values, [
+ 0,
+ 2
+ ]);
+ }
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ values = [];
+ resizeAfter = 1;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert(!someHelper(fixedLengthWithOffset, CollectValuesAndResize));
+ if (hasUndefined) {
+ assert.compareArray(values, [
+ 4,
+ undefined
+ ]);
+ } else {
+ assert.compareArray(values, [4]);
+ }
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ values = [];
+ resizeAfter = 2;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert(!someHelper(lengthTracking, CollectValuesAndResize));
+ if (hasUndefined) {
+ assert.compareArray(values, [
+ 0,
+ 2,
+ 4,
+ undefined
+ ]);
+ } else {
+ assert.compareArray(values, [
+ 0,
+ 2,
+ 4
+ ]);
+ }
+ }
+ for (let ctor of ctors) {
+ rab = CreateRabForTest(ctor);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ values = [];
+ resizeAfter = 1;
+ resizeTo = 3 * ctor.BYTES_PER_ELEMENT;
+ assert(!someHelper(lengthTrackingWithOffset, CollectValuesAndResize));
+ if (hasUndefined) {
+ assert.compareArray(values, [
+ 4,
+ undefined
+ ]);
+ } else {
+ assert.compareArray(values, [4]);
+ }
+ }
+}
+
+SomeShrinkMidIteration(TypedArraySomeHelper, true);
+SomeShrinkMidIteration(ArraySomeHelper, false);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/sort-callback-grows-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/sort-callback-grows-strict.js
new file mode 100644
index 0000000000..983b2e53f0
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/sort-callback-grows-strict.js
@@ -0,0 +1,143 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from SortCallbackGrows test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+const TypedArraySortHelper = (ta, ...rest) => {
+ ta.sort(...rest);
+};
+
+const ArraySortHelper = (ta, ...rest) => {
+ Array.prototype.sort.call(ta, ...rest);
+};
+
+function SortCallbackGrows(sortHelper) {
+ function WriteUnsortedData(taFull) {
+ for (let i = 0; i < taFull.length; ++i) {
+ WriteToTypedArray(taFull, i, 10 - i);
+ }
+ }
+ let rab;
+ let resizeTo;
+ function CustomComparison(a, b) {
+ rab.resize(resizeTo);
+ if (a < b) {
+ return -1;
+ }
+ if (a > b) {
+ return 1;
+ }
+ return 0;
+ }
+
+ // Fixed length TA.
+ for (let ctor of ctors) {
+ rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ resizeTo = 6 * ctor.BYTES_PER_ELEMENT;
+ const fixedLength = new ctor(rab, 0, 4);
+ const taFull = new ctor(rab, 0);
+ WriteUnsortedData(taFull);
+ sortHelper(fixedLength, CustomComparison);
+ // Growing doesn't affect the sorting.
+ assert.compareArray(ToNumbers(taFull), [
+ 7,
+ 8,
+ 9,
+ 10,
+ 0,
+ 0
+ ]);
+ }
+
+ // Length-tracking TA.
+ for (let ctor of ctors) {
+ rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ resizeTo = 6 * ctor.BYTES_PER_ELEMENT;
+ const lengthTracking = new ctor(rab, 0);
+ const taFull = new ctor(rab, 0);
+ WriteUnsortedData(taFull);
+ sortHelper(lengthTracking, CustomComparison);
+ // Growing doesn't affect the sorting. Only the elements that were part of
+ // the original TA are sorted.
+ assert.compareArray(ToNumbers(taFull), [
+ 7,
+ 8,
+ 9,
+ 10,
+ 0,
+ 0
+ ]);
+ }
+}
+
+SortCallbackGrows(TypedArraySortHelper);
+SortCallbackGrows(ArraySortHelper);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/sort-callback-shrinks-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/sort-callback-shrinks-strict.js
new file mode 100644
index 0000000000..21a3def6a9
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/sort-callback-shrinks-strict.js
@@ -0,0 +1,145 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from SortCallbackShrinks test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer, Array.prototype.includes]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+const TypedArraySortHelper = (ta, ...rest) => {
+ ta.sort(...rest);
+};
+
+const ArraySortHelper = (ta, ...rest) => {
+ Array.prototype.sort.call(ta, ...rest);
+};
+
+function SortCallbackShrinks(sortHelper) {
+ function WriteUnsortedData(taFull) {
+ for (let i = 0; i < taFull.length; ++i) {
+ WriteToTypedArray(taFull, i, 10 - i);
+ }
+ }
+ let rab;
+ let resizeTo;
+ function CustomComparison(a, b) {
+ rab.resize(resizeTo);
+ if (a < b) {
+ return -1;
+ }
+ if (a > b) {
+ return 1;
+ }
+ return 0;
+ }
+
+ // Fixed length TA.
+ for (let ctor of ctors) {
+ rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ resizeTo = 2 * ctor.BYTES_PER_ELEMENT;
+ const fixedLength = new ctor(rab, 0, 4);
+ const taFull = new ctor(rab, 0);
+ WriteUnsortedData(taFull);
+ sortHelper(fixedLength, CustomComparison);
+ // The data is unchanged.
+ assert.compareArray(ToNumbers(taFull), [
+ 10,
+ 9
+ ]);
+ }
+
+ // Length-tracking TA.
+ for (let ctor of ctors) {
+ rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ resizeTo = 2 * ctor.BYTES_PER_ELEMENT;
+ const lengthTracking = new ctor(rab, 0);
+ const taFull = new ctor(rab, 0);
+ WriteUnsortedData(taFull);
+ sortHelper(lengthTracking, CustomComparison);
+ // The sort result is implementation defined, but it contains 2 elements out
+ // of the 4 original ones.
+ const newData = ToNumbers(taFull);
+ assert.sameValue(newData.length, 2);
+ assert([
+ 10,
+ 9,
+ 8,
+ 7
+ ].includes(newData[0]));
+ assert([
+ 10,
+ 9,
+ 8,
+ 7
+ ].includes(newData[1]));
+ }
+}
+
+SortCallbackShrinks(TypedArraySortHelper);
+SortCallbackShrinks(ArraySortHelper);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/sort-with-custom-comparison-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/sort-with-custom-comparison-strict.js
new file mode 100644
index 0000000000..fc1f227003
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/sort-with-custom-comparison-strict.js
@@ -0,0 +1,309 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from SortWithCustomComparison test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+const TypedArraySortHelper = (ta, ...rest) => {
+ ta.sort(...rest);
+};
+
+const ArraySortHelper = (ta, ...rest) => {
+ Array.prototype.sort.call(ta, ...rest);
+};
+
+function SortWithCustomComparison(sortHelper, oobThrows) {
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ const lengthTracking = new ctor(rab, 0);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ const taFull = new ctor(rab, 0);
+ function WriteUnsortedData() {
+ // Write some data into the array.
+ for (let i = 0; i < taFull.length; ++i) {
+ WriteToTypedArray(taFull, i, 10 - i);
+ }
+ }
+ function CustomComparison(a, b) {
+ // Sort all odd numbers before even numbers.
+ a = Number(a);
+ b = Number(b);
+ if (a % 2 == 1 && b % 2 == 0) {
+ return -1;
+ }
+ if (a % 2 == 0 && b % 2 == 1) {
+ return 1;
+ }
+ if (a < b) {
+ return -1;
+ }
+ if (a > b) {
+ return 1;
+ }
+ return 0;
+ }
+ // Orig. array: [10, 9, 8, 7]
+ // [10, 9, 8, 7] << fixedLength
+ // [8, 7] << fixedLengthWithOffset
+ // [10, 9, 8, 7, ...] << lengthTracking
+ // [8, 7, ...] << lengthTrackingWithOffset
+
+ WriteUnsortedData();
+ sortHelper(fixedLength, CustomComparison);
+ assert.compareArray(ToNumbers(taFull), [
+ 7,
+ 9,
+ 8,
+ 10
+ ]);
+ WriteUnsortedData();
+ sortHelper(fixedLengthWithOffset, CustomComparison);
+ assert.compareArray(ToNumbers(taFull), [
+ 10,
+ 9,
+ 7,
+ 8
+ ]);
+ WriteUnsortedData();
+ sortHelper(lengthTracking, CustomComparison);
+ assert.compareArray(ToNumbers(taFull), [
+ 7,
+ 9,
+ 8,
+ 10
+ ]);
+ WriteUnsortedData();
+ sortHelper(lengthTrackingWithOffset, CustomComparison);
+ assert.compareArray(ToNumbers(taFull), [
+ 10,
+ 9,
+ 7,
+ 8
+ ]);
+
+ // Shrink so that fixed length TAs go out of bounds.
+ rab.resize(3 * ctor.BYTES_PER_ELEMENT);
+
+ // Orig. array: [10, 9, 8]
+ // [10, 9, 8, ...] << lengthTracking
+ // [8, ...] << lengthTrackingWithOffset
+
+ WriteUnsortedData();
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ sortHelper(fixedLength, CustomComparison);
+ });
+ assert.compareArray(ToNumbers(taFull), [
+ 10,
+ 9,
+ 8
+ ]);
+ assert.throws(TypeError, () => {
+ sortHelper(fixedLengthWithOffset, CustomComparison);
+ });
+ assert.compareArray(ToNumbers(taFull), [
+ 10,
+ 9,
+ 8
+ ]);
+ } else {
+ sortHelper(fixedLength, CustomComparison);
+ assert.compareArray(ToNumbers(taFull), [
+ 10,
+ 9,
+ 8
+ ]);
+ sortHelper(fixedLengthWithOffset, CustomComparison);
+ assert.compareArray(ToNumbers(taFull), [
+ 10,
+ 9,
+ 8
+ ]);
+ }
+ WriteUnsortedData();
+ sortHelper(lengthTracking, CustomComparison);
+ assert.compareArray(ToNumbers(taFull), [
+ 9,
+ 8,
+ 10
+ ]);
+ WriteUnsortedData();
+ sortHelper(lengthTrackingWithOffset, CustomComparison);
+ assert.compareArray(ToNumbers(taFull), [
+ 10,
+ 9,
+ 8
+ ]);
+
+ // Shrink so that the TAs with offset go out of bounds.
+ rab.resize(1 * ctor.BYTES_PER_ELEMENT);
+ WriteUnsortedData();
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ sortHelper(fixedLength, CustomComparison);
+ });
+ assert.compareArray(ToNumbers(taFull), [10]);
+ assert.throws(TypeError, () => {
+ sortHelper(fixedLengthWithOffset, CustomComparison);
+ });
+ assert.compareArray(ToNumbers(taFull), [10]);
+ assert.throws(TypeError, () => {
+ sortHelper(lengthTrackingWithOffset, CustomComparison);
+ });
+ assert.compareArray(ToNumbers(taFull), [10]);
+ } else {
+ sortHelper(fixedLength, CustomComparison);
+ assert.compareArray(ToNumbers(taFull), [10]);
+ sortHelper(fixedLengthWithOffset, CustomComparison);
+ assert.compareArray(ToNumbers(taFull), [10]);
+ sortHelper(lengthTrackingWithOffset, CustomComparison);
+ assert.compareArray(ToNumbers(taFull), [10]);
+ }
+ WriteUnsortedData();
+ sortHelper(lengthTracking, CustomComparison);
+ assert.compareArray(ToNumbers(taFull), [10]);
+
+ // Shrink to zero.
+ rab.resize(0);
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ sortHelper(fixedLength, CustomComparison);
+ });
+ assert.throws(TypeError, () => {
+ sortHelper(fixedLengthWithOffset, CustomComparison);
+ });
+ assert.throws(TypeError, () => {
+ sortHelper(lengthTrackingWithOffset, CustomComparison);
+ });
+ } else {
+ sortHelper(fixedLength, CustomComparison);
+ sortHelper(fixedLengthWithOffset, CustomComparison);
+ sortHelper(lengthTrackingWithOffset, CustomComparison);
+ }
+ sortHelper(lengthTracking, CustomComparison);
+ assert.compareArray(ToNumbers(taFull), []);
+
+ // Grow so that all TAs are back in-bounds.
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+
+ // Orig. array: [10, 9, 8, 7, 6, 5]
+ // [10, 9, 8, 7] << fixedLength
+ // [8, 7] << fixedLengthWithOffset
+ // [10, 9, 8, 7, 6, 5, ...] << lengthTracking
+ // [8, 7, 6, 5, ...] << lengthTrackingWithOffset
+
+ WriteUnsortedData();
+ sortHelper(fixedLength, CustomComparison);
+ assert.compareArray(ToNumbers(taFull), [
+ 7,
+ 9,
+ 8,
+ 10,
+ 6,
+ 5
+ ]);
+ WriteUnsortedData();
+ sortHelper(fixedLengthWithOffset, CustomComparison);
+ assert.compareArray(ToNumbers(taFull), [
+ 10,
+ 9,
+ 7,
+ 8,
+ 6,
+ 5
+ ]);
+ WriteUnsortedData();
+ sortHelper(lengthTracking, CustomComparison);
+ assert.compareArray(ToNumbers(taFull), [
+ 5,
+ 7,
+ 9,
+ 6,
+ 8,
+ 10
+ ]);
+ WriteUnsortedData();
+ sortHelper(lengthTrackingWithOffset, CustomComparison);
+ assert.compareArray(ToNumbers(taFull), [
+ 10,
+ 9,
+ 5,
+ 7,
+ 6,
+ 8
+ ]);
+ }
+}
+
+SortWithCustomComparison(TypedArraySortHelper, true);
+SortWithCustomComparison(ArraySortHelper, false);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/sort-with-default-comparison-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/sort-with-default-comparison-strict.js
new file mode 100644
index 0000000000..6f739ae977
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/sort-with-default-comparison-strict.js
@@ -0,0 +1,245 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from SortWithDefaultComparison test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+// This test cannot be reused between TypedArray.protoype.sort and
+// Array.prototype.sort, since the default sorting functions differ.
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ const lengthTracking = new ctor(rab, 0);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ const taFull = new ctor(rab, 0);
+ function WriteUnsortedData() {
+ // Write some data into the array.
+ for (let i = 0; i < taFull.length; ++i) {
+ WriteToTypedArray(taFull, i, 10 - 2 * i);
+ }
+ }
+ // Orig. array: [10, 8, 6, 4]
+ // [10, 8, 6, 4] << fixedLength
+ // [6, 4] << fixedLengthWithOffset
+ // [10, 8, 6, 4, ...] << lengthTracking
+ // [6, 4, ...] << lengthTrackingWithOffset
+
+ WriteUnsortedData();
+ fixedLength.sort();
+ assert.compareArray(ToNumbers(taFull), [
+ 4,
+ 6,
+ 8,
+ 10
+ ]);
+ WriteUnsortedData();
+ fixedLengthWithOffset.sort();
+ assert.compareArray(ToNumbers(taFull), [
+ 10,
+ 8,
+ 4,
+ 6
+ ]);
+ WriteUnsortedData();
+ lengthTracking.sort();
+ assert.compareArray(ToNumbers(taFull), [
+ 4,
+ 6,
+ 8,
+ 10
+ ]);
+ WriteUnsortedData();
+ lengthTrackingWithOffset.sort();
+ assert.compareArray(ToNumbers(taFull), [
+ 10,
+ 8,
+ 4,
+ 6
+ ]);
+
+ // Shrink so that fixed length TAs go out of bounds.
+ rab.resize(3 * ctor.BYTES_PER_ELEMENT);
+
+ // Orig. array: [10, 8, 6]
+ // [10, 8, 6, ...] << lengthTracking
+ // [6, ...] << lengthTrackingWithOffset
+
+ WriteUnsortedData();
+ assert.throws(TypeError, () => {
+ fixedLength.sort();
+ });
+ WriteUnsortedData();
+ assert.throws(TypeError, () => {
+ fixedLengthWithOffset.sort();
+ });
+ WriteUnsortedData();
+ lengthTracking.sort();
+ assert.compareArray(ToNumbers(taFull), [
+ 6,
+ 8,
+ 10
+ ]);
+ WriteUnsortedData();
+ lengthTrackingWithOffset.sort();
+ assert.compareArray(ToNumbers(taFull), [
+ 10,
+ 8,
+ 6
+ ]);
+
+ // Shrink so that the TAs with offset go out of bounds.
+ rab.resize(1 * ctor.BYTES_PER_ELEMENT);
+ WriteUnsortedData();
+ assert.throws(TypeError, () => {
+ fixedLength.sort();
+ });
+ WriteUnsortedData();
+ assert.throws(TypeError, () => {
+ fixedLengthWithOffset.sort();
+ });
+ WriteUnsortedData();
+ lengthTracking.sort();
+ assert.compareArray(ToNumbers(taFull), [10]);
+ WriteUnsortedData();
+ assert.throws(TypeError, () => {
+ lengthTrackingWithOffset.sort();
+ });
+
+ // Shrink to zero.
+ rab.resize(0);
+ WriteUnsortedData();
+ assert.throws(TypeError, () => {
+ fixedLength.sort();
+ });
+ WriteUnsortedData();
+ assert.throws(TypeError, () => {
+ fixedLengthWithOffset.sort();
+ });
+ WriteUnsortedData();
+ lengthTracking.sort();
+ assert.compareArray(ToNumbers(taFull), []);
+ WriteUnsortedData();
+ assert.throws(TypeError, () => {
+ lengthTrackingWithOffset.sort();
+ });
+
+ // Grow so that all TAs are back in-bounds.
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+
+ // Orig. array: [10, 8, 6, 4, 2, 0]
+ // [10, 8, 6, 4] << fixedLength
+ // [6, 4] << fixedLengthWithOffset
+ // [10, 8, 6, 4, 2, 0, ...] << lengthTracking
+ // [6, 4, 2, 0, ...] << lengthTrackingWithOffset
+
+ WriteUnsortedData();
+ fixedLength.sort();
+ assert.compareArray(ToNumbers(taFull), [
+ 4,
+ 6,
+ 8,
+ 10,
+ 2,
+ 0
+ ]);
+ WriteUnsortedData();
+ fixedLengthWithOffset.sort();
+ assert.compareArray(ToNumbers(taFull), [
+ 10,
+ 8,
+ 4,
+ 6,
+ 2,
+ 0
+ ]);
+ WriteUnsortedData();
+ lengthTracking.sort();
+ assert.compareArray(ToNumbers(taFull), [
+ 0,
+ 2,
+ 4,
+ 6,
+ 8,
+ 10
+ ]);
+ WriteUnsortedData();
+ lengthTrackingWithOffset.sort();
+ assert.compareArray(ToNumbers(taFull), [
+ 10,
+ 8,
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/subarray-parameter-conversion-grows-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/subarray-parameter-conversion-grows-strict.js
new file mode 100644
index 0000000000..9727cb6e30
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/subarray-parameter-conversion-grows-strict.js
@@ -0,0 +1,141 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from SubarrayParameterConversionGrows test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+// Orig. array: [0, 2, 4, 6]
+// [0, 2, 4, 6] << fixedLength
+// [0, 2, 4, 6, ...] << lengthTracking
+function CreateRabForTest(ctor) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+ return rab;
+}
+
+// Growing a fixed length TA back in bounds.
+for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ // Make `fixedLength` OOB.
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ const evil = {
+ valueOf: () => {
+ rab.resize(4 * ctor.BYTES_PER_ELEMENT);
+ return 0;
+ }
+ };
+ // The length computation is done before parameter conversion. At that
+ // point, the length is 0, since the TA is OOB.
+ assert.compareArray(ToNumbers(fixedLength.subarray(evil, 0, 1)), []);
+}
+
+// Growing + fixed-length TA. Growing won't affect anything.
+for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ const evil = {
+ valueOf: () => {
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ return 0;
+ }
+ };
+ assert.compareArray(ToNumbers(fixedLength.subarray(evil)), [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+}
+
+// Growing + length-tracking TA. The length computation is done with the
+// original length.
+for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab, 0);
+ const evil = {
+ valueOf: () => {
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ return 0;
+ }
+ };
+ assert.compareArray(
+ ToNumbers(lengthTracking.subarray(evil, lengthTracking.length)), [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/subarray-parameter-conversion-shrinks-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/subarray-parameter-conversion-shrinks-strict.js
new file mode 100644
index 0000000000..24b2ccd8ad
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/subarray-parameter-conversion-shrinks-strict.js
@@ -0,0 +1,223 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from SubarrayParameterConversionShrinks test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+// Orig. array: [0, 2, 4, 6]
+// [0, 2, 4, 6] << fixedLength
+// [0, 2, 4, 6, ...] << lengthTracking
+function CreateRabForTest(ctor) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+ return rab;
+}
+
+// Fixed-length TA + first parameter conversion shrinks. The old length is
+// used in the length computation, and the subarray construction fails.
+for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ let evil = {
+ valueOf: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 0;
+ }
+ };
+ assert.throws(RangeError, () => {
+ fixedLength.subarray(evil);
+ });
+}
+
+// Like the previous test, but now we construct a smaller subarray and it
+// succeeds.
+for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ let evil = {
+ valueOf: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 0;
+ }
+ };
+ assert.compareArray(ToNumbers(fixedLength.subarray(evil, 1)), [0]);
+}
+
+// Fixed-length TA + second parameter conversion shrinks. The old length is
+// used in the length computation, and the subarray construction fails.
+for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ let evil = {
+ valueOf: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 3;
+ }
+ };
+ assert.throws(RangeError, () => {
+ fixedLength.subarray(0, evil);
+ });
+}
+
+// Like the previous test, but now we construct a smaller subarray and it
+// succeeds.
+for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ let evil = {
+ valueOf: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 1;
+ }
+ };
+ assert.compareArray(ToNumbers(fixedLength.subarray(0, evil)), [0]);
+}
+
+// Shrinking + fixed-length TA, subarray construction succeeds even though the
+// TA goes OOB.
+for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const fixedLength = new ctor(rab, 0, 4);
+ const evil = {
+ valueOf: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 0;
+ }
+ };
+ assert.compareArray(ToNumbers(fixedLength.subarray(evil, 1)), [0]);
+}
+
+// Length-tracking TA + first parameter conversion shrinks. The old length is
+// used in the length computation, and the subarray construction fails.
+for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab);
+ let evil = {
+ valueOf: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 0;
+ }
+ };
+ assert.throws(RangeError, () => {
+ lengthTracking.subarray(evil, lengthTracking.length);
+ });
+}
+
+// Like the previous test, but now we construct a smaller subarray and it
+// succeeds.
+for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab);
+ let evil = {
+ valueOf: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 0;
+ }
+ };
+ assert.compareArray(ToNumbers(lengthTracking.subarray(evil, 1)), [0]);
+}
+
+// Length-tracking TA + first parameter conversion shrinks. The second
+// parameter is negative -> the relative index is not recomputed, and the
+// subarray construction fails.
+for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab);
+ let evil = {
+ valueOf: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 0;
+ }
+ };
+ assert.throws(RangeError, () => {
+ lengthTracking.subarray(evil, -1);
+ });
+}
+
+// Length-tracking TA + second parameter conversion shrinks. The second
+// parameter is too large -> the subarray construction fails.
+for (let ctor of ctors) {
+ const rab = CreateRabForTest(ctor);
+ const lengthTracking = new ctor(rab);
+ let evil = {
+ valueOf: () => {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ return 3;
+ }
+ };
+ assert.throws(RangeError, () => {
+ lengthTracking.subarray(0, evil);
+ });
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/subarray-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/subarray-strict.js
new file mode 100644
index 0000000000..93735d9326
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/subarray-strict.js
@@ -0,0 +1,234 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from Subarray test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ const lengthTracking = new ctor(rab, 0);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+
+ // Orig. array: [0, 2, 4, 6]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, ...] << lengthTracking
+ // [4, 6, ...] << lengthTrackingWithOffset
+
+ const fixedLengthSubFull = fixedLength.subarray(0);
+ assert.compareArray(ToNumbers(fixedLengthSubFull), [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ const fixedLengthWithOffsetSubFull = fixedLengthWithOffset.subarray(0);
+ assert.compareArray(ToNumbers(fixedLengthWithOffsetSubFull), [
+ 4,
+ 6
+ ]);
+ const lengthTrackingSubFull = lengthTracking.subarray(0);
+ assert.compareArray(ToNumbers(lengthTrackingSubFull), [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ const lengthTrackingWithOffsetSubFull = lengthTrackingWithOffset.subarray(0);
+ assert.compareArray(ToNumbers(lengthTrackingWithOffsetSubFull), [
+ 4,
+ 6
+ ]);
+
+ // Relative offsets
+ assert.compareArray(ToNumbers(fixedLength.subarray(-2)), [
+ 4,
+ 6
+ ]);
+ assert.compareArray(ToNumbers(fixedLengthWithOffset.subarray(-1)), [6]);
+ assert.compareArray(ToNumbers(lengthTracking.subarray(-2)), [
+ 4,
+ 6
+ ]);
+ assert.compareArray(ToNumbers(lengthTrackingWithOffset.subarray(-1)), [6]);
+
+ // Shrink so that fixed length TAs go out of bounds.
+ rab.resize(3 * ctor.BYTES_PER_ELEMENT);
+
+ // Orig. array: [0, 2, 4]
+ // [0, 2, 4, ...] << lengthTracking
+ // [4, ...] << lengthTrackingWithOffset
+
+ // We can create subarrays of OOB arrays (which have length 0), as long as
+ // the new arrays are not OOB.
+ assert.compareArray(ToNumbers(fixedLength.subarray(0)), []);
+ assert.compareArray(ToNumbers(fixedLengthWithOffset.subarray(0)), []);
+ assert.compareArray(ToNumbers(lengthTracking.subarray(0)), [
+ 0,
+ 2,
+ 4
+ ]);
+ assert.compareArray(ToNumbers(lengthTrackingWithOffset.subarray(0)), [4]);
+
+ // Also the previously created subarrays are OOB.
+ assert.sameValue(fixedLengthSubFull.length, 0);
+ assert.sameValue(fixedLengthWithOffsetSubFull.length, 0);
+
+ // Relative offsets
+ assert.compareArray(ToNumbers(lengthTracking.subarray(-2)), [
+ 2,
+ 4
+ ]);
+ assert.compareArray(ToNumbers(lengthTrackingWithOffset.subarray(-1)), [4]);
+
+ // Shrink so that the TAs with offset go out of bounds.
+ rab.resize(1 * ctor.BYTES_PER_ELEMENT);
+ assert.compareArray(ToNumbers(fixedLength.subarray(0)), []);
+ assert.compareArray(ToNumbers(lengthTracking.subarray(0)), [0]);
+
+ // Even the 0-length subarray of fixedLengthWithOffset would be OOB ->
+ // this throws.
+ assert.throws(RangeError, () => {
+ fixedLengthWithOffset.subarray(0);
+ });
+
+ // Also the previously created subarrays are OOB.
+ assert.sameValue(fixedLengthSubFull.length, 0);
+ assert.sameValue(fixedLengthWithOffsetSubFull.length, 0);
+ assert.sameValue(lengthTrackingWithOffsetSubFull.length, 0);
+
+ // Shrink to zero.
+ rab.resize(0);
+ assert.compareArray(ToNumbers(fixedLength.subarray(0)), []);
+ assert.compareArray(ToNumbers(lengthTracking.subarray(0)), []);
+ assert.throws(RangeError, () => {
+ fixedLengthWithOffset.subarray(0);
+ });
+ assert.throws(RangeError, () => {
+ lengthTrackingWithOffset.subarray(0);
+ });
+
+ // Also the previously created subarrays are OOB.
+ assert.sameValue(fixedLengthSubFull.length, 0);
+ assert.sameValue(fixedLengthWithOffsetSubFull.length, 0);
+ assert.sameValue(lengthTrackingWithOffsetSubFull.length, 0);
+
+ // Grow so that all TAs are back in-bounds.
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ for (let i = 0; i < 6; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+
+ // Orig. array: [0, 2, 4, 6, 8, 10]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, 8, 10, ...] << lengthTracking
+ // [4, 6, 8, 10, ...] << lengthTrackingWithOffset
+
+ assert.compareArray(ToNumbers(fixedLength.subarray(0)), [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ assert.compareArray(ToNumbers(fixedLengthWithOffset.subarray(0)), [
+ 4,
+ 6
+ ]);
+ assert.compareArray(ToNumbers(lengthTracking.subarray(0)), [
+ 0,
+ 2,
+ 4,
+ 6,
+ 8,
+ 10
+ ]);
+ assert.compareArray(ToNumbers(lengthTrackingWithOffset.subarray(0)), [
+ 4,
+ 6,
+ 8,
+ 10
+ ]);
+
+ // Also the previously created subarrays are no longer OOB.
+ assert.sameValue(fixedLengthSubFull.length, 4);
+ assert.sameValue(fixedLengthWithOffsetSubFull.length, 2);
+ // Subarrays of length-tracking TAs are also length-tracking.
+ assert.sameValue(lengthTrackingSubFull.length, 6);
+ assert.sameValue(lengthTrackingWithOffsetSubFull.length, 4);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/test-copy-within-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/test-copy-within-strict.js
new file mode 100644
index 0000000000..897e8ea4ef
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/test-copy-within-strict.js
@@ -0,0 +1,272 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from TestCopyWithin test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+const TypedArrayCopyWithinHelper = (ta, ...rest) => {
+ ta.copyWithin(...rest);
+};
+
+const ArrayCopyWithinHelper = (ta, ...rest) => {
+ Array.prototype.copyWithin.call(ta, ...rest);
+};
+
+function TestCopyWithin(helper, oobThrows) {
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ const lengthTracking = new ctor(rab, 0);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, i);
+ }
+
+ // Orig. array: [0, 1, 2, 3]
+ // [0, 1, 2, 3] << fixedLength
+ // [2, 3] << fixedLengthWithOffset
+ // [0, 1, 2, 3, ...] << lengthTracking
+ // [2, 3, ...] << lengthTrackingWithOffset
+
+ helper(fixedLength, 0, 2);
+ assert.compareArray(ToNumbers(fixedLength), [
+ 2,
+ 3,
+ 2,
+ 3
+ ]);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, i);
+ }
+ helper(fixedLengthWithOffset, 0, 1);
+ assert.compareArray(ToNumbers(fixedLengthWithOffset), [
+ 3,
+ 3
+ ]);
+ for (let i = 0; i < 4; ++i) {
+ WriteToTypedArray(taWrite, i, i);
+ }
+ helper(lengthTracking, 0, 2);
+ assert.compareArray(ToNumbers(lengthTracking), [
+ 2,
+ 3,
+ 2,
+ 3
+ ]);
+ helper(lengthTrackingWithOffset, 0, 1);
+ assert.compareArray(ToNumbers(lengthTrackingWithOffset), [
+ 3,
+ 3
+ ]);
+
+ // Shrink so that fixed length TAs go out of bounds.
+ rab.resize(3 * ctor.BYTES_PER_ELEMENT);
+ for (let i = 0; i < 3; ++i) {
+ WriteToTypedArray(taWrite, i, i);
+ }
+
+ // Orig. array: [0, 1, 2]
+ // [0, 1, 2, ...] << lengthTracking
+ // [2, ...] << lengthTrackingWithOffset
+
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ helper(fixedLength, 0, 1);
+ });
+ assert.throws(TypeError, () => {
+ helper(fixedLengthWithOffset, 0, 1);
+ });
+ } else {
+ helper(fixedLength, 0, 1);
+ helper(fixedLengthWithOffset, 0, 1);
+ // We'll check below that these were no-op.
+ }
+ assert.compareArray(ToNumbers(lengthTracking), [
+ 0,
+ 1,
+ 2
+ ]);
+ helper(lengthTracking, 0, 1);
+ assert.compareArray(ToNumbers(lengthTracking), [
+ 1,
+ 2,
+ 2
+ ]);
+ helper(lengthTrackingWithOffset, 0, 1);
+ assert.compareArray(ToNumbers(lengthTrackingWithOffset), [2]);
+
+ // Shrink so that the TAs with offset go out of bounds.
+ rab.resize(1 * ctor.BYTES_PER_ELEMENT);
+ WriteToTypedArray(taWrite, 0, 0);
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ helper(fixedLength, 0, 1, 1);
+ });
+ assert.throws(TypeError, () => {
+ helper(fixedLengthWithOffset, 0, 1, 1);
+ });
+ assert.throws(TypeError, () => {
+ helper(lengthTrackingWithOffset, 0, 1, 1);
+ });
+ } else {
+ helper(fixedLength, 0, 1, 1);
+ helper(fixedLengthWithOffset, 0, 1, 1);
+ helper(lengthTrackingWithOffset, 0, 1, 1);
+ }
+ assert.compareArray(ToNumbers(lengthTracking), [0]);
+ helper(lengthTracking, 0, 0, 1);
+ assert.compareArray(ToNumbers(lengthTracking), [0]);
+
+ // Shrink to zero.
+ rab.resize(0);
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ helper(fixedLength, 0, 1, 1);
+ });
+ assert.throws(TypeError, () => {
+ helper(fixedLengthWithOffset, 0, 1, 1);
+ });
+ assert.throws(TypeError, () => {
+ helper(lengthTrackingWithOffset, 0, 1, 1);
+ });
+ } else {
+ helper(fixedLength, 0, 1, 1);
+ helper(fixedLengthWithOffset, 0, 1, 1);
+ helper(lengthTrackingWithOffset, 0, 1, 1);
+ }
+ assert.compareArray(ToNumbers(lengthTracking), []);
+ helper(lengthTracking, 0, 0, 1);
+ assert.compareArray(ToNumbers(lengthTracking), []);
+
+ // Grow so that all TAs are back in-bounds.
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ for (let i = 0; i < 6; ++i) {
+ WriteToTypedArray(taWrite, i, i);
+ }
+
+ // Orig. array: [0, 1, 2, 3, 4, 5]
+ // [0, 1, 2, 3] << fixedLength
+ // [2, 3] << fixedLengthWithOffset
+ // [0, 1, 2, 3, 4, 5, ...] << lengthTracking
+ // [2, 3, 4, 5, ...] << lengthTrackingWithOffset
+
+ helper(fixedLength, 0, 2);
+ assert.compareArray(ToNumbers(fixedLength), [
+ 2,
+ 3,
+ 2,
+ 3
+ ]);
+ for (let i = 0; i < 6; ++i) {
+ WriteToTypedArray(taWrite, i, i);
+ }
+ helper(fixedLengthWithOffset, 0, 1);
+ assert.compareArray(ToNumbers(fixedLengthWithOffset), [
+ 3,
+ 3
+ ]);
+ for (let i = 0; i < 6; ++i) {
+ WriteToTypedArray(taWrite, i, i);
+ }
+
+ // [0, 1, 2, 3, 4, 5, ...] << lengthTracking
+ // target ^ ^ start
+ helper(lengthTracking, 0, 2);
+ assert.compareArray(ToNumbers(lengthTracking), [
+ 2,
+ 3,
+ 4,
+ 5,
+ 4,
+ 5
+ ]);
+ for (let i = 0; i < 6; ++i) {
+ WriteToTypedArray(taWrite, i, i);
+ }
+
+ // [2, 3, 4, 5, ...] << lengthTrackingWithOffset
+ // target ^ ^ start
+ helper(lengthTrackingWithOffset, 0, 1);
+ assert.compareArray(ToNumbers(lengthTrackingWithOffset), [
+ 3,
+ 4,
+ 5,
+ 5
+ ]);
+ }
+}
+
+TestCopyWithin(TypedArrayCopyWithinHelper, true);
+TestCopyWithin(ArrayCopyWithinHelper, false);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/test-fill-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/test-fill-strict.js
new file mode 100644
index 0000000000..644b9066ad
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/test-fill-strict.js
@@ -0,0 +1,243 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from TestFill test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function ReadDataFromBuffer(ab, ctor) {
+ let result = [];
+ const ta = new ctor(ab, 0, ab.byteLength / ctor.BYTES_PER_ELEMENT);
+ for (let item of ta) {
+ result.push(Number(item));
+ }
+ return result;
+}
+
+function TypedArrayFillHelper(ta, n, start, end) {
+ if (ta instanceof BigInt64Array || ta instanceof BigUint64Array) {
+ ta.fill(BigInt(n), start, end);
+ } else {
+ ta.fill(n, start, end);
+ }
+}
+
+function ArrayFillHelper(ta, n, start, end) {
+ if (ta instanceof BigInt64Array || ta instanceof BigUint64Array) {
+ Array.prototype.fill.call(ta, BigInt(n), start, end);
+ } else {
+ Array.prototype.fill.call(ta, n, start, end);
+ }
+}
+
+function TestFill(helper, oobThrows) {
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ const lengthTracking = new ctor(rab, 0);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+ assert.compareArray(ReadDataFromBuffer(rab, ctor), [
+ 0,
+ 0,
+ 0,
+ 0
+ ]);
+ helper(fixedLength, 1);
+ assert.compareArray(ReadDataFromBuffer(rab, ctor), [
+ 1,
+ 1,
+ 1,
+ 1
+ ]);
+ helper(fixedLengthWithOffset, 2);
+ assert.compareArray(ReadDataFromBuffer(rab, ctor), [
+ 1,
+ 1,
+ 2,
+ 2
+ ]);
+ helper(lengthTracking, 3);
+ assert.compareArray(ReadDataFromBuffer(rab, ctor), [
+ 3,
+ 3,
+ 3,
+ 3
+ ]);
+ helper(lengthTrackingWithOffset, 4);
+ assert.compareArray(ReadDataFromBuffer(rab, ctor), [
+ 3,
+ 3,
+ 4,
+ 4
+ ]);
+
+ // Shrink so that fixed length TAs go out of bounds.
+ rab.resize(3 * ctor.BYTES_PER_ELEMENT);
+ if (oobThrows) {
+ assert.throws(TypeError, () => helper(fixedLength, 5));
+ assert.throws(TypeError, () => helper(fixedLengthWithOffset, 6));
+ } else {
+ helper(fixedLength, 5);
+ helper(fixedLengthWithOffset, 6);
+ // We'll check below that these were no-op.
+ }
+ assert.compareArray(ReadDataFromBuffer(rab, ctor), [
+ 3,
+ 3,
+ 4
+ ]);
+ helper(lengthTracking, 7);
+ assert.compareArray(ReadDataFromBuffer(rab, ctor), [
+ 7,
+ 7,
+ 7
+ ]);
+ helper(lengthTrackingWithOffset, 8);
+ assert.compareArray(ReadDataFromBuffer(rab, ctor), [
+ 7,
+ 7,
+ 8
+ ]);
+
+ // Shrink so that the TAs with offset go out of bounds.
+ rab.resize(1 * ctor.BYTES_PER_ELEMENT);
+ if (oobThrows) {
+ assert.throws(TypeError, () => helper(fixedLength, 9));
+ assert.throws(TypeError, () => helper(fixedLengthWithOffset, 10));
+ assert.throws(TypeError, () => helper(lengthTrackingWithOffset, 11));
+ } else {
+ // We'll check below that these were no-op.
+ helper(fixedLength, 9);
+ helper(fixedLengthWithOffset, 10);
+ helper(lengthTrackingWithOffset, 11);
+ }
+ assert.compareArray(ReadDataFromBuffer(rab, ctor), [7]);
+ helper(lengthTracking, 12);
+ assert.compareArray(ReadDataFromBuffer(rab, ctor), [12]);
+
+ // Grow so that all TAs are back in-bounds.
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ helper(fixedLength, 13);
+ assert.compareArray(ReadDataFromBuffer(rab, ctor), [
+ 13,
+ 13,
+ 13,
+ 13,
+ 0,
+ 0
+ ]);
+ helper(fixedLengthWithOffset, 14);
+ assert.compareArray(ReadDataFromBuffer(rab, ctor), [
+ 13,
+ 13,
+ 14,
+ 14,
+ 0,
+ 0
+ ]);
+ helper(lengthTracking, 15);
+ assert.compareArray(ReadDataFromBuffer(rab, ctor), [
+ 15,
+ 15,
+ 15,
+ 15,
+ 15,
+ 15
+ ]);
+ helper(lengthTrackingWithOffset, 16);
+ assert.compareArray(ReadDataFromBuffer(rab, ctor), [
+ 15,
+ 15,
+ 16,
+ 16,
+ 16,
+ 16
+ ]);
+
+ // Filling with non-undefined start & end.
+ helper(fixedLength, 17, 1, 3);
+ assert.compareArray(ReadDataFromBuffer(rab, ctor), [
+ 15,
+ 17,
+ 17,
+ 16,
+ 16,
+ 16
+ ]);
+ helper(fixedLengthWithOffset, 18, 1, 2);
+ assert.compareArray(ReadDataFromBuffer(rab, ctor), [
+ 15,
+ 17,
+ 17,
+ 18,
+ 16,
+ 16
+ ]);
+ helper(lengthTracking, 19, 1, 3);
+ assert.compareArray(ReadDataFromBuffer(rab, ctor), [
+ 15,
+ 19,
+ 19,
+ 18,
+ 16,
+ 16
+ ]);
+ helper(lengthTrackingWithOffset, 20, 1, 2);
+ assert.compareArray(ReadDataFromBuffer(rab, ctor), [
+ 15,
+ 19,
+ 19,
+ 20,
+ 16,
+ 16
+ ]);
+ }
+}
+
+TestFill(TypedArrayFillHelper, true);
+TestFill(ArrayFillHelper, false);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/test-map-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/test-map-strict.js
new file mode 100644
index 0000000000..255a4ef9e9
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/test-map-strict.js
@@ -0,0 +1,247 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from TestMap test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+function WriteToTypedArray(array, index, value) {
+ if (array instanceof BigInt64Array || array instanceof BigUint64Array) {
+ array[index] = BigInt(value);
+ } else {
+ array[index] = value;
+ }
+}
+
+function Convert(item) {
+ if (typeof item == 'bigint') {
+ return Number(item);
+ }
+ return item;
+}
+
+function ToNumbers(array) {
+ let result = [];
+ for (let item of array) {
+ result.push(Convert(item));
+ }
+ return result;
+}
+
+const TypedArrayMapHelper = (ta, ...rest) => {
+ return ta.map(...rest);
+};
+
+const ArrayMapHelper = (ta, ...rest) => {
+ return Array.prototype.map.call(ta, ...rest);
+};
+
+function TestMap(mapHelper, oobThrows) {
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
+ const lengthTracking = new ctor(rab, 0);
+ const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
+
+ // Write some data into the array.
+ const taWrite = new ctor(rab);
+ for (let i = 0; i < taWrite.length; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+
+ // Orig. array: [0, 2, 4, 6]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, ...] << lengthTracking
+ // [4, 6, ...] << lengthTrackingWithOffset
+
+ function Helper(array) {
+ const values = [];
+ function GatherValues(n, ix) {
+ assert.sameValue(ix, values.length);
+ values.push(n);
+ if (typeof n == 'bigint') {
+ return n + 1n;
+ }
+ return n + 1;
+ }
+ const newValues = mapHelper(array, GatherValues);
+ for (let i = 0; i < values.length; ++i) {
+ if (typeof values[i] == 'bigint') {
+ assert.sameValue(values[i] + 1n, newValues[i]);
+ } else {
+ assert.sameValue(values[i] + 1, newValues[i]);
+ }
+ }
+ return ToNumbers(values);
+ }
+ assert.compareArray(Helper(fixedLength), [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ assert.compareArray(Helper(fixedLengthWithOffset), [
+ 4,
+ 6
+ ]);
+ assert.compareArray(Helper(lengthTracking), [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ assert.compareArray(Helper(lengthTrackingWithOffset), [
+ 4,
+ 6
+ ]);
+
+ // Shrink so that fixed length TAs go out of bounds.
+ rab.resize(3 * ctor.BYTES_PER_ELEMENT);
+
+ // Orig. array: [0, 2, 4]
+ // [0, 2, 4, ...] << lengthTracking
+ // [4, ...] << lengthTrackingWithOffset
+
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ Helper(fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ Helper(fixedLengthWithOffset);
+ });
+ } else {
+ assert.compareArray(Helper(fixedLength), []);
+ assert.compareArray(Helper(fixedLengthWithOffset), []);
+ }
+ assert.compareArray(Helper(lengthTracking), [
+ 0,
+ 2,
+ 4
+ ]);
+ assert.compareArray(Helper(lengthTrackingWithOffset), [4]);
+
+ // Shrink so that the TAs with offset go out of bounds.
+ rab.resize(1 * ctor.BYTES_PER_ELEMENT);
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ Helper(fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ Helper(fixedLengthWithOffset);
+ });
+ assert.throws(TypeError, () => {
+ Helper(lengthTrackingWithOffset);
+ });
+ } else {
+ assert.compareArray(Helper(fixedLength), []);
+ assert.compareArray(Helper(fixedLengthWithOffset), []);
+ assert.compareArray(Helper(lengthTrackingWithOffset), []);
+ }
+ assert.compareArray(Helper(lengthTracking), [0]);
+
+ // Shrink to zero.
+ rab.resize(0);
+ if (oobThrows) {
+ assert.throws(TypeError, () => {
+ Helper(fixedLength);
+ });
+ assert.throws(TypeError, () => {
+ Helper(fixedLengthWithOffset);
+ });
+ assert.throws(TypeError, () => {
+ Helper(lengthTrackingWithOffset);
+ });
+ } else {
+ assert.compareArray(Helper(fixedLength), []);
+ assert.compareArray(Helper(fixedLengthWithOffset), []);
+ assert.compareArray(Helper(lengthTrackingWithOffset), []);
+ }
+ assert.compareArray(Helper(lengthTracking), []);
+
+ // Grow so that all TAs are back in-bounds.
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ for (let i = 0; i < 6; ++i) {
+ WriteToTypedArray(taWrite, i, 2 * i);
+ }
+
+ // Orig. array: [0, 2, 4, 6, 8, 10]
+ // [0, 2, 4, 6] << fixedLength
+ // [4, 6] << fixedLengthWithOffset
+ // [0, 2, 4, 6, 8, 10, ...] << lengthTracking
+ // [4, 6, 8, 10, ...] << lengthTrackingWithOffset
+
+ assert.compareArray(Helper(fixedLength), [
+ 0,
+ 2,
+ 4,
+ 6
+ ]);
+ assert.compareArray(Helper(fixedLengthWithOffset), [
+ 4,
+ 6
+ ]);
+ assert.compareArray(Helper(lengthTracking), [
+ 0,
+ 2,
+ 4,
+ 6,
+ 8,
+ 10
+ ]);
+ assert.compareArray(Helper(lengthTrackingWithOffset), [
+ 4,
+ 6,
+ 8,
+ 10
+ ]);
+ }
+}
+
+TestMap(TypedArrayMapHelper, true);
+TestMap(ArrayMapHelper, false);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/to-locale-string-number-prototype-to-locale-string-grows-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/to-locale-string-number-prototype-to-locale-string-grows-strict.js
new file mode 100644
index 0000000000..c1cba050b7
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/to-locale-string-number-prototype-to-locale-string-grows-strict.js
@@ -0,0 +1,116 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from ToLocaleStringNumberPrototypeToLocaleStringGrows test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+const TypedArrayToLocaleStringHelper = (ta, ...rest) => {
+ return ta.toLocaleString(...rest);
+};
+
+const ArrayToLocaleStringHelper = (ta, ...rest) => {
+ return Array.prototype.toLocaleString.call(ta, ...rest);
+};
+
+function ToLocaleStringNumberPrototypeToLocaleStringGrows(toLocaleStringHelper) {
+ const oldNumberPrototypeToLocaleString = Number.prototype.toLocaleString;
+ const oldBigIntPrototypeToLocaleString = BigInt.prototype.toLocaleString;
+
+ // Growing + fixed-length TA.
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ let resizeAfter = 2;
+ Number.prototype.toLocaleString = function () {
+ --resizeAfter;
+ if (resizeAfter == 0) {
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ }
+ return oldNumberPrototypeToLocaleString.call(this);
+ };
+ BigInt.prototype.toLocaleString = function () {
+ --resizeAfter;
+ if (resizeAfter == 0) {
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ }
+ return oldBigIntPrototypeToLocaleString.call(this);
+ };
+
+ // We iterate 4 elements since it was the starting length. Resizing doesn't
+ // affect the TA.
+ assert.sameValue(toLocaleStringHelper(fixedLength), '0,0,0,0');
+ }
+
+ // Growing + length-tracking TA.
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const lengthTracking = new ctor(rab);
+ let resizeAfter = 2;
+ Number.prototype.toLocaleString = function () {
+ --resizeAfter;
+ if (resizeAfter == 0) {
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ }
+ return oldNumberPrototypeToLocaleString.call(this);
+ };
+ BigInt.prototype.toLocaleString = function () {
+ --resizeAfter;
+ if (resizeAfter == 0) {
+ rab.resize(6 * ctor.BYTES_PER_ELEMENT);
+ }
+ return oldBigIntPrototypeToLocaleString.call(this);
+ };
+
+ // We iterate 4 elements since it was the starting length.
+ assert.sameValue(toLocaleStringHelper(lengthTracking), '0,0,0,0');
+ }
+ Number.prototype.toLocaleString = oldNumberPrototypeToLocaleString;
+ BigInt.prototype.toLocaleString = oldBigIntPrototypeToLocaleString;
+}
+
+ToLocaleStringNumberPrototypeToLocaleStringGrows(TypedArrayToLocaleStringHelper);
+ToLocaleStringNumberPrototypeToLocaleStringGrows(ArrayToLocaleStringHelper);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/to-locale-string-number-prototype-to-locale-string-shrinks-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/to-locale-string-number-prototype-to-locale-string-shrinks-strict.js
new file mode 100644
index 0000000000..ba448bb4dc
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/to-locale-string-number-prototype-to-locale-string-shrinks-strict.js
@@ -0,0 +1,117 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from ToLocaleStringNumberPrototypeToLocaleStringShrinks test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+const TypedArrayToLocaleStringHelper = (ta, ...rest) => {
+ return ta.toLocaleString(...rest);
+};
+
+const ArrayToLocaleStringHelper = (ta, ...rest) => {
+ return Array.prototype.toLocaleString.call(ta, ...rest);
+};
+
+function ToLocaleStringNumberPrototypeToLocaleStringShrinks(toLocaleStringHelper) {
+ const oldNumberPrototypeToLocaleString = Number.prototype.toLocaleString;
+ const oldBigIntPrototypeToLocaleString = BigInt.prototype.toLocaleString;
+
+ // Shrinking + fixed-length TA.
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const fixedLength = new ctor(rab, 0, 4);
+ let resizeAfter = 2;
+ Number.prototype.toLocaleString = function () {
+ --resizeAfter;
+ if (resizeAfter == 0) {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ }
+ return oldNumberPrototypeToLocaleString.call(this);
+ };
+ BigInt.prototype.toLocaleString = function () {
+ --resizeAfter;
+ if (resizeAfter == 0) {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ }
+ return oldBigIntPrototypeToLocaleString.call(this);
+ };
+
+ // We iterate 4 elements, since it was the starting length. The TA goes
+ // OOB after 2 elements.
+ assert.sameValue(toLocaleStringHelper(fixedLength), '0,0,,');
+ }
+
+ // Shrinking + length-tracking TA.
+ for (let ctor of ctors) {
+ const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT);
+ const lengthTracking = new ctor(rab);
+ let resizeAfter = 2;
+ Number.prototype.toLocaleString = function () {
+ --resizeAfter;
+ if (resizeAfter == 0) {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ }
+ return oldNumberPrototypeToLocaleString.call(this);
+ };
+ BigInt.prototype.toLocaleString = function () {
+ --resizeAfter;
+ if (resizeAfter == 0) {
+ rab.resize(2 * ctor.BYTES_PER_ELEMENT);
+ }
+ return oldBigIntPrototypeToLocaleString.call(this);
+ };
+
+ // We iterate 4 elements, since it was the starting length. Elements beyond
+ // the new length are converted to the empty string.
+ assert.sameValue(toLocaleStringHelper(lengthTracking), '0,0,,');
+ }
+ Number.prototype.toLocaleString = oldNumberPrototypeToLocaleString;
+ BigInt.prototype.toLocaleString = oldBigIntPrototypeToLocaleString;
+}
+
+ToLocaleStringNumberPrototypeToLocaleStringShrinks(TypedArrayToLocaleStringHelper);
+ToLocaleStringNumberPrototypeToLocaleStringShrinks(ArrayToLocaleStringHelper);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/typed-array-length-and-byte-length-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/typed-array-length-and-byte-length-strict.js
new file mode 100644
index 0000000000..24d7c90f9c
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/typed-array-length-and-byte-length-strict.js
@@ -0,0 +1,83 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from TypedArrayLengthAndByteLength test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+includes: [compareArray.js]
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+const rab = CreateResizableArrayBuffer(40, 80);
+for (let ctor of ctors) {
+ const ta = new ctor(rab, 0, 3);
+ assert.compareArray(ta.buffer, rab);
+ assert.sameValue(ta.length, 3);
+ assert.sameValue(ta.byteLength, 3 * ctor.BYTES_PER_ELEMENT);
+ const empty_ta = new ctor(rab, 0, 0);
+ assert.compareArray(empty_ta.buffer, rab);
+ assert.sameValue(empty_ta.length, 0);
+ assert.sameValue(empty_ta.byteLength, 0);
+ const ta_with_offset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 3);
+ assert.compareArray(ta_with_offset.buffer, rab);
+ assert.sameValue(ta_with_offset.length, 3);
+ assert.sameValue(ta_with_offset.byteLength, 3 * ctor.BYTES_PER_ELEMENT);
+ const empty_ta_with_offset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 0);
+ assert.compareArray(empty_ta_with_offset.buffer, rab);
+ assert.sameValue(empty_ta_with_offset.length, 0);
+ assert.sameValue(empty_ta_with_offset.byteLength, 0);
+ const length_tracking_ta = new ctor(rab);
+ assert.compareArray(length_tracking_ta.buffer, rab);
+ assert.sameValue(length_tracking_ta.length, 40 / ctor.BYTES_PER_ELEMENT);
+ assert.sameValue(length_tracking_ta.byteLength, 40);
+ const offset = 8;
+ const length_tracking_ta_with_offset = new ctor(rab, offset);
+ assert.compareArray(length_tracking_ta_with_offset.buffer, rab);
+ assert.sameValue(length_tracking_ta_with_offset.length, (40 - offset) / ctor.BYTES_PER_ELEMENT);
+ assert.sameValue(length_tracking_ta_with_offset.byteLength, 40 - offset);
+ const empty_length_tracking_ta_with_offset = new ctor(rab, 40);
+ assert.compareArray(empty_length_tracking_ta_with_offset.buffer, rab);
+ assert.sameValue(empty_length_tracking_ta_with_offset.length, 0);
+ assert.sameValue(empty_length_tracking_ta_with_offset.byteLength, 0);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/typed-array-length-when-resized-out-of-bounds-1-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/typed-array-length-when-resized-out-of-bounds-1-strict.js
new file mode 100644
index 0000000000..632241a333
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/typed-array-length-when-resized-out-of-bounds-1-strict.js
@@ -0,0 +1,81 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from TypedArrayLengthWhenResizedOutOfBounds1 test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+const rab = CreateResizableArrayBuffer(16, 40);
+
+// Create TAs which cover the bytes 0-7.
+let tas_and_lengths = [];
+for (let ctor of ctors) {
+ const length = 8 / ctor.BYTES_PER_ELEMENT;
+ tas_and_lengths.push([
+ new ctor(rab, 0, length),
+ length
+ ]);
+}
+for (let [ta, length] of tas_and_lengths) {
+ assert.sameValue(ta.length, length);
+ assert.sameValue(ta.byteLength, length * ta.BYTES_PER_ELEMENT);
+}
+rab.resize(2);
+for (let [ta, length] of tas_and_lengths) {
+ assert.sameValue(ta.length, 0);
+ assert.sameValue(ta.byteLength, 0);
+}
+// Resize the rab so that it just barely covers the needed 8 bytes.
+rab.resize(8);
+for (let [ta, length] of tas_and_lengths) {
+ assert.sameValue(ta.length, length);
+ assert.sameValue(ta.byteLength, length * ta.BYTES_PER_ELEMENT);
+}
+rab.resize(40);
+for (let [ta, length] of tas_and_lengths) {
+ assert.sameValue(ta.length, length);
+ assert.sameValue(ta.byteLength, length * ta.BYTES_PER_ELEMENT);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/typed-array-length-when-resized-out-of-bounds-2-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/typed-array-length-when-resized-out-of-bounds-2-strict.js
new file mode 100644
index 0000000000..a46a4558af
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/typed-array-length-when-resized-out-of-bounds-2-strict.js
@@ -0,0 +1,87 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from TypedArrayLengthWhenResizedOutOfBounds2 test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+// typed-array-length-when-resized-out-of-bounds-1 but with offsets.
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+const rab = CreateResizableArrayBuffer(20, 40);
+
+// Create TAs which cover the bytes 8-15.
+let tas_and_lengths = [];
+for (let ctor of ctors) {
+ const length = 8 / ctor.BYTES_PER_ELEMENT;
+ tas_and_lengths.push([
+ new ctor(rab, 8, length),
+ length
+ ]);
+}
+for (let [ta, length] of tas_and_lengths) {
+ assert.sameValue(ta.length, length);
+ assert.sameValue(ta.byteLength, length * ta.BYTES_PER_ELEMENT);
+ assert.sameValue(ta.byteOffset, 8);
+}
+rab.resize(10);
+for (let [ta, length] of tas_and_lengths) {
+ assert.sameValue(ta.length, 0);
+ assert.sameValue(ta.byteLength, 0);
+ assert.sameValue(ta.byteOffset, 0);
+}
+// Resize the rab so that it just barely covers the needed 8 bytes.
+rab.resize(16);
+for (let [ta, length] of tas_and_lengths) {
+ assert.sameValue(ta.length, length);
+ assert.sameValue(ta.byteLength, length * ta.BYTES_PER_ELEMENT);
+ assert.sameValue(ta.byteOffset, 8);
+}
+rab.resize(40);
+for (let [ta, length] of tas_and_lengths) {
+ assert.sameValue(ta.length, length);
+ assert.sameValue(ta.byteLength, length * ta.BYTES_PER_ELEMENT);
+ assert.sameValue(ta.byteOffset, 8);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/resizable/typed-array-prototype-strict.js b/js/src/tests/test262/staging/ArrayBuffer/resizable/typed-array-prototype-strict.js
new file mode 100644
index 0000000000..33225c8fd6
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/resizable/typed-array-prototype-strict.js
@@ -0,0 +1,57 @@
+// |reftest| shell-option(--enable-arraybuffer-resizable) skip-if(!ArrayBuffer.prototype.resize||!xulRuntime.shell) -- resizable-arraybuffer is not enabled unconditionally, requires shell-options
+'use strict';
+// Copyright 2021 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-arraybuffer-length
+description: >
+ Automatically ported from TypedArrayPrototype test
+ in V8's mjsunit test typedarray-resizablearraybuffer.js
+features: [resizable-arraybuffer]
+flags: [onlyStrict]
+---*/
+
+class MyUint8Array extends Uint8Array {
+}
+
+class MyFloat32Array extends Float32Array {
+}
+
+class MyBigInt64Array extends BigInt64Array {
+}
+
+const builtinCtors = [
+ Uint8Array,
+ Int8Array,
+ Uint16Array,
+ Int16Array,
+ Uint32Array,
+ Int32Array,
+ Float32Array,
+ Float64Array,
+ Uint8ClampedArray,
+ BigUint64Array,
+ BigInt64Array
+];
+
+const ctors = [
+ ...builtinCtors,
+ MyUint8Array,
+ MyFloat32Array,
+ MyBigInt64Array
+];
+
+function CreateResizableArrayBuffer(byteLength, maxByteLength) {
+ return new ArrayBuffer(byteLength, { maxByteLength: maxByteLength });
+}
+
+const rab = CreateResizableArrayBuffer(40, 80);
+const ab = new ArrayBuffer(80);
+for (let ctor of ctors) {
+ const ta_rab = new ctor(rab, 0, 3);
+ const ta_ab = new ctor(ab, 0, 3);
+ assert.sameValue(ta_ab.__proto__, ta_rab.__proto__);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/ArrayBuffer/shell.js b/js/src/tests/test262/staging/ArrayBuffer/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/ArrayBuffer/shell.js
diff --git a/js/src/tests/test262/staging/Intl402/Temporal/browser.js b/js/src/tests/test262/staging/Intl402/Temporal/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Intl402/Temporal/browser.js
diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/addition-across-lunisolar-leap-months.js b/js/src/tests/test262/staging/Intl402/Temporal/old/addition-across-lunisolar-leap-months.js
new file mode 100644
index 0000000000..6e44e1974f
--- /dev/null
+++ b/js/src/tests/test262/staging/Intl402/Temporal/old/addition-across-lunisolar-leap-months.js
@@ -0,0 +1,96 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-intl
+description: Addition across lunisolar leap months
+features: [Temporal]
+---*/
+
+// Adding years across Hebrew leap month
+var date = Temporal.PlainDate.from({
+ year: 5783,
+ monthCode: "M08",
+ day: 2,
+ calendar: "hebrew"
+});
+var added = date.add({ years: 1 });
+assert.sameValue(added.monthCode, date.monthCode);
+assert.sameValue(added.year, date.year + 1);
+
+// Adding months across Hebrew leap month
+var date = Temporal.PlainDate.from({
+ year: 5783,
+ monthCode: "M08",
+ day: 2,
+ calendar: "hebrew"
+});
+var added = date.add({ months: 13 });
+assert.sameValue(added.monthCode, date.monthCode);
+assert.sameValue(added.year, date.year + 1);
+
+// Adding months and years across Hebrew leap month
+var date = Temporal.PlainDate.from({
+ year: 5783,
+ monthCode: "M08",
+ day: 2,
+ calendar: "hebrew"
+});
+var added = date.add({
+ years: 1,
+ months: 12
+});
+assert.sameValue(added.monthCode, date.monthCode);
+assert.sameValue(added.year, date.year + 2);
+var testChineseData = new Date("2001-02-01T00:00Z").toLocaleString("en-US-u-ca-chinese", {
+ day: "numeric",
+ month: "numeric",
+ year: "numeric",
+ era: "short",
+ timeZone: "UTC"
+});
+var hasOutdatedChineseIcuData = !testChineseData.endsWith("2001");
+
+// Adding years across Chinese leap month"
+if(hasOutdatedChineseIcuData) {
+ var date = Temporal.PlainDate.from({
+ year: 2000,
+ monthCode: "M08",
+ day: 2,
+ calendar: "chinese"
+ });
+ var added = date.add({ years: 1 });
+ assert.sameValue(added.monthCode, date.monthCode);
+ assert.sameValue(added.year, date.year + 1);
+}
+// Adding months across Chinese leap month
+if(hasOutdatedChineseIcuData) {
+ var date = Temporal.PlainDate.from({
+ year: 2000,
+ monthCode: "M08",
+ day: 2,
+ calendar: "chinese"
+ });
+ var added = date.add({ months: 13 });
+ assert.sameValue(added.monthCode, date.monthCode);
+ assert.sameValue(added.year, date.year + 1);
+}
+
+// Adding months and years across Chinese leap month
+if(hasOutdatedChineseIcuData) {
+ var date = Temporal.PlainDate.from({
+ year: 2001,
+ monthCode: "M08",
+ day: 2,
+ calendar: "chinese"
+ });
+ var added = date.add({
+ years: 1,
+ months: 12
+ });
+ assert.sameValue(added.monthCode, date.monthCode);
+ assert.sameValue(added.year, date.year + 2);
+};
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/browser.js b/js/src/tests/test262/staging/Intl402/Temporal/old/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Intl402/Temporal/old/browser.js
diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/date-time-format.js b/js/src/tests/test262/staging/Intl402/Temporal/old/date-time-format.js
new file mode 100644
index 0000000000..2729754353
--- /dev/null
+++ b/js/src/tests/test262/staging/Intl402/Temporal/old/date-time-format.js
@@ -0,0 +1,1668 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-intl
+description: DateTimeFormat
+includes: [deepEqual.js]
+features: [Temporal]
+---*/
+
+// Tolerate implementation variance by expecting consistency without being prescriptive.
+// TODO: can we change tests to be less reliant on CLDR formats while still testing that
+// Temporal and Intl are behaving as expected?
+const usDayPeriodSpace =
+ new Intl.DateTimeFormat("en-US", { timeStyle: "short" })
+ .formatToParts(0)
+ .find((part, i, parts) => part.type === "literal" && parts[i + 1].type === "dayPeriod")?.value || "";
+const usDateRangeSeparator = new Intl.DateTimeFormat("en-US", { dateStyle: "short" })
+ .formatRangeToParts(1 * 86400 * 1000, 366 * 86400 * 1000)
+ .find((part) => part.type === "literal" && part.source === "shared").value;
+const deDateRangeSeparator = new Intl.DateTimeFormat("de-AT", { dateStyle: "short" })
+ .formatRangeToParts(1 * 86400 * 1000, 366 * 86400 * 1000)
+ .find((part) => part.type === "literal" && part.source === "shared").value;
+
+// Workarounds for https:// unicode-org.atlassian.net/browse/CLDR-16243
+const deMonthDayRangeSeparator = new Intl.DateTimeFormat("de-AT", { month: "numeric", day: "numeric" })
+ .formatRangeToParts(1 * 86400 * 1000, 90 * 86400 * 1000)
+ .find((part) => part.type === "literal" && part.source === "shared").value;
+const deMonthYearSeparator = new Intl.DateTimeFormat("de-AT", { year: "numeric", month: "numeric" })
+ .formatToParts(0)
+ .find((part) => part.type === "literal").value;
+const deMonthInYearMonthHasLeadingZero = new Intl.DateTimeFormat("de-AT", {
+ year: "numeric",
+ month: "numeric"
+})
+ .formatToParts(new Date(2000, 3, 1))
+ .find((part) => part.type === "month")
+ .value.startsWith("0");
+
+// should return an Array
+assert(Array.isArray(Intl.DateTimeFormat.supportedLocalesOf()));
+var onlyOnce = (value) => {
+ var obj = {
+ calls: 0,
+ toString() {
+ if (++this.calls > 1) throw new RangeError("prop read twice");
+ return value;
+ }
+ };
+ return obj;
+};
+var optionsAT = { timeZone: onlyOnce("Europe/Vienna") };
+var optionsUS = {
+ calls: 0,
+ value: "America/New_York",
+ get timeZone() {
+ if (++this.calls > 1) throw new RangeError("prop read twice");
+ return this.value;
+ },
+ set timeZone(val) {
+ this.value = val;
+ }
+};
+var localesAT = ["de-AT"];
+var us = new Intl.DateTimeFormat("en-US", optionsUS);
+var at = new Intl.DateTimeFormat(localesAT, optionsAT);
+optionsAT.timeZone = {
+ toString: () => "Bogus/Time-Zone",
+ toJSON: () => "Bogus/Time-Zone"
+};
+optionsUS.timeZone = "Bogus/Time-Zone";
+var us2 = new Intl.DateTimeFormat("en-US");
+var at2 = new Intl.DateTimeFormat(localesAT);
+localesAT[0] = ["invalid locale"];
+var usCalendar = us.resolvedOptions().calendar;
+var atCalendar = at.resolvedOptions().calendar;
+var t1 = "1976-11-18T14:23:30+00:00[UTC]";
+var t2 = "2020-02-20T15:44:56-05:00[America/New_York]";
+var start = new Date("1922-12-30");
+var end = new Date("1991-12-26");
+
+// should work for Instant
+assert.sameValue(us.format(Temporal.Instant.from(t1)), `11/18/1976, 9:23:30${usDayPeriodSpace}AM`);
+assert.sameValue(at.format(Temporal.Instant.from(t1)), "18.11.1976, 15:23:30");
+
+// should work for DateTime
+assert.sameValue(us.format(Temporal.PlainDateTime.from(t1)), `11/18/1976, 2:23:30${usDayPeriodSpace}PM`);
+assert.sameValue(at.format(Temporal.PlainDateTime.from(t1)), "18.11.1976, 14:23:30");
+
+// should work for Time
+assert.sameValue(us.format(Temporal.PlainTime.from(t1)), `2:23:30${usDayPeriodSpace}PM`);
+assert.sameValue(at.format(Temporal.PlainTime.from(t1)), "14:23:30");
+
+// should work for Date
+assert.sameValue(us.format(Temporal.PlainDate.from(t1)), "11/18/1976");
+assert.sameValue(at.format(Temporal.PlainDate.from(t1)), "18.11.1976");
+
+// should work for YearMonth
+var t = Temporal.PlainDate.from(t1);
+assert.sameValue(us.format(t.withCalendar(usCalendar).toPlainYearMonth()), "11/1976");
+assert.sameValue(at.format(t.withCalendar(atCalendar).toPlainYearMonth()), `11${deMonthYearSeparator}1976`);
+
+// should work for MonthDay
+var t = Temporal.PlainDate.from(t1);
+assert.sameValue(us.format(t.withCalendar(usCalendar).toPlainMonthDay()), "11/18");
+assert.sameValue(at.format(t.withCalendar(atCalendar).toPlainMonthDay()), "18.11.");
+
+// should not break legacy Date
+assert.sameValue(us.format(start), "12/29/1922");
+assert.sameValue(at.format(start), "30.12.1922");
+
+// should work for Instant
+assert.deepEqual(us.formatToParts(Temporal.Instant.from(t2)), [
+ {
+ type: "month",
+ value: "2"
+ },
+ {
+ type: "literal",
+ value: "/"
+ },
+ {
+ type: "day",
+ value: "20"
+ },
+ {
+ type: "literal",
+ value: "/"
+ },
+ {
+ type: "year",
+ value: "2020"
+ },
+ {
+ type: "literal",
+ value: ", "
+ },
+ {
+ type: "hour",
+ value: "3"
+ },
+ {
+ type: "literal",
+ value: ":"
+ },
+ {
+ type: "minute",
+ value: "44"
+ },
+ {
+ type: "literal",
+ value: ":"
+ },
+ {
+ type: "second",
+ value: "56"
+ },
+ {
+ type: "literal",
+ value: usDayPeriodSpace
+ },
+ {
+ type: "dayPeriod",
+ value: "PM"
+ }
+]);
+assert.deepEqual(at.formatToParts(Temporal.Instant.from(t2)), [
+ {
+ type: "day",
+ value: "20"
+ },
+ {
+ type: "literal",
+ value: "."
+ },
+ {
+ type: "month",
+ value: "2"
+ },
+ {
+ type: "literal",
+ value: "."
+ },
+ {
+ type: "year",
+ value: "2020"
+ },
+ {
+ type: "literal",
+ value: ", "
+ },
+ {
+ type: "hour",
+ value: "21"
+ },
+ {
+ type: "literal",
+ value: ":"
+ },
+ {
+ type: "minute",
+ value: "44"
+ },
+ {
+ type: "literal",
+ value: ":"
+ },
+ {
+ type: "second",
+ value: "56"
+ }
+]);
+// should work for DateTime
+assert.deepEqual(us.formatToParts(Temporal.PlainDateTime.from(t2)), [
+ {
+ type: "month",
+ value: "2"
+ },
+ {
+ type: "literal",
+ value: "/"
+ },
+ {
+ type: "day",
+ value: "20"
+ },
+ {
+ type: "literal",
+ value: "/"
+ },
+ {
+ type: "year",
+ value: "2020"
+ },
+ {
+ type: "literal",
+ value: ", "
+ },
+ {
+ type: "hour",
+ value: "3"
+ },
+ {
+ type: "literal",
+ value: ":"
+ },
+ {
+ type: "minute",
+ value: "44"
+ },
+ {
+ type: "literal",
+ value: ":"
+ },
+ {
+ type: "second",
+ value: "56"
+ },
+ {
+ type: "literal",
+ value: usDayPeriodSpace
+ },
+ {
+ type: "dayPeriod",
+ value: "PM"
+ }
+]);
+assert.deepEqual(at.formatToParts(Temporal.PlainDateTime.from(t2)), [
+ {
+ type: "day",
+ value: "20"
+ },
+ {
+ type: "literal",
+ value: "."
+ },
+ {
+ type: "month",
+ value: "2"
+ },
+ {
+ type: "literal",
+ value: "."
+ },
+ {
+ type: "year",
+ value: "2020"
+ },
+ {
+ type: "literal",
+ value: ", "
+ },
+ {
+ type: "hour",
+ value: "15"
+ },
+ {
+ type: "literal",
+ value: ":"
+ },
+ {
+ type: "minute",
+ value: "44"
+ },
+ {
+ type: "literal",
+ value: ":"
+ },
+ {
+ type: "second",
+ value: "56"
+ }
+]);
+// should work for Time
+assert.deepEqual(us.formatToParts(Temporal.PlainTime.from(t2)), [
+ {
+ type: "hour",
+ value: "3"
+ },
+ {
+ type: "literal",
+ value: ":"
+ },
+ {
+ type: "minute",
+ value: "44"
+ },
+ {
+ type: "literal",
+ value: ":"
+ },
+ {
+ type: "second",
+ value: "56"
+ },
+ {
+ type: "literal",
+ value: usDayPeriodSpace
+ },
+ {
+ type: "dayPeriod",
+ value: "PM"
+ }
+]);
+assert.deepEqual(at.formatToParts(Temporal.PlainTime.from(t2)), [
+ {
+ type: "hour",
+ value: "15"
+ },
+ {
+ type: "literal",
+ value: ":"
+ },
+ {
+ type: "minute",
+ value: "44"
+ },
+ {
+ type: "literal",
+ value: ":"
+ },
+ {
+ type: "second",
+ value: "56"
+ }
+]);
+// should work for Date
+assert.deepEqual(us.formatToParts(Temporal.PlainDate.from(t2)), [
+ {
+ type: "month",
+ value: "2"
+ },
+ {
+ type: "literal",
+ value: "/"
+ },
+ {
+ type: "day",
+ value: "20"
+ },
+ {
+ type: "literal",
+ value: "/"
+ },
+ {
+ type: "year",
+ value: "2020"
+ }
+]);
+assert.deepEqual(at.formatToParts(Temporal.PlainDate.from(t2)), [
+ {
+ type: "day",
+ value: "20"
+ },
+ {
+ type: "literal",
+ value: "."
+ },
+ {
+ type: "month",
+ value: "2"
+ },
+ {
+ type: "literal",
+ value: "."
+ },
+ {
+ type: "year",
+ value: "2020"
+ }
+]);
+// should work for YearMonth
+var t = Temporal.PlainDate.from(t2);
+assert.deepEqual(us.formatToParts(t.withCalendar(usCalendar).toPlainYearMonth()), [
+ {
+ type: "month",
+ value: "2"
+ },
+ {
+ type: "literal",
+ value: "/"
+ },
+ {
+ type: "year",
+ value: "2020"
+ }
+]);
+assert.deepEqual(at.formatToParts(t.withCalendar(atCalendar).toPlainYearMonth()), [
+ {
+ type: "month",
+ value: deMonthInYearMonthHasLeadingZero ? "02" : "2"
+ },
+ {
+ type: "literal",
+ value: deMonthYearSeparator
+ },
+ {
+ type: "year",
+ value: "2020"
+ }
+]);
+// should work for MonthDay
+var t = Temporal.PlainDate.from(t2);
+assert.deepEqual(us.formatToParts(t.withCalendar(usCalendar).toPlainMonthDay()), [
+ {
+ type: "month",
+ value: "2"
+ },
+ {
+ type: "literal",
+ value: "/"
+ },
+ {
+ type: "day",
+ value: "20"
+ }
+]);
+assert.deepEqual(at.formatToParts(t.withCalendar(atCalendar).toPlainMonthDay()), [
+ {
+ type: "day",
+ value: "20"
+ },
+ {
+ type: "literal",
+ value: "."
+ },
+ {
+ type: "month",
+ value: "2"
+ },
+ {
+ type: "literal",
+ value: "."
+ }
+]);
+// should not break legacy Date
+assert.deepEqual(us.formatToParts(end), [
+ {
+ type: "month",
+ value: "12"
+ },
+ {
+ type: "literal",
+ value: "/"
+ },
+ {
+ type: "day",
+ value: "25"
+ },
+ {
+ type: "literal",
+ value: "/"
+ },
+ {
+ type: "year",
+ value: "1991"
+ }
+]);
+assert.deepEqual(at.formatToParts(end), [
+ {
+ type: "day",
+ value: "26"
+ },
+ {
+ type: "literal",
+ value: "."
+ },
+ {
+ type: "month",
+ value: "12"
+ },
+ {
+ type: "literal",
+ value: "."
+ },
+ {
+ type: "year",
+ value: "1991"
+ }
+]);
+// formatRange
+// should work for Instant
+assert.sameValue(
+ us.formatRange(Temporal.Instant.from(t1), Temporal.Instant.from(t2)),
+ `11/18/1976, 9:23:30${usDayPeriodSpace}AM${usDateRangeSeparator}2/20/2020, 3:44:56${usDayPeriodSpace}PM`
+);
+assert.sameValue(
+ at.formatRange(Temporal.Instant.from(t1), Temporal.Instant.from(t2)),
+ `18.11.1976, 15:23:30${deDateRangeSeparator}20.2.2020, 21:44:56`
+);
+
+// should work for DateTime
+assert.sameValue(
+ us.formatRange(Temporal.PlainDateTime.from(t1), Temporal.PlainDateTime.from(t2)),
+ `11/18/1976, 2:23:30${usDayPeriodSpace}PM${usDateRangeSeparator}2/20/2020, 3:44:56${usDayPeriodSpace}PM`
+);
+assert.sameValue(
+ at.formatRange(Temporal.PlainDateTime.from(t1), Temporal.PlainDateTime.from(t2)),
+ `18.11.1976, 14:23:30${deDateRangeSeparator}20.2.2020, 15:44:56`
+);
+
+// should work for Time
+assert.sameValue(
+ us.formatRange(Temporal.PlainTime.from(t1), Temporal.PlainTime.from(t2)),
+ `2:23:30${usDayPeriodSpace}PM${usDateRangeSeparator}3:44:56${usDayPeriodSpace}PM`
+);
+assert.sameValue(
+ at.formatRange(Temporal.PlainTime.from(t1), Temporal.PlainTime.from(t2)),
+ `14:23:30${deDateRangeSeparator}15:44:56`
+);
+
+// should work for Date
+assert.sameValue(
+ us.formatRange(Temporal.PlainDate.from(t1), Temporal.PlainDate.from(t2)),
+ `11/18/1976${usDateRangeSeparator}2/20/2020`
+);
+assert.sameValue(
+ at.formatRange(Temporal.PlainDate.from(t1), Temporal.PlainDate.from(t2)),
+ `18.11.1976${deDateRangeSeparator}20.02.2020`
+);
+
+// should work for YearMonth
+var date1 = Temporal.PlainDate.from(t1);
+var date2 = Temporal.PlainDate.from(t2);
+assert.sameValue(
+ us.formatRange(date1.withCalendar(usCalendar).toPlainYearMonth(), date2.withCalendar(usCalendar).toPlainYearMonth()),
+ `11/1976${usDateRangeSeparator}2/2020`
+);
+assert.sameValue(
+ at.formatRange(date1.withCalendar(atCalendar).toPlainYearMonth(), date2.withCalendar(atCalendar).toPlainYearMonth()),
+ `11${deMonthYearSeparator}1976${deDateRangeSeparator}02${deMonthYearSeparator}2020`
+);
+
+// should work for MonthDay
+var date1 = Temporal.PlainDate.from(t1);
+var date2 = Temporal.PlainDate.from(t2);
+assert.sameValue(
+ us.formatRange(date2.withCalendar(usCalendar).toPlainMonthDay(), date1.withCalendar(usCalendar).toPlainMonthDay()),
+ `2/20${usDateRangeSeparator}11/18`
+);
+assert.sameValue(
+ at.formatRange(date2.withCalendar(atCalendar).toPlainMonthDay(), date1.withCalendar(atCalendar).toPlainMonthDay()),
+ `20.02${deMonthDayRangeSeparator}18.11.`
+);
+
+// should not break legacy Date
+assert.sameValue(us.formatRange(start, end), `12/29/1922${usDateRangeSeparator}12/25/1991`);
+assert.sameValue(at.formatRange(start, end), `30.12.1922${deDateRangeSeparator}26.12.1991`);
+
+// should throw a RangeError when called with different calendars
+assert.throws(RangeError, () =>
+ us.formatRange(Temporal.PlainDateTime.from(t1), Temporal.PlainDateTime.from(t2).withCalendar("japanese"))
+);
+assert.throws(RangeError, () =>
+ us.formatRange(Temporal.PlainDate.from(t1), Temporal.PlainDate.from(t2).withCalendar("japanese"))
+);
+
+// formatRangeToParts
+// should work for Instant
+assert.deepEqual(us.formatRangeToParts(Temporal.Instant.from(t1), Temporal.Instant.from(t2)), [
+ {
+ type: "month",
+ value: "11",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: "/",
+ source: "startRange"
+ },
+ {
+ type: "day",
+ value: "18",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: "/",
+ source: "startRange"
+ },
+ {
+ type: "year",
+ value: "1976",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: ", ",
+ source: "startRange"
+ },
+ {
+ type: "hour",
+ value: "9",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: ":",
+ source: "startRange"
+ },
+ {
+ type: "minute",
+ value: "23",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: ":",
+ source: "startRange"
+ },
+ {
+ type: "second",
+ value: "30",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: usDayPeriodSpace,
+ source: "startRange"
+ },
+ {
+ type: "dayPeriod",
+ value: "AM",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: usDateRangeSeparator,
+ source: "shared"
+ },
+ {
+ type: "month",
+ value: "2",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: "/",
+ source: "endRange"
+ },
+ {
+ type: "day",
+ value: "20",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: "/",
+ source: "endRange"
+ },
+ {
+ type: "year",
+ value: "2020",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: ", ",
+ source: "endRange"
+ },
+ {
+ type: "hour",
+ value: "3",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: ":",
+ source: "endRange"
+ },
+ {
+ type: "minute",
+ value: "44",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: ":",
+ source: "endRange"
+ },
+ {
+ type: "second",
+ value: "56",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: usDayPeriodSpace,
+ source: "endRange"
+ },
+ {
+ type: "dayPeriod",
+ value: "PM",
+ source: "endRange"
+ }
+]);
+assert.deepEqual(at.formatRangeToParts(Temporal.Instant.from(t1), Temporal.Instant.from(t2)), [
+ {
+ type: "day",
+ value: "18",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: ".",
+ source: "startRange"
+ },
+ {
+ type: "month",
+ value: "11",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: ".",
+ source: "startRange"
+ },
+ {
+ type: "year",
+ value: "1976",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: ", ",
+ source: "startRange"
+ },
+ {
+ type: "hour",
+ value: "15",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: ":",
+ source: "startRange"
+ },
+ {
+ type: "minute",
+ value: "23",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: ":",
+ source: "startRange"
+ },
+ {
+ type: "second",
+ value: "30",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: deDateRangeSeparator,
+ source: "shared"
+ },
+ {
+ type: "day",
+ value: "20",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: ".",
+ source: "endRange"
+ },
+ {
+ type: "month",
+ value: "2",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: ".",
+ source: "endRange"
+ },
+ {
+ type: "year",
+ value: "2020",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: ", ",
+ source: "endRange"
+ },
+ {
+ type: "hour",
+ value: "21",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: ":",
+ source: "endRange"
+ },
+ {
+ type: "minute",
+ value: "44",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: ":",
+ source: "endRange"
+ },
+ {
+ type: "second",
+ value: "56",
+ source: "endRange"
+ }
+]);
+// should work for DateTime
+assert.deepEqual(us.formatRangeToParts(Temporal.PlainDateTime.from(t1), Temporal.PlainDateTime.from(t2)), [
+ {
+ type: "month",
+ value: "11",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: "/",
+ source: "startRange"
+ },
+ {
+ type: "day",
+ value: "18",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: "/",
+ source: "startRange"
+ },
+ {
+ type: "year",
+ value: "1976",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: ", ",
+ source: "startRange"
+ },
+ {
+ type: "hour",
+ value: "2",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: ":",
+ source: "startRange"
+ },
+ {
+ type: "minute",
+ value: "23",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: ":",
+ source: "startRange"
+ },
+ {
+ type: "second",
+ value: "30",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: usDayPeriodSpace,
+ source: "startRange"
+ },
+ {
+ type: "dayPeriod",
+ value: "PM",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: usDateRangeSeparator,
+ source: "shared"
+ },
+ {
+ type: "month",
+ value: "2",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: "/",
+ source: "endRange"
+ },
+ {
+ type: "day",
+ value: "20",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: "/",
+ source: "endRange"
+ },
+ {
+ type: "year",
+ value: "2020",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: ", ",
+ source: "endRange"
+ },
+ {
+ type: "hour",
+ value: "3",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: ":",
+ source: "endRange"
+ },
+ {
+ type: "minute",
+ value: "44",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: ":",
+ source: "endRange"
+ },
+ {
+ type: "second",
+ value: "56",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: usDayPeriodSpace,
+ source: "endRange"
+ },
+ {
+ type: "dayPeriod",
+ value: "PM",
+ source: "endRange"
+ }
+]);
+assert.deepEqual(at.formatRangeToParts(Temporal.PlainDateTime.from(t1), Temporal.PlainDateTime.from(t2)), [
+ {
+ type: "day",
+ value: "18",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: ".",
+ source: "startRange"
+ },
+ {
+ type: "month",
+ value: "11",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: ".",
+ source: "startRange"
+ },
+ {
+ type: "year",
+ value: "1976",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: ", ",
+ source: "startRange"
+ },
+ {
+ type: "hour",
+ value: "14",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: ":",
+ source: "startRange"
+ },
+ {
+ type: "minute",
+ value: "23",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: ":",
+ source: "startRange"
+ },
+ {
+ type: "second",
+ value: "30",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: deDateRangeSeparator,
+ source: "shared"
+ },
+ {
+ type: "day",
+ value: "20",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: ".",
+ source: "endRange"
+ },
+ {
+ type: "month",
+ value: "2",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: ".",
+ source: "endRange"
+ },
+ {
+ type: "year",
+ value: "2020",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: ", ",
+ source: "endRange"
+ },
+ {
+ type: "hour",
+ value: "15",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: ":",
+ source: "endRange"
+ },
+ {
+ type: "minute",
+ value: "44",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: ":",
+ source: "endRange"
+ },
+ {
+ type: "second",
+ value: "56",
+ source: "endRange"
+ }
+]);
+// should work for Time
+assert.deepEqual(us.formatRangeToParts(Temporal.PlainTime.from(t1), Temporal.PlainTime.from(t2)), [
+ {
+ type: "hour",
+ value: "2",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: ":",
+ source: "startRange"
+ },
+ {
+ type: "minute",
+ value: "23",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: ":",
+ source: "startRange"
+ },
+ {
+ type: "second",
+ value: "30",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: usDayPeriodSpace,
+ source: "startRange"
+ },
+ {
+ type: "dayPeriod",
+ value: "PM",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: usDateRangeSeparator,
+ source: "shared"
+ },
+ {
+ type: "hour",
+ value: "3",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: ":",
+ source: "endRange"
+ },
+ {
+ type: "minute",
+ value: "44",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: ":",
+ source: "endRange"
+ },
+ {
+ type: "second",
+ value: "56",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: usDayPeriodSpace,
+ source: "endRange"
+ },
+ {
+ type: "dayPeriod",
+ value: "PM",
+ source: "endRange"
+ }
+]);
+assert.deepEqual(at.formatRangeToParts(Temporal.PlainTime.from(t1), Temporal.PlainTime.from(t2)), [
+ {
+ type: "hour",
+ value: "14",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: ":",
+ source: "startRange"
+ },
+ {
+ type: "minute",
+ value: "23",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: ":",
+ source: "startRange"
+ },
+ {
+ type: "second",
+ value: "30",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: deDateRangeSeparator,
+ source: "shared"
+ },
+ {
+ type: "hour",
+ value: "15",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: ":",
+ source: "endRange"
+ },
+ {
+ type: "minute",
+ value: "44",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: ":",
+ source: "endRange"
+ },
+ {
+ type: "second",
+ value: "56",
+ source: "endRange"
+ }
+]);
+// should work for Date
+assert.deepEqual(us.formatRangeToParts(Temporal.PlainDate.from(t1), Temporal.PlainDate.from(t2)), [
+ {
+ type: "month",
+ value: "11",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: "/",
+ source: "startRange"
+ },
+ {
+ type: "day",
+ value: "18",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: "/",
+ source: "startRange"
+ },
+ {
+ type: "year",
+ value: "1976",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: usDateRangeSeparator,
+ source: "shared"
+ },
+ {
+ type: "month",
+ value: "2",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: "/",
+ source: "endRange"
+ },
+ {
+ type: "day",
+ value: "20",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: "/",
+ source: "endRange"
+ },
+ {
+ type: "year",
+ value: "2020",
+ source: "endRange"
+ }
+]);
+assert.deepEqual(at.formatRangeToParts(Temporal.PlainDate.from(t1), Temporal.PlainDate.from(t2)), [
+ {
+ type: "day",
+ value: "18",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: ".",
+ source: "startRange"
+ },
+ {
+ type: "month",
+ value: "11",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: ".",
+ source: "startRange"
+ },
+ {
+ type: "year",
+ value: "1976",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: deDateRangeSeparator,
+ source: "shared"
+ },
+ {
+ type: "day",
+ value: "20",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: ".",
+ source: "endRange"
+ },
+ {
+ type: "month",
+ value: "02",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: ".",
+ source: "endRange"
+ },
+ {
+ type: "year",
+ value: "2020",
+ source: "endRange"
+ }
+]);
+// should work for YearMonth
+var date1 = Temporal.PlainDate.from(t1);
+var date2 = Temporal.PlainDate.from(t2);
+assert.deepEqual(
+ us.formatRangeToParts(
+ date1.withCalendar(usCalendar).toPlainYearMonth(),
+ date2.withCalendar(usCalendar).toPlainYearMonth()
+ ),
+ [
+ {
+ type: "month",
+ value: "11",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: "/",
+ source: "startRange"
+ },
+ {
+ type: "year",
+ value: "1976",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: usDateRangeSeparator,
+ source: "shared"
+ },
+ {
+ type: "month",
+ value: "2",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: "/",
+ source: "endRange"
+ },
+ {
+ type: "year",
+ value: "2020",
+ source: "endRange"
+ }
+ ]
+);
+assert.deepEqual(
+ at.formatRangeToParts(
+ date1.withCalendar(atCalendar).toPlainYearMonth(),
+ date2.withCalendar(atCalendar).toPlainYearMonth()
+ ),
+ [
+ {
+ type: "month",
+ value: "11",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: deMonthYearSeparator,
+ source: "startRange"
+ },
+ {
+ type: "year",
+ value: "1976",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: deDateRangeSeparator,
+ source: "shared"
+ },
+ {
+ type: "month",
+ value: "02",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: deMonthYearSeparator,
+ source: "endRange"
+ },
+ {
+ type: "year",
+ value: "2020",
+ source: "endRange"
+ }
+ ]
+);
+// should work for MonthDay
+var date1 = Temporal.PlainDate.from(t1);
+var date2 = Temporal.PlainDate.from(t2);
+assert.deepEqual(
+ us.formatRangeToParts(
+ date2.withCalendar(usCalendar).toPlainMonthDay(),
+ date1.withCalendar(usCalendar).toPlainMonthDay()
+ ),
+ [
+ {
+ type: "month",
+ value: "2",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: "/",
+ source: "startRange"
+ },
+ {
+ type: "day",
+ value: "20",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: usDateRangeSeparator,
+ source: "shared"
+ },
+ {
+ type: "month",
+ value: "11",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: "/",
+ source: "endRange"
+ },
+ {
+ type: "day",
+ value: "18",
+ source: "endRange"
+ }
+ ]
+);
+assert.deepEqual(
+ at.formatRangeToParts(
+ date2.withCalendar(atCalendar).toPlainMonthDay(),
+ date1.withCalendar(atCalendar).toPlainMonthDay()
+ ),
+ [
+ {
+ type: "day",
+ value: "20",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: ".",
+ source: "startRange"
+ },
+ {
+ type: "month",
+ value: "02",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: deMonthDayRangeSeparator,
+ source: "shared"
+ },
+ {
+ type: "day",
+ value: "18",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: ".",
+ source: "endRange"
+ },
+ {
+ type: "month",
+ value: "11",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: ".",
+ source: "shared"
+ }
+ ]
+);
+// should not break legacy Date
+assert.deepEqual(us.formatRangeToParts(start, end), [
+ {
+ type: "month",
+ value: "12",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: "/",
+ source: "startRange"
+ },
+ {
+ type: "day",
+ value: "29",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: "/",
+ source: "startRange"
+ },
+ {
+ type: "year",
+ value: "1922",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: usDateRangeSeparator,
+ source: "shared"
+ },
+ {
+ type: "month",
+ value: "12",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: "/",
+ source: "endRange"
+ },
+ {
+ type: "day",
+ value: "25",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: "/",
+ source: "endRange"
+ },
+ {
+ type: "year",
+ value: "1991",
+ source: "endRange"
+ }
+]);
+assert.deepEqual(at.formatRangeToParts(start, end), [
+ {
+ type: "day",
+ value: "30",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: ".",
+ source: "startRange"
+ },
+ {
+ type: "month",
+ value: "12",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: ".",
+ source: "startRange"
+ },
+ {
+ type: "year",
+ value: "1922",
+ source: "startRange"
+ },
+ {
+ type: "literal",
+ value: deDateRangeSeparator,
+ source: "shared"
+ },
+ {
+ type: "day",
+ value: "26",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: ".",
+ source: "endRange"
+ },
+ {
+ type: "month",
+ value: "12",
+ source: "endRange"
+ },
+ {
+ type: "literal",
+ value: ".",
+ source: "endRange"
+ },
+ {
+ type: "year",
+ value: "1991",
+ source: "endRange"
+ }
+]);
+// should throw a TypeError when called with dissimilar types
+assert.throws(TypeError, () => at.formatRangeToParts(Temporal.Instant.from(t1), Temporal.PlainDateTime.from(t2)));
+// should throw a RangeError when called with different calendars
+assert.throws(RangeError, () =>
+ at.formatRangeToParts(Temporal.PlainDateTime.from(t1), Temporal.PlainDateTime.from(t2).withCalendar("japanese"))
+);
+assert.throws(RangeError, () =>
+ at.formatRangeToParts(Temporal.PlainDate.from(t1), Temporal.PlainDate.from(t2).withCalendar("japanese"))
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/date-toLocaleString.js b/js/src/tests/test262/staging/Intl402/Temporal/old/date-toLocaleString.js
new file mode 100644
index 0000000000..973705f4ac
--- /dev/null
+++ b/js/src/tests/test262/staging/Intl402/Temporal/old/date-toLocaleString.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-intl
+description: date.toLocaleString()
+features: [Temporal]
+---*/
+
+function maybeGetWeekdayOnlyFormat() {
+ const fmt = new Intl.DateTimeFormat('en', { weekday: 'long', timeZone: 'Europe/Vienna' });
+ if (
+ ['era', 'year', 'month', 'day', 'hour', 'minute', 'second', 'timeZoneName'].some(
+ (prop) => prop in fmt.resolvedOptions()
+ )
+ ) {
+ //no weekday-only format available
+ return null;
+ }
+ return fmt;
+}
+
+var date = Temporal.PlainDate.from("1976-11-18T15:23:30");
+assert.sameValue(`${ date.toLocaleString("en-US", { timeZone: "America/New_York" }) }`, "11/18/1976");
+assert.sameValue(`${ date.toLocaleString("de-AT", { timeZone: "Europe/Vienna" }) }`, "18.11.1976");
+var fmt = maybeGetWeekdayOnlyFormat();
+if (fmt) assert.sameValue(fmt.format(date), "Thursday");
+
+// should ignore units not in the data type
+assert.sameValue(date.toLocaleString("en-US", { timeZoneName: "long" }), "11/18/1976");
+assert.sameValue(date.toLocaleString("en-US", { hour: "numeric" }), "11/18/1976");
+assert.sameValue(date.toLocaleString("en-US", { minute: "numeric" }), "11/18/1976");
+assert.sameValue(date.toLocaleString("en-US", { second: "numeric" }), "11/18/1976");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/datetime-toLocaleString.js b/js/src/tests/test262/staging/Intl402/Temporal/old/datetime-toLocaleString.js
new file mode 100644
index 0000000000..3cdced3faa
--- /dev/null
+++ b/js/src/tests/test262/staging/Intl402/Temporal/old/datetime-toLocaleString.js
@@ -0,0 +1,54 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-intl
+description: datetime.toLocaleString()
+features: [Temporal]
+---*/
+
+// Tolerate implementation variance by expecting consistency without being prescriptive.
+// TODO: can we change tests to be less reliant on CLDR formats while still testing that
+// Temporal and Intl are behaving as expected?
+const usDayPeriodSpace =
+ new Intl.DateTimeFormat("en-US", { timeStyle: "short" })
+ .formatToParts(0)
+ .find((part, i, parts) => part.type === "literal" && parts[i + 1].type === "dayPeriod")?.value || "";
+
+function maybeGetWeekdayOnlyFormat() {
+ const fmt = new Intl.DateTimeFormat("en-US", { weekday: "long", timeZone: "Europe/Vienna" });
+ if (
+ ["era", "year", "month", "day", "hour", "minute", "second", "timeZoneName"].some(
+ (prop) => prop in fmt.resolvedOptions()
+ )
+ ) {
+ // no weekday-only format available
+ return null;
+ }
+ return fmt;
+}
+
+var datetime = Temporal.PlainDateTime.from("1976-11-18T15:23:30");
+assert.sameValue(
+ `${datetime.toLocaleString("en-US", { timeZone: "America/New_York" })}`,
+ `11/18/1976, 3:23:30${usDayPeriodSpace}PM`
+);
+assert.sameValue(`${datetime.toLocaleString("de-AT", { timeZone: "Europe/Vienna" })}`, "18.11.1976, 15:23:30");
+var fmt = maybeGetWeekdayOnlyFormat();
+if (fmt) assert.sameValue(fmt.format(datetime), "Thursday");
+
+// should ignore units not in the data type
+assert.sameValue(
+ datetime.toLocaleString("en-US", { timeZoneName: "long" }),
+ `11/18/1976, 3:23:30${usDayPeriodSpace}PM`
+);
+
+// should use compatible disambiguation option
+var dstStart = new Temporal.PlainDateTime(2020, 3, 8, 2, 30);
+assert.sameValue(
+ `${dstStart.toLocaleString("en-US", { timeZone: "America/Los_Angeles" })}`,
+ `3/8/2020, 3:30:00${usDayPeriodSpace}AM`
+);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/duration-arithmetic-dst.js b/js/src/tests/test262/staging/Intl402/Temporal/old/duration-arithmetic-dst.js
new file mode 100644
index 0000000000..08e9c8b428
--- /dev/null
+++ b/js/src/tests/test262/staging/Intl402/Temporal/old/duration-arithmetic-dst.js
@@ -0,0 +1,76 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-duration-objects
+description: >
+ Various DST arithmetic tests that it's impractical to do without a time zone
+ database in the implementation
+features: [Temporal]
+---*/
+
+// Tests for arithmetic that start inside a repeated hour, and end in a skipped
+// hour. We have TemporalHelpers.springForwardFallBackTimeZone which is
+// sufficient to test this for Temporal.Duration.prototype.add, and
+// Temporal.Duration.prototype.round, but it's impractical to replicate all the
+// TZDB data for testing it with other methods such as subtract() where we need
+// to calculate to the _next_ transition
+
+var skippedHourDay = Temporal.ZonedDateTime.from("2019-03-10T00:00[America/Vancouver]");
+var repeatedHourDay = Temporal.ZonedDateTime.from("2019-11-03T00:00[America/Vancouver]");
+var inRepeatedHour = Temporal.ZonedDateTime.from("2019-11-03T01:00-07:00[America/Vancouver]");
+
+// subtract()
+
+var oneDay = new Temporal.Duration(0, 0, 0, 1);
+assert.sameValue(`${ Temporal.Duration.from({
+ days: 127,
+ hours: 1
+}).subtract(oneDay, { relativeTo: inRepeatedHour }) }`, "P126DT1H");
+var hours24 = new Temporal.Duration(0, 0, 0, 0, 24);
+assert.sameValue(`${ Temporal.Duration.from({
+ days: 127,
+ hours: 1
+}).subtract(hours24, { relativeTo: inRepeatedHour }) }`, "P126D");
+
+// total()
+var totalDays = Temporal.Duration.from({
+ days: 126,
+ hours: 1
+}).total({
+ unit: "days",
+ relativeTo: inRepeatedHour
+});
+assert(Math.abs(totalDays - (126 + 1 / 23)) < Number.EPSILON);
+assert.sameValue(Temporal.Duration.from({
+ days: 126,
+ hours: 1
+}).total({
+ unit: "hours",
+ relativeTo: inRepeatedHour
+}), 3026);
+
+// Tests for casting relativeTo to ZonedDateTime when possible:
+// Without a TZDB, it's not possible to get a ZonedDateTime with DST from a
+// string.
+
+assert.sameValue(
+ `${ oneDay.add(hours24, { relativeTo: "2019-11-02T00:00[America/Vancouver]" }) }`,
+ "P1DT24H"
+);
+var hours25 = new Temporal.Duration(0, 0, 0, 0, 25);
+assert.sameValue(`${ hours25.round({
+ largestUnit: "days",
+ relativeTo: "2019-11-03T00:00[America/Vancouver]"
+}) }`, "P1D");
+assert.sameValue(
+ `${ oneDay.subtract(hours24, { relativeTo: "2019-11-03T00:00[America/Vancouver]" }) }`,
+ "PT1H"
+);
+assert.sameValue(oneDay.total({
+ unit: "hours",
+ relativeTo: "2019-11-03T00:00[America/Vancouver]"
+}), 25);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/hebrew-leap-months.js b/js/src/tests/test262/staging/Intl402/Temporal/old/hebrew-leap-months.js
new file mode 100644
index 0000000000..bdd410c4cf
--- /dev/null
+++ b/js/src/tests/test262/staging/Intl402/Temporal/old/hebrew-leap-months.js
@@ -0,0 +1,63 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-intl
+description: Hebrew leap months
+features: [Temporal]
+---*/
+
+// Valid leap month: Adar I 5779
+var date = Temporal.PlainDate.from({
+ year: 5779,
+ month: 6,
+ day: 1,
+ calendar: "hebrew"
+});
+assert.sameValue(date.month, 6);
+assert.sameValue(date.monthCode, "M05L");
+assert.sameValue(date.day, 1);
+date = Temporal.PlainDate.from({
+ year: 5779,
+ monthCode: "M05L",
+ day: 1,
+ calendar: "hebrew"
+});
+assert.sameValue(date.month, 6);
+assert.sameValue(date.monthCode, "M05L");
+assert.sameValue(date.day, 1);
+
+// Creating dates in later months in a leap year
+var date = Temporal.PlainDate.from({
+ year: 5779,
+ month: 7,
+ day: 1,
+ calendar: "hebrew"
+});
+assert.sameValue(date.month, 7);
+assert.sameValue(date.monthCode, "M06");
+assert.sameValue(date.day, 1);
+date = Temporal.PlainDate.from({
+ year: 5779,
+ monthCode: "M06",
+ day: 1,
+ calendar: "hebrew"
+});
+assert.sameValue(date.month, 7);
+assert.sameValue(date.monthCode, "M06");
+assert.sameValue(date.day, 1);
+
+// Invalid leap months: e.g. M02L
+for (var i = 1; i <= 12; i++) {
+ if (i === 5)
+ continue;
+ assert.throws(RangeError, () => Temporal.PlainDate.from({
+ year: 5779,
+ monthCode: `M${ i.toString().padStart(2, "0") }L`,
+ day: 1,
+ calendar: "hebrew"
+ }));
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/indian-calendar.js b/js/src/tests/test262/staging/Intl402/Temporal/old/indian-calendar.js
new file mode 100644
index 0000000000..1e82b4bc3f
--- /dev/null
+++ b/js/src/tests/test262/staging/Intl402/Temporal/old/indian-calendar.js
@@ -0,0 +1,39 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-intl
+description: Indian calendar
+features: [Temporal]
+---*/
+
+
+// throws in Node 12 & 14 before 1 CE
+var vulnerableToBceBug = new Date("0000-01-01T00:00Z").toLocaleDateString("en-US-u-ca-indian", { timeZone: "UTC" }) !== "10/11/-79 Saka";
+if (vulnerableToBceBug) {
+ assert.throws(RangeError, () => Temporal.PlainDate.from("0000-01-01").withCalendar("indian").day);
+}
+
+// handles leap days
+var leapYearFirstDay = Temporal.PlainDate.from("2004-03-21[u-ca=indian]");
+assert.sameValue(leapYearFirstDay.year, 2004 - 78);
+assert.sameValue(leapYearFirstDay.month, 1);
+assert.sameValue(leapYearFirstDay.day, 1);
+var leapYearLastDay = leapYearFirstDay.with({ day: 31 });
+assert.sameValue(leapYearLastDay.year, 2004 - 78);
+assert.sameValue(leapYearLastDay.month, 1);
+assert.sameValue(leapYearLastDay.day, 31);
+
+// handles non-leap years
+var nonLeapYearFirstDay = Temporal.PlainDate.from("2005-03-22[u-ca=indian]");
+assert.sameValue(nonLeapYearFirstDay.year, 2005 - 78);
+assert.sameValue(nonLeapYearFirstDay.month, 1);
+assert.sameValue(nonLeapYearFirstDay.day, 1);
+var leapYearLastDay = nonLeapYearFirstDay.with({ day: 31 });
+assert.sameValue(leapYearLastDay.year, 2005 - 78);
+assert.sameValue(leapYearLastDay.month, 1);
+assert.sameValue(leapYearLastDay.day, 30);
+assert.throws(RangeError, () => nonLeapYearFirstDay.with({ day: 31 }, { overflow: "reject" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/instant-toLocaleString.js b/js/src/tests/test262/staging/Intl402/Temporal/old/instant-toLocaleString.js
new file mode 100644
index 0000000000..5854d0dcdf
--- /dev/null
+++ b/js/src/tests/test262/staging/Intl402/Temporal/old/instant-toLocaleString.js
@@ -0,0 +1,48 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-intl
+description: Instant.toLocaleString()
+features: [Temporal]
+---*/
+
+// Tolerate implementation variance by expecting consistency without being prescriptive.
+// TODO: can we change tests to be less reliant on CLDR formats while still testing that
+// Temporal and Intl are behaving as expected?
+const usDayPeriodSpace =
+ new Intl.DateTimeFormat("en-US", { timeStyle: "short" })
+ .formatToParts(0)
+ .find((part, i, parts) => part.type === "literal" && parts[i + 1].type === "dayPeriod")?.value || "";
+
+function maybeGetWeekdayOnlyFormat() {
+ const fmt = new Intl.DateTimeFormat("en-US", { weekday: "long", timeZone: "Europe/Vienna" });
+ if (
+ ["era", "year", "month", "day", "hour", "minute", "second", "timeZoneName"].some(
+ (prop) => prop in fmt.resolvedOptions()
+ )
+ ) {
+ // no weekday-only format available
+ return null;
+ }
+ return fmt;
+}
+
+var instant = Temporal.Instant.from("1976-11-18T14:23:30Z");
+assert.sameValue(
+ `${instant.toLocaleString("en-US", { timeZone: "America/New_York" })}`,
+ `11/18/1976, 9:23:30${usDayPeriodSpace}AM`
+);
+assert.sameValue(`${instant.toLocaleString("de-AT", { timeZone: "Europe/Vienna" })}`, "18.11.1976, 15:23:30");
+var fmt = maybeGetWeekdayOnlyFormat();
+if (fmt) assert.sameValue(fmt.format(instant), "Thursday");
+
+// outputs timeZoneName if requested
+var str = instant.toLocaleString("en-US", {
+ timeZone: "America/New_York",
+ timeZoneName: "short"
+});
+assert(str.includes("EST"));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/islamic-calendars.js b/js/src/tests/test262/staging/Intl402/Temporal/old/islamic-calendars.js
new file mode 100644
index 0000000000..c0726b0cae
--- /dev/null
+++ b/js/src/tests/test262/staging/Intl402/Temporal/old/islamic-calendars.js
@@ -0,0 +1,94 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Justin Grant. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-intl
+description: Islamic calendars (note there are 6 variants)
+features: [Temporal]
+---*/
+
+// Test data obtained from ICU
+
+const tests = [
+ {
+ calendar: "islamic",
+ inLeapYear: false,
+ daysInYear: 354,
+ daysInMonth12: 29,
+ isoYear: 2023,
+ isoMonth: 7,
+ isoDay: 18
+ },
+ {
+ calendar: "islamic-umalqura",
+ inLeapYear: false,
+ daysInYear: 354,
+ daysInMonth12: 30,
+ isoYear: 2023,
+ isoMonth: 7,
+ isoDay: 19
+ },
+ {
+ calendar: "islamic-civil",
+ inLeapYear: true,
+ daysInYear: 355,
+ daysInMonth12: 30,
+ isoYear: 2023,
+ isoMonth: 7,
+ isoDay: 19
+ },
+ {
+ calendar: "islamicc", // deprecated version of islamic-civil
+ inLeapYear: true,
+ daysInYear: 355,
+ daysInMonth12: 30,
+ isoYear: 2023,
+ isoMonth: 7,
+ isoDay: 19
+ },
+ {
+ calendar: "islamic-rgsa",
+ inLeapYear: false,
+ daysInYear: 354,
+ daysInMonth12: 29,
+ isoYear: 2023,
+ isoMonth: 7,
+ isoDay: 18
+ },
+ {
+ calendar: "islamic-tbla",
+ inLeapYear: true,
+ daysInYear: 355,
+ daysInMonth12: 30,
+ isoYear: 2023,
+ isoMonth: 7,
+ isoDay: 18
+ }
+];
+
+for (const test of tests) {
+ const { calendar, inLeapYear, daysInYear, daysInMonth12, isoYear, isoMonth, isoDay } = test;
+ const year = 1445;
+ const date = Temporal.PlainDate.from({ year, month: 1, day: 1, calendar });
+ const isoFields = date.getISOFields();
+ assert.sameValue(date.calendarId, calendar);
+ assert.sameValue(date.year, year);
+ assert.sameValue(date.month, 1);
+ assert.sameValue(date.monthCode, "M01");
+ assert.sameValue(date.day, 1);
+ assert.sameValue(date.inLeapYear, inLeapYear);
+ assert.sameValue(date.daysInYear, daysInYear);
+ assert.sameValue(date.monthsInYear, 12);
+ assert.sameValue(date.dayOfYear, 1);
+ const startOfNextYear = date.with({ year: year + 1 });
+ const lastDayOfThisYear = startOfNextYear.subtract({ days: 1 });
+ assert.sameValue(lastDayOfThisYear.dayOfYear, daysInYear);
+ const dateMonth12 = date.with({ month: 12 });
+ assert.sameValue(dateMonth12.daysInMonth, daysInMonth12);
+ assert.sameValue(isoYear, isoFields.isoYear);
+ assert.sameValue(isoMonth, isoFields.isoMonth);
+ assert.sameValue(isoDay, isoFields.isoDay);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/japanese-era.js b/js/src/tests/test262/staging/Intl402/Temporal/old/japanese-era.js
new file mode 100644
index 0000000000..47e16fbcb5
--- /dev/null
+++ b/js/src/tests/test262/staging/Intl402/Temporal/old/japanese-era.js
@@ -0,0 +1,140 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-intl
+description: Japanese eras
+features: [Temporal]
+---*/
+
+
+// Reiwa (2019-)
+var date = Temporal.PlainDate.from({
+ era: "reiwa",
+ eraYear: 2,
+ month: 1,
+ day: 1,
+ calendar: "japanese"
+});
+assert.sameValue(`${ date }`, "2020-01-01[u-ca=japanese]");
+
+// Heisei (1989-2019)
+var date = Temporal.PlainDate.from({
+ era: "heisei",
+ eraYear: 2,
+ month: 1,
+ day: 1,
+ calendar: "japanese"
+});
+assert.sameValue(`${ date }`, "1990-01-01[u-ca=japanese]");
+
+// Showa (1926-1989)
+var date = Temporal.PlainDate.from({
+ era: "showa",
+ eraYear: 2,
+ month: 1,
+ day: 1,
+ calendar: "japanese"
+});
+assert.sameValue(`${ date }`, "1927-01-01[u-ca=japanese]");
+
+// Taisho (1912-1926)
+var date = Temporal.PlainDate.from({
+ era: "taisho",
+ eraYear: 2,
+ month: 1,
+ day: 1,
+ calendar: "japanese"
+});
+assert.sameValue(`${ date }`, "1913-01-01[u-ca=japanese]");
+
+// Meiji (1868-1912)
+var date = Temporal.PlainDate.from({
+ era: "meiji",
+ eraYear: 2,
+ month: 1,
+ day: 1,
+ calendar: "japanese"
+});
+assert.sameValue(`${ date }`, "1869-01-01[u-ca=japanese]");
+
+// Dates in same year before Japanese era starts will resolve to previous era
+var date = Temporal.PlainDate.from({
+ era: "reiwa",
+ eraYear: 1,
+ month: 1,
+ day: 1,
+ calendar: "japanese"
+});
+assert.sameValue(`${ date }`, "2019-01-01[u-ca=japanese]");
+assert.sameValue(date.era, "heisei");
+assert.sameValue(date.eraYear, 31);
+
+date = Temporal.PlainDate.from({
+ era: "heisei",
+ eraYear: 1,
+ month: 1,
+ day: 1,
+ calendar: "japanese"
+});
+assert.sameValue(`${ date }`, "1989-01-01[u-ca=japanese]");
+assert.sameValue(date.era, "showa");
+assert.sameValue(date.eraYear, 64);
+date = Temporal.PlainDate.from({
+ era: "showa",
+ eraYear: 1,
+ month: 1,
+ day: 1,
+ calendar: "japanese"
+});
+assert.sameValue(`${ date }`, "1926-01-01[u-ca=japanese]");
+assert.sameValue(date.era, "taisho");
+assert.sameValue(date.eraYear, 15);
+
+date = Temporal.PlainDate.from({
+ era: "taisho",
+ eraYear: 1,
+ month: 1,
+ day: 1,
+ calendar: "japanese"
+});
+assert.sameValue(`${ date }`, "1912-01-01[u-ca=japanese]");
+assert.sameValue(date.era, "meiji");
+assert.sameValue(date.eraYear, 45);
+
+date = Temporal.PlainDate.from({
+ era: "meiji",
+ eraYear: 1,
+ month: 1,
+ day: 1,
+ calendar: "japanese"
+});
+assert.sameValue(`${ date }`, "1868-01-01[u-ca=japanese]");
+assert.sameValue(date.era, "ce");
+assert.sameValue(date.eraYear, 1868);
+
+// Verify that CE and BCE eras (before Meiji) are recognized
+date = Temporal.PlainDate.from({
+ era: "ce",
+ eraYear: 1000,
+ month: 1,
+ day: 1,
+ calendar: "japanese"
+});
+assert.sameValue(`${date}`, "1000-01-01[u-ca=japanese]");
+assert.sameValue(date.era, "ce");
+assert.sameValue(date.eraYear, 1000);
+
+date = Temporal.PlainDate.from({
+ era: "bce",
+ eraYear: 1,
+ month: 1,
+ day: 1,
+ calendar: "japanese"
+});
+assert.sameValue(`${date}`, "0000-01-01[u-ca=japanese]");
+assert.sameValue(date.era, "bce");
+assert.sameValue(date.eraYear, 1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/monthday-toLocaleString.js b/js/src/tests/test262/staging/Intl402/Temporal/old/monthday-toLocaleString.js
new file mode 100644
index 0000000000..a0452c1ab1
--- /dev/null
+++ b/js/src/tests/test262/staging/Intl402/Temporal/old/monthday-toLocaleString.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-intl
+description: monthday.toLocaleString()
+features: [Temporal]
+---*/
+
+var calendar = new Intl.DateTimeFormat("en-US").resolvedOptions().calendar;
+var monthday = Temporal.PlainMonthDay.from({
+ monthCode: "M11",
+ day: 18,
+ calendar
+});
+assert.sameValue(`${ monthday.toLocaleString("en-US", { timeZone: "America/New_York" }) }`, "11/18");
+assert.sameValue(`${ monthday.toLocaleString("de-AT", {
+ timeZone: "Europe/Vienna",
+ calendar
+}) }`, "18.11.");
+
+// should ignore units not in the data type
+assert.sameValue(monthday.toLocaleString("en-US", { timeZoneName: "long" }), "11/18");
+assert.sameValue(monthday.toLocaleString("en-US", { year: "numeric" }), "11/18");
+assert.sameValue(monthday.toLocaleString("en-US", { hour: "numeric" }), "11/18");
+assert.sameValue(monthday.toLocaleString("en-US", { minute: "numeric" }), "11/18");
+assert.sameValue(monthday.toLocaleString("en-US", { second: "numeric" }), "11/18");
+assert.sameValue(monthday.toLocaleString("en-US", { weekday: "long" }), "11/18");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/non-iso-calendars.js b/js/src/tests/test262/staging/Intl402/Temporal/old/non-iso-calendars.js
new file mode 100644
index 0000000000..a4f1a2908f
--- /dev/null
+++ b/js/src/tests/test262/staging/Intl402/Temporal/old/non-iso-calendars.js
@@ -0,0 +1,1380 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-intl
+description: Non-ISO Calendars
+features: [Temporal, Array.prototype.includes]
+---*/
+
+var testChineseData = new Date("2001-02-01T00:00Z").toLocaleString("en-US-u-ca-chinese", {
+ day: "numeric",
+ month: "numeric",
+ year: "numeric",
+ era: "short",
+ timeZone: "UTC"
+});
+var hasOutdatedChineseIcuData = !testChineseData.endsWith("2001");
+
+// verify that Intl.DateTimeFormat.formatToParts output matches snapshot data
+var getLocalizedDates = isoString => {
+ var calendars = [
+ "iso8601",
+ "buddhist",
+ "chinese",
+ "coptic",
+ "dangi",
+ "ethioaa",
+ "ethiopic",
+ "gregory",
+ "hebrew",
+ "indian",
+ "islamic",
+ "islamic-umalqura",
+ "islamic-tbla",
+ "islamic-civil",
+ "islamic-rgsa",
+ "islamicc",
+ "japanese",
+ "persian",
+ "roc"
+ ];
+ var date = new Date(isoString);
+ return calendars.map(id => `${ id }: ${ date.toLocaleDateString(`en-US-u-ca-${ id }`, { timeZone: "UTC" }) }`).join("\n");
+};
+var year2000Content = getLocalizedDates("2000-01-01T00:00Z");
+var year1Content = getLocalizedDates("0001-01-01T00:00Z");
+var year2000Snapshot = "iso8601: 1/1/2000\n" + "buddhist: 1/1/2543 BE\n" + "chinese: 11/25/1999\n" + "coptic: 4/22/1716 ERA1\n" + "dangi: 11/25/1999\n" + "ethioaa: 4/22/7492 ERA0\n" + "ethiopic: 4/22/1992 ERA1\n" + "gregory: 1/1/2000\n" + "hebrew: 23 Tevet 5760\n" + "indian: 10/11/1921 Saka\n" + "islamic: 9/25/1420 AH\n" + "islamic-umalqura: 9/24/1420 AH\n" + "islamic-tbla: 9/25/1420 AH\n" + "islamic-civil: 9/24/1420 AH\n" + "islamic-rgsa: 9/25/1420 AH\n" + "islamicc: 9/24/1420 AH\n" + "japanese: 1/1/12 H\n" + "persian: 10/11/1378 AP\n" + "roc: 1/1/89 Minguo";
+assert.sameValue(year2000Content, year2000Snapshot);
+
+// Several calendars based on the Gregorian calendar use Julian dates (not
+// proleptic Gregorian dates) before the Gregorian switchover in Oct 1582. See
+// https://bugs.chromium.org/p/chromium/issues/detail?id=1173158. The code below
+// allows these tests to pass regardless of the bug, while still remaining
+// sensitive to other bugs that may crop up.
+const yearOneMonthDay = new Map(
+ ["iso8601", "gregory", "roc", "buddhist", "japanese"].map(calendar => {
+ hasGregorianSwitchoverBug = new Date("+001001-01-01T00:00Z")
+ .toLocaleDateString(`en-US-u-ca-${calendar}`, { timeZone: "UTC" })
+ .startsWith("12");
+ return [calendar, hasGregorianSwitchoverBug ? "1/3" : "1/1"]
+ }));
+var year1Snapshot =
+ `iso8601: ${yearOneMonthDay.get("iso8601")}/1\n` +
+ `buddhist: ${yearOneMonthDay.get("buddhist")}/544 BE\n` +
+ "chinese: 11/21/0\n" +
+ "coptic: 5/8/284 ERA0\n" +
+ "dangi: 11/21/0\n" +
+ "ethioaa: 5/8/5493 ERA0\n" +
+ "ethiopic: 5/8/5493 ERA0\n" +
+ `gregory: ${yearOneMonthDay.get("gregory")}/1\n` +
+ "hebrew: 18 Tevet 3761\n" +
+ "indian: 10/11/-78 Saka\n" +
+ "islamic: 5/20/-640 AH\n" +
+ "islamic-umalqura: 5/18/-640 AH\n" +
+ "islamic-tbla: 5/19/-640 AH\n" +
+ "islamic-civil: 5/18/-640 AH\n" +
+ "islamic-rgsa: 5/20/-640 AH\n" +
+ "islamicc: 5/18/-640 AH\n" +
+ `japanese: ${yearOneMonthDay.get("japanese")}/-643 Taika (645\u2013650)\n` +
+ "persian: 10/11/-621 AP\n" +
+ `roc: ${yearOneMonthDay.get("roc")}/1911 B.R.O.C.`;
+assert.sameValue(year1Content, year1Snapshot);
+var fromWithCases = {
+ iso8601: {
+ year2000: {
+ year: 2000,
+ month: 1,
+ day: 1
+ },
+ year1: {
+ year: 1,
+ month: 1,
+ day: 1
+ }
+ },
+ buddhist: {
+ year2000: {
+ year: 2543,
+ month: 1,
+ day: 1,
+ era: "be"
+ },
+ year1: RangeError
+ },
+ chinese: {
+ year2000: {
+ year: 1999,
+ month: 11,
+ day: 25
+ },
+ year1: {
+ year: 0,
+ month: 12,
+ monthCode: "M11",
+ day: 21
+ }
+ },
+ coptic: {
+ year2000: {
+ year: 1716,
+ month: 4,
+ day: 22,
+ era: "era1"
+ },
+ year1: {
+ year: -283,
+ eraYear: 284,
+ month: 5,
+ day: 8,
+ era: "era0"
+ }
+ },
+ dangi: {
+ year2000: {
+ year: 1999,
+ month: 11,
+ day: 25
+ },
+ year1: {
+ year: 0,
+ month: 12,
+ monthCode: "M11",
+ day: 21
+ }
+ },
+ ethioaa: {
+ year2000: {
+ year: 7492,
+ month: 4,
+ day: 22,
+ era: "era0"
+ },
+ year1: {
+ year: 5493,
+ month: 5,
+ day: 8,
+ era: "era0"
+ }
+ },
+ ethiopic: {
+ year2000: {
+ eraYear: 1992,
+ year: 7492,
+ month: 4,
+ day: 22,
+ era: "era1"
+ },
+ year1: {
+ year: 5493,
+ month: 5,
+ day: 8,
+ era: "era0"
+ }
+ },
+ gregory: {
+ year2000: {
+ year: 2000,
+ month: 1,
+ day: 1,
+ era: "ce"
+ },
+ year1: {
+ year: 1,
+ month: 1,
+ day: 1,
+ era: "ce"
+ }
+ },
+ hebrew: {
+ year2000: {
+ year: 5760,
+ month: 4,
+ day: 23
+ },
+ year1: {
+ year: 3761,
+ month: 4,
+ day: 18
+ }
+ },
+ indian: {
+ year2000: {
+ year: 1921,
+ month: 10,
+ day: 11,
+ era: "saka"
+ },
+ year1: {
+ year: -78,
+ month: 10,
+ day: 11,
+ era: "saka"
+ }
+ },
+ islamic: {
+ year2000: {
+ year: 1420,
+ month: 9,
+ day: 25,
+ era: "ah"
+ },
+ year1: {
+ year: -640,
+ month: 5,
+ day: 20,
+ era: "ah"
+ }
+ },
+ "islamic-umalqura": {
+ year2000: {
+ year: 1420,
+ month: 9,
+ day: 24,
+ era: "ah"
+ },
+ year1: {
+ year: -640,
+ month: 5,
+ day: 18,
+ era: "ah"
+ }
+ },
+ "islamic-tbla": {
+ year2000: {
+ year: 1420,
+ month: 9,
+ day: 25,
+ era: "ah"
+ },
+ year1: {
+ year: -640,
+ month: 5,
+ day: 19,
+ era: "ah"
+ }
+ },
+ "islamic-civil": {
+ year2000: {
+ year: 1420,
+ month: 9,
+ day: 24,
+ era: "ah"
+ },
+ year1: {
+ year: -640,
+ month: 5,
+ day: 18,
+ era: "ah"
+ }
+ },
+ "islamic-rgsa": {
+ year2000: {
+ year: 1420,
+ month: 9,
+ day: 25,
+ era: "ah"
+ },
+ year1: {
+ year: -640,
+ month: 5,
+ day: 20,
+ era: "ah"
+ }
+ },
+ islamicc: {
+ year2000: {
+ year: 1420,
+ month: 9,
+ day: 24,
+ era: "ah"
+ },
+ year1: {
+ year: -640,
+ month: 5,
+ day: 18,
+ era: "ah"
+ }
+ },
+ japanese: {
+ year2000: {
+ year: 2000,
+ eraYear: 12,
+ month: 1,
+ day: 1,
+ era: "heisei"
+ },
+ year1: RangeError
+ },
+ persian: {
+ year2000: {
+ year: 1378,
+ month: 10,
+ day: 11,
+ era: "ap"
+ },
+ year1: {
+ year: -621,
+ month: 10,
+ day: 11,
+ era: "ap"
+ }
+ },
+ roc: {
+ year2000: {
+ year: 89,
+ month: 1,
+ day: 1,
+ era: "minguo"
+ },
+ year1: RangeError
+ }
+};
+var logPerf = false;
+var totalNow = 0;
+for (var [id, tests] of Object.entries(fromWithCases)) {
+ var dates = {
+ year2000: Temporal.PlainDate.from("2000-01-01"),
+ year1: Temporal.PlainDate.from("0001-01-01")
+ };
+ for (var [name, date] of Object.entries(dates)) {
+ var values = tests[name];
+ var errorExpected = values === RangeError;
+ if ((id === "chinese" || id === "dangi") && hasOutdatedChineseIcuData ) {
+ var now = globalThis.performance ? globalThis.performance.now() : Date.now();
+ if (errorExpected) {
+ assert.throws(RangeError, () => {
+ var inCal = date.withCalendar(id);
+ Temporal.PlainDate.from({
+ calendar: id,
+ year: inCal.year,
+ day: inCal.day,
+ monthCode: inCal.monthCode
+ });
+ });
+ }
+ var inCal = date.withCalendar(id);
+ assert.sameValue(`${ name } ${ id } day: ${ inCal.day }`, `${ name } ${ id } day: ${ values.day }`);
+ if (values.eraYear === undefined && values.era !== undefined)
+ values.eraYear = values.year;
+ assert.sameValue(`${ name } ${ id } eraYear: ${ inCal.eraYear }`, `${ name } ${ id } eraYear: ${ values.eraYear }`);
+ assert.sameValue(`${ name } ${ id } era: ${ inCal.era }`, `${ name } ${ id } era: ${ values.era }`);
+ assert.sameValue(`${ name } ${ id } year: ${ inCal.year }`, `${ name } ${ id } year: ${ values.year }`);
+ assert.sameValue(`${ name } ${ id } month: ${ inCal.month }`, `${ name } ${ id } month: ${ values.month }`);
+ if (values.monthCode === undefined)
+ values.monthCode = `M${ values.month.toString().padStart(2, "0") }`;
+ assert.sameValue(`${ name } ${ id } monthCode: ${ inCal.monthCode }`, `${ name } ${ id } monthCode: ${ values.monthCode }`);
+ if (values.era) {
+ var dateRoundtrip1 = Temporal.PlainDate.from({
+ calendar: id,
+ eraYear: values.eraYear,
+ era: values.era,
+ day: values.day,
+ monthCode: values.monthCode
+ });
+ assert.sameValue(dateRoundtrip1.toString(), inCal.toString());
+ assert.throws(RangeError, () => Temporal.PlainDate.from({
+ calendar: id,
+ eraYear: values.eraYear,
+ era: values.era,
+ day: values.day,
+ monthCode: values.monthCode,
+ year: values.year + 1
+ }));
+ }
+ var dateRoundtrip2 = Temporal.PlainDate.from({
+ calendar: id,
+ year: values.year,
+ day: values.day,
+ monthCode: values.monthCode
+ });
+ assert.sameValue(dateRoundtrip2.toString(), inCal.toString());
+ var dateRoundtrip3 = Temporal.PlainDate.from({
+ calendar: id,
+ year: values.year,
+ day: values.day,
+ month: values.month
+ });
+ assert.sameValue(dateRoundtrip3.toString(), inCal.toString());
+ var dateRoundtrip4 = Temporal.PlainDate.from({
+ calendar: id,
+ year: values.year,
+ day: values.day,
+ monthCode: values.monthCode
+ });
+ assert.sameValue(dateRoundtrip4.toString(), inCal.toString());
+ assert.throws(RangeError, () => Temporal.PlainDate.from({
+ calendar: id,
+ day: values.day,
+ month: values.month === 1 ? 2 : values.month - 1,
+ monthCode: values.monthCode,
+ year: values.year
+ }));
+ var ms = (globalThis.performance ? globalThis.performance.now() : Date.now()) - now;
+ totalNow += ms;
+ if (logPerf)
+ console.log(`from: ${ id } ${ name }: ${ ms.toFixed(2) }ms, total: ${ totalNow.toFixed(2) }ms`);
+ };
+ if ((id === "chinese" || id === "dangi") && hasOutdatedChineseIcuData ) {
+ var now = globalThis.performance ? globalThis.performance.now() : Date.now();
+ var inCal = date.withCalendar(id);
+ if (errorExpected) {
+ assert.throws(RangeError, () => inCal.with({ day: 1 }).year);
+ }
+ var afterWithDay = inCal.with({ day: 1 });
+ var t = "(after setting day)";
+ assert.sameValue(`${ t } year: ${ afterWithDay.year }`, `${ t } year: ${ inCal.year }`);
+ assert.sameValue(`${ t } month: ${ afterWithDay.month }`, `${ t } month: ${ inCal.month }`);
+ assert.sameValue(`${ t } day: ${ afterWithDay.day }`, `${ t } day: 1`);
+ var afterWithMonth = afterWithDay.with({ month: 1 });
+ t = "(after setting month)";
+ assert.sameValue(`${ t } year: ${ afterWithMonth.year }`, `${ t } year: ${ inCal.year }`);
+ assert.sameValue(`${ t } month: ${ afterWithMonth.month }`, `${ t } month: 1`);
+ assert.sameValue(`${ t } day: ${ afterWithMonth.day }`, `${ t } day: 1`);
+ var afterWithYear = afterWithMonth.with({ year: 2220 });
+ t = "(after setting year)";
+ assert.sameValue(`${ t } year: ${ afterWithYear.year }`, `${ t } year: 2220`);
+ assert.sameValue(`${ t } month: ${ afterWithYear.month }`, `${ t } month: 1`);
+ assert.sameValue(`${ t } day: ${ afterWithYear.day }`, `${ t } day: 1`);
+ var ms = (globalThis.performance ? globalThis.performance.now() : Date.now()) - now;
+ totalNow += ms;
+ if (logPerf)
+ console.log(`with: ${ id } ${ name }: ${ ms.toFixed(2) }ms, total: ${ totalNow.toFixed(2) }ms`);
+ };
+ }
+}
+var addDaysWeeksCases = {
+ iso8601: {
+ year: 2000,
+ month: 10,
+ day: 7,
+ monthCode: "M10",
+ eraYear: undefined,
+ era: undefined
+ },
+ buddhist: RangeError,
+ chinese: {
+ year: 2000,
+ month: 10,
+ day: 16,
+ monthCode: "M10",
+ eraYear: undefined,
+ era: undefined
+ },
+ coptic: {
+ year: 2000,
+ month: 10,
+ day: 11,
+ monthCode: "M10",
+ eraYear: 2000,
+ era: "era1"
+ },
+ dangi: {
+ year: 2000,
+ month: 10,
+ day: 16,
+ monthCode: "M10",
+ eraYear: undefined,
+ era: undefined
+ },
+ ethioaa: {
+ year: 2000,
+ month: 10,
+ day: 11,
+ monthCode: "M10",
+ eraYear: 2000,
+ era: "era0"
+ },
+ ethiopic: {
+ year: 2000,
+ month: 10,
+ day: 11,
+ monthCode: "M10",
+ eraYear: 2000,
+ era: "era0"
+ },
+ gregory: {
+ year: 2000,
+ month: 10,
+ day: 7,
+ monthCode: "M10",
+ eraYear: 2000,
+ era: "ce"
+ },
+ hebrew: {
+ year: 2000,
+ month: 10,
+ day: 14,
+ monthCode: "M10",
+ eraYear: undefined,
+ era: undefined
+ },
+ indian: {
+ year: 2000,
+ month: 10,
+ day: 6,
+ monthCode: "M10",
+ eraYear: 2000,
+ era: "saka"
+ },
+ islamic: {
+ year: 2000,
+ month: 10,
+ day: 15,
+ monthCode: "M10",
+ eraYear: 2000,
+ era: "ah"
+ },
+ "islamic-umalqura": {
+ year: 2000,
+ month: 10,
+ day: 15,
+ monthCode: "M10",
+ eraYear: 2000,
+ era: "ah"
+ },
+ "islamic-tbla": {
+ year: 2000,
+ month: 10,
+ day: 15,
+ monthCode: "M10",
+ eraYear: 2000,
+ era: "ah"
+ },
+ "islamic-civil": {
+ year: 2000,
+ month: 10,
+ day: 15,
+ monthCode: "M10",
+ eraYear: 2000,
+ era: "ah"
+ },
+ "islamic-rgsa": {
+ year: 2000,
+ month: 10,
+ day: 15,
+ monthCode: "M10",
+ eraYear: 2000,
+ era: "ah"
+ },
+ islamicc: {
+ year: 2000,
+ month: 10,
+ day: 15,
+ monthCode: "M10",
+ eraYear: 2000,
+ era: "ah"
+ },
+ japanese: {
+ year: 2000,
+ month: 10,
+ day: 7,
+ monthCode: "M10",
+ eraYear: 12,
+ era: "heisei"
+ },
+ persian: {
+ year: 2000,
+ month: 10,
+ day: 5,
+ monthCode: "M10",
+ eraYear: 2000,
+ era: "ap"
+ },
+ roc: {
+ year: 2000,
+ month: 10,
+ day: 8,
+ monthCode: "M10",
+ eraYear: 2000,
+ era: "minguo"
+ }
+};
+const addMonthsCases = {
+ iso8601: { year: 2001, month: 6, day: 1, monthCode: 'M06', eraYear: undefined, era: undefined },
+ // See https://bugs.chromium.org/p/chromium/issues/detail?id=1173158
+ buddhist: RangeError, // { year: 2001, month: 6, day: 1, monthCode: 'M06', eraYear: 2001, era: 'be' },
+ chinese: { year: 2001, month: 6, day: 1, monthCode: 'M05', eraYear: undefined, era: undefined },
+ coptic: { year: 2001, month: 5, day: 1, monthCode: 'M05', eraYear: 2001, era: 'era1' },
+ dangi: { year: 2001, month: 6, day: 1, monthCode: 'M05', eraYear: undefined, era: undefined },
+ ethioaa: { year: 2001, month: 5, day: 1, monthCode: 'M05', eraYear: 2001, era: 'era0' },
+ ethiopic: { year: 2001, month: 5, day: 1, monthCode: 'M05', eraYear: 2001, era: 'era0' },
+ gregory: { year: 2001, month: 6, day: 1, monthCode: 'M06', eraYear: 2001, era: 'ce' },
+ hebrew: { year: 2001, month: 6, day: 1, monthCode: 'M05L', eraYear: undefined, era: undefined },
+ indian: { year: 2001, month: 6, day: 1, monthCode: 'M06', eraYear: 2001, era: 'saka' },
+ islamic: { year: 2001, month: 6, day: 1, monthCode: 'M06', eraYear: 2001, era: 'ah' },
+ 'islamic-umalqura': { year: 2001, month: 6, day: 1, monthCode: 'M06', eraYear: 2001, era: 'ah' },
+ 'islamic-tbla': { year: 2001, month: 6, day: 1, monthCode: 'M06', eraYear: 2001, era: 'ah' },
+ 'islamic-civil': { year: 2001, month: 6, day: 1, monthCode: 'M06', eraYear: 2001, era: 'ah' },
+ 'islamic-rgsa': { year: 2001, month: 6, day: 1, monthCode: 'M06', eraYear: 2001, era: 'ah' },
+ islamicc: { year: 2001, month: 6, day: 1, monthCode: 'M06', eraYear: 2001, era: 'ah' },
+ japanese: { year: 2001, month: 6, day: 1, monthCode: 'M06', eraYear: 13, era: 'heisei' },
+ persian: { year: 2001, month: 6, day: 1, monthCode: 'M06', eraYear: 2001, era: 'ap' },
+ roc: { year: 2001, month: 6, day: 1, monthCode: 'M06', eraYear: 2001, era: 'minguo' }
+};
+var i = 0;
+const addYearsMonthsDaysCases = Object.entries(addMonthsCases).reduce((obj, entry) => {
+ obj[entry[0]] = entry[1] === RangeError ? RangeError : { ...entry[1], day: 18 };
+ return obj;
+}, {});
+var tests = {
+ days: {
+ duration: { days: 280 },
+ results: addDaysWeeksCases,
+ startDate: {
+ year: 2000,
+ month: 1,
+ day: 1
+ }
+ },
+ weeks: {
+ duration: { weeks: 40 },
+ results: addDaysWeeksCases,
+ startDate: {
+ year: 2000,
+ month: 1,
+ day: 1
+ }
+ },
+ months: {
+ duration: { months: 6 },
+ results: addMonthsCases,
+ startDate: {
+ year: 2000,
+ month: 12,
+ day: 1
+ }
+ },
+ years: {
+ duration: {
+ years: 3,
+ months: 6,
+ days: 17
+ },
+ results: addYearsMonthsDaysCases,
+ startDate: {
+ year: 1997,
+ monthCode: "M12",
+ day: 1
+ }
+ }
+};
+var calendars = Object.keys(addMonthsCases);
+for (var id of calendars) {
+ for (var [unit, {duration, results, startDate}] of Object.entries(tests)) {
+ var values = results[id];
+ duration = Temporal.Duration.from(duration);
+ if ((id === "chinese" || id === "dangi") && hasOutdatedChineseIcuData ) {
+ var now = globalThis.performance ? globalThis.performance.now() : Date.now();
+ if (values === RangeError) {
+ assert.throws(RangeError, () => Temporal.PlainDate.from({
+ ...startDate,
+ calendar: id
+ }));
+ }
+ var start = Temporal.PlainDate.from({
+ ...startDate,
+ calendar: id
+ });
+ var end = start.add(duration);
+ assert.sameValue(`add ${ unit } ${ id } day: ${ end.day }`, `add ${ unit } ${ id } day: ${ values.day }`);
+ assert.sameValue(`add ${ unit } ${ id } eraYear: ${ end.eraYear }`, `add ${ unit } ${ id } eraYear: ${ values.eraYear }`);
+ assert.sameValue(`add ${ unit } ${ id } era: ${ end.era }`, `add ${ unit } ${ id } era: ${ values.era }`);
+ assert.sameValue(`add ${ unit } ${ id } year: ${ end.year }`, `add ${ unit } ${ id } year: ${ values.year }`);
+ assert.sameValue(`add ${ unit } ${ id } month: ${ end.month }`, `add ${ unit } ${ id } month: ${ values.month }`);
+ assert.sameValue(`add ${ unit } ${ id } monthCode: ${ end.monthCode }`, `add ${ unit } ${ id } monthCode: ${ values.monthCode }`);
+ var calculatedStart = end.subtract(duration);
+ var isLunisolar = [
+ "chinese",
+ "dangi",
+ "hebrew"
+ ].includes(id);
+ var expectedCalculatedStart = isLunisolar && duration.years !== 0 && !end.monthCode.endsWith("L") ? start.subtract({ months: 1 }) : start;
+ assert.sameValue(`start ${ calculatedStart.toString() }`, `start ${ expectedCalculatedStart.toString() }`);
+ var diff = start.until(end, { largestUnit: unit });
+ assert.sameValue(`diff ${ unit } ${ id }: ${ diff }`, `diff ${ unit } ${ id }: ${ duration }`);
+ if (unit === "months") {
+ var startYesterday = start.subtract({ days: 1 });
+ var endYesterday = startYesterday.add(duration);
+ assert.sameValue(`add from end-of-month ${ unit } ${ id } day (initial): ${ endYesterday.day }`, `add from end-of-month ${ unit } ${ id } day (initial): ${ Math.min(startYesterday.day, endYesterday.daysInMonth) }`);
+ var endYesterdayNextDay = endYesterday.add({ days: 1 });
+ while (endYesterdayNextDay.day !== 1) {
+ endYesterdayNextDay = endYesterdayNextDay.add({ days: 1 });
+ }
+ assert.sameValue(`add from end-of-month ${ unit } ${ id } day: ${ endYesterdayNextDay.day }`, `add from end-of-month ${ unit } ${ id } day: ${ values.day }`);
+ assert.sameValue(`add from end-of-month ${ unit } ${ id } eraYear: ${ endYesterdayNextDay.eraYear }`, `add from end-of-month ${ unit } ${ id } eraYear: ${ values.eraYear }`);
+ assert.sameValue(`add from end-of-month ${ unit } ${ id } era: ${ endYesterdayNextDay.era }`, `add from end-of-month ${ unit } ${ id } era: ${ values.era }`);
+ assert.sameValue(`add from end-of-month ${ unit } ${ id } year: ${ endYesterdayNextDay.year }`, `add from end-of-month ${ unit } ${ id } year: ${ values.year }`);
+ assert.sameValue(`add from end-of-month ${ unit } ${ id } month: ${ endYesterdayNextDay.month }`, `add from end-of-month ${ unit } ${ id } month: ${ values.month }`);
+ assert.sameValue(`add from end-of-month ${ unit } ${ id } monthCode: ${ endYesterdayNextDay.monthCode }`, `add from end-of-month ${ unit } ${ id } monthCode: ${ values.monthCode }`);
+ var endReverse = endYesterdayNextDay.subtract({ days: 1 });
+ var startReverse = endReverse.subtract(duration);
+ assert.sameValue(`subtract from end-of-month ${ unit } ${ id } day (initial): ${ startReverse.day }`, `subtract from end-of-month ${ unit } ${ id } day (initial): ${ Math.min(endReverse.day, startReverse.daysInMonth) }`);
+ var startReverseNextDay = startReverse.add({ days: 1 });
+ while (startReverseNextDay.day !== 1) {
+ startReverseNextDay = startReverseNextDay.add({ days: 1 });
+ }
+ assert.sameValue(`subtract from end-of-month ${ unit } ${ id } day: ${ startReverseNextDay.day }`, `subtract from end-of-month ${ unit } ${ id } day: ${ start.day }`);
+ assert.sameValue(`subtract from end-of-month ${ unit } ${ id } eraYear: ${ startReverseNextDay.eraYear }`, `subtract from end-of-month ${ unit } ${ id } eraYear: ${ start.eraYear }`);
+ assert.sameValue(`subtract from end-of-month ${ unit } ${ id } era: ${ startReverseNextDay.era }`, `subtract from end-of-month ${ unit } ${ id } era: ${ start.era }`);
+ assert.sameValue(`subtract from end-of-month ${ unit } ${ id } year: ${ startReverseNextDay.year }`, `subtract from end-of-month ${ unit } ${ id } year: ${ start.year }`);
+ assert.sameValue(`subtract from end-of-month ${ unit } ${ id } month: ${ startReverseNextDay.month }`, `subtract from end-of-month ${ unit } ${ id } month: ${ start.month }`);
+ assert.sameValue(`subtract from end-of-month ${ unit } ${ id } monthCode: ${ startReverseNextDay.monthCode }`, `subtract from end-of-month ${ unit } ${ id } monthCode: ${ start.monthCode }`);
+ }
+ var ms = (globalThis.performance ? globalThis.performance.now() : Date.now()) - now;
+ totalNow += ms;
+ if (logPerf)
+ console.log(`${ id } add ${ duration }: ${ ms.toFixed(2) }ms, total: ${ totalNow.toFixed(2) }ms`);
+ };
+ }
+}
+var daysInMonthCases = {
+ iso8601: {
+ year: 2001,
+ leap: false,
+ days: [
+ 31,
+ 28,
+ 31,
+ 30,
+ 31,
+ 30,
+ 31,
+ 31,
+ 30,
+ 31,
+ 30,
+ 31
+ ]
+ },
+ buddhist: {
+ year: 4001,
+ leap: false,
+ days: [
+ 31,
+ 28,
+ 31,
+ 30,
+ 31,
+ 30,
+ 31,
+ 31,
+ 30,
+ 31,
+ 30,
+ 31
+ ]
+ },
+ chinese: {
+ year: 2001,
+ leap: "M04L",
+ days: [
+ 30,
+ 30,
+ 29,
+ 30,
+ 29,
+ 30,
+ 29,
+ 29,
+ 30,
+ 29,
+ 30,
+ 29,
+ 30
+ ]
+ },
+ coptic: {
+ year: 2001,
+ leap: false,
+ days: [
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 5
+ ]
+ },
+ dangi: {
+ year: 2001,
+ leap: "M04L",
+ days: [
+ 30,
+ 30,
+ 30,
+ 29,
+ 29,
+ 30,
+ 29,
+ 29,
+ 30,
+ 29,
+ 30,
+ 29,
+ 30
+ ]
+ },
+ ethioaa: {
+ year: 2001,
+ leap: false,
+ days: [
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 5
+ ]
+ },
+ ethiopic: {
+ year: 2001,
+ leap: false,
+ days: [
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 5
+ ]
+ },
+ gregory: {
+ year: 2001,
+ leap: false,
+ days: [
+ 31,
+ 28,
+ 31,
+ 30,
+ 31,
+ 30,
+ 31,
+ 31,
+ 30,
+ 31,
+ 30,
+ 31
+ ]
+ },
+ hebrew: {
+ year: 2001,
+ leap: "M05L",
+ days: [
+ 30,
+ 30,
+ 30,
+ 29,
+ 30,
+ 30,
+ 29,
+ 30,
+ 29,
+ 30,
+ 29,
+ 30,
+ 29
+ ]
+ },
+ indian: {
+ year: 2001,
+ leap: false,
+ days: [
+ 30,
+ 31,
+ 31,
+ 31,
+ 31,
+ 31,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30
+ ]
+ },
+ islamic: {
+ year: 2001,
+ leap: false,
+ days: [
+ 29,
+ 30,
+ 29,
+ 29,
+ 30,
+ 29,
+ 30,
+ 30,
+ 29,
+ 30,
+ 30,
+ 29
+ ]
+ },
+ "islamic-umalqura": {
+ year: 2001,
+ leap: true,
+ days: [
+ 30,
+ 29,
+ 30,
+ 29,
+ 30,
+ 29,
+ 30,
+ 29,
+ 30,
+ 29,
+ 30,
+ 30
+ ]
+ },
+ "islamic-tbla": {
+ year: 2001,
+ leap: true,
+ days: [
+ 30,
+ 29,
+ 30,
+ 29,
+ 30,
+ 29,
+ 30,
+ 29,
+ 30,
+ 29,
+ 30,
+ 30
+ ]
+ },
+ "islamic-civil": {
+ year: 2001,
+ leap: true,
+ days: [
+ 30,
+ 29,
+ 30,
+ 29,
+ 30,
+ 29,
+ 30,
+ 29,
+ 30,
+ 29,
+ 30,
+ 30
+ ]
+ },
+ "islamic-rgsa": {
+ year: 2001,
+ leap: false,
+ days: [
+ 29,
+ 30,
+ 29,
+ 29,
+ 30,
+ 29,
+ 30,
+ 30,
+ 29,
+ 30,
+ 30,
+ 29
+ ]
+ },
+ islamicc: {
+ year: 2001,
+ leap: true,
+ days: [
+ 30,
+ 29,
+ 30,
+ 29,
+ 30,
+ 29,
+ 30,
+ 29,
+ 30,
+ 29,
+ 30,
+ 30
+ ]
+ },
+ japanese: {
+ year: 2001,
+ leap: false,
+ days: [
+ 31,
+ 28,
+ 31,
+ 30,
+ 31,
+ 30,
+ 31,
+ 31,
+ 30,
+ 31,
+ 30,
+ 31
+ ]
+ },
+ persian: {
+ year: 2001,
+ leap: false,
+ days: [
+ 31,
+ 31,
+ 31,
+ 31,
+ 31,
+ 31,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 29
+ ]
+ },
+ roc: {
+ year: 2001,
+ leap: true,
+ days: [
+ 31,
+ 29,
+ 31,
+ 30,
+ 31,
+ 30,
+ 31,
+ 31,
+ 30,
+ 31,
+ 30,
+ 31
+ ]
+ }
+};
+totalNow = 0;
+for (var id of calendars) {
+ var {year, leap, days} = daysInMonthCases[id];
+ var date = hasOutdatedChineseIcuData && (id === "chinese" || id === "dangi") ? undefined : Temporal.PlainDate.from({
+ year,
+ month: 1,
+ day: 1,
+ calendar: id
+ });
+ if ((id === "chinese" || id === "dangi") && hasOutdatedChineseIcuData ) {
+ if (typeof leap === "boolean") {
+ assert.sameValue(date.inLeapYear, leap);
+ } else {
+ assert.sameValue(date.inLeapYear, true);
+ var leapMonth = date.with({ monthCode: leap });
+ assert.sameValue(leapMonth.monthCode, leap);
+ }
+ };
+ if ((id === "chinese" || id === "dangi") && hasOutdatedChineseIcuData ) {
+ var now = globalThis.performance ? globalThis.performance.now() : Date.now();
+ var {monthsInYear} = date;
+ assert.sameValue(monthsInYear, days.length);
+ for (var i = monthsInYear, leapMonthIndex = undefined, monthStart = undefined; i >= 1; i--) {
+ monthStart = monthStart ? monthStart.add({ months: -1 }) : date.add({ months: monthsInYear - 1 });
+ var {month, monthCode, daysInMonth} = monthStart;
+ assert.sameValue(`${ id } month ${ i } (code ${ monthCode }) days: ${ daysInMonth }`, `${ id } month ${ i } (code ${ monthCode }) days: ${ days[i - 1] }`);
+ if (monthCode.endsWith("L")) {
+ assert.sameValue(date.with({ monthCode }).monthCode, monthCode);
+ leapMonthIndex = i;
+ } else {
+ if (leapMonthIndex && i === leapMonthIndex - 1) {
+ var inLeapMonth = monthStart.with({ monthCode: `M${ month.toString().padStart(2, "0") }L` });
+ assert.sameValue(inLeapMonth.monthCode, `${ monthCode }L`);
+ } else {
+ assert.throws(RangeError, () => monthStart.with({ monthCode: `M${ month.toString().padStart(2, "0") }L` }, { overflow: "reject" }));
+ if ([
+ "chinese",
+ "dangi"
+ ].includes(id)) {
+ if (i === 1 || i === 12 || i === 13) {
+ assert.throws(RangeError, () => monthStart.with({ monthCode: `M${ month.toString().padStart(2, "0") }L` }));
+ } else {
+ var fakeL = monthStart.with({
+ monthCode: `M${ month.toString().padStart(2, "0") }L`,
+ day: 5
+ });
+ assert.sameValue(`fake leap month ${ fakeL.monthCode }`, `fake leap month M${ month.toString().padStart(2, "0") }`);
+ assert.sameValue(fakeL.day, fakeL.daysInMonth);
+ }
+ }
+ }
+ if (![
+ "chinese",
+ "dangi",
+ "hebrew"
+ ].includes(id)) {
+ assert.throws(RangeError, () => monthStart.with({ monthCode: `M${ month.toString().padStart(2, "0") }L` }));
+ }
+ }
+ assert.throws(RangeError, () => monthStart.with({ day: daysInMonth + 1 }, { overflow: "reject" }));
+ var oneDayPastMonthEnd = monthStart.with({ day: daysInMonth + 1 });
+ assert.sameValue(oneDayPastMonthEnd.day, daysInMonth);
+ }
+ var ms = (globalThis.performance ? globalThis.performance.now() : Date.now()) - now;
+ totalNow += ms;
+ if (logPerf)
+ console.log(`${ id } months check ${ id }: ${ ms.toFixed(2) }ms, total: ${ totalNow.toFixed(2) }ms`);
+ };
+}
+var monthDayCases = [
+ {
+ calendar: "iso8601",
+ isoReferenceYear: 1972,
+ year: 2004,
+ month: 2,
+ day: 29
+ },
+ {
+ calendar: "buddhist",
+ isoReferenceYear: 1972,
+ year: 2004,
+ month: 2,
+ day: 29
+ },
+ {
+ calendar: "chinese",
+ isoReferenceYear: 1963,
+ year: 2001,
+ month: 5,
+ monthCode: "M04L",
+ day: 15
+ },
+ {
+ calendar: "chinese",
+ isoReferenceYear: 1971,
+ year: 2000,
+ month: 6,
+ day: 30
+ },
+ {
+ calendar: "coptic",
+ isoReferenceYear: 1971,
+ year: 2006,
+ month: 13,
+ day: 6
+ },
+ {
+ calendar: "dangi",
+ isoReferenceYear: 1963,
+ year: 2001,
+ month: 5,
+ monthCode: "M04L",
+ day: 15
+ },
+ {
+ calendar: "dangi",
+ isoReferenceYear: 1971,
+ year: 2000,
+ month: 6,
+ day: 30
+ },
+ {
+ calendar: "ethiopic",
+ isoReferenceYear: 1971,
+ year: 2006,
+ month: 13,
+ day: 6
+ },
+ {
+ calendar: "ethioaa",
+ isoReferenceYear: 1971,
+ year: 2006,
+ month: 13,
+ day: 6
+ },
+ {
+ calendar: "gregory",
+ isoReferenceYear: 1972,
+ year: 2004,
+ month: 2,
+ day: 29
+ },
+ {
+ calendar: "hebrew",
+ isoReferenceYear: 1970,
+ year: 5779,
+ month: 6,
+ monthCode: "M05L",
+ day: 15
+ },
+ {
+ calendar: "hebrew",
+ isoReferenceYear: 1971,
+ year: 5776,
+ month: 2,
+ day: 30
+ },
+ {
+ calendar: "hebrew",
+ isoReferenceYear: 1971,
+ year: 5772,
+ month: 3,
+ day: 30
+ },
+ {
+ calendar: "indian",
+ isoReferenceYear: 1968,
+ year: 2002,
+ month: 1,
+ day: 31
+ },
+ {
+ calendar: "islamic",
+ isoReferenceYear: 1970,
+ year: 2001,
+ month: 1,
+ day: 30
+ },
+ {
+ calendar: "islamic-umalqura",
+ isoReferenceYear: 1969,
+ year: 2001,
+ month: 1,
+ day: 30
+ },
+ {
+ calendar: "islamic-tbla",
+ isoReferenceYear: 1971,
+ year: 2001,
+ month: 1,
+ day: 30
+ },
+ {
+ calendar: "islamic-civil",
+ isoReferenceYear: 1971,
+ year: 2001,
+ month: 1,
+ day: 30
+ },
+ {
+ calendar: "islamic-rgsa",
+ isoReferenceYear: 1970,
+ year: 2001,
+ month: 1,
+ day: 30
+ },
+ {
+ calendar: "islamicc",
+ isoReferenceYear: 1971,
+ year: 2001,
+ month: 1,
+ day: 30
+ },
+ {
+ calendar: "japanese",
+ isoReferenceYear: 1972,
+ year: 2004,
+ month: 2,
+ day: 29
+ },
+ {
+ calendar: "persian",
+ isoReferenceYear: 1972,
+ year: 2004,
+ month: 12,
+ day: 30
+ },
+ {
+ calendar: "roc",
+ isoReferenceYear: 1972,
+ year: 93,
+ month: 2,
+ day: 29
+ }
+];
+var i = 0;
+for (var test of monthDayCases) {
+ var id = test.calendar;
+ if ((id === "chinese" || id === "dangi") && hasOutdatedChineseIcuData ) {
+ if (test.monthCode === undefined)
+ test.monthCode = `M${ test.month.toString().padStart(2, "0") }`;
+ var {calendar, monthCode, month, day, year, isoReferenceYear} = test;
+ var md = Temporal.PlainMonthDay.from({
+ year,
+ month,
+ day,
+ calendar
+ });
+ var isoString = md.toString();
+ var mdFromIso = Temporal.PlainMonthDay.from(isoString);
+ assert.sameValue(mdFromIso, md);
+ var isoFields = md.getISOFields();
+ assert.sameValue(md.monthCode, monthCode);
+ assert.sameValue(md.day, day);
+ assert.sameValue(isoFields.isoYear, isoReferenceYear);
+ var md2 = Temporal.PlainMonthDay.from({
+ monthCode,
+ day,
+ calendar
+ });
+ var isoFields2 = md2.getISOFields();
+ assert.sameValue(md2.monthCode, monthCode);
+ assert.sameValue(md2.day, day);
+ assert.sameValue(isoFields2.isoYear, isoReferenceYear);
+ assert.sameValue(md.equals(md2), true);
+ assert.throws(RangeError, () => {
+ Temporal.PlainMonthDay.from({
+ monthCode: "M15",
+ day: 1,
+ calendar
+ }, { overflow: "reject" });
+ });
+ assert.throws(RangeError, () => {
+ Temporal.PlainMonthDay.from({
+ monthCode: "M15",
+ day: 1,
+ calendar
+ });
+ });
+ assert.throws(RangeError, () => {
+ Temporal.PlainMonthDay.from({
+ year,
+ month: 15,
+ day: 1,
+ calendar
+ }, { overflow: "reject" });
+ });
+ var constrained = Temporal.PlainMonthDay.from({
+ year,
+ month: 15,
+ day: 1,
+ calendar
+ });
+ var {monthCode: monthCodeConstrained} = constrained;
+ assert(monthCodeConstrained === "M12" || monthCodeConstrained === "M13");
+ };
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/persian-calendar.js b/js/src/tests/test262/staging/Intl402/Temporal/old/persian-calendar.js
new file mode 100644
index 0000000000..a3578f3208
--- /dev/null
+++ b/js/src/tests/test262/staging/Intl402/Temporal/old/persian-calendar.js
@@ -0,0 +1,93 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2023 Justin Grant. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-intl
+description: Persian calendar
+features: [Temporal]
+---*/
+
+// Test data obtained from ICU
+
+const tests = [
+ {
+ testYear: 1395,
+ inLeapYear: true,
+ daysInYear: 366,
+ daysInMonth12: 30,
+ isoYear: 2016,
+ isoMonth: 3,
+ isoDay: 20
+ },
+ {
+ testYear: 1396,
+ inLeapYear: false,
+ daysInYear: 365,
+ daysInMonth12: 29,
+ isoYear: 2017,
+ isoMonth: 3,
+ isoDay: 21
+ },
+ {
+ testYear: 1397,
+ inLeapYear: false,
+ daysInYear: 365,
+ daysInMonth12: 29,
+ isoYear: 2018,
+ isoMonth: 3,
+ isoDay: 21
+ },
+ {
+ testYear: 1398,
+ inLeapYear: false,
+ daysInYear: 365,
+ daysInMonth12: 29,
+ isoYear: 2019,
+ isoMonth: 3,
+ isoDay: 21
+ },
+ {
+ testYear: 1399,
+ inLeapYear: true,
+ daysInYear: 366,
+ daysInMonth12: 30,
+ isoYear: 2020,
+ isoMonth: 3,
+ isoDay: 20
+ },
+ {
+ testYear: 1400,
+ inLeapYear: false,
+ daysInYear: 365,
+ daysInMonth12: 29,
+ isoYear: 2021,
+ isoMonth: 3,
+ isoDay: 21
+ }
+];
+
+for (const test of tests) {
+ const { testYear, inLeapYear, daysInYear, daysInMonth12, isoYear, isoMonth, isoDay } = test;
+ const date = Temporal.PlainDate.from({ year: testYear, month: 1, day: 1, calendar: "persian" });
+ const isoFields = date.getISOFields();
+ assert.sameValue(date.calendarId, "persian");
+ assert.sameValue(date.year, testYear);
+ assert.sameValue(date.month, 1);
+ assert.sameValue(date.monthCode, "M01");
+ assert.sameValue(date.day, 1);
+ assert.sameValue(date.inLeapYear, inLeapYear);
+ assert.sameValue(date.daysInYear, daysInYear);
+ assert.sameValue(date.monthsInYear, 12);
+ assert.sameValue(date.dayOfYear, 1);
+ const startOfNextYear = date.with({ year: testYear + 1 });
+ const lastDayOfThisYear = startOfNextYear.subtract({ days: 1 });
+ assert.sameValue(lastDayOfThisYear.dayOfYear, daysInYear);
+ const dateMonth12 = date.with({ month: 12 });
+ assert.sameValue(dateMonth12.daysInMonth, daysInMonth12);
+ assert.sameValue(isoYear, isoFields.isoYear);
+ assert.sameValue(isoMonth, isoFields.isoMonth);
+ assert.sameValue(isoDay, isoFields.isoDay);
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/shell.js b/js/src/tests/test262/staging/Intl402/Temporal/old/shell.js
new file mode 100644
index 0000000000..276ee734ff
--- /dev/null
+++ b/js/src/tests/test262/staging/Intl402/Temporal/old/shell.js
@@ -0,0 +1,329 @@
+// GENERATED, DO NOT EDIT
+// file: deepEqual.js
+// Copyright 2019 Ron Buckton. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: >
+ Compare two values structurally
+defines: [assert.deepEqual]
+---*/
+
+assert.deepEqual = function(actual, expected, message) {
+ var format = assert.deepEqual.format;
+ assert(
+ assert.deepEqual._compare(actual, expected),
+ `Expected ${format(actual)} to be structurally equal to ${format(expected)}. ${(message || '')}`
+ );
+};
+
+assert.deepEqual.format = function(value, seen) {
+ switch (typeof value) {
+ case 'string':
+ return typeof JSON !== "undefined" ? JSON.stringify(value) : `"${value}"`;
+ case 'number':
+ case 'boolean':
+ case 'symbol':
+ case 'bigint':
+ return value.toString();
+ case 'undefined':
+ return 'undefined';
+ case 'function':
+ return `[Function${value.name ? `: ${value.name}` : ''}]`;
+ case 'object':
+ if (value === null) return 'null';
+ if (value instanceof Date) return `Date "${value.toISOString()}"`;
+ if (value instanceof RegExp) return value.toString();
+ if (!seen) {
+ seen = {
+ counter: 0,
+ map: new Map()
+ };
+ }
+
+ let usage = seen.map.get(value);
+ if (usage) {
+ usage.used = true;
+ return `[Ref: #${usage.id}]`;
+ }
+
+ usage = { id: ++seen.counter, used: false };
+ seen.map.set(value, usage);
+
+ if (typeof Set !== "undefined" && value instanceof Set) {
+ return `Set {${Array.from(value).map(value => assert.deepEqual.format(value, seen)).join(', ')}}${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ if (typeof Map !== "undefined" && value instanceof Map) {
+ return `Map {${Array.from(value).map(pair => `${assert.deepEqual.format(pair[0], seen)} => ${assert.deepEqual.format(pair[1], seen)}}`).join(', ')}}${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ if (Array.isArray ? Array.isArray(value) : value instanceof Array) {
+ return `[${value.map(value => assert.deepEqual.format(value, seen)).join(', ')}]${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ let tag = Symbol.toStringTag in value ? value[Symbol.toStringTag] : 'Object';
+ if (tag === 'Object' && Object.getPrototypeOf(value) === null) {
+ tag = '[Object: null prototype]';
+ }
+ return `${tag ? `${tag} ` : ''}{ ${Object.keys(value).map(key => `${key.toString()}: ${assert.deepEqual.format(value[key], seen)}`).join(', ')} }${usage.used ? ` as #${usage.id}` : ''}`;
+ default:
+ return typeof value;
+ }
+};
+
+assert.deepEqual._compare = (function () {
+ var EQUAL = 1;
+ var NOT_EQUAL = -1;
+ var UNKNOWN = 0;
+
+ function deepEqual(a, b) {
+ return compareEquality(a, b) === EQUAL;
+ }
+
+ function compareEquality(a, b, cache) {
+ return compareIf(a, b, isOptional, compareOptionality)
+ || compareIf(a, b, isPrimitiveEquatable, comparePrimitiveEquality)
+ || compareIf(a, b, isObjectEquatable, compareObjectEquality, cache)
+ || NOT_EQUAL;
+ }
+
+ function compareIf(a, b, test, compare, cache) {
+ return !test(a)
+ ? !test(b) ? UNKNOWN : NOT_EQUAL
+ : !test(b) ? NOT_EQUAL : cacheComparison(a, b, compare, cache);
+ }
+
+ function tryCompareStrictEquality(a, b) {
+ return a === b ? EQUAL : UNKNOWN;
+ }
+
+ function tryCompareTypeOfEquality(a, b) {
+ return typeof a !== typeof b ? NOT_EQUAL : UNKNOWN;
+ }
+
+ function tryCompareToStringTagEquality(a, b) {
+ var aTag = Symbol.toStringTag in a ? a[Symbol.toStringTag] : undefined;
+ var bTag = Symbol.toStringTag in b ? b[Symbol.toStringTag] : undefined;
+ return aTag !== bTag ? NOT_EQUAL : UNKNOWN;
+ }
+
+ function isOptional(value) {
+ return value === undefined
+ || value === null;
+ }
+
+ function compareOptionality(a, b) {
+ return tryCompareStrictEquality(a, b)
+ || NOT_EQUAL;
+ }
+
+ function isPrimitiveEquatable(value) {
+ switch (typeof value) {
+ case 'string':
+ case 'number':
+ case 'bigint':
+ case 'boolean':
+ case 'symbol':
+ return true;
+ default:
+ return isBoxed(value);
+ }
+ }
+
+ function comparePrimitiveEquality(a, b) {
+ if (isBoxed(a)) a = a.valueOf();
+ if (isBoxed(b)) b = b.valueOf();
+ return tryCompareStrictEquality(a, b)
+ || tryCompareTypeOfEquality(a, b)
+ || compareIf(a, b, isNaNEquatable, compareNaNEquality)
+ || NOT_EQUAL;
+ }
+
+ function isNaNEquatable(value) {
+ return typeof value === 'number';
+ }
+
+ function compareNaNEquality(a, b) {
+ return isNaN(a) && isNaN(b) ? EQUAL : NOT_EQUAL;
+ }
+
+ function isObjectEquatable(value) {
+ return typeof value === 'object';
+ }
+
+ function compareObjectEquality(a, b, cache) {
+ if (!cache) cache = new Map();
+ return getCache(cache, a, b)
+ || setCache(cache, a, b, EQUAL) // consider equal for now
+ || cacheComparison(a, b, tryCompareStrictEquality, cache)
+ || cacheComparison(a, b, tryCompareToStringTagEquality, cache)
+ || compareIf(a, b, isValueOfEquatable, compareValueOfEquality)
+ || compareIf(a, b, isToStringEquatable, compareToStringEquality)
+ || compareIf(a, b, isArrayLikeEquatable, compareArrayLikeEquality, cache)
+ || compareIf(a, b, isStructurallyEquatable, compareStructuralEquality, cache)
+ || compareIf(a, b, isIterableEquatable, compareIterableEquality, cache)
+ || cacheComparison(a, b, fail, cache);
+ }
+
+ function isBoxed(value) {
+ return value instanceof String
+ || value instanceof Number
+ || value instanceof Boolean
+ || typeof Symbol === 'function' && value instanceof Symbol
+ || typeof BigInt === 'function' && value instanceof BigInt;
+ }
+
+ function isValueOfEquatable(value) {
+ return value instanceof Date;
+ }
+
+ function compareValueOfEquality(a, b) {
+ return compareIf(a.valueOf(), b.valueOf(), isPrimitiveEquatable, comparePrimitiveEquality)
+ || NOT_EQUAL;
+ }
+
+ function isToStringEquatable(value) {
+ return value instanceof RegExp;
+ }
+
+ function compareToStringEquality(a, b) {
+ return compareIf(a.toString(), b.toString(), isPrimitiveEquatable, comparePrimitiveEquality)
+ || NOT_EQUAL;
+ }
+
+ function isArrayLikeEquatable(value) {
+ return (Array.isArray ? Array.isArray(value) : value instanceof Array)
+ || (typeof Uint8Array === 'function' && value instanceof Uint8Array)
+ || (typeof Uint8ClampedArray === 'function' && value instanceof Uint8ClampedArray)
+ || (typeof Uint16Array === 'function' && value instanceof Uint16Array)
+ || (typeof Uint32Array === 'function' && value instanceof Uint32Array)
+ || (typeof Int8Array === 'function' && value instanceof Int8Array)
+ || (typeof Int16Array === 'function' && value instanceof Int16Array)
+ || (typeof Int32Array === 'function' && value instanceof Int32Array)
+ || (typeof Float32Array === 'function' && value instanceof Float32Array)
+ || (typeof Float64Array === 'function' && value instanceof Float64Array)
+ || (typeof BigUint64Array === 'function' && value instanceof BigUint64Array)
+ || (typeof BigInt64Array === 'function' && value instanceof BigInt64Array);
+ }
+
+ function compareArrayLikeEquality(a, b, cache) {
+ if (a.length !== b.length) return NOT_EQUAL;
+ for (var i = 0; i < a.length; i++) {
+ if (compareEquality(a[i], b[i], cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ }
+ return EQUAL;
+ }
+
+ function isStructurallyEquatable(value) {
+ return !(typeof Promise === 'function' && value instanceof Promise // only comparable by reference
+ || typeof WeakMap === 'function' && value instanceof WeakMap // only comparable by reference
+ || typeof WeakSet === 'function' && value instanceof WeakSet // only comparable by reference
+ || typeof Map === 'function' && value instanceof Map // comparable via @@iterator
+ || typeof Set === 'function' && value instanceof Set); // comparable via @@iterator
+ }
+
+ function compareStructuralEquality(a, b, cache) {
+ var aKeys = [];
+ for (var key in a) aKeys.push(key);
+
+ var bKeys = [];
+ for (var key in b) bKeys.push(key);
+
+ if (aKeys.length !== bKeys.length) {
+ return NOT_EQUAL;
+ }
+
+ aKeys.sort();
+ bKeys.sort();
+
+ for (var i = 0; i < aKeys.length; i++) {
+ var aKey = aKeys[i];
+ var bKey = bKeys[i];
+ if (compareEquality(aKey, bKey, cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ if (compareEquality(a[aKey], b[bKey], cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ }
+
+ return compareIf(a, b, isIterableEquatable, compareIterableEquality, cache)
+ || EQUAL;
+ }
+
+ function isIterableEquatable(value) {
+ return typeof Symbol === 'function'
+ && typeof value[Symbol.iterator] === 'function';
+ }
+
+ function compareIteratorEquality(a, b, cache) {
+ if (typeof Map === 'function' && a instanceof Map && b instanceof Map ||
+ typeof Set === 'function' && a instanceof Set && b instanceof Set) {
+ if (a.size !== b.size) return NOT_EQUAL; // exit early if we detect a difference in size
+ }
+
+ var ar, br;
+ while (true) {
+ ar = a.next();
+ br = b.next();
+ if (ar.done) {
+ if (br.done) return EQUAL;
+ if (b.return) b.return();
+ return NOT_EQUAL;
+ }
+ if (br.done) {
+ if (a.return) a.return();
+ return NOT_EQUAL;
+ }
+ if (compareEquality(ar.value, br.value, cache) === NOT_EQUAL) {
+ if (a.return) a.return();
+ if (b.return) b.return();
+ return NOT_EQUAL;
+ }
+ }
+ }
+
+ function compareIterableEquality(a, b, cache) {
+ return compareIteratorEquality(a[Symbol.iterator](), b[Symbol.iterator](), cache);
+ }
+
+ function cacheComparison(a, b, compare, cache) {
+ var result = compare(a, b, cache);
+ if (cache && (result === EQUAL || result === NOT_EQUAL)) {
+ setCache(cache, a, b, /** @type {EQUAL | NOT_EQUAL} */(result));
+ }
+ return result;
+ }
+
+ function fail() {
+ return NOT_EQUAL;
+ }
+
+ function setCache(cache, left, right, result) {
+ var otherCache;
+
+ otherCache = cache.get(left);
+ if (!otherCache) cache.set(left, otherCache = new Map());
+ otherCache.set(right, result);
+
+ otherCache = cache.get(right);
+ if (!otherCache) cache.set(right, otherCache = new Map());
+ otherCache.set(left, result);
+ }
+
+ function getCache(cache, left, right) {
+ var otherCache;
+ var result;
+
+ otherCache = cache.get(left);
+ result = otherCache && otherCache.get(right);
+ if (result) return result;
+
+ otherCache = cache.get(right);
+ result = otherCache && otherCache.get(left);
+ if (result) return result;
+
+ return UNKNOWN;
+ }
+
+ return deepEqual;
+})();
diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/time-toLocaleString.js b/js/src/tests/test262/staging/Intl402/Temporal/old/time-toLocaleString.js
new file mode 100644
index 0000000000..a521123333
--- /dev/null
+++ b/js/src/tests/test262/staging/Intl402/Temporal/old/time-toLocaleString.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: time.toLocaleString()
+features: [Temporal]
+---*/
+
+// Tolerate implementation variance by expecting consistency without being prescriptive.
+// TODO: can we change tests to be less reliant on CLDR formats while still testing that
+// Temporal and Intl are behaving as expected?
+const usDayPeriodSpace =
+ new Intl.DateTimeFormat("en-US", { timeStyle: "short" })
+ .formatToParts(0)
+ .find((part, i, parts) => part.type === "literal" && parts[i + 1].type === "dayPeriod")?.value || "";
+
+var time = Temporal.PlainTime.from("1976-11-18T15:23:30");
+assert.sameValue(`${time.toLocaleString("en-US", { timeZone: "America/New_York" })}`, `3:23:30${usDayPeriodSpace}PM`);
+assert.sameValue(`${time.toLocaleString("de-AT", { timeZone: "Europe/Vienna" })}`, "15:23:30");
+
+// should ignore units not in the data type
+assert.sameValue(time.toLocaleString("en-US", { timeZoneName: "long" }), `3:23:30${usDayPeriodSpace}PM`);
+assert.sameValue(time.toLocaleString("en-US", { year: "numeric" }), `3:23:30${usDayPeriodSpace}PM`);
+assert.sameValue(time.toLocaleString("en-US", { month: "numeric" }), `3:23:30${usDayPeriodSpace}PM`);
+assert.sameValue(time.toLocaleString("en-US", { day: "numeric" }), `3:23:30${usDayPeriodSpace}PM`);
+assert.sameValue(time.toLocaleString("en-US", { weekday: "long" }), `3:23:30${usDayPeriodSpace}PM`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/timezone-america-la.js b/js/src/tests/test262/staging/Intl402/Temporal/old/timezone-america-la.js
new file mode 100644
index 0000000000..662f54d05c
--- /dev/null
+++ b/js/src/tests/test262/staging/Intl402/Temporal/old/timezone-america-la.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-timezone-objects
+description: America/Los_Angeles
+features: [Temporal]
+---*/
+
+var zone = new Temporal.TimeZone("America/Los_Angeles");
+var inst = new Temporal.Instant(0n);
+var dtm = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+assert.sameValue(zone.id, `${ zone }`)
+assert.sameValue(zone.getOffsetNanosecondsFor(inst), -8 * 3600000000000)
+assert.sameValue(zone.getOffsetStringFor(inst), "-08:00")
+assert(zone.getInstantFor(dtm) instanceof Temporal.Instant)
+for (var i = 0, txn = inst; i < 4; i++) {
+ var transition = zone.getNextTransition(txn);
+ assert(transition instanceof Temporal.Instant);
+ assert(!transition.equals(txn));
+ txn = transition;
+}
+for (var i = 0, txn = inst; i < 4; i++) {
+ var transition = zone.getPreviousTransition(txn);
+ assert(transition instanceof Temporal.Instant);
+ assert(!transition.equals(txn));
+ txn = transition;
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/tzdb-string-parsing.js b/js/src/tests/test262/staging/Intl402/Temporal/old/tzdb-string-parsing.js
new file mode 100644
index 0000000000..16690270bc
--- /dev/null
+++ b/js/src/tests/test262/staging/Intl402/Temporal/old/tzdb-string-parsing.js
@@ -0,0 +1,101 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: String parsing cases that require a TZDB in the implementation
+features: [Temporal]
+---*/
+
+// parses with an IANA zone but no offset (with disambiguation)
+var zdt = Temporal.ZonedDateTime.from("2020-03-08T02:30[America/Los_Angeles]", { disambiguation: "earlier" });
+assert.sameValue(zdt.toString(), "2020-03-08T01:30:00-08:00[America/Los_Angeles]");
+
+// "Z" means preserve the exact time in the given IANA time zone
+var zdt = Temporal.ZonedDateTime.from("2020-03-08T09:00:00Z[America/Los_Angeles]");
+assert.sameValue(zdt.toString(), "2020-03-08T01:00:00-08:00[America/Los_Angeles]");
+
+// Offset options
+
+// { offset: 'prefer' } if offset matches time zone (first 1:30 when DST ends)
+var zdt = Temporal.ZonedDateTime.from("2020-11-01T01:30-07:00[America/Los_Angeles]", { offset: "prefer" });
+assert.sameValue(zdt.toString(), "2020-11-01T01:30:00-07:00[America/Los_Angeles]");
+
+// { offset: 'prefer' } if offset matches time zone (second 1:30 when DST ends)
+var zdt = Temporal.ZonedDateTime.from("2020-11-01T01:30-08:00[America/Los_Angeles]", { offset: "prefer" });
+assert.sameValue(zdt.toString(), "2020-11-01T01:30:00-08:00[America/Los_Angeles]");
+
+// { offset: 'prefer' } if offset does not match time zone
+var zdt = Temporal.ZonedDateTime.from("2020-11-01T04:00-07:00[America/Los_Angeles]", { offset: "prefer" });
+assert.sameValue(zdt.toString(), "2020-11-01T04:00:00-08:00[America/Los_Angeles]");
+
+// { offset: 'ignore' } uses time zone only
+var zdt = Temporal.ZonedDateTime.from("2020-11-01T04:00-12:00[America/Los_Angeles]", { offset: "ignore" });
+assert.sameValue(zdt.toString(), "2020-11-01T04:00:00-08:00[America/Los_Angeles]");
+
+// { offset: 'use' } uses offset only
+var zdt = Temporal.ZonedDateTime.from("2020-11-01T04:00-07:00[America/Los_Angeles]", { offset: "use" });
+assert.sameValue(zdt.toString(), "2020-11-01T03:00:00-08:00[America/Los_Angeles]");
+
+// Disambiguation options
+
+// plain datetime with multiple instants - Fall DST in Brazil
+var str = "2019-02-16T23:45[America/Sao_Paulo]";
+assert.sameValue(`${ Temporal.ZonedDateTime.from(str) }`, "2019-02-16T23:45:00-02:00[America/Sao_Paulo]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from(str, { disambiguation: "compatible" }) }`, "2019-02-16T23:45:00-02:00[America/Sao_Paulo]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from(str, { disambiguation: "earlier" }) }`, "2019-02-16T23:45:00-02:00[America/Sao_Paulo]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from(str, { disambiguation: "later" }) }`, "2019-02-16T23:45:00-03:00[America/Sao_Paulo]");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(str, { disambiguation: "reject" }));
+
+// plain datetime with multiple instants - Spring DST in Los Angeles
+var str = "2020-03-08T02:30[America/Los_Angeles]";
+assert.sameValue(`${ Temporal.ZonedDateTime.from(str) }`, "2020-03-08T03:30:00-07:00[America/Los_Angeles]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from(str, { disambiguation: "compatible" }) }`, "2020-03-08T03:30:00-07:00[America/Los_Angeles]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from(str, { disambiguation: "earlier" }) }`, "2020-03-08T01:30:00-08:00[America/Los_Angeles]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from(str, { disambiguation: "later" }) }`, "2020-03-08T03:30:00-07:00[America/Los_Angeles]");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(str, { disambiguation: "reject" }));
+
+// uses disambiguation if offset is ignored
+var str = "2020-03-08T02:30[America/Los_Angeles]";
+var offset = "ignore";
+assert.sameValue(`${ Temporal.ZonedDateTime.from(str, { offset }) }`, "2020-03-08T03:30:00-07:00[America/Los_Angeles]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from(str, {
+ offset,
+ disambiguation: "compatible"
+}) }`, "2020-03-08T03:30:00-07:00[America/Los_Angeles]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from(str, {
+ offset,
+ disambiguation: "earlier"
+}) }`, "2020-03-08T01:30:00-08:00[America/Los_Angeles]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from(str, {
+ offset,
+ disambiguation: "later"
+}) }`, "2020-03-08T03:30:00-07:00[America/Los_Angeles]");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(str, {
+ offset,
+ disambiguation: "reject"
+}));
+
+// uses disambiguation if offset is wrong and option is prefer
+var str = "2020-03-08T02:30-23:59[America/Los_Angeles]";
+var offset = "prefer";
+assert.sameValue(`${ Temporal.ZonedDateTime.from(str, { offset }) }`, "2020-03-08T03:30:00-07:00[America/Los_Angeles]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from(str, {
+ offset,
+ disambiguation: "compatible"
+}) }`, "2020-03-08T03:30:00-07:00[America/Los_Angeles]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from(str, {
+ offset,
+ disambiguation: "earlier"
+}) }`, "2020-03-08T01:30:00-08:00[America/Los_Angeles]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from(str, {
+ offset,
+ disambiguation: "later"
+}) }`, "2020-03-08T03:30:00-07:00[America/Los_Angeles]");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(str, {
+ offset,
+ disambiguation: "reject"
+}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/tzdb-transitions.js b/js/src/tests/test262/staging/Intl402/Temporal/old/tzdb-transitions.js
new file mode 100644
index 0000000000..52978e40fb
--- /dev/null
+++ b/js/src/tests/test262/staging/Intl402/Temporal/old/tzdb-transitions.js
@@ -0,0 +1,47 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-timezone-objects
+description: Tests that are impractical to do without a TZDB
+features: [Temporal]
+---*/
+
+// getNextTransition()
+
+var nyc = Temporal.TimeZone.from("America/New_York");
+
+// should not have bug #510
+var a1 = Temporal.Instant.from("2019-04-16T21:01Z");
+var a2 = Temporal.Instant.from("1800-01-01T00:00Z");
+assert.sameValue(nyc.getNextTransition(a1).toString(), "2019-11-03T06:00:00Z");
+assert.sameValue(nyc.getNextTransition(a2).toString(), "1883-11-18T17:00:00Z");
+
+// should not return the same as its input if the input is a transition point
+var inst = Temporal.Instant.from("2019-01-01T00:00Z");
+assert.sameValue(`${ nyc.getNextTransition(inst) }`, "2019-03-10T07:00:00Z");
+assert.sameValue(`${ nyc.getNextTransition(nyc.getNextTransition(inst)) }`, "2019-11-03T06:00:00Z");
+
+// casts argument
+assert.sameValue(`${ nyc.getNextTransition("2019-04-16T21:01Z") }`, "2019-11-03T06:00:00Z");
+
+// getPreviousTransition()
+
+var london = Temporal.TimeZone.from("Europe/London");
+
+// should return first and last transition
+var a1 = Temporal.Instant.from("2020-06-11T21:01Z");
+var a2 = Temporal.Instant.from("1848-01-01T00:00Z");
+assert.sameValue(london.getPreviousTransition(a1).toString(), "2020-03-29T01:00:00Z");
+assert.sameValue(london.getPreviousTransition(a2).toString(), "1847-12-01T00:01:15Z");
+
+// should not return the same as its input if the input is a transition point
+var inst = Temporal.Instant.from("2020-06-01T00:00Z");
+assert.sameValue(`${ london.getPreviousTransition(inst) }`, "2020-03-29T01:00:00Z");
+assert.sameValue(`${ london.getPreviousTransition(london.getPreviousTransition(inst)) }`, "2019-10-27T01:00:00Z");
+
+// casts argument
+assert.sameValue(`${ london.getPreviousTransition("2020-06-11T21:01Z") }`, "2020-03-29T01:00:00Z");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/yearmonth-toLocaleString.js b/js/src/tests/test262/staging/Intl402/Temporal/old/yearmonth-toLocaleString.js
new file mode 100644
index 0000000000..c92aa506d5
--- /dev/null
+++ b/js/src/tests/test262/staging/Intl402/Temporal/old/yearmonth-toLocaleString.js
@@ -0,0 +1,42 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-intl
+description: yearmonth.toLocaleString()
+features: [Temporal]
+---*/
+
+// Tolerate implementation variance by expecting consistency without being prescriptive.
+// TODO: can we change tests to be less reliant on CLDR formats while still testing that
+// Temporal and Intl are behaving as expected?
+// Workarounds for https://unicode-org.atlassian.net/browse/CLDR-16243
+const deMonthDayRangeSeparator = new Intl.DateTimeFormat("de-AT", { month: "numeric", day: "numeric" })
+ .formatRangeToParts(1 * 86400 * 1000, 90 * 86400 * 1000)
+ .find((part) => part.type === "literal" && part.source === "shared").value;
+const deMonthYearSeparator = new Intl.DateTimeFormat("de-AT", { year: "numeric", month: "numeric" })
+ .formatToParts(0)
+ .find((part) => part.type === "literal").value;
+
+var calendar = new Intl.DateTimeFormat("en-US").resolvedOptions().calendar;
+var yearmonth = Temporal.PlainYearMonth.from({
+ year: 1976,
+ month: 11,
+ calendar
+});
+assert.sameValue(`${yearmonth.toLocaleString("en-US", { timeZone: "America/New_York" })}`, "11/1976");
+assert.sameValue(
+ `${yearmonth.toLocaleString("de-AT", { timeZone: "Europe/Vienna", calendar })}`,
+ `11${deMonthYearSeparator}1976`
+);
+
+// should ignore units not in the data type
+assert.sameValue(yearmonth.toLocaleString("en-US", { timeZoneName: "long" }), "11/1976");
+assert.sameValue(yearmonth.toLocaleString("en-US", { day: "numeric" }), "11/1976");
+assert.sameValue(yearmonth.toLocaleString("en-US", { hour: "numeric" }), "11/1976");
+assert.sameValue(yearmonth.toLocaleString("en-US", { minute: "numeric" }), "11/1976");
+assert.sameValue(yearmonth.toLocaleString("en-US", { second: "numeric" }), "11/1976");
+assert.sameValue(yearmonth.toLocaleString("en-US", { weekday: "long" }), "11/1976");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Intl402/Temporal/old/zoneddatetime-dst-corner-cases.js b/js/src/tests/test262/staging/Intl402/Temporal/old/zoneddatetime-dst-corner-cases.js
new file mode 100644
index 0000000000..d66a8d0715
--- /dev/null
+++ b/js/src/tests/test262/staging/Intl402/Temporal/old/zoneddatetime-dst-corner-cases.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: Corner cases of time zone offset shifts
+features: [Temporal]
+---*/
+
+// hoursInDay works with non-hour DST change
+var zdt1 = Temporal.ZonedDateTime.from("2020-10-04T12:00[Australia/Lord_Howe]");
+assert.sameValue(zdt1.hoursInDay, 23.5);
+var zdt2 = Temporal.ZonedDateTime.from("2020-04-05T12:00[Australia/Lord_Howe]");
+assert.sameValue(zdt2.hoursInDay, 24.5);
+
+// hoursInDay works with non-half-hour DST change
+var zdt = Temporal.ZonedDateTime.from("1933-01-01T12:00[Asia/Singapore]");
+assert(Math.abs(zdt.hoursInDay - 23.666666666666668) < Number.EPSILON);
+
+// hoursInDay works when day starts at 1:00 due to DST start at midnight
+var zdt = Temporal.ZonedDateTime.from("2015-10-18T12:00:00-02:00[America/Sao_Paulo]");
+assert.sameValue(zdt.hoursInDay, 23);
+
+// startOfDay works when day starts at 1:00 due to DST start at midnight
+var zdt = Temporal.ZonedDateTime.from("2015-10-18T12:00:00-02:00[America/Sao_Paulo]");
+assert.sameValue(`${ zdt.startOfDay().toPlainTime() }`, "01:00:00");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Intl402/Temporal/shell.js b/js/src/tests/test262/staging/Intl402/Temporal/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Intl402/Temporal/shell.js
diff --git a/js/src/tests/test262/staging/Intl402/browser.js b/js/src/tests/test262/staging/Intl402/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Intl402/browser.js
diff --git a/js/src/tests/test262/staging/Intl402/shell.js b/js/src/tests/test262/staging/Intl402/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Intl402/shell.js
diff --git a/js/src/tests/test262/staging/JSON/browser.js b/js/src/tests/test262/staging/JSON/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/JSON/browser.js
diff --git a/js/src/tests/test262/staging/JSON/json-parse-with-source-snapshot.js b/js/src/tests/test262/staging/JSON/json-parse-with-source-snapshot.js
new file mode 100644
index 0000000000..45465333d4
--- /dev/null
+++ b/js/src/tests/test262/staging/JSON/json-parse-with-source-snapshot.js
@@ -0,0 +1,97 @@
+// |reftest| skip -- json-parse-with-source is not supported
+// Copyright (C) 2023 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: V8 mjsunit test for JSON.parse with source snapshotting
+includes: [deepEqual.js]
+features: [json-parse-with-source]
+---*/
+
+const replacements = [42,
+ ['foo'],
+ {foo:'bar'},
+ 'foo'];
+
+function TestArrayForwardModify(replacement) {
+ let alreadyReplaced = false;
+ let expectedKeys = ['0','1',''];
+ // lol who designed reviver semantics
+ if (typeof replacement === 'object') {
+ expectedKeys.splice(1, 0, ...Object.keys(replacement));
+ }
+ const o = JSON.parse('[1, 2]', function (k, v, { source }) {
+ assert.sameValue(expectedKeys.shift(), k);
+ if (k === '0') {
+ if (!alreadyReplaced) {
+ this[1] = replacement;
+ alreadyReplaced = true;
+ }
+ } else if (k !== '') {
+ assert.sameValue(undefined, source);
+ }
+ return this[k];
+ });
+ assert.sameValue(0, expectedKeys.length);
+ assert.deepEqual([1, replacement], o);
+}
+
+function TestObjectForwardModify(replacement) {
+ let alreadyReplaced = false;
+ let expectedKeys = ['p','q',''];
+ if (typeof replacement === 'object') {
+ expectedKeys.splice(1, 0, ...Object.keys(replacement));
+ }
+ const o = JSON.parse('{"p":1, "q":2}', function (k, v, { source }) {
+ assert.sameValue(expectedKeys.shift(), k);
+ if (k === 'p') {
+ if (!alreadyReplaced) {
+ this.q = replacement;
+ alreadyReplaced = true;
+ }
+ } else if (k !== '') {
+ assert.sameValue(undefined, source);
+ }
+ return this[k];
+ });
+ assert.sameValue(0, expectedKeys.length);
+ assert.deepEqual({p:1, q:replacement}, o);
+}
+
+for (const r of replacements) {
+ TestArrayForwardModify(r);
+ TestObjectForwardModify(r);
+}
+
+(function TestArrayAppend() {
+ let log = [];
+ const o = JSON.parse('[1,[]]', function (k, v, { source }) {
+ log.push([k, v, source]);
+ if (v === 1) {
+ this[1].push('barf');
+ }
+ return this[k];
+ });
+ assert.deepEqual([['0', 1, '1'],
+ ['0', 'barf', undefined],
+ ['1', ['barf'], undefined],
+ ['', [1, ['barf']], undefined]],
+ log);
+})();
+
+(function TestObjectAddProperty() {
+ let log = [];
+ const o = JSON.parse('{"p":1,"q":{}}', function (k, v, { source }) {
+ log.push([k, v, source]);
+ if (v === 1) {
+ this.q.added = 'barf';
+ }
+ return this[k];
+ });
+ assert.deepEqual([['p', 1, '1'],
+ ['added', 'barf', undefined],
+ ['q', {added:'barf'}, undefined],
+ ['', {p:1, q:{added:'barf'}}, undefined]],
+ log);
+})();
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/JSON/json-parse-with-source.js b/js/src/tests/test262/staging/JSON/json-parse-with-source.js
new file mode 100644
index 0000000000..f8674511ef
--- /dev/null
+++ b/js/src/tests/test262/staging/JSON/json-parse-with-source.js
@@ -0,0 +1,292 @@
+// |reftest| skip -- json-parse-with-source is not supported
+// Copyright (C) 2023 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: V8 mjsunit test for JSON.parse with source
+includes: [deepEqual.js]
+features: [json-parse-with-source]
+---*/
+
+(function TestBigInt() {
+ const tooBigForNumber = BigInt(Number.MAX_SAFE_INTEGER) + 2n;
+ const intToBigInt = (key, val, { source }) =>
+ typeof val === 'number' && val % 1 === 0 ? BigInt(source) : val;
+ const roundTripped = JSON.parse(String(tooBigForNumber), intToBigInt);
+ assert.sameValue(tooBigForNumber, roundTripped);
+
+ const bigIntToRawJSON = (key, val) =>
+ typeof val === 'bigint' ? JSON.rawJSON(val) : val;
+ const embedded = JSON.stringify({ tooBigForNumber }, bigIntToRawJSON);
+ assert.sameValue('{"tooBigForNumber":9007199254740993}', embedded);
+})();
+
+function GenerateParseReviverFunction(texts) {
+ let i = 0;
+ return function (key, value, context) {
+ assert(typeof context === 'object');
+ assert.sameValue(Object.prototype, Object.getPrototypeOf(context));
+ // The json value is a primitive value, it's context only has a source property.
+ if (texts[i] !== undefined) {
+ const descriptor = Object.getOwnPropertyDescriptor(context, 'source');
+ assert(descriptor.configurable);
+ assert(descriptor.enumerable);
+ assert(descriptor.writable);
+ assert.sameValue(undefined, descriptor.get);
+ assert.sameValue(undefined, descriptor.set);
+ assert.sameValue(texts[i++], descriptor.value);
+
+ assert.deepEqual(['source'], Object.getOwnPropertyNames(context));
+ assert.deepEqual([], Object.getOwnPropertySymbols(context));
+ } else {
+ // The json value is JSArray or JSObject, it's context has no property.
+ assert(!Object.hasOwn(context, 'source'));
+ assert.deepEqual([], Object.getOwnPropertyNames(context));
+ assert.deepEqual([], Object.getOwnPropertySymbols(context));
+ i++;
+ }
+ return value;
+ };
+}
+
+(function TestNumber() {
+ assert.sameValue(1, JSON.parse('1', GenerateParseReviverFunction(['1'])));
+ assert.sameValue(1.1, JSON.parse('1.1', GenerateParseReviverFunction(['1.1'])));
+ assert.sameValue(-1, JSON.parse('-1', GenerateParseReviverFunction(['-1'])));
+ assert.sameValue(
+ -1.1,
+ JSON.parse('-1.1', GenerateParseReviverFunction(['-1.1']))
+ );
+ assert.sameValue(
+ 11,
+ JSON.parse('1.1e1', GenerateParseReviverFunction(['1.1e1']))
+ );
+ assert.sameValue(
+ 11,
+ JSON.parse('1.1e+1', GenerateParseReviverFunction(['1.1e+1']))
+ );
+ assert.sameValue(
+ 0.11,
+ JSON.parse('1.1e-1', GenerateParseReviverFunction(['1.1e-1']))
+ );
+ assert.sameValue(
+ 11,
+ JSON.parse('1.1E1', GenerateParseReviverFunction(['1.1E1']))
+ );
+ assert.sameValue(
+ 11,
+ JSON.parse('1.1E+1', GenerateParseReviverFunction(['1.1E+1']))
+ );
+ assert.sameValue(
+ 0.11,
+ JSON.parse('1.1E-1', GenerateParseReviverFunction(['1.1E-1']))
+ );
+
+ assert.sameValue('1', JSON.stringify(JSON.rawJSON(1)));
+ assert.sameValue('1.1', JSON.stringify(JSON.rawJSON(1.1)));
+ assert.sameValue('-1', JSON.stringify(JSON.rawJSON(-1)));
+ assert.sameValue('-1.1', JSON.stringify(JSON.rawJSON(-1.1)));
+ assert.sameValue('11', JSON.stringify(JSON.rawJSON(1.1e1)));
+ assert.sameValue('0.11', JSON.stringify(JSON.rawJSON(1.1e-1)));
+})();
+
+(function TestBasic() {
+ assert.sameValue(
+ null,
+ JSON.parse('null', GenerateParseReviverFunction(['null']))
+ );
+ assert.sameValue(
+ true,
+ JSON.parse('true', GenerateParseReviverFunction(['true']))
+ );
+ assert.sameValue(
+ false,
+ JSON.parse('false', GenerateParseReviverFunction(['false']))
+ );
+ assert.sameValue(
+ 'foo',
+ JSON.parse('"foo"', GenerateParseReviverFunction(['"foo"']))
+ );
+
+ assert.sameValue('null', JSON.stringify(JSON.rawJSON(null)));
+ assert.sameValue('true', JSON.stringify(JSON.rawJSON(true)));
+ assert.sameValue('false', JSON.stringify(JSON.rawJSON(false)));
+ assert.sameValue('"foo"', JSON.stringify(JSON.rawJSON('"foo"')));
+})();
+
+(function TestObject() {
+ assert.deepEqual(
+ {},
+ JSON.parse('{}', GenerateParseReviverFunction([]))
+ );
+ assert.deepEqual(
+ { 42: 37 },
+ JSON.parse('{"42":37}', GenerateParseReviverFunction(['37']))
+ );
+ assert.deepEqual(
+ { x: 1, y: 2 },
+ JSON.parse('{"x": 1, "y": 2}', GenerateParseReviverFunction(['1', '2']))
+ );
+ // undefined means the json value is JSObject or JSArray and the passed
+ // context to the reviver function has no source property.
+ assert.deepEqual(
+ { x: [1, 2], y: [2, 3] },
+ JSON.parse(
+ '{"x": [1,2], "y": [2,3]}',
+ GenerateParseReviverFunction(['1', '2', undefined, '2', '3', undefined])
+ )
+ );
+ assert.deepEqual(
+ { x: { x: 1, y: 2 } },
+ JSON.parse(
+ '{"x": {"x": 1, "y": 2}}',
+ GenerateParseReviverFunction(['1', '2', undefined, undefined])
+ )
+ );
+
+ assert.sameValue('{"42":37}', JSON.stringify({ 42: JSON.rawJSON(37) }));
+ assert.sameValue(
+ '{"x":1,"y":2}',
+ JSON.stringify({ x: JSON.rawJSON(1), y: JSON.rawJSON(2) })
+ );
+ assert.sameValue(
+ '{"x":{"x":1,"y":2}}',
+ JSON.stringify({ x: { x: JSON.rawJSON(1), y: JSON.rawJSON(2) } })
+ );
+})();
+
+(function TestArray() {
+ assert.deepEqual([1], JSON.parse('[1.0]', GenerateParseReviverFunction(['1.0'])));
+ assert.deepEqual(
+ [1.1],
+ JSON.parse('[1.1]', GenerateParseReviverFunction(['1.1']))
+ );
+ assert.deepEqual([], JSON.parse('[]', GenerateParseReviverFunction([])));
+ assert.deepEqual(
+ [1, '2', true, null, { x: 1, y: 1 }],
+ JSON.parse(
+ '[1, "2", true, null, {"x": 1, "y": 1}]',
+ GenerateParseReviverFunction(['1', '"2"', 'true', 'null', '1', '1'])
+ )
+ );
+
+ assert.sameValue('[1,1.1]', JSON.stringify([JSON.rawJSON(1), JSON.rawJSON(1.1)]));
+ assert.sameValue(
+ '["1",true,null,false]',
+ JSON.stringify([
+ JSON.rawJSON('"1"'),
+ JSON.rawJSON(true),
+ JSON.rawJSON(null),
+ JSON.rawJSON(false),
+ ])
+ );
+ assert.sameValue(
+ '[{"x":1,"y":1}]',
+ JSON.stringify([{ x: JSON.rawJSON(1), y: JSON.rawJSON(1) }])
+ );
+})();
+
+function assertIsRawJson(rawJson, expectedRawJsonValue) {
+ assert.sameValue(null, Object.getPrototypeOf(rawJson));
+ assert(Object.hasOwn(rawJson, 'rawJSON'));
+ assert.deepEqual(['rawJSON'], Object.getOwnPropertyNames(rawJson));
+ assert.deepEqual([], Object.getOwnPropertySymbols(rawJson));
+ assert.sameValue(expectedRawJsonValue, rawJson.rawJSON);
+}
+
+(function TestRawJson() {
+ assertIsRawJson(JSON.rawJSON(1), '1');
+ assertIsRawJson(JSON.rawJSON(null), 'null');
+ assertIsRawJson(JSON.rawJSON(true), 'true');
+ assertIsRawJson(JSON.rawJSON(false), 'false');
+ assertIsRawJson(JSON.rawJSON('"foo"'), '"foo"');
+
+ assert.throws(TypeError, () => {
+ JSON.rawJSON(Symbol('123'));
+ });
+
+ assert.throws(SyntaxError, () => {
+ JSON.rawJSON(undefined);
+ });
+
+ assert.throws(SyntaxError, () => {
+ JSON.rawJSON({});
+ });
+
+ assert.throws(SyntaxError, () => {
+ JSON.rawJSON([]);
+ });
+
+ const ILLEGAL_END_CHARS = ['\n', '\t', '\r', ' '];
+ for (const char of ILLEGAL_END_CHARS) {
+ assert.throws(SyntaxError, () => {
+ JSON.rawJSON(`${char}123`);
+ });
+ assert.throws(SyntaxError, () => {
+ JSON.rawJSON(`123${char}`);
+ });
+ }
+
+ assert.throws(SyntaxError, () => {
+ JSON.rawJSON('');
+ });
+
+ const values = [1, 1.1, null, false, true, '123'];
+ for (const value of values) {
+ assert(!JSON.isRawJSON(value));
+ assert(JSON.isRawJSON(JSON.rawJSON(value)));
+ }
+ assert(!JSON.isRawJSON(undefined));
+ assert(!JSON.isRawJSON(Symbol('123')));
+ assert(!JSON.isRawJSON([]));
+ assert(!JSON.isRawJSON({ rawJSON: '123' }));
+})();
+
+(function TestReviverModifyJsonValue() {
+ {
+ let reviverCallIndex = 0;
+ const expectedKeys = ['a', 'b', 'c', ''];
+ const reviver = function(key, value, {source}) {
+ assert.sameValue(expectedKeys[reviverCallIndex++], key);
+ if (key == 'a') {
+ this.b = 2;
+ assert.sameValue('0', source);
+ } else if (key == 'b') {
+ this.c = 3;
+ assert.sameValue(2, value);
+ assert.sameValue(undefined, source);
+ } else if (key == 'c') {
+ assert.sameValue(3, value);
+ assert.sameValue(undefined, source);
+ }
+ return value;
+ }
+ assert.deepEqual({a: 0, b: 2, c: 3}, JSON.parse('{"a": 0, "b": 1, "c": [1, 2]}', reviver));
+ }
+ {
+ let reviverCallIndex = 0;
+ const expectedKeys = ['0', '1', '2', '3', ''];
+ const reviver = function(key, value, {source}) {
+ assert.sameValue(expectedKeys[reviverCallIndex++], key);
+ if (key == '0') {
+ this[1] = 3;
+ assert.sameValue(1, value);
+ assert.sameValue('1', source);
+ } else if (key == '1') {
+ this[2] = 4;
+ assert.sameValue(3, value);
+ assert.sameValue(undefined, source);
+ } else if(key == '2') {
+ this[3] = 5;
+ assert.sameValue(4, value);
+ assert.sameValue(undefined, source);
+ } else if(key == '5'){
+ assert.sameValue(5, value);
+ assert.sameValue(undefined, source);
+ }
+ return value;
+ }
+ assert.deepEqual([1, 3, 4, 5], JSON.parse('[1, 2, 3, {"a": 1}]', reviver));
+ }
+})();
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/JSON/shell.js b/js/src/tests/test262/staging/JSON/shell.js
new file mode 100644
index 0000000000..276ee734ff
--- /dev/null
+++ b/js/src/tests/test262/staging/JSON/shell.js
@@ -0,0 +1,329 @@
+// GENERATED, DO NOT EDIT
+// file: deepEqual.js
+// Copyright 2019 Ron Buckton. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: >
+ Compare two values structurally
+defines: [assert.deepEqual]
+---*/
+
+assert.deepEqual = function(actual, expected, message) {
+ var format = assert.deepEqual.format;
+ assert(
+ assert.deepEqual._compare(actual, expected),
+ `Expected ${format(actual)} to be structurally equal to ${format(expected)}. ${(message || '')}`
+ );
+};
+
+assert.deepEqual.format = function(value, seen) {
+ switch (typeof value) {
+ case 'string':
+ return typeof JSON !== "undefined" ? JSON.stringify(value) : `"${value}"`;
+ case 'number':
+ case 'boolean':
+ case 'symbol':
+ case 'bigint':
+ return value.toString();
+ case 'undefined':
+ return 'undefined';
+ case 'function':
+ return `[Function${value.name ? `: ${value.name}` : ''}]`;
+ case 'object':
+ if (value === null) return 'null';
+ if (value instanceof Date) return `Date "${value.toISOString()}"`;
+ if (value instanceof RegExp) return value.toString();
+ if (!seen) {
+ seen = {
+ counter: 0,
+ map: new Map()
+ };
+ }
+
+ let usage = seen.map.get(value);
+ if (usage) {
+ usage.used = true;
+ return `[Ref: #${usage.id}]`;
+ }
+
+ usage = { id: ++seen.counter, used: false };
+ seen.map.set(value, usage);
+
+ if (typeof Set !== "undefined" && value instanceof Set) {
+ return `Set {${Array.from(value).map(value => assert.deepEqual.format(value, seen)).join(', ')}}${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ if (typeof Map !== "undefined" && value instanceof Map) {
+ return `Map {${Array.from(value).map(pair => `${assert.deepEqual.format(pair[0], seen)} => ${assert.deepEqual.format(pair[1], seen)}}`).join(', ')}}${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ if (Array.isArray ? Array.isArray(value) : value instanceof Array) {
+ return `[${value.map(value => assert.deepEqual.format(value, seen)).join(', ')}]${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ let tag = Symbol.toStringTag in value ? value[Symbol.toStringTag] : 'Object';
+ if (tag === 'Object' && Object.getPrototypeOf(value) === null) {
+ tag = '[Object: null prototype]';
+ }
+ return `${tag ? `${tag} ` : ''}{ ${Object.keys(value).map(key => `${key.toString()}: ${assert.deepEqual.format(value[key], seen)}`).join(', ')} }${usage.used ? ` as #${usage.id}` : ''}`;
+ default:
+ return typeof value;
+ }
+};
+
+assert.deepEqual._compare = (function () {
+ var EQUAL = 1;
+ var NOT_EQUAL = -1;
+ var UNKNOWN = 0;
+
+ function deepEqual(a, b) {
+ return compareEquality(a, b) === EQUAL;
+ }
+
+ function compareEquality(a, b, cache) {
+ return compareIf(a, b, isOptional, compareOptionality)
+ || compareIf(a, b, isPrimitiveEquatable, comparePrimitiveEquality)
+ || compareIf(a, b, isObjectEquatable, compareObjectEquality, cache)
+ || NOT_EQUAL;
+ }
+
+ function compareIf(a, b, test, compare, cache) {
+ return !test(a)
+ ? !test(b) ? UNKNOWN : NOT_EQUAL
+ : !test(b) ? NOT_EQUAL : cacheComparison(a, b, compare, cache);
+ }
+
+ function tryCompareStrictEquality(a, b) {
+ return a === b ? EQUAL : UNKNOWN;
+ }
+
+ function tryCompareTypeOfEquality(a, b) {
+ return typeof a !== typeof b ? NOT_EQUAL : UNKNOWN;
+ }
+
+ function tryCompareToStringTagEquality(a, b) {
+ var aTag = Symbol.toStringTag in a ? a[Symbol.toStringTag] : undefined;
+ var bTag = Symbol.toStringTag in b ? b[Symbol.toStringTag] : undefined;
+ return aTag !== bTag ? NOT_EQUAL : UNKNOWN;
+ }
+
+ function isOptional(value) {
+ return value === undefined
+ || value === null;
+ }
+
+ function compareOptionality(a, b) {
+ return tryCompareStrictEquality(a, b)
+ || NOT_EQUAL;
+ }
+
+ function isPrimitiveEquatable(value) {
+ switch (typeof value) {
+ case 'string':
+ case 'number':
+ case 'bigint':
+ case 'boolean':
+ case 'symbol':
+ return true;
+ default:
+ return isBoxed(value);
+ }
+ }
+
+ function comparePrimitiveEquality(a, b) {
+ if (isBoxed(a)) a = a.valueOf();
+ if (isBoxed(b)) b = b.valueOf();
+ return tryCompareStrictEquality(a, b)
+ || tryCompareTypeOfEquality(a, b)
+ || compareIf(a, b, isNaNEquatable, compareNaNEquality)
+ || NOT_EQUAL;
+ }
+
+ function isNaNEquatable(value) {
+ return typeof value === 'number';
+ }
+
+ function compareNaNEquality(a, b) {
+ return isNaN(a) && isNaN(b) ? EQUAL : NOT_EQUAL;
+ }
+
+ function isObjectEquatable(value) {
+ return typeof value === 'object';
+ }
+
+ function compareObjectEquality(a, b, cache) {
+ if (!cache) cache = new Map();
+ return getCache(cache, a, b)
+ || setCache(cache, a, b, EQUAL) // consider equal for now
+ || cacheComparison(a, b, tryCompareStrictEquality, cache)
+ || cacheComparison(a, b, tryCompareToStringTagEquality, cache)
+ || compareIf(a, b, isValueOfEquatable, compareValueOfEquality)
+ || compareIf(a, b, isToStringEquatable, compareToStringEquality)
+ || compareIf(a, b, isArrayLikeEquatable, compareArrayLikeEquality, cache)
+ || compareIf(a, b, isStructurallyEquatable, compareStructuralEquality, cache)
+ || compareIf(a, b, isIterableEquatable, compareIterableEquality, cache)
+ || cacheComparison(a, b, fail, cache);
+ }
+
+ function isBoxed(value) {
+ return value instanceof String
+ || value instanceof Number
+ || value instanceof Boolean
+ || typeof Symbol === 'function' && value instanceof Symbol
+ || typeof BigInt === 'function' && value instanceof BigInt;
+ }
+
+ function isValueOfEquatable(value) {
+ return value instanceof Date;
+ }
+
+ function compareValueOfEquality(a, b) {
+ return compareIf(a.valueOf(), b.valueOf(), isPrimitiveEquatable, comparePrimitiveEquality)
+ || NOT_EQUAL;
+ }
+
+ function isToStringEquatable(value) {
+ return value instanceof RegExp;
+ }
+
+ function compareToStringEquality(a, b) {
+ return compareIf(a.toString(), b.toString(), isPrimitiveEquatable, comparePrimitiveEquality)
+ || NOT_EQUAL;
+ }
+
+ function isArrayLikeEquatable(value) {
+ return (Array.isArray ? Array.isArray(value) : value instanceof Array)
+ || (typeof Uint8Array === 'function' && value instanceof Uint8Array)
+ || (typeof Uint8ClampedArray === 'function' && value instanceof Uint8ClampedArray)
+ || (typeof Uint16Array === 'function' && value instanceof Uint16Array)
+ || (typeof Uint32Array === 'function' && value instanceof Uint32Array)
+ || (typeof Int8Array === 'function' && value instanceof Int8Array)
+ || (typeof Int16Array === 'function' && value instanceof Int16Array)
+ || (typeof Int32Array === 'function' && value instanceof Int32Array)
+ || (typeof Float32Array === 'function' && value instanceof Float32Array)
+ || (typeof Float64Array === 'function' && value instanceof Float64Array)
+ || (typeof BigUint64Array === 'function' && value instanceof BigUint64Array)
+ || (typeof BigInt64Array === 'function' && value instanceof BigInt64Array);
+ }
+
+ function compareArrayLikeEquality(a, b, cache) {
+ if (a.length !== b.length) return NOT_EQUAL;
+ for (var i = 0; i < a.length; i++) {
+ if (compareEquality(a[i], b[i], cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ }
+ return EQUAL;
+ }
+
+ function isStructurallyEquatable(value) {
+ return !(typeof Promise === 'function' && value instanceof Promise // only comparable by reference
+ || typeof WeakMap === 'function' && value instanceof WeakMap // only comparable by reference
+ || typeof WeakSet === 'function' && value instanceof WeakSet // only comparable by reference
+ || typeof Map === 'function' && value instanceof Map // comparable via @@iterator
+ || typeof Set === 'function' && value instanceof Set); // comparable via @@iterator
+ }
+
+ function compareStructuralEquality(a, b, cache) {
+ var aKeys = [];
+ for (var key in a) aKeys.push(key);
+
+ var bKeys = [];
+ for (var key in b) bKeys.push(key);
+
+ if (aKeys.length !== bKeys.length) {
+ return NOT_EQUAL;
+ }
+
+ aKeys.sort();
+ bKeys.sort();
+
+ for (var i = 0; i < aKeys.length; i++) {
+ var aKey = aKeys[i];
+ var bKey = bKeys[i];
+ if (compareEquality(aKey, bKey, cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ if (compareEquality(a[aKey], b[bKey], cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ }
+
+ return compareIf(a, b, isIterableEquatable, compareIterableEquality, cache)
+ || EQUAL;
+ }
+
+ function isIterableEquatable(value) {
+ return typeof Symbol === 'function'
+ && typeof value[Symbol.iterator] === 'function';
+ }
+
+ function compareIteratorEquality(a, b, cache) {
+ if (typeof Map === 'function' && a instanceof Map && b instanceof Map ||
+ typeof Set === 'function' && a instanceof Set && b instanceof Set) {
+ if (a.size !== b.size) return NOT_EQUAL; // exit early if we detect a difference in size
+ }
+
+ var ar, br;
+ while (true) {
+ ar = a.next();
+ br = b.next();
+ if (ar.done) {
+ if (br.done) return EQUAL;
+ if (b.return) b.return();
+ return NOT_EQUAL;
+ }
+ if (br.done) {
+ if (a.return) a.return();
+ return NOT_EQUAL;
+ }
+ if (compareEquality(ar.value, br.value, cache) === NOT_EQUAL) {
+ if (a.return) a.return();
+ if (b.return) b.return();
+ return NOT_EQUAL;
+ }
+ }
+ }
+
+ function compareIterableEquality(a, b, cache) {
+ return compareIteratorEquality(a[Symbol.iterator](), b[Symbol.iterator](), cache);
+ }
+
+ function cacheComparison(a, b, compare, cache) {
+ var result = compare(a, b, cache);
+ if (cache && (result === EQUAL || result === NOT_EQUAL)) {
+ setCache(cache, a, b, /** @type {EQUAL | NOT_EQUAL} */(result));
+ }
+ return result;
+ }
+
+ function fail() {
+ return NOT_EQUAL;
+ }
+
+ function setCache(cache, left, right, result) {
+ var otherCache;
+
+ otherCache = cache.get(left);
+ if (!otherCache) cache.set(left, otherCache = new Map());
+ otherCache.set(right, result);
+
+ otherCache = cache.get(right);
+ if (!otherCache) cache.set(right, otherCache = new Map());
+ otherCache.set(left, result);
+ }
+
+ function getCache(cache, left, right) {
+ var otherCache;
+ var result;
+
+ otherCache = cache.get(left);
+ result = otherCache && otherCache.get(right);
+ if (result) return result;
+
+ otherCache = cache.get(right);
+ result = otherCache && otherCache.get(left);
+ if (result) return result;
+
+ return UNKNOWN;
+ }
+
+ return deepEqual;
+})();
diff --git a/js/src/tests/test262/staging/Temporal/Duration/browser.js b/js/src/tests/test262/staging/Temporal/Duration/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Duration/browser.js
diff --git a/js/src/tests/test262/staging/Temporal/Duration/old/add.js b/js/src/tests/test262/staging/Temporal/Duration/old/add.js
new file mode 100644
index 0000000000..6e2220b32d
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Duration/old/add.js
@@ -0,0 +1,140 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-duration-objects
+description: Temporal.Duration.prototype.add() works as expected
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+var oneDay = new Temporal.Duration(0, 0, 0, 1);
+var hours24 = new Temporal.Duration(0, 0, 0, 0, 24);
+
+// relativeTo does not affect days if PlainDate
+var relativeTo = Temporal.PlainDate.from("2017-01-01");
+assert.sameValue(`${ oneDay.add(hours24, { relativeTo }) }`, "P2D");
+
+// relativeTo does not affect days if ZonedDateTime, and duration encompasses no DST change
+var relativeTo = Temporal.ZonedDateTime.from("2017-01-01T00:00[+04:30]");
+assert.sameValue(`${ oneDay.add(hours24, { relativeTo }) }`, "P2D");
+
+// relativeTo affects days if ZonedDateTime, and duration encompasses DST change
+var timeZone = TemporalHelpers.springForwardFallBackTimeZone();
+var skippedHourDay = Temporal.PlainDateTime.from("2000-04-02").toZonedDateTime(timeZone);
+var repeatedHourDay = Temporal.PlainDateTime.from("2000-10-29").toZonedDateTime(timeZone);
+var inRepeatedHour = new Temporal.ZonedDateTime(972806400_000_000_000n, timeZone);
+var hours12 = new Temporal.Duration(0, 0, 0, 0, 12);
+var hours25 = new Temporal.Duration(0, 0, 0, 0, 25);
+
+// start inside repeated hour, end after
+assert.sameValue(`${ hours25.add(oneDay, { relativeTo: inRepeatedHour }) }`, "P2D");
+assert.sameValue(`${ oneDay.add(hours25, { relativeTo: inRepeatedHour }) }`, "P2DT1H");
+
+// start after repeated hour, end inside (negative)
+var relativeTo = Temporal.PlainDateTime.from("2000-10-31T01:00").toZonedDateTime(timeZone);
+assert.sameValue(`${ hours25.negated().add(oneDay.negated(), { relativeTo }) }`, "-P2DT1H");
+assert.sameValue(`${ oneDay.negated().add(hours25.negated(), { relativeTo }) }`, "-P2D");
+
+// start inside repeated hour, end in skipped hour
+assert.sameValue(`${ hours25.add(Temporal.Duration.from({
+ days: 125,
+ hours: 1
+}), { relativeTo: inRepeatedHour }) }`, "P126DT1H");
+assert.sameValue(`${ oneDay.add(Temporal.Duration.from({
+ days: 125,
+ hours: 1
+}), { relativeTo: inRepeatedHour }) }`, "P126DT1H");
+
+// start in normal hour, end in skipped hour
+var relativeTo = Temporal.PlainDateTime.from("2000-03-31T02:30").toZonedDateTime(timeZone);
+assert.sameValue(`${ oneDay.add(hours25, { relativeTo }) }`, "P2DT1H");
+assert.sameValue(`${ hours25.add(oneDay, { relativeTo }) }`, "P2D");
+
+// start before skipped hour, end >1 day after
+assert.sameValue(`${ hours25.add(oneDay, { relativeTo: skippedHourDay }) }`, "P2DT2H");
+assert.sameValue(`${ oneDay.add(hours25, { relativeTo: skippedHourDay }) }`, "P2DT1H");
+
+// start after skipped hour, end >1 day before (negative)
+var relativeTo = Temporal.PlainDateTime.from("2000-04-03T00:00").toZonedDateTime(timeZone);
+assert.sameValue(`${ hours25.negated().add(oneDay.negated(), { relativeTo }) }`, "-P2DT2H");
+assert.sameValue(`${ oneDay.negated().add(hours25.negated(), { relativeTo }) }`, "-P2DT1H");
+
+// start before skipped hour, end <1 day after
+assert.sameValue(`${ hours12.add(oneDay, { relativeTo: skippedHourDay }) }`, "P1DT13H");
+assert.sameValue(`${ oneDay.add(hours12, { relativeTo: skippedHourDay }) }`, "P1DT12H");
+
+// start after skipped hour, end <1 day before (negative)
+var relativeTo = Temporal.PlainDateTime.from("2000-04-02T12:00").toZonedDateTime(timeZone);
+assert.sameValue(`${ hours12.negated().add(oneDay.negated(), { relativeTo }) }`, "-P1DT13H");
+assert.sameValue(`${ oneDay.negated().add(hours12.negated(), { relativeTo }) }`, "-P1DT12H");
+
+// start before repeated hour, end >1 day after
+assert.sameValue(`${ hours25.add(oneDay, { relativeTo: repeatedHourDay }) }`, "P2D");
+assert.sameValue(`${ oneDay.add(hours25, { relativeTo: repeatedHourDay }) }`, "P2DT1H");
+
+// start after repeated hour, end >1 day before (negative)
+var relativeTo = Temporal.PlainDateTime.from("2000-10-30T00:00").toZonedDateTime(timeZone);
+assert.sameValue(`${ hours25.negated().add(oneDay.negated(), { relativeTo }) }`, "-P2D");
+assert.sameValue(`${ oneDay.negated().add(hours25.negated(), { relativeTo }) }`, "-P2DT1H");
+
+// start before repeated hour, end <1 day after
+assert.sameValue(`${ hours12.add(oneDay, { relativeTo: repeatedHourDay }) }`, "P1DT11H");
+assert.sameValue(`${ oneDay.add(hours12, { relativeTo: repeatedHourDay }) }`, "P1DT12H");
+
+// start after repeated hour, end <1 day before (negative)
+var relativeTo = Temporal.PlainDateTime.from("2000-10-29T12:00").toZonedDateTime(timeZone);
+assert.sameValue(`${ hours12.negated().add(oneDay.negated(), { relativeTo }) }`, "-P1DT11H");
+assert.sameValue(`${ oneDay.negated().add(hours12.negated(), { relativeTo }) }`, "-P1DT12H");
+
+// Samoa skipped 24 hours
+var fakeSamoa = TemporalHelpers.crossDateLineTimeZone();
+var relativeTo = Temporal.PlainDateTime.from("2011-12-29T12:00").toZonedDateTime(fakeSamoa);
+assert.sameValue(`${ hours25.add(oneDay, { relativeTo }) }`, "P3DT1H");
+assert.sameValue(`${ oneDay.add(hours25, { relativeTo }) }`, "P3DT1H");
+
+// casts relativeTo to ZonedDateTime if possible
+assert.sameValue(`${ oneDay.add(hours24, {
+ relativeTo: {
+ year: 2000,
+ month: 10,
+ day: 28,
+ timeZone
+ }
+}) }`, "P1DT24H");
+
+// casts relativeTo to PlainDate if possible
+assert.sameValue(`${ oneDay.add(hours24, { relativeTo: "2019-11-02" }) }`, "P2D");
+assert.sameValue(`${ oneDay.add(hours24, {
+ relativeTo: {
+ year: 2019,
+ month: 11,
+ day: 2
+ }
+}) }`, "P2D");
+
+// throws on wrong offset for ZonedDateTime relativeTo string
+assert.throws(RangeError, () => oneDay.add(hours24, { relativeTo: "1971-01-01T00:00+02:00[-00:44:30]" }));
+
+// at least the required properties must be present in relativeTo
+assert.throws(TypeError, () => oneDay.add(hours24, {
+ relativeTo: {
+ month: 11,
+ day: 3
+ }
+}));
+assert.throws(TypeError, () => oneDay.add(hours24, {
+ relativeTo: {
+ year: 2019,
+ month: 11
+ }
+}));
+assert.throws(TypeError, () => oneDay.add(hours24, {
+ relativeTo: {
+ year: 2019,
+ day: 3
+ }
+}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/Duration/old/browser.js b/js/src/tests/test262/staging/Temporal/Duration/old/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Duration/old/browser.js
diff --git a/js/src/tests/test262/staging/Temporal/Duration/old/compare-no-precision-loss.js b/js/src/tests/test262/staging/Temporal/Duration/old/compare-no-precision-loss.js
new file mode 100644
index 0000000000..e5137e4452
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Duration/old/compare-no-precision-loss.js
@@ -0,0 +1,16 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-duration-objects
+description: Temporal.Duration.compare() does not lose precision when totaling everything down to nanoseconds
+features: [Temporal]
+---*/
+
+assert.notSameValue(Temporal.Duration.compare({ days: 200 }, {
+ days: 200,
+ nanoseconds: 1
+}), 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/Duration/old/limits.js b/js/src/tests/test262/staging/Temporal/Duration/old/limits.js
new file mode 100644
index 0000000000..5c1e275430
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Duration/old/limits.js
@@ -0,0 +1,37 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-duration-objects
+description: min/max values
+features: [Temporal]
+---*/
+
+var units = [
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds",
+ "nanoseconds"
+];
+
+// minimum is zero
+assert.sameValue(`${ new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 0) }`, "PT0S");
+units.forEach(unit => assert.sameValue(`${ Temporal.Duration.from({ [unit]: 0 }) }`, "PT0S"));
+[
+ "P0Y",
+ "P0M",
+ "P0W",
+ "P0D",
+ "PT0H",
+ "PT0M",
+ "PT0S"
+].forEach(str => assert.sameValue(`${ Temporal.Duration.from(str) }`, "PT0S"));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/Duration/old/round.js b/js/src/tests/test262/staging/Temporal/Duration/old/round.js
new file mode 100644
index 0000000000..ccb04aeffc
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Duration/old/round.js
@@ -0,0 +1,868 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-duration-objects
+description: Temporal.Duration.prototype.round() works as expected
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+var d = new Temporal.Duration(5, 5, 5, 5, 5, 5, 5, 5, 5, 5);
+var d2 = new Temporal.Duration(0, 0, 0, 5, 5, 5, 5, 5, 5, 5);
+var relativeTo = Temporal.PlainDate.from("2020-01-01");
+
+// succeeds with largestUnit: 'auto'
+assert.sameValue(`${ Temporal.Duration.from({ hours: 25 }).round({ largestUnit: "auto" }) }`, "PT25H");
+var hours25 = new Temporal.Duration(0, 0, 0, 0, 25);
+
+// days are 24 hours if relativeTo not given
+assert.sameValue(`${ hours25.round({ largestUnit: "days" }) }`, "P1DT1H");
+
+// days are 24 hours if relativeTo is PlainDate
+var relativeTo = Temporal.PlainDate.from("2017-01-01");
+assert.sameValue(`${ hours25.round({
+ largestUnit: "days",
+ relativeTo
+}) }`, "P1DT1H");
+
+// days are 24 hours if relativeTo is ZonedDateTime, and duration encompasses no DST change
+var relativeTo = Temporal.ZonedDateTime.from("2017-01-01T00:00[+04:30]");
+assert.sameValue(`${ hours25.round({
+ largestUnit: "days",
+ relativeTo
+}) }`, "P1DT1H");
+
+// relativeTo affects days if ZonedDateTime, and duration encompasses DST change
+var timeZone = TemporalHelpers.springForwardFallBackTimeZone();
+var skippedHourDay = Temporal.PlainDateTime.from("2000-04-02").toZonedDateTime(timeZone);
+var repeatedHourDay = Temporal.PlainDateTime.from("2000-10-29").toZonedDateTime(timeZone);
+var inRepeatedHour = new Temporal.ZonedDateTime(972806400_000_000_000n, timeZone);
+var oneDay = new Temporal.Duration(0, 0, 0, 1);
+var hours12 = new Temporal.Duration(0, 0, 0, 0, 12);
+
+// start inside repeated hour, end after
+assert.sameValue(`${ hours25.round({
+ largestUnit: "days",
+ relativeTo: inRepeatedHour
+}) }`, "P1D");
+assert.sameValue(`${ oneDay.round({
+ largestUnit: "hours",
+ relativeTo: inRepeatedHour
+}) }`, "PT25H");
+
+// start after repeated hour, end inside (negative)
+var relativeTo = Temporal.PlainDateTime.from("2000-10-30T01:00").toZonedDateTime(timeZone);
+assert.sameValue(`${ hours25.negated().round({
+ largestUnit: "days",
+ relativeTo
+}) }`, "-P1D");
+assert.sameValue(`${ oneDay.negated().round({
+ largestUnit: "hours",
+ relativeTo
+}) }`, "-PT25H");
+
+// start inside repeated hour, end in skipped hour
+assert.sameValue(`${ Temporal.Duration.from({
+ days: 126,
+ hours: 1
+}).round({
+ largestUnit: "days",
+ relativeTo: inRepeatedHour
+}) }`, "P126DT1H");
+assert.sameValue(`${ Temporal.Duration.from({
+ days: 126,
+ hours: 1
+}).round({
+ largestUnit: "hours",
+ relativeTo: inRepeatedHour
+}) }`, "PT3026H");
+
+// start in normal hour, end in skipped hour
+var relativeTo = Temporal.PlainDateTime.from("2000-04-01T02:30").toZonedDateTime(timeZone);
+assert.sameValue(`${ hours25.round({
+ largestUnit: "days",
+ relativeTo
+}) }`, "P1DT1H");
+assert.sameValue(`${ oneDay.round({
+ largestUnit: "hours",
+ relativeTo
+}) }`, "PT24H");
+
+// start before skipped hour, end >1 day after
+assert.sameValue(`${ hours25.round({
+ largestUnit: "days",
+ relativeTo: skippedHourDay
+}) }`, "P1DT2H");
+assert.sameValue(`${ oneDay.round({
+ largestUnit: "hours",
+ relativeTo: skippedHourDay
+}) }`, "PT23H");
+
+// start after skipped hour, end >1 day before (negative)
+var relativeTo = Temporal.PlainDateTime.from("2000-04-03T00:00").toZonedDateTime(timeZone);
+assert.sameValue(`${ hours25.negated().round({
+ largestUnit: "days",
+ relativeTo
+}) }`, "-P1DT2H");
+assert.sameValue(`${ oneDay.negated().round({
+ largestUnit: "hours",
+ relativeTo
+}) }`, "-PT23H");
+
+// start before skipped hour, end <1 day after
+assert.sameValue(`${ hours12.round({
+ largestUnit: "days",
+ relativeTo: skippedHourDay
+}) }`, "PT12H");
+
+// start after skipped hour, end <1 day before (negative)
+var relativeTo = Temporal.PlainDateTime.from("2000-04-02T12:00").toZonedDateTime(timeZone);
+assert.sameValue(`${ hours12.negated().round({
+ largestUnit: "days",
+ relativeTo
+}) }`, "-PT12H");
+
+// start before repeated hour, end >1 day after
+assert.sameValue(`${ hours25.round({
+ largestUnit: "days",
+ relativeTo: repeatedHourDay
+}) }`, "P1D");
+assert.sameValue(`${ oneDay.round({
+ largestUnit: "hours",
+ relativeTo: repeatedHourDay
+}) }`, "PT25H");
+
+// start after repeated hour, end >1 day before (negative)
+var relativeTo = Temporal.PlainDateTime.from("2000-10-30T00:00").toZonedDateTime(timeZone);
+assert.sameValue(`${ hours25.negated().round({
+ largestUnit: "days",
+ relativeTo
+}) }`, "-P1D");
+assert.sameValue(`${ oneDay.negated().round({
+ largestUnit: "hours",
+ relativeTo
+}) }`, "-PT25H");
+
+// start before repeated hour, end <1 day after
+assert.sameValue(`${ hours12.round({
+ largestUnit: "days",
+ relativeTo: repeatedHourDay
+}) }`, "PT12H");
+
+// start after repeated hour, end <1 day before (negative)
+var relativeTo = Temporal.PlainDateTime.from("2000-10-29T12:00").toZonedDateTime(timeZone);
+assert.sameValue(`${ hours12.negated().round({
+ largestUnit: "days",
+ relativeTo
+}) }`, "-PT12H");
+
+// Samoa skipped 24 hours
+var fakeSamoa = TemporalHelpers.crossDateLineTimeZone();
+var relativeTo = Temporal.PlainDateTime.from("2011-12-29T12:00").toZonedDateTime(fakeSamoa);
+assert.sameValue(`${ hours25.round({
+ largestUnit: "days",
+ relativeTo
+}) }`, "P2DT1H");
+assert.sameValue(`${ Temporal.Duration.from({ hours: 48 }).round({
+ largestUnit: "days",
+ relativeTo
+}) }`, "P3D");
+
+// casts relativeTo to ZonedDateTime if possible
+assert.sameValue(`${ hours25.round({
+ largestUnit: "days",
+ relativeTo: {
+ year: 2000,
+ month: 10,
+ day: 29,
+ timeZone
+ }
+}) }`, "P1D");
+
+// casts relativeTo to PlainDate if possible
+assert.sameValue(`${ hours25.round({
+ largestUnit: "days",
+ relativeTo: "2019-11-02"
+}) }`, "P1DT1H");
+assert.sameValue(`${ hours25.round({
+ largestUnit: "days",
+ relativeTo: {
+ year: 2019,
+ month: 11,
+ day: 2
+ }
+}) }`, "P1DT1H");
+
+// accepts datetime strings or fields for relativeTo
+[
+ "2020-01-01",
+ "20200101",
+ "2020-01-01T00:00:00.000000000",
+ {
+ year: 2020,
+ month: 1,
+ day: 1
+ }
+].forEach(relativeTo => {
+ assert.sameValue(`${ d.round({
+ smallestUnit: "seconds",
+ relativeTo
+ }) }`, "P5Y6M10DT5H5M5S");
+});
+
+// does not accept non-string primitives for relativeTo
+[
+ 20200101,
+ 20200101n,
+ null,
+ true,
+].forEach(relativeTo => {
+ assert.throws(
+ TypeError, () => d.round({ smallestUnit: "seconds", relativeTo})
+ );
+});
+
+// throws on wrong offset for ZonedDateTime relativeTo string
+assert.throws(RangeError, () => d.round({
+ smallestUnit: "seconds",
+ relativeTo: "1971-01-01T00:00+02:00[-00:44:30]"
+}));
+
+// relativeTo object must contain at least the required correctly-spelled properties
+assert.throws(TypeError, () => hours25.round({
+ largestUnit: "days",
+ relativeTo: {
+ month: 11,
+ day: 3
+ }
+}));
+assert.throws(TypeError, () => hours25.round({
+ largestUnit: "days",
+ relativeTo: {
+ year: 2019,
+ month: 11
+ }
+}));
+assert.throws(TypeError, () => hours25.round({
+ largestUnit: "days",
+ relativeTo: {
+ year: 2019,
+ day: 3
+ }
+}));
+
+// incorrectly-spelled properties are ignored in relativeTo
+var oneMonth = Temporal.Duration.from({ months: 1 });
+assert.sameValue(`${ oneMonth.round({
+ largestUnit: "days",
+ relativeTo: {
+ year: 2020,
+ month: 1,
+ day: 1,
+ months: 2
+ }
+}) }`, "P31D");
+
+// throws if neither one of largestUnit or smallestUnit is given
+var hoursOnly = new Temporal.Duration(0, 0, 0, 0, 1);
+[
+ {},
+ () => {
+ },
+ { roundingMode: "ceil" }
+].forEach(roundTo => {
+ assert.throws(RangeError, () => d.round(roundTo));
+ assert.throws(RangeError, () => hoursOnly.round(roundTo));
+});
+
+// relativeTo not required to round non-calendar units in durations w/o calendar units (string param)
+assert.sameValue(`${ d2.round("days") }`, "P5D");
+assert.sameValue(`${ d2.round("hours") }`, "P5DT5H");
+assert.sameValue(`${ d2.round("minutes") }`, "P5DT5H5M");
+assert.sameValue(`${ d2.round("seconds") }`, "P5DT5H5M5S");
+assert.sameValue(`${ d2.round("milliseconds") }`, "P5DT5H5M5.005S");
+assert.sameValue(`${ d2.round("microseconds") }`, "P5DT5H5M5.005005S");
+assert.sameValue(`${ d2.round("nanoseconds") }`, "P5DT5H5M5.005005005S");
+
+// relativeTo is required to round calendar units even in durations w/o calendar units (string param)
+assert.throws(RangeError, () => d2.round("years"));
+assert.throws(RangeError, () => d2.round("months"));
+assert.throws(RangeError, () => d2.round("weeks"));
+
+// relativeTo not required to round non-calendar units in durations w/o calendar units (object param)
+assert.sameValue(`${ d2.round({ smallestUnit: "days" }) }`, "P5D");
+assert.sameValue(`${ d2.round({ smallestUnit: "hours" }) }`, "P5DT5H");
+assert.sameValue(`${ d2.round({ smallestUnit: "minutes" }) }`, "P5DT5H5M");
+assert.sameValue(`${ d2.round({ smallestUnit: "seconds" }) }`, "P5DT5H5M5S");
+assert.sameValue(`${ d2.round({ smallestUnit: "milliseconds" }) }`, "P5DT5H5M5.005S");
+assert.sameValue(`${ d2.round({ smallestUnit: "microseconds" }) }`, "P5DT5H5M5.005005S");
+assert.sameValue(`${ d2.round({ smallestUnit: "nanoseconds" }) }`, "P5DT5H5M5.005005005S");
+
+// relativeTo is required to round calendar units even in durations w/o calendar units (object param)
+assert.throws(RangeError, () => d2.round({ smallestUnit: "years" }));
+assert.throws(RangeError, () => d2.round({ smallestUnit: "months" }));
+assert.throws(RangeError, () => d2.round({ smallestUnit: "weeks" }));
+
+// relativeTo is required for rounding durations with calendar units
+assert.throws(RangeError, () => d.round({ largestUnit: "years" }));
+assert.throws(RangeError, () => d.round({ largestUnit: "months" }));
+assert.throws(RangeError, () => d.round({ largestUnit: "weeks" }));
+assert.throws(RangeError, () => d.round({ largestUnit: "days" }));
+assert.throws(RangeError, () => d.round({ largestUnit: "hours" }));
+assert.throws(RangeError, () => d.round({ largestUnit: "minutes" }));
+assert.throws(RangeError, () => d.round({ largestUnit: "seconds" }));
+assert.throws(RangeError, () => d.round({ largestUnit: "milliseconds" }));
+assert.throws(RangeError, () => d.round({ largestUnit: "microseconds" }));
+assert.throws(RangeError, () => d.round({ largestUnit: "nanoseconds" }));
+
+// durations do not balance beyond their current largest unit by default
+var relativeTo = Temporal.PlainDate.from("2020-01-01");
+var fortyDays = Temporal.Duration.from({ days: 40 });
+assert.sameValue(`${ fortyDays.round({ smallestUnit: "seconds" }) }`, "P40D");
+var roundAndBalanceResults = {
+ years: {
+ years: "P6Y",
+ months: "P5Y6M",
+ weeks: "P5Y5M6W",
+ days: "P5Y6M10D",
+ hours: "P5Y6M10DT5H",
+ minutes: "P5Y6M10DT5H5M",
+ seconds: "P5Y6M10DT5H5M5S",
+ milliseconds: "P5Y6M10DT5H5M5.005S",
+ microseconds: "P5Y6M10DT5H5M5.005005S",
+ nanoseconds: "P5Y6M10DT5H5M5.005005005S"
+ },
+ months: {
+ months: "P66M",
+ weeks: "P65M6W",
+ days: "P66M10D",
+ hours: "P66M10DT5H",
+ minutes: "P66M10DT5H5M",
+ seconds: "P66M10DT5H5M5S",
+ milliseconds: "P66M10DT5H5M5.005S",
+ microseconds: "P66M10DT5H5M5.005005S",
+ nanoseconds: "P66M10DT5H5M5.005005005S"
+ },
+ weeks: {
+ weeks: "P288W",
+ days: "P288W2D",
+ hours: "P288W2DT5H",
+ minutes: "P288W2DT5H5M",
+ seconds: "P288W2DT5H5M5S",
+ milliseconds: "P288W2DT5H5M5.005S",
+ microseconds: "P288W2DT5H5M5.005005S",
+ nanoseconds: "P288W2DT5H5M5.005005005S"
+ },
+ days: {
+ days: "P2018D",
+ hours: "P2018DT5H",
+ minutes: "P2018DT5H5M",
+ seconds: "P2018DT5H5M5S",
+ milliseconds: "P2018DT5H5M5.005S",
+ microseconds: "P2018DT5H5M5.005005S",
+ nanoseconds: "P2018DT5H5M5.005005005S"
+ },
+ hours: {
+ hours: "PT48437H",
+ minutes: "PT48437H5M",
+ seconds: "PT48437H5M5S",
+ milliseconds: "PT48437H5M5.005S",
+ microseconds: "PT48437H5M5.005005S",
+ nanoseconds: "PT48437H5M5.005005005S"
+ },
+ minutes: {
+ minutes: "PT2906225M",
+ seconds: "PT2906225M5S",
+ milliseconds: "PT2906225M5.005S",
+ microseconds: "PT2906225M5.005005S",
+ nanoseconds: "PT2906225M5.005005005S"
+ },
+ seconds: {
+ seconds: "PT174373505S",
+ milliseconds: "PT174373505.005S",
+ microseconds: "PT174373505.005005S",
+ nanoseconds: "PT174373505.005005005S"
+ },
+ milliseconds: {
+ milliseconds: "PT174373505.005S",
+ microseconds: "PT174373505.005005S",
+ nanoseconds: "PT174373505.005005005S"
+ }
+};
+for (var [largestUnit, entry] of Object.entries(roundAndBalanceResults)) {
+ for (var [smallestUnit, expected] of Object.entries(entry)) {
+ assert.sameValue(`${ d.round({
+ largestUnit,
+ smallestUnit,
+ relativeTo
+ }) }`, expected);
+ }
+}
+var balanceLosePrecisionResults = {
+ microseconds: [
+ "microseconds",
+ "nanoseconds"
+ ],
+ nanoseconds: ["nanoseconds"]
+};
+
+// Round may lose precision below ms
+for (var [largestUnit, entry] of Object.entries(balanceLosePrecisionResults)) {
+ for (var smallestUnit of entry) {
+ assert(`${ d.round({
+ largestUnit,
+ smallestUnit,
+ relativeTo
+ }) }`.startsWith("PT174373505.005"));
+ }
+}
+
+// halfExpand is the default
+assert.sameValue(`${ d.round({
+ smallestUnit: "years",
+ relativeTo
+}) }`, "P6Y");
+assert.sameValue(`${ d.negated().round({
+ smallestUnit: "years",
+ relativeTo
+}) }`, "-P6Y");
+
+// balances up differently depending on relativeTo
+var fortyDays = Temporal.Duration.from({ days: 40 });
+assert.sameValue(`${ fortyDays.round({
+ largestUnit: "years",
+ relativeTo: "2020-01-01"
+}) }`, "P1M9D");
+assert.sameValue(`${ fortyDays.round({
+ largestUnit: "years",
+ relativeTo: "2020-02-01"
+}) }`, "P1M11D");
+assert.sameValue(`${ fortyDays.round({
+ largestUnit: "years",
+ relativeTo: "2020-03-01"
+}) }`, "P1M9D");
+assert.sameValue(`${ fortyDays.round({
+ largestUnit: "years",
+ relativeTo: "2020-04-01"
+}) }`, "P1M10D");
+var minusForty = Temporal.Duration.from({ days: -40 });
+assert.sameValue(`${ minusForty.round({
+ largestUnit: "years",
+ relativeTo: "2020-02-01"
+}) }`, "-P1M9D");
+assert.sameValue(`${ minusForty.round({
+ largestUnit: "years",
+ relativeTo: "2020-01-01"
+}) }`, "-P1M9D");
+assert.sameValue(`${ minusForty.round({
+ largestUnit: "years",
+ relativeTo: "2020-03-01"
+}) }`, "-P1M11D");
+assert.sameValue(`${ minusForty.round({
+ largestUnit: "years",
+ relativeTo: "2020-04-01"
+}) }`, "-P1M9D");
+
+// balances up to the next unit after rounding
+var almostWeek = Temporal.Duration.from({
+ days: 6,
+ hours: 20
+});
+assert.sameValue(`${ almostWeek.round({
+ largestUnit: "weeks",
+ smallestUnit: "days",
+ relativeTo: "2020-01-01"
+}) }`, "P1W");
+
+// balances days up to both years and months
+var twoYears = Temporal.Duration.from({
+ months: 11,
+ days: 396
+});
+assert.sameValue(`${ twoYears.round({
+ largestUnit: "years",
+ relativeTo: "2017-01-01"
+}) }`, "P2Y");
+
+// does not balance up to weeks if largestUnit is larger than weeks
+var monthAlmostWeek = Temporal.Duration.from({
+ months: 1,
+ days: 6,
+ hours: 20
+});
+assert.sameValue(`${ monthAlmostWeek.round({
+ smallestUnit: "days",
+ relativeTo: "2020-01-01"
+}) }`, "P1M7D");
+
+// balances down differently depending on relativeTo
+var oneYear = Temporal.Duration.from({ years: 1 });
+assert.sameValue(`${ oneYear.round({
+ largestUnit: "days",
+ relativeTo: "2019-01-01"
+}) }`, "P365D");
+assert.sameValue(`${ oneYear.round({
+ largestUnit: "days",
+ relativeTo: "2019-07-01"
+}) }`, "P366D");
+var minusYear = Temporal.Duration.from({ years: -1 });
+assert.sameValue(`${ minusYear.round({
+ largestUnit: "days",
+ relativeTo: "2020-01-01"
+}) }`, "-P365D");
+assert.sameValue(`${ minusYear.round({
+ largestUnit: "days",
+ relativeTo: "2020-07-01"
+}) }`, "-P366D");
+
+// rounds to an increment of hours
+assert.sameValue(`${ d.round({
+ smallestUnit: "hours",
+ roundingIncrement: 3,
+ relativeTo
+}) }`, "P5Y6M10DT6H");
+
+// rounds to an increment of minutes
+assert.sameValue(`${ d.round({
+ smallestUnit: "minutes",
+ roundingIncrement: 30,
+ relativeTo
+}) }`, "P5Y6M10DT5H");
+
+// rounds to an increment of seconds
+assert.sameValue(`${ d.round({
+ smallestUnit: "seconds",
+ roundingIncrement: 15,
+ relativeTo
+}) }`, "P5Y6M10DT5H5M");
+
+// rounds to an increment of milliseconds
+assert.sameValue(`${ d.round({
+ smallestUnit: "milliseconds",
+ roundingIncrement: 10,
+ relativeTo
+}) }`, "P5Y6M10DT5H5M5.01S");
+
+// rounds to an increment of microseconds
+assert.sameValue(`${ d.round({
+ smallestUnit: "microseconds",
+ roundingIncrement: 10,
+ relativeTo
+}) }`, "P5Y6M10DT5H5M5.00501S");
+
+// rounds to an increment of nanoseconds
+assert.sameValue(`${ d.round({
+ smallestUnit: "nanoseconds",
+ roundingIncrement: 10,
+ relativeTo
+}) }`, "P5Y6M10DT5H5M5.00500501S");
+
+// valid hour increments divide into 24
+[
+ 1,
+ 2,
+ 3,
+ 4,
+ 6,
+ 8,
+ 12
+].forEach(roundingIncrement => {
+ var options = {
+ smallestUnit: "hours",
+ roundingIncrement,
+ relativeTo
+ };
+ assert(d.round(options) instanceof Temporal.Duration);
+});
+[
+ "minutes",
+ "seconds"
+].forEach(smallestUnit => {
+ // valid minutes/seconds increments divide into 60
+ [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 10,
+ 12,
+ 15,
+ 20,
+ 30
+ ].forEach(roundingIncrement => {
+ var roundTo = {
+ smallestUnit,
+ roundingIncrement,
+ relativeTo
+ };
+ assert(d.round(roundTo) instanceof Temporal.Duration);
+ });
+ });
+[
+ "milliseconds",
+ "microseconds",
+ "nanoseconds"
+].forEach(smallestUnit => {
+ // valid milliseconds/microseconds/nanoseconds increments divide into 1000
+ [
+ 1,
+ 2,
+ 4,
+ 5,
+ 8,
+ 10,
+ 20,
+ 25,
+ 40,
+ 50,
+ 100,
+ 125,
+ 200,
+ 250,
+ 500
+ ].forEach(roundingIncrement => {
+ var roundTo = {
+ smallestUnit,
+ roundingIncrement,
+ relativeTo
+ };
+ assert(d.round(roundTo) instanceof Temporal.Duration);
+ });
+ });
+
+// throws on increments that do not divide evenly into the next highest
+assert.throws(RangeError, () => d.round({
+ relativeTo,
+ smallestUnit: "hours",
+ roundingIncrement: 11
+}));
+assert.throws(RangeError, () => d.round({
+ relativeTo,
+ smallestUnit: "minutes",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => d.round({
+ relativeTo,
+ smallestUnit: "seconds",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => d.round({
+ relativeTo,
+ smallestUnit: "milliseconds",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => d.round({
+ relativeTo,
+ smallestUnit: "microseconds",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => d.round({
+ relativeTo,
+ smallestUnit: "nanoseconds",
+ roundingIncrement: 29
+}));
+
+// throws on increments that are equal to the next highest
+assert.throws(RangeError, () => d.round({
+ relativeTo,
+ smallestUnit: "hours",
+ roundingIncrement: 24
+}));
+assert.throws(RangeError, () => d.round({
+ relativeTo,
+ smallestUnit: "minutes",
+ roundingIncrement: 60
+}));
+assert.throws(RangeError, () => d.round({
+ relativeTo,
+ smallestUnit: "seconds",
+ roundingIncrement: 60
+}));
+assert.throws(RangeError, () => d.round({
+ relativeTo,
+ smallestUnit: "milliseconds",
+ roundingIncrement: 1000
+}));
+assert.throws(RangeError, () => d.round({
+ relativeTo,
+ smallestUnit: "microseconds",
+ roundingIncrement: 1000
+}));
+assert.throws(RangeError, () => d.round({
+ relativeTo,
+ smallestUnit: "nanoseconds",
+ roundingIncrement: 1000
+}));
+
+// accepts singular units
+assert.sameValue(`${ d.round({
+ largestUnit: "year",
+ relativeTo
+}) }`, `${ d.round({
+ largestUnit: "years",
+ relativeTo
+}) }`);
+assert.sameValue(`${ d.round({
+ smallestUnit: "year",
+ relativeTo
+}) }`, `${ d.round({
+ smallestUnit: "years",
+ relativeTo
+}) }`);
+assert.sameValue(`${ d.round({
+ largestUnit: "month",
+ relativeTo
+}) }`, `${ d.round({
+ largestUnit: "months",
+ relativeTo
+}) }`);
+assert.sameValue(`${ d.round({
+ smallestUnit: "month",
+ relativeTo
+}) }`, `${ d.round({
+ smallestUnit: "months",
+ relativeTo
+}) }`);
+assert.sameValue(`${ d.round({
+ largestUnit: "day",
+ relativeTo
+}) }`, `${ d.round({
+ largestUnit: "days",
+ relativeTo
+}) }`);
+assert.sameValue(`${ d.round({
+ smallestUnit: "day",
+ relativeTo
+}) }`, `${ d.round({
+ smallestUnit: "days",
+ relativeTo
+}) }`);
+assert.sameValue(`${ d.round({
+ largestUnit: "hour",
+ relativeTo
+}) }`, `${ d.round({
+ largestUnit: "hours",
+ relativeTo
+}) }`);
+assert.sameValue(`${ d.round({
+ smallestUnit: "hour",
+ relativeTo
+}) }`, `${ d.round({
+ smallestUnit: "hours",
+ relativeTo
+}) }`);
+assert.sameValue(`${ d.round({
+ largestUnit: "minute",
+ relativeTo
+}) }`, `${ d.round({
+ largestUnit: "minutes",
+ relativeTo
+}) }`);
+assert.sameValue(`${ d.round({
+ smallestUnit: "minute",
+ relativeTo
+}) }`, `${ d.round({
+ smallestUnit: "minutes",
+ relativeTo
+}) }`);
+assert.sameValue(`${ d.round({
+ largestUnit: "second",
+ relativeTo
+}) }`, `${ d.round({
+ largestUnit: "seconds",
+ relativeTo
+}) }`);
+assert.sameValue(`${ d.round({
+ smallestUnit: "second",
+ relativeTo
+}) }`, `${ d.round({
+ smallestUnit: "seconds",
+ relativeTo
+}) }`);
+assert.sameValue(`${ d.round({
+ largestUnit: "millisecond",
+ relativeTo
+}) }`, `${ d.round({
+ largestUnit: "milliseconds",
+ relativeTo
+}) }`);
+assert.sameValue(`${ d.round({
+ smallestUnit: "millisecond",
+ relativeTo
+}) }`, `${ d.round({
+ smallestUnit: "milliseconds",
+ relativeTo
+}) }`);
+assert.sameValue(`${ d.round({
+ largestUnit: "microsecond",
+ relativeTo
+}) }`, `${ d.round({
+ largestUnit: "microseconds",
+ relativeTo
+}) }`);
+assert.sameValue(`${ d.round({
+ smallestUnit: "microsecond",
+ relativeTo
+}) }`, `${ d.round({
+ smallestUnit: "microseconds",
+ relativeTo
+}) }`);
+assert.sameValue(`${ d.round({
+ largestUnit: "nanosecond",
+ relativeTo
+}) }`, `${ d.round({
+ largestUnit: "nanoseconds",
+ relativeTo
+}) }`);
+assert.sameValue(`${ d.round({
+ smallestUnit: "nanosecond",
+ relativeTo
+}) }`, `${ d.round({
+ smallestUnit: "nanoseconds",
+ relativeTo
+}) }`);
+
+// counts the correct number of days when rounding relative to a date
+var days = Temporal.Duration.from({ days: 45 });
+assert.sameValue(`${ days.round({
+ relativeTo: "2019-01-01",
+ smallestUnit: "months"
+}) }`, "P2M");
+assert.sameValue(`${ days.negated().round({
+ relativeTo: "2019-02-15",
+ smallestUnit: "months"
+}) }`, "-P1M");
+var yearAndHalf = Temporal.Duration.from({
+ days: 547,
+ hours: 12
+});
+assert.sameValue(`${ yearAndHalf.round({
+ relativeTo: "2018-01-01",
+ smallestUnit: "years"
+}) }`, "P2Y");
+assert.sameValue(`${ yearAndHalf.round({
+ relativeTo: "2018-07-01",
+ smallestUnit: "years"
+}) }`, "P1Y");
+assert.sameValue(`${ yearAndHalf.round({
+ relativeTo: "2019-01-01",
+ smallestUnit: "years"
+}) }`, "P1Y");
+assert.sameValue(`${ yearAndHalf.round({
+ relativeTo: "2019-07-01",
+ smallestUnit: "years"
+}) }`, "P1Y");
+assert.sameValue(`${ yearAndHalf.round({
+ relativeTo: "2020-01-01",
+ smallestUnit: "years"
+}) }`, "P1Y");
+assert.sameValue(`${ yearAndHalf.round({
+ relativeTo: "2020-07-01",
+ smallestUnit: "years"
+}) }`, "P2Y");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/Duration/old/shell.js b/js/src/tests/test262/staging/Temporal/Duration/old/shell.js
new file mode 100644
index 0000000000..60f74c2518
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Duration/old/shell.js
@@ -0,0 +1,2158 @@
+// GENERATED, DO NOT EDIT
+// file: temporalHelpers.js
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: |
+ This defines helper objects and functions for testing Temporal.
+defines: [TemporalHelpers]
+features: [Symbol.species, Symbol.iterator, Temporal]
+---*/
+
+const ASCII_IDENTIFIER = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/u;
+
+function formatPropertyName(propertyKey, objectName = "") {
+ switch (typeof propertyKey) {
+ case "symbol":
+ if (Symbol.keyFor(propertyKey) !== undefined) {
+ return `${objectName}[Symbol.for('${Symbol.keyFor(propertyKey)}')]`;
+ } else if (propertyKey.description.startsWith('Symbol.')) {
+ return `${objectName}[${propertyKey.description}]`;
+ } else {
+ return `${objectName}[Symbol('${propertyKey.description}')]`
+ }
+ case "string":
+ if (propertyKey !== String(Number(propertyKey))) {
+ if (ASCII_IDENTIFIER.test(propertyKey)) {
+ return objectName ? `${objectName}.${propertyKey}` : propertyKey;
+ }
+ return `${objectName}['${propertyKey.replace(/'/g, "\\'")}']`
+ }
+ // fall through
+ default:
+ // integer or string integer-index
+ return `${objectName}[${propertyKey}]`;
+ }
+}
+
+const SKIP_SYMBOL = Symbol("Skip");
+
+var TemporalHelpers = {
+ /*
+ * Codes and maximum lengths of months in the ISO 8601 calendar.
+ */
+ ISOMonths: [
+ { month: 1, monthCode: "M01", daysInMonth: 31 },
+ { month: 2, monthCode: "M02", daysInMonth: 29 },
+ { month: 3, monthCode: "M03", daysInMonth: 31 },
+ { month: 4, monthCode: "M04", daysInMonth: 30 },
+ { month: 5, monthCode: "M05", daysInMonth: 31 },
+ { month: 6, monthCode: "M06", daysInMonth: 30 },
+ { month: 7, monthCode: "M07", daysInMonth: 31 },
+ { month: 8, monthCode: "M08", daysInMonth: 31 },
+ { month: 9, monthCode: "M09", daysInMonth: 30 },
+ { month: 10, monthCode: "M10", daysInMonth: 31 },
+ { month: 11, monthCode: "M11", daysInMonth: 30 },
+ { month: 12, monthCode: "M12", daysInMonth: 31 }
+ ],
+
+ /*
+ * assertDuration(duration, years, ..., nanoseconds[, description]):
+ *
+ * Shorthand for asserting that each field of a Temporal.Duration is equal to
+ * an expected value.
+ */
+ assertDuration(duration, years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(duration instanceof Temporal.Duration, `${prefix}instanceof`);
+ assert.sameValue(duration.years, years, `${prefix}years result:`);
+ assert.sameValue(duration.months, months, `${prefix}months result:`);
+ assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`);
+ assert.sameValue(duration.days, days, `${prefix}days result:`);
+ assert.sameValue(duration.hours, hours, `${prefix}hours result:`);
+ assert.sameValue(duration.minutes, minutes, `${prefix}minutes result:`);
+ assert.sameValue(duration.seconds, seconds, `${prefix}seconds result:`);
+ assert.sameValue(duration.milliseconds, milliseconds, `${prefix}milliseconds result:`);
+ assert.sameValue(duration.microseconds, microseconds, `${prefix}microseconds result:`);
+ assert.sameValue(duration.nanoseconds, nanoseconds, `${prefix}nanoseconds result`);
+ },
+
+ /*
+ * assertDateDuration(duration, years, months, weeks, days, [, description]):
+ *
+ * Shorthand for asserting that each date field of a Temporal.Duration is
+ * equal to an expected value.
+ */
+ assertDateDuration(duration, years, months, weeks, days, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(duration instanceof Temporal.Duration, `${prefix}instanceof`);
+ assert.sameValue(duration.years, years, `${prefix}years result:`);
+ assert.sameValue(duration.months, months, `${prefix}months result:`);
+ assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`);
+ assert.sameValue(duration.days, days, `${prefix}days result:`);
+ assert.sameValue(duration.hours, 0, `${prefix}hours result should be zero:`);
+ assert.sameValue(duration.minutes, 0, `${prefix}minutes result should be zero:`);
+ assert.sameValue(duration.seconds, 0, `${prefix}seconds result should be zero:`);
+ assert.sameValue(duration.milliseconds, 0, `${prefix}milliseconds result should be zero:`);
+ assert.sameValue(duration.microseconds, 0, `${prefix}microseconds result should be zero:`);
+ assert.sameValue(duration.nanoseconds, 0, `${prefix}nanoseconds result should be zero:`);
+ },
+
+ /*
+ * assertDurationsEqual(actual, expected[, description]):
+ *
+ * Shorthand for asserting that each field of a Temporal.Duration is equal to
+ * the corresponding field in another Temporal.Duration.
+ */
+ assertDurationsEqual(actual, expected, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(expected instanceof Temporal.Duration, `${prefix}expected value should be a Temporal.Duration`);
+ TemporalHelpers.assertDuration(actual, expected.years, expected.months, expected.weeks, expected.days, expected.hours, expected.minutes, expected.seconds, expected.milliseconds, expected.microseconds, expected.nanoseconds, description);
+ },
+
+ /*
+ * assertInstantsEqual(actual, expected[, description]):
+ *
+ * Shorthand for asserting that two Temporal.Instants are of the correct type
+ * and equal according to their equals() methods.
+ */
+ assertInstantsEqual(actual, expected, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(expected instanceof Temporal.Instant, `${prefix}expected value should be a Temporal.Instant`);
+ assert(actual instanceof Temporal.Instant, `${prefix}instanceof`);
+ assert(actual.equals(expected), `${prefix}equals method`);
+ },
+
+ /*
+ * assertPlainDate(date, year, ..., nanosecond[, description[, era, eraYear]]):
+ *
+ * Shorthand for asserting that each field of a Temporal.PlainDate is equal to
+ * an expected value. (Except the `calendar` property, since callers may want
+ * to assert either object equality with an object they put in there, or the
+ * value of date.calendarId.)
+ */
+ assertPlainDate(date, year, month, monthCode, day, description = "", era = undefined, eraYear = undefined) {
+ const prefix = description ? `${description}: ` : "";
+ assert(date instanceof Temporal.PlainDate, `${prefix}instanceof`);
+ assert.sameValue(date.era, era, `${prefix}era result:`);
+ assert.sameValue(date.eraYear, eraYear, `${prefix}eraYear result:`);
+ assert.sameValue(date.year, year, `${prefix}year result:`);
+ assert.sameValue(date.month, month, `${prefix}month result:`);
+ assert.sameValue(date.monthCode, monthCode, `${prefix}monthCode result:`);
+ assert.sameValue(date.day, day, `${prefix}day result:`);
+ },
+
+ /*
+ * assertPlainDateTime(datetime, year, ..., nanosecond[, description[, era, eraYear]]):
+ *
+ * Shorthand for asserting that each field of a Temporal.PlainDateTime is
+ * equal to an expected value. (Except the `calendar` property, since callers
+ * may want to assert either object equality with an object they put in there,
+ * or the value of datetime.calendarId.)
+ */
+ assertPlainDateTime(datetime, year, month, monthCode, day, hour, minute, second, millisecond, microsecond, nanosecond, description = "", era = undefined, eraYear = undefined) {
+ const prefix = description ? `${description}: ` : "";
+ assert(datetime instanceof Temporal.PlainDateTime, `${prefix}instanceof`);
+ assert.sameValue(datetime.era, era, `${prefix}era result:`);
+ assert.sameValue(datetime.eraYear, eraYear, `${prefix}eraYear result:`);
+ assert.sameValue(datetime.year, year, `${prefix}year result:`);
+ assert.sameValue(datetime.month, month, `${prefix}month result:`);
+ assert.sameValue(datetime.monthCode, monthCode, `${prefix}monthCode result:`);
+ assert.sameValue(datetime.day, day, `${prefix}day result:`);
+ assert.sameValue(datetime.hour, hour, `${prefix}hour result:`);
+ assert.sameValue(datetime.minute, minute, `${prefix}minute result:`);
+ assert.sameValue(datetime.second, second, `${prefix}second result:`);
+ assert.sameValue(datetime.millisecond, millisecond, `${prefix}millisecond result:`);
+ assert.sameValue(datetime.microsecond, microsecond, `${prefix}microsecond result:`);
+ assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`);
+ },
+
+ /*
+ * assertPlainDateTimesEqual(actual, expected[, description]):
+ *
+ * Shorthand for asserting that two Temporal.PlainDateTimes are of the correct
+ * type, equal according to their equals() methods, and additionally that
+ * their calendar internal slots are the same value.
+ */
+ assertPlainDateTimesEqual(actual, expected, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(expected instanceof Temporal.PlainDateTime, `${prefix}expected value should be a Temporal.PlainDateTime`);
+ assert(actual instanceof Temporal.PlainDateTime, `${prefix}instanceof`);
+ assert(actual.equals(expected), `${prefix}equals method`);
+ assert.sameValue(
+ actual.getISOFields().calendar,
+ expected.getISOFields().calendar,
+ `${prefix}calendar same value:`
+ );
+ },
+
+ /*
+ * assertPlainMonthDay(monthDay, monthCode, day[, description [, referenceISOYear]]):
+ *
+ * Shorthand for asserting that each field of a Temporal.PlainMonthDay is
+ * equal to an expected value. (Except the `calendar` property, since callers
+ * may want to assert either object equality with an object they put in there,
+ * or the value of monthDay.calendarId().)
+ */
+ assertPlainMonthDay(monthDay, monthCode, day, description = "", referenceISOYear = 1972) {
+ const prefix = description ? `${description}: ` : "";
+ assert(monthDay instanceof Temporal.PlainMonthDay, `${prefix}instanceof`);
+ assert.sameValue(monthDay.monthCode, monthCode, `${prefix}monthCode result:`);
+ assert.sameValue(monthDay.day, day, `${prefix}day result:`);
+ assert.sameValue(monthDay.getISOFields().isoYear, referenceISOYear, `${prefix}referenceISOYear result:`);
+ },
+
+ /*
+ * assertPlainTime(time, hour, ..., nanosecond[, description]):
+ *
+ * Shorthand for asserting that each field of a Temporal.PlainTime is equal to
+ * an expected value.
+ */
+ assertPlainTime(time, hour, minute, second, millisecond, microsecond, nanosecond, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(time instanceof Temporal.PlainTime, `${prefix}instanceof`);
+ assert.sameValue(time.hour, hour, `${prefix}hour result:`);
+ assert.sameValue(time.minute, minute, `${prefix}minute result:`);
+ assert.sameValue(time.second, second, `${prefix}second result:`);
+ assert.sameValue(time.millisecond, millisecond, `${prefix}millisecond result:`);
+ assert.sameValue(time.microsecond, microsecond, `${prefix}microsecond result:`);
+ assert.sameValue(time.nanosecond, nanosecond, `${prefix}nanosecond result:`);
+ },
+
+ /*
+ * assertPlainTimesEqual(actual, expected[, description]):
+ *
+ * Shorthand for asserting that two Temporal.PlainTimes are of the correct
+ * type and equal according to their equals() methods.
+ */
+ assertPlainTimesEqual(actual, expected, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(expected instanceof Temporal.PlainTime, `${prefix}expected value should be a Temporal.PlainTime`);
+ assert(actual instanceof Temporal.PlainTime, `${prefix}instanceof`);
+ assert(actual.equals(expected), `${prefix}equals method`);
+ },
+
+ /*
+ * assertPlainYearMonth(yearMonth, year, month, monthCode[, description[, era, eraYear, referenceISODay]]):
+ *
+ * Shorthand for asserting that each field of a Temporal.PlainYearMonth is
+ * equal to an expected value. (Except the `calendar` property, since callers
+ * may want to assert either object equality with an object they put in there,
+ * or the value of yearMonth.calendarId.)
+ */
+ assertPlainYearMonth(yearMonth, year, month, monthCode, description = "", era = undefined, eraYear = undefined, referenceISODay = 1) {
+ const prefix = description ? `${description}: ` : "";
+ assert(yearMonth instanceof Temporal.PlainYearMonth, `${prefix}instanceof`);
+ assert.sameValue(yearMonth.era, era, `${prefix}era result:`);
+ assert.sameValue(yearMonth.eraYear, eraYear, `${prefix}eraYear result:`);
+ assert.sameValue(yearMonth.year, year, `${prefix}year result:`);
+ assert.sameValue(yearMonth.month, month, `${prefix}month result:`);
+ assert.sameValue(yearMonth.monthCode, monthCode, `${prefix}monthCode result:`);
+ assert.sameValue(yearMonth.getISOFields().isoDay, referenceISODay, `${prefix}referenceISODay result:`);
+ },
+
+ /*
+ * assertZonedDateTimesEqual(actual, expected[, description]):
+ *
+ * Shorthand for asserting that two Temporal.ZonedDateTimes are of the correct
+ * type, equal according to their equals() methods, and additionally that
+ * their time zones and calendar internal slots are the same value.
+ */
+ assertZonedDateTimesEqual(actual, expected, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(expected instanceof Temporal.ZonedDateTime, `${prefix}expected value should be a Temporal.ZonedDateTime`);
+ assert(actual instanceof Temporal.ZonedDateTime, `${prefix}instanceof`);
+ assert(actual.equals(expected), `${prefix}equals method`);
+ assert.sameValue(actual.timeZone, expected.timeZone, `${prefix}time zone same value:`);
+ assert.sameValue(
+ actual.getISOFields().calendar,
+ expected.getISOFields().calendar,
+ `${prefix}calendar same value:`
+ );
+ },
+
+ /*
+ * assertUnreachable(description):
+ *
+ * Helper for asserting that code is not executed. This is useful for
+ * assertions that methods of user calendars and time zones are not called.
+ */
+ assertUnreachable(description) {
+ let message = "This code should not be executed";
+ if (description) {
+ message = `${message}: ${description}`;
+ }
+ throw new Test262Error(message);
+ },
+
+ /*
+ * checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls):
+ *
+ * When an options object with a largestUnit property is synthesized inside
+ * Temporal and passed to user code such as calendar.dateUntil(), the value of
+ * the largestUnit property should be in the singular form, even if the input
+ * was given in the plural form.
+ * (This doesn't apply when the options object is passed through verbatim.)
+ *
+ * func(calendar, largestUnit, index) is the operation under test. It's called
+ * with an instance of a calendar that keeps track of which largestUnit is
+ * passed to dateUntil(), each key of expectedLargestUnitCalls in turn, and
+ * the key's numerical index in case the function needs to generate test data
+ * based on the index. At the end, the actual values passed to dateUntil() are
+ * compared with the array values of expectedLargestUnitCalls.
+ */
+ checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls) {
+ const actual = [];
+
+ class DateUntilOptionsCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateUntil(earlier, later, options) {
+ actual.push(options.largestUnit);
+ return super.dateUntil(earlier, later, options);
+ }
+
+ toString() {
+ return "date-until-options";
+ }
+ }
+
+ const calendar = new DateUntilOptionsCalendar();
+ Object.entries(expectedLargestUnitCalls).forEach(([largestUnit, expected], index) => {
+ func(calendar, largestUnit, index);
+ assert.compareArray(actual, expected, `largestUnit passed to calendar.dateUntil() for largestUnit ${largestUnit}`);
+ actual.splice(0); // empty it for the next check
+ });
+ },
+
+ /*
+ * checkPlainDateTimeConversionFastPath(func):
+ *
+ * ToTemporalDate and ToTemporalTime should both, if given a
+ * Temporal.PlainDateTime instance, convert to the desired type by reading the
+ * PlainDateTime's internal slots, rather than calling any getters.
+ *
+ * func(datetime, calendar) is the actual operation to test, that must
+ * internally call the abstract operation ToTemporalDate or ToTemporalTime.
+ * It is passed a Temporal.PlainDateTime instance, as well as the instance's
+ * calendar object (so that it doesn't have to call the calendar getter itself
+ * if it wants to make any assertions about the calendar.)
+ */
+ checkPlainDateTimeConversionFastPath(func, message = "checkPlainDateTimeConversionFastPath") {
+ const actual = [];
+ const expected = [];
+
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+ const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDateTime.prototype);
+ ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => {
+ Object.defineProperty(datetime, property, {
+ get() {
+ actual.push(`get ${formatPropertyName(property)}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${formatPropertyName(property)}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${formatPropertyName(property)}`);
+ return value;
+ },
+ };
+ },
+ });
+ });
+ Object.defineProperty(datetime, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ func(datetime, calendar);
+ assert.compareArray(actual, expected, `${message}: property getters not called`);
+ },
+
+ /*
+ * Check that an options bag that accepts units written in the singular form,
+ * also accepts the same units written in the plural form.
+ * func(unit) should call the method with the appropriate options bag
+ * containing unit as a value. This will be called twice for each element of
+ * validSingularUnits, once with singular and once with plural, and the
+ * results of each pair should be the same (whether a Temporal object or a
+ * primitive value.)
+ */
+ checkPluralUnitsAccepted(func, validSingularUnits) {
+ const plurals = {
+ year: 'years',
+ month: 'months',
+ week: 'weeks',
+ day: 'days',
+ hour: 'hours',
+ minute: 'minutes',
+ second: 'seconds',
+ millisecond: 'milliseconds',
+ microsecond: 'microseconds',
+ nanosecond: 'nanoseconds',
+ };
+
+ validSingularUnits.forEach((unit) => {
+ const singularValue = func(unit);
+ const pluralValue = func(plurals[unit]);
+ const desc = `Plural ${plurals[unit]} produces the same result as singular ${unit}`;
+ if (singularValue instanceof Temporal.Duration) {
+ TemporalHelpers.assertDurationsEqual(pluralValue, singularValue, desc);
+ } else if (singularValue instanceof Temporal.Instant) {
+ TemporalHelpers.assertInstantsEqual(pluralValue, singularValue, desc);
+ } else if (singularValue instanceof Temporal.PlainDateTime) {
+ TemporalHelpers.assertPlainDateTimesEqual(pluralValue, singularValue, desc);
+ } else if (singularValue instanceof Temporal.PlainTime) {
+ TemporalHelpers.assertPlainTimesEqual(pluralValue, singularValue, desc);
+ } else if (singularValue instanceof Temporal.ZonedDateTime) {
+ TemporalHelpers.assertZonedDateTimesEqual(pluralValue, singularValue, desc);
+ } else {
+ assert.sameValue(pluralValue, singularValue);
+ }
+ });
+ },
+
+ /*
+ * checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc):
+ *
+ * Checks the type handling of the roundingIncrement option.
+ * checkFunc(roundingIncrement) is a function which takes the value of
+ * roundingIncrement to test, and calls the method under test with it,
+ * returning the result. assertTrueResultFunc(result, description) should
+ * assert that result is the expected result with roundingIncrement: true, and
+ * assertObjectResultFunc(result, description) should assert that result is
+ * the expected result with roundingIncrement being an object with a valueOf()
+ * method.
+ */
+ checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc) {
+ // null converts to 0, which is out of range
+ assert.throws(RangeError, () => checkFunc(null), "null");
+ // Booleans convert to either 0 or 1, and 1 is allowed
+ const trueResult = checkFunc(true);
+ assertTrueResultFunc(trueResult, "true");
+ assert.throws(RangeError, () => checkFunc(false), "false");
+ // Symbols and BigInts cannot convert to numbers
+ assert.throws(TypeError, () => checkFunc(Symbol()), "symbol");
+ assert.throws(TypeError, () => checkFunc(2n), "bigint");
+
+ // Objects prefer their valueOf() methods when converting to a number
+ assert.throws(RangeError, () => checkFunc({}), "plain object");
+
+ const expected = [
+ "get roundingIncrement.valueOf",
+ "call roundingIncrement.valueOf",
+ ];
+ const actual = [];
+ const observer = TemporalHelpers.toPrimitiveObserver(actual, 2, "roundingIncrement");
+ const objectResult = checkFunc(observer);
+ assertObjectResultFunc(objectResult, "object with valueOf");
+ assert.compareArray(actual, expected, "order of operations");
+ },
+
+ /*
+ * checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc):
+ *
+ * Checks the type handling of a string option, of which there are several in
+ * Temporal.
+ * propertyName is the name of the option, and value is the value that
+ * assertFunc should expect it to have.
+ * checkFunc(value) is a function which takes the value of the option to test,
+ * and calls the method under test with it, returning the result.
+ * assertFunc(result, description) should assert that result is the expected
+ * result with the option value being an object with a toString() method
+ * which returns the given value.
+ */
+ checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc) {
+ // null converts to the string "null", which is an invalid string value
+ assert.throws(RangeError, () => checkFunc(null), "null");
+ // Booleans convert to the strings "true" or "false", which are invalid
+ assert.throws(RangeError, () => checkFunc(true), "true");
+ assert.throws(RangeError, () => checkFunc(false), "false");
+ // Symbols cannot convert to strings
+ assert.throws(TypeError, () => checkFunc(Symbol()), "symbol");
+ // Numbers convert to strings which are invalid
+ assert.throws(RangeError, () => checkFunc(2), "number");
+ // BigInts convert to strings which are invalid
+ assert.throws(RangeError, () => checkFunc(2n), "bigint");
+
+ // Objects prefer their toString() methods when converting to a string
+ assert.throws(RangeError, () => checkFunc({}), "plain object");
+
+ const expected = [
+ `get ${propertyName}.toString`,
+ `call ${propertyName}.toString`,
+ ];
+ const actual = [];
+ const observer = TemporalHelpers.toPrimitiveObserver(actual, value, propertyName);
+ const result = checkFunc(observer);
+ assertFunc(result, "object with toString");
+ assert.compareArray(actual, expected, "order of operations");
+ },
+
+ /*
+ * checkSubclassingIgnored(construct, constructArgs, method, methodArgs,
+ * resultAssertions):
+ *
+ * Methods of Temporal classes that return a new instance of the same class,
+ * must not take the constructor of a subclass into account, nor the @@species
+ * property. This helper runs tests to ensure this.
+ *
+ * construct(...constructArgs) must yield a valid instance of the Temporal
+ * class. instance[method](...methodArgs) is the method call under test, which
+ * must also yield a valid instance of the same Temporal class, not a
+ * subclass. See below for the individual tests that this runs.
+ * resultAssertions() is a function that performs additional assertions on the
+ * instance returned by the method under test.
+ */
+ checkSubclassingIgnored(...args) {
+ this.checkSubclassConstructorNotObject(...args);
+ this.checkSubclassConstructorUndefined(...args);
+ this.checkSubclassConstructorThrows(...args);
+ this.checkSubclassConstructorNotCalled(...args);
+ this.checkSubclassSpeciesInvalidResult(...args);
+ this.checkSubclassSpeciesNotAConstructor(...args);
+ this.checkSubclassSpeciesNull(...args);
+ this.checkSubclassSpeciesUndefined(...args);
+ this.checkSubclassSpeciesThrows(...args);
+ },
+
+ /*
+ * Checks that replacing the 'constructor' property of the instance with
+ * various primitive values does not affect the returned new instance.
+ */
+ checkSubclassConstructorNotObject(construct, constructArgs, method, methodArgs, resultAssertions) {
+ function check(value, description) {
+ const instance = new construct(...constructArgs);
+ instance.constructor = value;
+ const result = instance[method](...methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
+ resultAssertions(result);
+ }
+
+ check(null, "null");
+ check(true, "true");
+ check("test", "string");
+ check(Symbol(), "Symbol");
+ check(7, "number");
+ check(7n, "bigint");
+ },
+
+ /*
+ * Checks that replacing the 'constructor' property of the subclass with
+ * undefined does not affect the returned new instance.
+ */
+ checkSubclassConstructorUndefined(construct, constructArgs, method, methodArgs, resultAssertions) {
+ let called = 0;
+
+ class MySubclass extends construct {
+ constructor() {
+ ++called;
+ super(...constructArgs);
+ }
+ }
+
+ const instance = new MySubclass();
+ assert.sameValue(called, 1);
+
+ MySubclass.prototype.constructor = undefined;
+
+ const result = instance[method](...methodArgs);
+ assert.sameValue(called, 1);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Checks that making the 'constructor' property of the instance throw when
+ * called does not affect the returned new instance.
+ */
+ checkSubclassConstructorThrows(construct, constructArgs, method, methodArgs, resultAssertions) {
+ function CustomError() {}
+ const instance = new construct(...constructArgs);
+ Object.defineProperty(instance, "constructor", {
+ get() {
+ throw new CustomError();
+ }
+ });
+ const result = instance[method](...methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Checks that when subclassing, the subclass constructor is not called by
+ * the method under test.
+ */
+ checkSubclassConstructorNotCalled(construct, constructArgs, method, methodArgs, resultAssertions) {
+ let called = 0;
+
+ class MySubclass extends construct {
+ constructor() {
+ ++called;
+ super(...constructArgs);
+ }
+ }
+
+ const instance = new MySubclass();
+ assert.sameValue(called, 1);
+
+ const result = instance[method](...methodArgs);
+ assert.sameValue(called, 1);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Check that the constructor's @@species property is ignored when it's a
+ * constructor that returns a non-object value.
+ */
+ checkSubclassSpeciesInvalidResult(construct, constructArgs, method, methodArgs, resultAssertions) {
+ function check(value, description) {
+ const instance = new construct(...constructArgs);
+ instance.constructor = {
+ [Symbol.species]: function() {
+ return value;
+ },
+ };
+ const result = instance[method](...methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
+ resultAssertions(result);
+ }
+
+ check(undefined, "undefined");
+ check(null, "null");
+ check(true, "true");
+ check("test", "string");
+ check(Symbol(), "Symbol");
+ check(7, "number");
+ check(7n, "bigint");
+ check({}, "plain object");
+ },
+
+ /*
+ * Check that the constructor's @@species property is ignored when it's not a
+ * constructor.
+ */
+ checkSubclassSpeciesNotAConstructor(construct, constructArgs, method, methodArgs, resultAssertions) {
+ function check(value, description) {
+ const instance = new construct(...constructArgs);
+ instance.constructor = {
+ [Symbol.species]: value,
+ };
+ const result = instance[method](...methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
+ resultAssertions(result);
+ }
+
+ check(true, "true");
+ check("test", "string");
+ check(Symbol(), "Symbol");
+ check(7, "number");
+ check(7n, "bigint");
+ check({}, "plain object");
+ },
+
+ /*
+ * Check that the constructor's @@species property is ignored when it's null.
+ */
+ checkSubclassSpeciesNull(construct, constructArgs, method, methodArgs, resultAssertions) {
+ let called = 0;
+
+ class MySubclass extends construct {
+ constructor() {
+ ++called;
+ super(...constructArgs);
+ }
+ }
+
+ const instance = new MySubclass();
+ assert.sameValue(called, 1);
+
+ MySubclass.prototype.constructor = {
+ [Symbol.species]: null,
+ };
+
+ const result = instance[method](...methodArgs);
+ assert.sameValue(called, 1);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Check that the constructor's @@species property is ignored when it's
+ * undefined.
+ */
+ checkSubclassSpeciesUndefined(construct, constructArgs, method, methodArgs, resultAssertions) {
+ let called = 0;
+
+ class MySubclass extends construct {
+ constructor() {
+ ++called;
+ super(...constructArgs);
+ }
+ }
+
+ const instance = new MySubclass();
+ assert.sameValue(called, 1);
+
+ MySubclass.prototype.constructor = {
+ [Symbol.species]: undefined,
+ };
+
+ const result = instance[method](...methodArgs);
+ assert.sameValue(called, 1);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Check that the constructor's @@species property is ignored when it throws,
+ * i.e. it is not called at all.
+ */
+ checkSubclassSpeciesThrows(construct, constructArgs, method, methodArgs, resultAssertions) {
+ function CustomError() {}
+
+ const instance = new construct(...constructArgs);
+ instance.constructor = {
+ get [Symbol.species]() {
+ throw new CustomError();
+ },
+ };
+
+ const result = instance[method](...methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ },
+
+ /*
+ * checkSubclassingIgnoredStatic(construct, method, methodArgs, resultAssertions):
+ *
+ * Static methods of Temporal classes that return a new instance of the class,
+ * must not use the this-value as a constructor. This helper runs tests to
+ * ensure this.
+ *
+ * construct[method](...methodArgs) is the static method call under test, and
+ * must yield a valid instance of the Temporal class, not a subclass. See
+ * below for the individual tests that this runs.
+ * resultAssertions() is a function that performs additional assertions on the
+ * instance returned by the method under test.
+ */
+ checkSubclassingIgnoredStatic(...args) {
+ this.checkStaticInvalidReceiver(...args);
+ this.checkStaticReceiverNotCalled(...args);
+ this.checkThisValueNotCalled(...args);
+ },
+
+ /*
+ * Check that calling the static method with a receiver that's not callable,
+ * still calls the intrinsic constructor.
+ */
+ checkStaticInvalidReceiver(construct, method, methodArgs, resultAssertions) {
+ function check(value, description) {
+ const result = construct[method].apply(value, methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ }
+
+ check(undefined, "undefined");
+ check(null, "null");
+ check(true, "true");
+ check("test", "string");
+ check(Symbol(), "symbol");
+ check(7, "number");
+ check(7n, "bigint");
+ check({}, "Non-callable object");
+ },
+
+ /*
+ * Check that calling the static method with a receiver that returns a value
+ * that's not callable, still calls the intrinsic constructor.
+ */
+ checkStaticReceiverNotCalled(construct, method, methodArgs, resultAssertions) {
+ function check(value, description) {
+ const receiver = function () {
+ return value;
+ };
+ const result = construct[method].apply(receiver, methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ }
+
+ check(undefined, "undefined");
+ check(null, "null");
+ check(true, "true");
+ check("test", "string");
+ check(Symbol(), "symbol");
+ check(7, "number");
+ check(7n, "bigint");
+ check({}, "Non-callable object");
+ },
+
+ /*
+ * Check that the receiver isn't called.
+ */
+ checkThisValueNotCalled(construct, method, methodArgs, resultAssertions) {
+ let called = false;
+
+ class MySubclass extends construct {
+ constructor(...args) {
+ called = true;
+ super(...args);
+ }
+ }
+
+ const result = MySubclass[method](...methodArgs);
+ assert.sameValue(called, false);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Check that any iterable returned from a custom time zone's
+ * getPossibleInstantsFor() method is exhausted.
+ * The custom time zone object is passed in to func().
+ * expected is an array of strings representing the expected calls to the
+ * getPossibleInstantsFor() method. The PlainDateTimes that it is called with,
+ * are compared (using their toString() results) with the array.
+ */
+ checkTimeZonePossibleInstantsIterable(func, expected) {
+ // A custom time zone that returns an iterable instead of an array from its
+ // getPossibleInstantsFor() method, and for testing purposes skips
+ // 00:00-01:00 UTC on January 1, 2030, and repeats 00:00-01:00 UTC+1 on
+ // January 3, 2030. Otherwise identical to the UTC time zone.
+ class TimeZonePossibleInstantsIterable extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ this.getPossibleInstantsForCallCount = 0;
+ this.getPossibleInstantsForCalledWith = [];
+ this.getPossibleInstantsForReturns = [];
+ this.iteratorExhausted = [];
+ }
+
+ toString() {
+ return "Custom/Iterable";
+ }
+
+ getOffsetNanosecondsFor(instant) {
+ if (Temporal.Instant.compare(instant, "2030-01-01T00:00Z") >= 0 &&
+ Temporal.Instant.compare(instant, "2030-01-03T01:00Z") < 0) {
+ return 3600_000_000_000;
+ } else {
+ return 0;
+ }
+ }
+
+ getPossibleInstantsFor(dateTime) {
+ this.getPossibleInstantsForCallCount++;
+ this.getPossibleInstantsForCalledWith.push(dateTime);
+
+ // Fake DST transition
+ let retval = super.getPossibleInstantsFor(dateTime);
+ if (dateTime.toPlainDate().equals("2030-01-01") && dateTime.hour === 0) {
+ retval = [];
+ } else if (dateTime.toPlainDate().equals("2030-01-03") && dateTime.hour === 0) {
+ retval.push(retval[0].subtract({ hours: 1 }));
+ } else if (dateTime.year === 2030 && dateTime.month === 1 && dateTime.day >= 1 && dateTime.day <= 2) {
+ retval[0] = retval[0].subtract({ hours: 1 });
+ }
+
+ this.getPossibleInstantsForReturns.push(retval);
+ this.iteratorExhausted.push(false);
+ return {
+ callIndex: this.getPossibleInstantsForCallCount - 1,
+ timeZone: this,
+ *[Symbol.iterator]() {
+ yield* this.timeZone.getPossibleInstantsForReturns[this.callIndex];
+ this.timeZone.iteratorExhausted[this.callIndex] = true;
+ },
+ };
+ }
+ }
+
+ const timeZone = new TimeZonePossibleInstantsIterable();
+ func(timeZone);
+
+ assert.sameValue(timeZone.getPossibleInstantsForCallCount, expected.length, "getPossibleInstantsFor() method called correct number of times");
+
+ for (let index = 0; index < expected.length; index++) {
+ assert.sameValue(timeZone.getPossibleInstantsForCalledWith[index].toString(), expected[index], "getPossibleInstantsFor() called with expected PlainDateTime");
+ assert(timeZone.iteratorExhausted[index], "iterated through the whole iterable");
+ }
+ },
+
+ /*
+ * Check that any calendar-carrying Temporal object has its [[Calendar]]
+ * internal slot read by ToTemporalCalendar, and does not fetch the calendar
+ * by calling getters.
+ * The custom calendar object is passed in to func() so that it can do its
+ * own additional assertions involving the calendar if necessary. (Sometimes
+ * there is nothing to assert as the calendar isn't stored anywhere that can
+ * be asserted about.)
+ */
+ checkToTemporalCalendarFastPath(func) {
+ class CalendarFastPathCheck extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateFromFields(...args) {
+ return super.dateFromFields(...args).withCalendar(this);
+ }
+
+ monthDayFromFields(...args) {
+ const { isoYear, isoMonth, isoDay } = super.monthDayFromFields(...args).getISOFields();
+ return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear);
+ }
+
+ yearMonthFromFields(...args) {
+ const { isoYear, isoMonth, isoDay } = super.yearMonthFromFields(...args).getISOFields();
+ return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay);
+ }
+
+ toString() {
+ return "fast-path-check";
+ }
+ }
+ const calendar = new CalendarFastPathCheck();
+
+ const plainDate = new Temporal.PlainDate(2000, 5, 2, calendar);
+ const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+ const plainMonthDay = new Temporal.PlainMonthDay(5, 2, calendar);
+ const plainYearMonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+ const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+
+ [plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((temporalObject) => {
+ const actual = [];
+ const expected = [];
+
+ Object.defineProperty(temporalObject, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ func(temporalObject, calendar);
+ assert.compareArray(actual, expected, "calendar getter not called");
+ });
+ },
+
+ checkToTemporalInstantFastPath(func) {
+ const actual = [];
+ const expected = [];
+
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ Object.defineProperty(datetime, 'toString', {
+ get() {
+ actual.push("get toString");
+ return function (options) {
+ actual.push("call toString");
+ return Temporal.ZonedDateTime.prototype.toString.call(this, options);
+ };
+ },
+ });
+
+ func(datetime);
+ assert.compareArray(actual, expected, "toString not called");
+ },
+
+ checkToTemporalPlainDateTimeFastPath(func) {
+ const actual = [];
+ const expected = [];
+
+ const calendar = new Temporal.Calendar("iso8601");
+ const date = new Temporal.PlainDate(2000, 5, 2, calendar);
+ const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDate.prototype);
+ ["year", "month", "monthCode", "day"].forEach((property) => {
+ Object.defineProperty(date, property, {
+ get() {
+ actual.push(`get ${formatPropertyName(property)}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return TemporalHelpers.toPrimitiveObserver(actual, value, property);
+ },
+ });
+ });
+ ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => {
+ Object.defineProperty(date, property, {
+ get() {
+ actual.push(`get ${formatPropertyName(property)}`);
+ return undefined;
+ },
+ });
+ });
+ Object.defineProperty(date, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ func(date, calendar);
+ assert.compareArray(actual, expected, "property getters not called");
+ },
+
+ /*
+ * A custom calendar used in prototype pollution checks. Verifies that the
+ * fromFields methods are always called with a null-prototype fields object.
+ */
+ calendarCheckFieldsPrototypePollution() {
+ class CalendarCheckFieldsPrototypePollution extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.dateFromFieldsCallCount = 0;
+ this.yearMonthFromFieldsCallCount = 0;
+ this.monthDayFromFieldsCallCount = 0;
+ }
+
+ // toString must remain "iso8601", so that some methods don't throw due to
+ // incompatible calendars
+
+ dateFromFields(fields, options = {}) {
+ this.dateFromFieldsCallCount++;
+ assert.sameValue(Object.getPrototypeOf(fields), null, "dateFromFields should be called with null-prototype fields object");
+ return super.dateFromFields(fields, options);
+ }
+
+ yearMonthFromFields(fields, options = {}) {
+ this.yearMonthFromFieldsCallCount++;
+ assert.sameValue(Object.getPrototypeOf(fields), null, "yearMonthFromFields should be called with null-prototype fields object");
+ return super.yearMonthFromFields(fields, options);
+ }
+
+ monthDayFromFields(fields, options = {}) {
+ this.monthDayFromFieldsCallCount++;
+ assert.sameValue(Object.getPrototypeOf(fields), null, "monthDayFromFields should be called with null-prototype fields object");
+ return super.monthDayFromFields(fields, options);
+ }
+ }
+
+ return new CalendarCheckFieldsPrototypePollution();
+ },
+
+ /*
+ * A custom calendar used in prototype pollution checks. Verifies that the
+ * mergeFields() method is always called with null-prototype fields objects.
+ */
+ calendarCheckMergeFieldsPrototypePollution() {
+ class CalendarCheckMergeFieldsPrototypePollution extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.mergeFieldsCallCount = 0;
+ }
+
+ toString() {
+ return "merge-fields-null-proto";
+ }
+
+ mergeFields(fields, additionalFields) {
+ this.mergeFieldsCallCount++;
+ assert.sameValue(Object.getPrototypeOf(fields), null, "mergeFields should be called with null-prototype fields object (first argument)");
+ assert.sameValue(Object.getPrototypeOf(additionalFields), null, "mergeFields should be called with null-prototype fields object (second argument)");
+ return super.mergeFields(fields, additionalFields);
+ }
+ }
+
+ return new CalendarCheckMergeFieldsPrototypePollution();
+ },
+
+ /*
+ * A custom calendar used in prototype pollution checks. Verifies that methods
+ * are always called with a null-prototype options object.
+ */
+ calendarCheckOptionsPrototypePollution() {
+ class CalendarCheckOptionsPrototypePollution extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.yearMonthFromFieldsCallCount = 0;
+ this.dateUntilCallCount = 0;
+ }
+
+ toString() {
+ return "options-null-proto";
+ }
+
+ yearMonthFromFields(fields, options) {
+ this.yearMonthFromFieldsCallCount++;
+ assert.sameValue(Object.getPrototypeOf(options), null, "yearMonthFromFields should be called with null-prototype options");
+ return super.yearMonthFromFields(fields, options);
+ }
+
+ dateUntil(one, two, options) {
+ this.dateUntilCallCount++;
+ assert.sameValue(Object.getPrototypeOf(options), null, "dateUntil should be called with null-prototype options");
+ return super.dateUntil(one, two, options);
+ }
+ }
+
+ return new CalendarCheckOptionsPrototypePollution();
+ },
+
+ /*
+ * A custom calendar that asserts its dateAdd() method is called with the
+ * options parameter having the value undefined.
+ */
+ calendarDateAddUndefinedOptions() {
+ class CalendarDateAddUndefinedOptions extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.dateAddCallCount = 0;
+ }
+
+ toString() {
+ return "dateadd-undef-options";
+ }
+
+ dateAdd(date, duration, options) {
+ this.dateAddCallCount++;
+ assert.sameValue(options, undefined, "dateAdd shouldn't be called with options");
+ return super.dateAdd(date, duration, options);
+ }
+ }
+ return new CalendarDateAddUndefinedOptions();
+ },
+
+ /*
+ * A custom calendar that asserts its dateAdd() method is called with a
+ * PlainDate instance. Optionally, it also asserts that the PlainDate instance
+ * is the specific object `this.specificPlainDate`, if it is set by the
+ * calling code.
+ */
+ calendarDateAddPlainDateInstance() {
+ class CalendarDateAddPlainDateInstance extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.dateAddCallCount = 0;
+ this.specificPlainDate = undefined;
+ }
+
+ toString() {
+ return "dateadd-plain-date-instance";
+ }
+
+ dateFromFields(...args) {
+ return super.dateFromFields(...args).withCalendar(this);
+ }
+
+ dateAdd(date, duration, options) {
+ this.dateAddCallCount++;
+ assert(date instanceof Temporal.PlainDate, "dateAdd() should be called with a PlainDate instance");
+ if (this.dateAddCallCount === 1 && this.specificPlainDate) {
+ assert.sameValue(date, this.specificPlainDate, `dateAdd() should be called first with the specific PlainDate instance ${this.specificPlainDate}`);
+ }
+ return super.dateAdd(date, duration, options).withCalendar(this);
+ }
+ }
+ return new CalendarDateAddPlainDateInstance();
+ },
+
+ /*
+ * A custom calendar that returns an iterable instead of an array from its
+ * fields() method, otherwise identical to the ISO calendar.
+ */
+ calendarFieldsIterable() {
+ class CalendarFieldsIterable extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.fieldsCallCount = 0;
+ this.fieldsCalledWith = [];
+ this.iteratorExhausted = [];
+ }
+
+ toString() {
+ return "fields-iterable";
+ }
+
+ fields(fieldNames) {
+ this.fieldsCallCount++;
+ this.fieldsCalledWith.push(fieldNames.slice());
+ this.iteratorExhausted.push(false);
+ return {
+ callIndex: this.fieldsCallCount - 1,
+ calendar: this,
+ *[Symbol.iterator]() {
+ yield* this.calendar.fieldsCalledWith[this.callIndex];
+ this.calendar.iteratorExhausted[this.callIndex] = true;
+ },
+ };
+ }
+ }
+ return new CalendarFieldsIterable();
+ },
+
+ /*
+ * A custom calendar that asserts its ...FromFields() methods are called with
+ * the options parameter having the value undefined.
+ */
+ calendarFromFieldsUndefinedOptions() {
+ class CalendarFromFieldsUndefinedOptions extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.dateFromFieldsCallCount = 0;
+ this.monthDayFromFieldsCallCount = 0;
+ this.yearMonthFromFieldsCallCount = 0;
+ }
+
+ toString() {
+ return "from-fields-undef-options";
+ }
+
+ dateFromFields(fields, options) {
+ this.dateFromFieldsCallCount++;
+ assert.sameValue(options, undefined, "dateFromFields shouldn't be called with options");
+ return super.dateFromFields(fields, options);
+ }
+
+ yearMonthFromFields(fields, options) {
+ this.yearMonthFromFieldsCallCount++;
+ assert.sameValue(options, undefined, "yearMonthFromFields shouldn't be called with options");
+ return super.yearMonthFromFields(fields, options);
+ }
+
+ monthDayFromFields(fields, options) {
+ this.monthDayFromFieldsCallCount++;
+ assert.sameValue(options, undefined, "monthDayFromFields shouldn't be called with options");
+ return super.monthDayFromFields(fields, options);
+ }
+ }
+ return new CalendarFromFieldsUndefinedOptions();
+ },
+
+ /*
+ * A custom calendar that modifies the fields object passed in to
+ * dateFromFields, sabotaging its time properties.
+ */
+ calendarMakeInfinityTime() {
+ class CalendarMakeInfinityTime extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateFromFields(fields, options) {
+ const retval = super.dateFromFields(fields, options);
+ fields.hour = Infinity;
+ fields.minute = Infinity;
+ fields.second = Infinity;
+ fields.millisecond = Infinity;
+ fields.microsecond = Infinity;
+ fields.nanosecond = Infinity;
+ return retval;
+ }
+ }
+ return new CalendarMakeInfinityTime();
+ },
+
+ /*
+ * A custom calendar that defines getters on the fields object passed into
+ * dateFromFields that throw, sabotaging its time properties.
+ */
+ calendarMakeInvalidGettersTime() {
+ class CalendarMakeInvalidGettersTime extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateFromFields(fields, options) {
+ const retval = super.dateFromFields(fields, options);
+ const throwingDescriptor = {
+ get() {
+ throw new Test262Error("reading a sabotaged time field");
+ },
+ };
+ Object.defineProperties(fields, {
+ hour: throwingDescriptor,
+ minute: throwingDescriptor,
+ second: throwingDescriptor,
+ millisecond: throwingDescriptor,
+ microsecond: throwingDescriptor,
+ nanosecond: throwingDescriptor,
+ });
+ return retval;
+ }
+ }
+ return new CalendarMakeInvalidGettersTime();
+ },
+
+ /*
+ * A custom calendar whose mergeFields() method returns a proxy object with
+ * all of its Get and HasProperty operations observable, as well as adding a
+ * "shouldNotBeCopied": true property.
+ */
+ calendarMergeFieldsGetters() {
+ class CalendarMergeFieldsGetters extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.mergeFieldsReturnOperations = [];
+ }
+
+ toString() {
+ return "merge-fields-getters";
+ }
+
+ dateFromFields(fields, options) {
+ assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied");
+ return super.dateFromFields(fields, options);
+ }
+
+ yearMonthFromFields(fields, options) {
+ assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied");
+ return super.yearMonthFromFields(fields, options);
+ }
+
+ monthDayFromFields(fields, options) {
+ assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied");
+ return super.monthDayFromFields(fields, options);
+ }
+
+ mergeFields(fields, additionalFields) {
+ const retval = super.mergeFields(fields, additionalFields);
+ retval._calendar = this;
+ retval.shouldNotBeCopied = true;
+ return new Proxy(retval, {
+ get(target, key) {
+ target._calendar.mergeFieldsReturnOperations.push(`get ${key}`);
+ const result = target[key];
+ if (result === undefined) {
+ return undefined;
+ }
+ return TemporalHelpers.toPrimitiveObserver(target._calendar.mergeFieldsReturnOperations, result, key);
+ },
+ has(target, key) {
+ target._calendar.mergeFieldsReturnOperations.push(`has ${key}`);
+ return key in target;
+ },
+ });
+ }
+ }
+ return new CalendarMergeFieldsGetters();
+ },
+
+ /*
+ * A custom calendar whose mergeFields() method returns a primitive value,
+ * given by @primitive, and which records the number of calls made to its
+ * dateFromFields(), yearMonthFromFields(), and monthDayFromFields() methods.
+ */
+ calendarMergeFieldsReturnsPrimitive(primitive) {
+ class CalendarMergeFieldsPrimitive extends Temporal.Calendar {
+ constructor(mergeFieldsReturnValue) {
+ super("iso8601");
+ this._mergeFieldsReturnValue = mergeFieldsReturnValue;
+ this.dateFromFieldsCallCount = 0;
+ this.monthDayFromFieldsCallCount = 0;
+ this.yearMonthFromFieldsCallCount = 0;
+ }
+
+ toString() {
+ return "merge-fields-primitive";
+ }
+
+ dateFromFields(fields, options) {
+ this.dateFromFieldsCallCount++;
+ return super.dateFromFields(fields, options);
+ }
+
+ yearMonthFromFields(fields, options) {
+ this.yearMonthFromFieldsCallCount++;
+ return super.yearMonthFromFields(fields, options);
+ }
+
+ monthDayFromFields(fields, options) {
+ this.monthDayFromFieldsCallCount++;
+ return super.monthDayFromFields(fields, options);
+ }
+
+ mergeFields() {
+ return this._mergeFieldsReturnValue;
+ }
+ }
+ return new CalendarMergeFieldsPrimitive(primitive);
+ },
+
+ /*
+ * A custom calendar whose fields() method returns the same value as the
+ * iso8601 calendar, with the addition of extraFields provided as parameter.
+ */
+ calendarWithExtraFields(fields) {
+ class CalendarWithExtraFields extends Temporal.Calendar {
+ constructor(extraFields) {
+ super("iso8601");
+ this._extraFields = extraFields;
+ }
+
+ fields(fieldNames) {
+ return super.fields(fieldNames).concat(this._extraFields);
+ }
+ }
+
+ return new CalendarWithExtraFields(fields);
+ },
+
+ /*
+ * crossDateLineTimeZone():
+ *
+ * This returns an instance of a custom time zone class that implements one
+ * single transition where the time zone moves from one side of the
+ * International Date Line to the other, for the purpose of testing time zone
+ * calculations without depending on system time zone data.
+ *
+ * The transition occurs at epoch second 1325239200 and goes from offset
+ * -10:00 to +14:00. In other words, the time zone skips the whole calendar
+ * day of 2011-12-30. This is the same as the real-life transition in the
+ * Pacific/Apia time zone.
+ */
+ crossDateLineTimeZone() {
+ const { compare } = Temporal.PlainDate;
+ const skippedDay = new Temporal.PlainDate(2011, 12, 30);
+ const transitionEpoch = 1325239200_000_000_000n;
+ const beforeOffset = new Temporal.TimeZone("-10:00");
+ const afterOffset = new Temporal.TimeZone("+14:00");
+
+ class CrossDateLineTimeZone extends Temporal.TimeZone {
+ constructor() {
+ super("+14:00");
+ }
+
+ getOffsetNanosecondsFor(instant) {
+ if (instant.epochNanoseconds < transitionEpoch) {
+ return beforeOffset.getOffsetNanosecondsFor(instant);
+ }
+ return afterOffset.getOffsetNanosecondsFor(instant);
+ }
+
+ getPossibleInstantsFor(datetime) {
+ const comparison = compare(datetime.toPlainDate(), skippedDay);
+ if (comparison === 0) {
+ return [];
+ }
+ if (comparison < 0) {
+ return [beforeOffset.getInstantFor(datetime)];
+ }
+ return [afterOffset.getInstantFor(datetime)];
+ }
+
+ getPreviousTransition(instant) {
+ if (instant.epochNanoseconds > transitionEpoch) return new Temporal.Instant(transitionEpoch);
+ return null;
+ }
+
+ getNextTransition(instant) {
+ if (instant.epochNanoseconds < transitionEpoch) return new Temporal.Instant(transitionEpoch);
+ return null;
+ }
+
+ toString() {
+ return "Custom/Date_Line";
+ }
+ }
+ return new CrossDateLineTimeZone();
+ },
+
+ /*
+ * observeProperty(calls, object, propertyName, value):
+ *
+ * Defines an own property @object.@propertyName with value @value, that
+ * will log any calls to its accessors to the array @calls.
+ */
+ observeProperty(calls, object, propertyName, value, objectName = "") {
+ Object.defineProperty(object, propertyName, {
+ get() {
+ calls.push(`get ${formatPropertyName(propertyName, objectName)}`);
+ return value;
+ },
+ set(v) {
+ calls.push(`set ${formatPropertyName(propertyName, objectName)}`);
+ }
+ });
+ },
+
+ /*
+ * observeMethod(calls, object, propertyName, value):
+ *
+ * Defines an own property @object.@propertyName with value @value, that
+ * will log any calls of @value to the array @calls.
+ */
+ observeMethod(calls, object, propertyName, objectName = "") {
+ const method = object[propertyName];
+ object[propertyName] = function () {
+ calls.push(`call ${formatPropertyName(propertyName, objectName)}`);
+ return method.apply(object, arguments);
+ };
+ },
+
+ /*
+ * Used for substituteMethod to indicate default behavior instead of a
+ * substituted value
+ */
+ SUBSTITUTE_SKIP: SKIP_SYMBOL,
+
+ /*
+ * substituteMethod(object, propertyName, values):
+ *
+ * Defines an own property @object.@propertyName that will, for each
+ * subsequent call to the method previously defined as
+ * @object.@propertyName:
+ * - Call the method, if no more values remain
+ * - Call the method, if the value in @values for the corresponding call
+ * is SUBSTITUTE_SKIP
+ * - Otherwise, return the corresponding value in @value
+ */
+ substituteMethod(object, propertyName, values) {
+ let calls = 0;
+ const method = object[propertyName];
+ object[propertyName] = function () {
+ if (calls >= values.length) {
+ return method.apply(object, arguments);
+ } else if (values[calls] === SKIP_SYMBOL) {
+ calls++;
+ return method.apply(object, arguments);
+ } else {
+ return values[calls++];
+ }
+ };
+ },
+
+ /*
+ * calendarObserver:
+ * A custom calendar that behaves exactly like the ISO 8601 calendar but
+ * tracks calls to any of its methods, and Get/Has operations on its
+ * properties, by appending messages to an array. This is for the purpose of
+ * testing order of operations that are observable from user code.
+ * objectName is used in the log.
+ */
+ calendarObserver(calls, objectName, methodOverrides = {}) {
+ function removeExtraHasPropertyChecks(objectName, calls) {
+ // Inserting the tracking calendar into the return values of methods
+ // that we chain up into the ISO calendar for, causes extra HasProperty
+ // checks, which we observe. This removes them so that we don't leak
+ // implementation details of the helper into the test code.
+ assert.sameValue(calls.pop(), `has ${objectName}.yearOfWeek`);
+ assert.sameValue(calls.pop(), `has ${objectName}.yearMonthFromFields`);
+ assert.sameValue(calls.pop(), `has ${objectName}.year`);
+ assert.sameValue(calls.pop(), `has ${objectName}.weekOfYear`);
+ assert.sameValue(calls.pop(), `has ${objectName}.monthsInYear`);
+ assert.sameValue(calls.pop(), `has ${objectName}.monthDayFromFields`);
+ assert.sameValue(calls.pop(), `has ${objectName}.monthCode`);
+ assert.sameValue(calls.pop(), `has ${objectName}.month`);
+ assert.sameValue(calls.pop(), `has ${objectName}.mergeFields`);
+ assert.sameValue(calls.pop(), `has ${objectName}.inLeapYear`);
+ assert.sameValue(calls.pop(), `has ${objectName}.id`);
+ assert.sameValue(calls.pop(), `has ${objectName}.fields`);
+ assert.sameValue(calls.pop(), `has ${objectName}.daysInYear`);
+ assert.sameValue(calls.pop(), `has ${objectName}.daysInWeek`);
+ assert.sameValue(calls.pop(), `has ${objectName}.daysInMonth`);
+ assert.sameValue(calls.pop(), `has ${objectName}.dayOfYear`);
+ assert.sameValue(calls.pop(), `has ${objectName}.dayOfWeek`);
+ assert.sameValue(calls.pop(), `has ${objectName}.day`);
+ assert.sameValue(calls.pop(), `has ${objectName}.dateUntil`);
+ assert.sameValue(calls.pop(), `has ${objectName}.dateFromFields`);
+ assert.sameValue(calls.pop(), `has ${objectName}.dateAdd`);
+ }
+
+ const iso8601 = new Temporal.Calendar("iso8601");
+ const trackingMethods = {
+ dateFromFields(...args) {
+ calls.push(`call ${objectName}.dateFromFields`);
+ if ('dateFromFields' in methodOverrides) {
+ const value = methodOverrides.dateFromFields;
+ return typeof value === "function" ? value(...args) : value;
+ }
+ const originalResult = iso8601.dateFromFields(...args);
+ // Replace the calendar in the result with the call-tracking calendar
+ const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
+ const result = new Temporal.PlainDate(isoYear, isoMonth, isoDay, this);
+ removeExtraHasPropertyChecks(objectName, calls);
+ return result;
+ },
+ yearMonthFromFields(...args) {
+ calls.push(`call ${objectName}.yearMonthFromFields`);
+ if ('yearMonthFromFields' in methodOverrides) {
+ const value = methodOverrides.yearMonthFromFields;
+ return typeof value === "function" ? value(...args) : value;
+ }
+ const originalResult = iso8601.yearMonthFromFields(...args);
+ // Replace the calendar in the result with the call-tracking calendar
+ const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
+ const result = new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay);
+ removeExtraHasPropertyChecks(objectName, calls);
+ return result;
+ },
+ monthDayFromFields(...args) {
+ calls.push(`call ${objectName}.monthDayFromFields`);
+ if ('monthDayFromFields' in methodOverrides) {
+ const value = methodOverrides.monthDayFromFields;
+ return typeof value === "function" ? value(...args) : value;
+ }
+ const originalResult = iso8601.monthDayFromFields(...args);
+ // Replace the calendar in the result with the call-tracking calendar
+ const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
+ const result = new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear);
+ removeExtraHasPropertyChecks(objectName, calls);
+ return result;
+ },
+ dateAdd(...args) {
+ calls.push(`call ${objectName}.dateAdd`);
+ if ('dateAdd' in methodOverrides) {
+ const value = methodOverrides.dateAdd;
+ return typeof value === "function" ? value(...args) : value;
+ }
+ const originalResult = iso8601.dateAdd(...args);
+ const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
+ const result = new Temporal.PlainDate(isoYear, isoMonth, isoDay, this);
+ removeExtraHasPropertyChecks(objectName, calls);
+ return result;
+ },
+ id: "iso8601",
+ };
+ // Automatically generate the other methods that don't need any custom code
+ [
+ "dateUntil",
+ "day",
+ "dayOfWeek",
+ "dayOfYear",
+ "daysInMonth",
+ "daysInWeek",
+ "daysInYear",
+ "era",
+ "eraYear",
+ "fields",
+ "inLeapYear",
+ "mergeFields",
+ "month",
+ "monthCode",
+ "monthsInYear",
+ "toString",
+ "weekOfYear",
+ "year",
+ "yearOfWeek",
+ ].forEach((methodName) => {
+ trackingMethods[methodName] = function (...args) {
+ calls.push(`call ${formatPropertyName(methodName, objectName)}`);
+ if (methodName in methodOverrides) {
+ const value = methodOverrides[methodName];
+ return typeof value === "function" ? value(...args) : value;
+ }
+ return iso8601[methodName](...args);
+ };
+ });
+ return new Proxy(trackingMethods, {
+ get(target, key, receiver) {
+ const result = Reflect.get(target, key, receiver);
+ calls.push(`get ${formatPropertyName(key, objectName)}`);
+ return result;
+ },
+ has(target, key) {
+ calls.push(`has ${formatPropertyName(key, objectName)}`);
+ return Reflect.has(target, key);
+ },
+ });
+ },
+
+ /*
+ * A custom calendar that does not allow any of its methods to be called, for
+ * the purpose of asserting that a particular operation does not call into
+ * user code.
+ */
+ calendarThrowEverything() {
+ class CalendarThrowEverything extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ toString() {
+ TemporalHelpers.assertUnreachable("toString should not be called");
+ }
+ dateFromFields() {
+ TemporalHelpers.assertUnreachable("dateFromFields should not be called");
+ }
+ yearMonthFromFields() {
+ TemporalHelpers.assertUnreachable("yearMonthFromFields should not be called");
+ }
+ monthDayFromFields() {
+ TemporalHelpers.assertUnreachable("monthDayFromFields should not be called");
+ }
+ dateAdd() {
+ TemporalHelpers.assertUnreachable("dateAdd should not be called");
+ }
+ dateUntil() {
+ TemporalHelpers.assertUnreachable("dateUntil should not be called");
+ }
+ era() {
+ TemporalHelpers.assertUnreachable("era should not be called");
+ }
+ eraYear() {
+ TemporalHelpers.assertUnreachable("eraYear should not be called");
+ }
+ year() {
+ TemporalHelpers.assertUnreachable("year should not be called");
+ }
+ month() {
+ TemporalHelpers.assertUnreachable("month should not be called");
+ }
+ monthCode() {
+ TemporalHelpers.assertUnreachable("monthCode should not be called");
+ }
+ day() {
+ TemporalHelpers.assertUnreachable("day should not be called");
+ }
+ fields() {
+ TemporalHelpers.assertUnreachable("fields should not be called");
+ }
+ mergeFields() {
+ TemporalHelpers.assertUnreachable("mergeFields should not be called");
+ }
+ }
+
+ return new CalendarThrowEverything();
+ },
+
+ /*
+ * oneShiftTimeZone(shiftInstant, shiftNanoseconds):
+ *
+ * In the case of a spring-forward time zone offset transition (skipped time),
+ * and disambiguation === 'earlier', BuiltinTimeZoneGetInstantFor subtracts a
+ * negative number of nanoseconds from a PlainDateTime, which should balance
+ * with the microseconds field.
+ *
+ * This returns an instance of a custom time zone class which skips a length
+ * of time equal to shiftNanoseconds (a number), at the Temporal.Instant
+ * shiftInstant. Before shiftInstant, it's identical to UTC, and after
+ * shiftInstant it's a constant-offset time zone.
+ *
+ * It provides a getPossibleInstantsForCalledWith member which is an array
+ * with the result of calling toString() on any PlainDateTimes passed to
+ * getPossibleInstantsFor().
+ */
+ oneShiftTimeZone(shiftInstant, shiftNanoseconds) {
+ class OneShiftTimeZone extends Temporal.TimeZone {
+ constructor(shiftInstant, shiftNanoseconds) {
+ super("+00:00");
+ this._shiftInstant = shiftInstant;
+ this._epoch1 = shiftInstant.epochNanoseconds;
+ this._epoch2 = this._epoch1 + BigInt(shiftNanoseconds);
+ this._shiftNanoseconds = shiftNanoseconds;
+ this._shift = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, this._shiftNanoseconds);
+ this.getPossibleInstantsForCalledWith = [];
+ }
+
+ _isBeforeShift(instant) {
+ return instant.epochNanoseconds < this._epoch1;
+ }
+
+ getOffsetNanosecondsFor(instant) {
+ return this._isBeforeShift(instant) ? 0 : this._shiftNanoseconds;
+ }
+
+ getPossibleInstantsFor(plainDateTime) {
+ this.getPossibleInstantsForCalledWith.push(plainDateTime.toString({ calendarName: "never" }));
+ const [instant] = super.getPossibleInstantsFor(plainDateTime);
+ if (this._shiftNanoseconds > 0) {
+ if (this._isBeforeShift(instant)) return [instant];
+ if (instant.epochNanoseconds < this._epoch2) return [];
+ return [instant.subtract(this._shift)];
+ }
+ if (instant.epochNanoseconds < this._epoch2) return [instant];
+ const shifted = instant.subtract(this._shift);
+ if (this._isBeforeShift(instant)) return [instant, shifted];
+ return [shifted];
+ }
+
+ getNextTransition(instant) {
+ return this._isBeforeShift(instant) ? this._shiftInstant : null;
+ }
+
+ getPreviousTransition(instant) {
+ return this._isBeforeShift(instant) ? null : this._shiftInstant;
+ }
+
+ toString() {
+ return "Custom/One_Shift";
+ }
+ }
+ return new OneShiftTimeZone(shiftInstant, shiftNanoseconds);
+ },
+
+ /*
+ * propertyBagObserver():
+ * Returns an object that behaves like the given propertyBag but tracks Get
+ * and Has operations on any of its properties, by appending messages to an
+ * array. If the value of a property in propertyBag is a primitive, the value
+ * of the returned object's property will additionally be a
+ * TemporalHelpers.toPrimitiveObserver that will track calls to its toString
+ * and valueOf methods in the same array. This is for the purpose of testing
+ * order of operations that are observable from user code. objectName is used
+ * in the log.
+ */
+ propertyBagObserver(calls, propertyBag, objectName) {
+ return new Proxy(propertyBag, {
+ ownKeys(target) {
+ calls.push(`ownKeys ${objectName}`);
+ return Reflect.ownKeys(target);
+ },
+ getOwnPropertyDescriptor(target, key) {
+ calls.push(`getOwnPropertyDescriptor ${formatPropertyName(key, objectName)}`);
+ return Reflect.getOwnPropertyDescriptor(target, key);
+ },
+ get(target, key, receiver) {
+ calls.push(`get ${formatPropertyName(key, objectName)}`);
+ const result = Reflect.get(target, key, receiver);
+ if (result === undefined) {
+ return undefined;
+ }
+ if ((result !== null && typeof result === "object") || typeof result === "function") {
+ return result;
+ }
+ return TemporalHelpers.toPrimitiveObserver(calls, result, `${formatPropertyName(key, objectName)}`);
+ },
+ has(target, key) {
+ calls.push(`has ${formatPropertyName(key, objectName)}`);
+ return Reflect.has(target, key);
+ },
+ });
+ },
+
+ /*
+ * specificOffsetTimeZone():
+ *
+ * This returns an instance of a custom time zone class, which returns a
+ * specific custom value from its getOffsetNanosecondsFrom() method. This is
+ * for the purpose of testing the validation of what this method returns.
+ *
+ * It also returns an empty array from getPossibleInstantsFor(), so as to
+ * trigger calls to getOffsetNanosecondsFor() when used from the
+ * BuiltinTimeZoneGetInstantFor operation.
+ */
+ specificOffsetTimeZone(offsetValue) {
+ class SpecificOffsetTimeZone extends Temporal.TimeZone {
+ constructor(offsetValue) {
+ super("UTC");
+ this._offsetValue = offsetValue;
+ }
+
+ getOffsetNanosecondsFor() {
+ return this._offsetValue;
+ }
+
+ getPossibleInstantsFor(dt) {
+ if (typeof this._offsetValue !== 'number' || Math.abs(this._offsetValue) >= 86400e9 || isNaN(this._offsetValue)) return [];
+ const zdt = dt.toZonedDateTime("UTC").add({ nanoseconds: -this._offsetValue });
+ return [zdt.toInstant()];
+ }
+
+ get id() {
+ return this.getOffsetStringFor(new Temporal.Instant(0n));
+ }
+ }
+ return new SpecificOffsetTimeZone(offsetValue);
+ },
+
+ /*
+ * springForwardFallBackTimeZone():
+ *
+ * This returns an instance of a custom time zone class that implements one
+ * single spring-forward/fall-back transition, for the purpose of testing the
+ * disambiguation option, without depending on system time zone data.
+ *
+ * The spring-forward occurs at epoch second 954669600 (2000-04-02T02:00
+ * local) and goes from offset -08:00 to -07:00.
+ *
+ * The fall-back occurs at epoch second 972810000 (2000-10-29T02:00 local) and
+ * goes from offset -07:00 to -08:00.
+ */
+ springForwardFallBackTimeZone() {
+ const { compare } = Temporal.PlainDateTime;
+ const springForwardLocal = new Temporal.PlainDateTime(2000, 4, 2, 2);
+ const springForwardEpoch = 954669600_000_000_000n;
+ const fallBackLocal = new Temporal.PlainDateTime(2000, 10, 29, 1);
+ const fallBackEpoch = 972810000_000_000_000n;
+ const winterOffset = new Temporal.TimeZone('-08:00');
+ const summerOffset = new Temporal.TimeZone('-07:00');
+
+ class SpringForwardFallBackTimeZone extends Temporal.TimeZone {
+ constructor() {
+ super("-08:00");
+ }
+
+ getOffsetNanosecondsFor(instant) {
+ if (instant.epochNanoseconds < springForwardEpoch ||
+ instant.epochNanoseconds >= fallBackEpoch) {
+ return winterOffset.getOffsetNanosecondsFor(instant);
+ }
+ return summerOffset.getOffsetNanosecondsFor(instant);
+ }
+
+ getPossibleInstantsFor(datetime) {
+ if (compare(datetime, springForwardLocal) >= 0 && compare(datetime, springForwardLocal.add({ hours: 1 })) < 0) {
+ return [];
+ }
+ if (compare(datetime, fallBackLocal) >= 0 && compare(datetime, fallBackLocal.add({ hours: 1 })) < 0) {
+ return [summerOffset.getInstantFor(datetime), winterOffset.getInstantFor(datetime)];
+ }
+ if (compare(datetime, springForwardLocal) < 0 || compare(datetime, fallBackLocal) >= 0) {
+ return [winterOffset.getInstantFor(datetime)];
+ }
+ return [summerOffset.getInstantFor(datetime)];
+ }
+
+ getPreviousTransition(instant) {
+ if (instant.epochNanoseconds > fallBackEpoch) return new Temporal.Instant(fallBackEpoch);
+ if (instant.epochNanoseconds > springForwardEpoch) return new Temporal.Instant(springForwardEpoch);
+ return null;
+ }
+
+ getNextTransition(instant) {
+ if (instant.epochNanoseconds < springForwardEpoch) return new Temporal.Instant(springForwardEpoch);
+ if (instant.epochNanoseconds < fallBackEpoch) return new Temporal.Instant(fallBackEpoch);
+ return null;
+ }
+
+ get id() {
+ return "Custom/Spring_Fall";
+ }
+
+ toString() {
+ return "Custom/Spring_Fall";
+ }
+ }
+ return new SpringForwardFallBackTimeZone();
+ },
+
+ /*
+ * timeZoneObserver:
+ * A custom calendar that behaves exactly like the UTC time zone but tracks
+ * calls to any of its methods, and Get/Has operations on its properties, by
+ * appending messages to an array. This is for the purpose of testing order of
+ * operations that are observable from user code. objectName is used in the
+ * log. methodOverrides is an optional object containing properties with the
+ * same name as Temporal.TimeZone methods. If the property value is a function
+ * it will be called with the proper arguments instead of the UTC method.
+ * Otherwise, the property value will be returned directly.
+ */
+ timeZoneObserver(calls, objectName, methodOverrides = {}) {
+ const utc = new Temporal.TimeZone("UTC");
+ const trackingMethods = {
+ id: "UTC",
+ };
+ // Automatically generate the methods
+ ["getOffsetNanosecondsFor", "getPossibleInstantsFor", "toString"].forEach((methodName) => {
+ trackingMethods[methodName] = function (...args) {
+ calls.push(`call ${formatPropertyName(methodName, objectName)}`);
+ if (methodName in methodOverrides) {
+ const value = methodOverrides[methodName];
+ return typeof value === "function" ? value(...args) : value;
+ }
+ return utc[methodName](...args);
+ };
+ });
+ return new Proxy(trackingMethods, {
+ get(target, key, receiver) {
+ const result = Reflect.get(target, key, receiver);
+ calls.push(`get ${formatPropertyName(key, objectName)}`);
+ return result;
+ },
+ has(target, key) {
+ calls.push(`has ${formatPropertyName(key, objectName)}`);
+ return Reflect.has(target, key);
+ },
+ });
+ },
+
+ /*
+ * A custom time zone that does not allow any of its methods to be called, for
+ * the purpose of asserting that a particular operation does not call into
+ * user code.
+ */
+ timeZoneThrowEverything() {
+ class TimeZoneThrowEverything extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ }
+ getOffsetNanosecondsFor() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be called");
+ }
+ getPossibleInstantsFor() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be called");
+ }
+ toString() {
+ TemporalHelpers.assertUnreachable("toString should not be called");
+ }
+ }
+
+ return new TimeZoneThrowEverything();
+ },
+
+ /*
+ * Returns an object that will append logs of any Gets or Calls of its valueOf
+ * or toString properties to the array calls. Both valueOf and toString will
+ * return the actual primitiveValue. propertyName is used in the log.
+ */
+ toPrimitiveObserver(calls, primitiveValue, propertyName) {
+ return {
+ get valueOf() {
+ calls.push(`get ${propertyName}.valueOf`);
+ return function () {
+ calls.push(`call ${propertyName}.valueOf`);
+ return primitiveValue;
+ };
+ },
+ get toString() {
+ calls.push(`get ${propertyName}.toString`);
+ return function () {
+ calls.push(`call ${propertyName}.toString`);
+ if (primitiveValue === undefined) return undefined;
+ return primitiveValue.toString();
+ };
+ },
+ };
+ },
+
+ /*
+ * An object containing further methods that return arrays of ISO strings, for
+ * testing parsers.
+ */
+ ISO: {
+ /*
+ * PlainMonthDay strings that are not valid.
+ */
+ plainMonthDayStringsInvalid() {
+ return [
+ "11-18junk",
+ "11-18[u-ca=gregory]",
+ "11-18[u-ca=hebrew]",
+ ];
+ },
+
+ /*
+ * PlainMonthDay strings that are valid and that should produce October 1st.
+ */
+ plainMonthDayStringsValid() {
+ return [
+ "10-01",
+ "1001",
+ "1965-10-01",
+ "1976-10-01T152330.1+00:00",
+ "19761001T15:23:30.1+00:00",
+ "1976-10-01T15:23:30.1+0000",
+ "1976-10-01T152330.1+0000",
+ "19761001T15:23:30.1+0000",
+ "19761001T152330.1+00:00",
+ "19761001T152330.1+0000",
+ "+001976-10-01T152330.1+00:00",
+ "+0019761001T15:23:30.1+00:00",
+ "+001976-10-01T15:23:30.1+0000",
+ "+001976-10-01T152330.1+0000",
+ "+0019761001T15:23:30.1+0000",
+ "+0019761001T152330.1+00:00",
+ "+0019761001T152330.1+0000",
+ "1976-10-01T15:23:00",
+ "1976-10-01T15:23",
+ "1976-10-01T15",
+ "1976-10-01",
+ "--10-01",
+ "--1001",
+ ];
+ },
+
+ /*
+ * PlainTime strings that may be mistaken for PlainMonthDay or
+ * PlainYearMonth strings, and so require a time designator.
+ */
+ plainTimeStringsAmbiguous() {
+ const ambiguousStrings = [
+ "2021-12", // ambiguity between YYYY-MM and HHMM-UU
+ "2021-12[-12:00]", // ditto, TZ does not disambiguate
+ "1214", // ambiguity between MMDD and HHMM
+ "0229", // ditto, including MMDD that doesn't occur every year
+ "1130", // ditto, including DD that doesn't occur in every month
+ "12-14", // ambiguity between MM-DD and HH-UU
+ "12-14[-14:00]", // ditto, TZ does not disambiguate
+ "202112", // ambiguity between YYYYMM and HHMMSS
+ "202112[UTC]", // ditto, TZ does not disambiguate
+ ];
+ // Adding a calendar annotation to one of these strings must not cause
+ // disambiguation in favour of time.
+ const stringsWithCalendar = ambiguousStrings.map((s) => s + '[u-ca=iso8601]');
+ return ambiguousStrings.concat(stringsWithCalendar);
+ },
+
+ /*
+ * PlainTime strings that are of similar form to PlainMonthDay and
+ * PlainYearMonth strings, but are not ambiguous due to components that
+ * aren't valid as months or days.
+ */
+ plainTimeStringsUnambiguous() {
+ return [
+ "2021-13", // 13 is not a month
+ "202113", // ditto
+ "2021-13[-13:00]", // ditto
+ "202113[-13:00]", // ditto
+ "0000-00", // 0 is not a month
+ "000000", // ditto
+ "0000-00[UTC]", // ditto
+ "000000[UTC]", // ditto
+ "1314", // 13 is not a month
+ "13-14", // ditto
+ "1232", // 32 is not a day
+ "0230", // 30 is not a day in February
+ "0631", // 31 is not a day in June
+ "0000", // 0 is neither a month nor a day
+ "00-00", // ditto
+ ];
+ },
+
+ /*
+ * PlainYearMonth-like strings that are not valid.
+ */
+ plainYearMonthStringsInvalid() {
+ return [
+ "2020-13",
+ ];
+ },
+
+ /*
+ * PlainYearMonth-like strings that are valid and should produce November
+ * 1976 in the ISO 8601 calendar.
+ */
+ plainYearMonthStringsValid() {
+ return [
+ "1976-11",
+ "1976-11-10",
+ "1976-11-01T09:00:00+00:00",
+ "1976-11-01T00:00:00+05:00",
+ "197611",
+ "+00197611",
+ "1976-11-18T15:23:30.1\u221202:00",
+ "1976-11-18T152330.1+00:00",
+ "19761118T15:23:30.1+00:00",
+ "1976-11-18T15:23:30.1+0000",
+ "1976-11-18T152330.1+0000",
+ "19761118T15:23:30.1+0000",
+ "19761118T152330.1+00:00",
+ "19761118T152330.1+0000",
+ "+001976-11-18T152330.1+00:00",
+ "+0019761118T15:23:30.1+00:00",
+ "+001976-11-18T15:23:30.1+0000",
+ "+001976-11-18T152330.1+0000",
+ "+0019761118T15:23:30.1+0000",
+ "+0019761118T152330.1+00:00",
+ "+0019761118T152330.1+0000",
+ "1976-11-18T15:23",
+ "1976-11-18T15",
+ "1976-11-18",
+ ];
+ },
+
+ /*
+ * PlainYearMonth-like strings that are valid and should produce November of
+ * the ISO year -9999.
+ */
+ plainYearMonthStringsValidNegativeYear() {
+ return [
+ "\u2212009999-11",
+ ];
+ },
+ }
+};
diff --git a/js/src/tests/test262/staging/Temporal/Duration/old/subtract.js b/js/src/tests/test262/staging/Temporal/Duration/old/subtract.js
new file mode 100644
index 0000000000..619dd54e9b
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Duration/old/subtract.js
@@ -0,0 +1,105 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-duration-objects
+description: Temporal.Duration.prototype.subtract() works as expected
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+var oneDay = new Temporal.Duration(0, 0, 0, 1);
+var hours24 = new Temporal.Duration(0, 0, 0, 0, 24);
+
+// relativeTo does not affect days if PlainDate
+var relativeTo = Temporal.PlainDate.from("2017-01-01");
+assert.sameValue(`${ oneDay.subtract(hours24, { relativeTo }) }`, "PT0S");
+
+// relativeTo does not affect days if ZonedDateTime, and duration encompasses no DST change
+var relativeTo = Temporal.ZonedDateTime.from("2017-01-01T00:00[+04:30]");
+assert.sameValue(`${ oneDay.subtract(hours24, { relativeTo }) }`, "PT0S");
+
+// relativeTo affects days if ZonedDateTime, and duration encompasses DST change
+var timeZone = TemporalHelpers.springForwardFallBackTimeZone();
+var skippedHourDay = Temporal.PlainDateTime.from("2000-04-02").toZonedDateTime(timeZone);
+var repeatedHourDay = Temporal.PlainDateTime.from("2000-10-29").toZonedDateTime(timeZone);
+var inRepeatedHour = new Temporal.ZonedDateTime(972806400_000_000_000n, timeZone);
+var twoDays = new Temporal.Duration(0, 0, 0, 2);
+var threeDays = new Temporal.Duration(0, 0, 0, 3);
+
+// start inside repeated hour, end after
+assert.sameValue(`${ hours24.subtract(oneDay, { relativeTo: inRepeatedHour }) }`, "-PT1H");
+assert.sameValue(`${ oneDay.subtract(hours24, { relativeTo: inRepeatedHour }) }`, "PT1H");
+
+// start in normal hour, end in skipped hour
+var relativeTo = Temporal.PlainDateTime.from("2000-04-01T02:30").toZonedDateTime(timeZone);
+assert.sameValue(`${ hours24.subtract(oneDay, { relativeTo }) }`, "PT1H");
+assert.sameValue(`${ oneDay.subtract(hours24, { relativeTo }) }`, "PT0S");
+
+// start before skipped hour, end >1 day after
+assert.sameValue(`${ threeDays.subtract(hours24, { relativeTo: skippedHourDay }) }`, "P2D");
+assert.sameValue(`${ hours24.subtract(threeDays, { relativeTo: skippedHourDay }) }`, "-P1DT23H");
+
+// start before skipped hour, end <1 day after
+assert.sameValue(`${ twoDays.subtract(hours24, { relativeTo: skippedHourDay }) }`, "P1D");
+assert.sameValue(`${ hours24.subtract(twoDays, { relativeTo: skippedHourDay }) }`, "-PT23H");
+
+// start before repeated hour, end >1 day after
+assert.sameValue(`${ threeDays.subtract(hours24, { relativeTo: repeatedHourDay }) }`, "P2D");
+assert.sameValue(`${ hours24.subtract(threeDays, { relativeTo: repeatedHourDay }) }`, "-P2DT1H");
+
+// start before repeated hour, end <1 day after
+assert.sameValue(`${ twoDays.subtract(hours24, { relativeTo: repeatedHourDay }) }`, "P1D");
+assert.sameValue(`${ hours24.subtract(twoDays, { relativeTo: repeatedHourDay }) }`, "-P1DT1H");
+
+// Samoa skipped 24 hours
+var fakeSamoa = TemporalHelpers.crossDateLineTimeZone();
+var relativeTo = Temporal.PlainDateTime.from("2011-12-29T12:00").toZonedDateTime(fakeSamoa);
+assert.sameValue(`${ twoDays.subtract(Temporal.Duration.from({ hours: 48 }), { relativeTo }) }`, "-P1D");
+assert.sameValue(`${ Temporal.Duration.from({ hours: 48 }).subtract(twoDays, { relativeTo }) }`, "P2D");
+
+// casts relativeTo to ZonedDateTime if possible
+assert.sameValue(`${ oneDay.subtract(hours24, {
+ relativeTo: {
+ year: 2000,
+ month: 10,
+ day: 29,
+ timeZone
+ }
+}) }`, "PT1H");
+
+// casts relativeTo to PlainDate if possible
+assert.sameValue(`${ oneDay.subtract(hours24, { relativeTo: "2019-11-02" }) }`, "PT0S");
+assert.sameValue(`${ oneDay.subtract(hours24, {
+ relativeTo: {
+ year: 2019,
+ month: 11,
+ day: 2
+ }
+}) }`, "PT0S");
+
+// throws on wrong offset for ZonedDateTime relativeTo string
+assert.throws(RangeError, () => oneDay.subtract(hours24, { relativeTo: "1971-01-01T00:00+02:00[-00:44:30]" }));
+
+// at least the required properties must be present in relativeTo
+assert.throws(TypeError, () => oneDay.subtract(hours24, {
+ relativeTo: {
+ month: 11,
+ day: 3
+ }
+}));
+assert.throws(TypeError, () => oneDay.subtract(hours24, {
+ relativeTo: {
+ year: 2019,
+ month: 11
+ }
+}));
+assert.throws(TypeError, () => oneDay.subtract(hours24, {
+ relativeTo: {
+ year: 2019,
+ day: 3
+ }
+}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/Duration/old/toString.js b/js/src/tests/test262/staging/Temporal/Duration/old/toString.js
new file mode 100644
index 0000000000..1f7e91fb06
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Duration/old/toString.js
@@ -0,0 +1,18 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-duration-objects
+description: toString() works as expected
+features: [Temporal]
+---*/
+
+// serializing balance doesn't lose precision when values are precise
+var d = Temporal.Duration.from({
+ milliseconds: Number.MAX_SAFE_INTEGER,
+ microseconds: Number.MAX_SAFE_INTEGER
+});
+assert.sameValue(`${ d }`, "PT9016206453995.731991S");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/Duration/old/total.js b/js/src/tests/test262/staging/Temporal/Duration/old/total.js
new file mode 100644
index 0000000000..75f4d526db
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Duration/old/total.js
@@ -0,0 +1,453 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-duration-objects
+description: Temporal.Duration.prototype.total()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+var d = new Temporal.Duration(5, 5, 5, 5, 5, 5, 5, 5, 5, 5);
+var d2 = new Temporal.Duration(0, 0, 0, 5, 5, 5, 5, 5, 5, 5);
+var relativeTo = Temporal.PlainDate.from("2020-01-01");
+
+// throws on disallowed or invalid unit (object param)
+[
+ "era",
+ "nonsense"
+].forEach(unit => {
+ assert.throws(RangeError, () => d.total({ unit }));
+});
+
+// throws on disallowed or invalid unit (string param)
+[
+ "era",
+ "nonsense"
+].forEach(unit => {
+ assert.throws(RangeError, () => d.total(unit));
+});
+
+// does not lose precision for seconds and smaller units
+var s = Temporal.Duration.from({
+ milliseconds: 2,
+ microseconds: 31
+}).total({ unit: "seconds" });
+assert.sameValue(s, 0.002031);
+
+// accepts datetime strings or fields for relativeTo
+[
+ "2020-01-01",
+ "20200101",
+ "2020-01-01T00:00:00.000000000",
+ {
+ year: 2020,
+ month: 1,
+ day: 1
+ }
+].forEach(relativeTo => {
+ var daysPastJuly1 = 5 * 7 + 5 - 30;
+ var partialDayNanos = d.hours * 3600000000000 + d.minutes * 60000000000 + d.seconds * 1000000000 + d.milliseconds * 1000000 + d.microseconds * 1000 + d.nanoseconds;
+ var partialDay = partialDayNanos / (3600000000000 * 24);
+ var partialMonth = (daysPastJuly1 + partialDay) / 31;
+ var totalMonths = 5 * 12 + 5 + 1 + partialMonth;
+ var total = d.total({
+ unit: "months",
+ relativeTo
+ });
+ assert.sameValue(total.toPrecision(15), totalMonths.toPrecision(15));
+});
+
+// does not accept non-string primitives for relativeTo
+[
+ 20200101,
+ 20200101n,
+ null,
+ true,
+].forEach(relativeTo => {
+ assert.throws(
+ TypeError, () => d.total({ unit: "months", relativeTo})
+ );
+});
+
+// throws on wrong offset for ZonedDateTime relativeTo string
+assert.throws(RangeError, () => d.total({
+ unit: "months",
+ relativeTo: "1971-01-01T00:00+02:00[-00:44:30]"
+}));
+
+// relativeTo object must contain at least the required correctly-spelled properties
+assert.throws(TypeError, () => d.total({
+ unit: "months",
+ relativeTo: {}
+}));
+assert.throws(TypeError, () => d.total({
+ unit: "months",
+ relativeTo: {
+ years: 2020,
+ month: 1,
+ day: 1
+ }
+}));
+
+// incorrectly-spelled properties are ignored in relativeTo
+var oneMonth = Temporal.Duration.from({ months: 1 });
+assert.sameValue(oneMonth.total({
+ unit: "months",
+ relativeTo: {
+ year: 2020,
+ month: 1,
+ day: 1,
+ months: 2
+ }
+}), 1);
+
+// throws RangeError if unit property is missing
+[
+ {},
+ () => {
+ },
+ { roundingMode: "ceil" }
+].forEach(roundTo => assert.throws(RangeError, () => d.total(roundTo)));
+
+// relativeTo required to round calendar units even in durations w/o calendar units (object param)
+assert.throws(RangeError, () => d2.total({ unit: "years" }));
+assert.throws(RangeError, () => d2.total({ unit: "months" }));
+assert.throws(RangeError, () => d2.total({ unit: "weeks" }));
+
+// relativeTo required to round calendar units even in durations w/o calendar units (string param)
+assert.throws(RangeError, () => d2.total("years"));
+assert.throws(RangeError, () => d2.total("months"));
+assert.throws(RangeError, () => d2.total("weeks"));
+
+// relativeTo is required to round durations with calendar units (object param)
+assert.throws(RangeError, () => d.total({ unit: "years" }));
+assert.throws(RangeError, () => d.total({ unit: "months" }));
+assert.throws(RangeError, () => d.total({ unit: "weeks" }));
+assert.throws(RangeError, () => d.total({ unit: "days" }));
+assert.throws(RangeError, () => d.total({ unit: "hours" }));
+assert.throws(RangeError, () => d.total({ unit: "minutes" }));
+assert.throws(RangeError, () => d.total({ unit: "seconds" }));
+assert.throws(RangeError, () => d.total({ unit: "milliseconds" }));
+assert.throws(RangeError, () => d.total({ unit: "microseconds" }));
+assert.throws(RangeError, () => d.total({ unit: "nanoseconds" }));
+
+// relativeTo is required to round durations with calendar units (string param)
+assert.throws(RangeError, () => d.total("years"));
+assert.throws(RangeError, () => d.total("months"));
+assert.throws(RangeError, () => d.total("weeks"));
+assert.throws(RangeError, () => d.total("days"));
+assert.throws(RangeError, () => d.total("hours"));
+assert.throws(RangeError, () => d.total("minutes"));
+assert.throws(RangeError, () => d.total("seconds"));
+assert.throws(RangeError, () => d.total("milliseconds"));
+assert.throws(RangeError, () => d.total("microseconds"));
+assert.throws(RangeError, () => d.total("nanoseconds"));
+var d2Nanoseconds = d2.days * 24 * 3600000000000 + d2.hours * 3600000000000 + d2.minutes * 60000000000 + d2.seconds * 1000000000 + d2.milliseconds * 1000000 + d2.microseconds * 1000 + d2.nanoseconds;
+var totalD2 = {
+ days: d2Nanoseconds / (24 * 3600000000000),
+ hours: d2Nanoseconds / 3600000000000,
+ minutes: d2Nanoseconds / 60000000000,
+ seconds: d2Nanoseconds / 1000000000,
+ milliseconds: d2Nanoseconds / 1000000,
+ microseconds: d2Nanoseconds / 1000,
+ nanoseconds: d2Nanoseconds
+};
+
+// relativeTo not required to round fixed-length units in durations without variable units
+assert(Math.abs(d2.total({ unit: "days" }) - totalD2.days) < Number.EPSILON);
+assert(Math.abs(d2.total({ unit: "hours" }) - totalD2.hours) < Number.EPSILON);
+assert(Math.abs(d2.total({ unit: "minutes" }) - totalD2.minutes) < Number.EPSILON);
+assert(Math.abs(d2.total({ unit: "seconds" }) - totalD2.seconds) < Number.EPSILON);
+assert(Math.abs(d2.total({ unit: "milliseconds" }) - totalD2.milliseconds) < Number.EPSILON);
+assert(Math.abs(d2.total({ unit: "microseconds" }) - totalD2.microseconds) < Number.EPSILON);
+assert.sameValue(d2.total({ unit: "nanoseconds" }), totalD2.nanoseconds);
+
+// relativeTo not required to round fixed-length units in durations without variable units (negative)
+var negativeD2 = d2.negated();
+assert(Math.abs(negativeD2.total({ unit: "days" }) - -totalD2.days) < Number.EPSILON);
+assert(Math.abs(negativeD2.total({ unit: "hours" }) - -totalD2.hours) < Number.EPSILON);
+assert(Math.abs(negativeD2.total({ unit: "minutes" }) - -totalD2.minutes) < Number.EPSILON);
+assert(Math.abs(negativeD2.total({ unit: "seconds" }) - -totalD2.seconds) < Number.EPSILON);
+assert(Math.abs(negativeD2.total({ unit: "milliseconds" }) - -totalD2.milliseconds) < Number.EPSILON);
+assert(Math.abs(negativeD2.total({ unit: "microseconds" }) - -totalD2.microseconds) < Number.EPSILON);
+assert.sameValue(negativeD2.total({ unit: "nanoseconds" }), -totalD2.nanoseconds);
+var endpoint = relativeTo.toPlainDateTime().add(d);
+var options = unit => ({
+ largestUnit: unit,
+ smallestUnit: unit,
+ roundingMode: "trunc"
+});
+var fullYears = 5;
+var fullDays = endpoint.since(relativeTo, options("days")).days;
+var fullMilliseconds = endpoint.since(relativeTo, options("milliseconds")).milliseconds;
+var partialDayMilliseconds = fullMilliseconds - fullDays * 24 * 3600000 + 0.005005;
+var fractionalDay = partialDayMilliseconds / (24 * 3600000);
+var partialYearDays = fullDays - (fullYears * 365 + 2);
+var fractionalYear = partialYearDays / 365 + fractionalDay / 365;
+var fractionalMonths = ((endpoint.day - 1) * (24 * 3600000) + partialDayMilliseconds) / (31 * 24 * 3600000);
+var totalResults = {
+ years: fullYears + fractionalYear,
+ months: 66 + fractionalMonths,
+ weeks: (fullDays + fractionalDay) / 7,
+ days: fullDays + fractionalDay,
+ hours: fullDays * 24 + partialDayMilliseconds / 3600000,
+ minutes: fullDays * 24 * 60 + partialDayMilliseconds / 60000,
+ seconds: fullDays * 24 * 60 * 60 + partialDayMilliseconds / 1000,
+ milliseconds: fullMilliseconds + 0.005005,
+ microseconds: fullMilliseconds * 1000 + 5.005,
+ nanoseconds: fullMilliseconds * 1000000 + 5005
+};
+for (var [unit, expected] of Object.entries(totalResults)) {
+ assert.sameValue(d.total({
+ unit,
+ relativeTo
+ }).toPrecision(15), expected.toPrecision(15));
+}
+for (var unit of [
+ "microseconds",
+ "nanoseconds"
+ ]) {
+ assert(d.total({
+ unit,
+ relativeTo
+ }).toString().startsWith("174373505005"));
+}
+
+// balances differently depending on relativeTo
+var fortyDays = Temporal.Duration.from({ days: 40 });
+assert.sameValue(fortyDays.total({
+ unit: "months",
+ relativeTo: "2020-02-01"
+}).toPrecision(16), (1 + 11 / 31).toPrecision(16));
+assert.sameValue(fortyDays.total({
+ unit: "months",
+ relativeTo: "2020-01-01"
+}).toPrecision(16), (1 + 9 / 29).toPrecision(16));
+
+// balances differently depending on relativeTo (negative)
+var negativeFortyDays = Temporal.Duration.from({ days: -40 });
+assert.sameValue(negativeFortyDays.total({
+ unit: "months",
+ relativeTo: "2020-03-01"
+}).toPrecision(16), (-(1 + 11 / 31)).toPrecision(16));
+assert.sameValue(negativeFortyDays.total({
+ unit: "months",
+ relativeTo: "2020-04-01"
+}).toPrecision(16), (-(1 + 9 / 29)).toPrecision(16));
+
+var oneDay = new Temporal.Duration(0, 0, 0, 1);
+// relativeTo does not affect days if PlainDate
+var relativeTo = Temporal.PlainDate.from("2017-01-01");
+assert.sameValue(oneDay.total({
+ unit: "hours",
+ relativeTo
+}), 24);
+
+// relativeTo does not affect days if ZonedDateTime, and duration encompasses no DST change
+var relativeTo = Temporal.ZonedDateTime.from("2017-01-01T00:00[+04:30]");
+assert.sameValue(oneDay.total({
+ unit: "hours",
+ relativeTo
+}), 24);
+
+// relativeTo affects days if ZonedDateTime, and duration encompasses DST change"
+var timeZone = TemporalHelpers.springForwardFallBackTimeZone();
+var skippedHourDay = Temporal.PlainDateTime.from("2000-04-02").toZonedDateTime(timeZone);
+var repeatedHourDay = Temporal.PlainDateTime.from("2000-10-29").toZonedDateTime(timeZone);
+var inRepeatedHour = new Temporal.ZonedDateTime(972806400_000_000_000n, timeZone);
+var hours12 = new Temporal.Duration(0, 0, 0, 0, 12);
+var hours25 = new Temporal.Duration(0, 0, 0, 0, 25);
+
+// start inside repeated hour, end after
+assert.sameValue(hours25.total({
+ unit: "days",
+ relativeTo: inRepeatedHour
+}), 1);
+assert.sameValue(oneDay.total({
+ unit: "hours",
+ relativeTo: inRepeatedHour
+}), 25);
+
+// start after repeated hour, end inside (negative)
+var relativeTo = Temporal.PlainDateTime.from("2000-10-30T01:00").toZonedDateTime(timeZone);
+assert.sameValue(hours25.negated().total({
+ unit: "days",
+ relativeTo
+}), -1);
+assert.sameValue(oneDay.negated().total({
+ unit: "hours",
+ relativeTo
+}), -25);
+
+// start in normal hour, end in skipped hour
+var relativeTo = Temporal.PlainDateTime.from("2000-04-01T02:30").toZonedDateTime(timeZone);
+var totalDays = hours25.total({
+ unit: "days",
+ relativeTo
+});
+assert(Math.abs(totalDays - (1 + 1 / 24)) < Number.EPSILON);
+assert.sameValue(oneDay.total({
+ unit: "hours",
+ relativeTo
+}), 24);
+
+// start before skipped hour, end >1 day after
+var totalDays = hours25.total({
+ unit: "days",
+ relativeTo: skippedHourDay
+});
+assert(Math.abs(totalDays - (1 + 2 / 24)) < Number.EPSILON);
+assert.sameValue(oneDay.total({
+ unit: "hours",
+ relativeTo: skippedHourDay
+}), 23);
+
+// start after skipped hour, end >1 day before (negative)
+var relativeTo = Temporal.PlainDateTime.from("2000-04-03T00:00").toZonedDateTime(timeZone);
+var totalDays = hours25.negated().total({
+ unit: "days",
+ relativeTo
+});
+assert(Math.abs(totalDays - (-1 - 2 / 24)) < Number.EPSILON);
+assert.sameValue(oneDay.negated().total({
+ unit: "hours",
+ relativeTo
+}), -23);
+
+// start before skipped hour, end <1 day after
+var totalDays = hours12.total({
+ unit: "days",
+ relativeTo: skippedHourDay
+});
+assert(Math.abs(totalDays - 12 / 23) < Number.EPSILON);
+
+// start after skipped hour, end <1 day before (negative)
+var relativeTo = Temporal.PlainDateTime.from("2000-04-02T12:00").toZonedDateTime(timeZone);
+var totalDays = hours12.negated().total({
+ unit: "days",
+ relativeTo
+});
+assert(Math.abs(totalDays - -12 / 23) < Number.EPSILON);
+
+// start before repeated hour, end >1 day after
+assert.sameValue(hours25.total({
+ unit: "days",
+ relativeTo: repeatedHourDay
+}), 1);
+assert.sameValue(oneDay.total({
+ unit: "hours",
+ relativeTo: repeatedHourDay
+}), 25);
+
+// start after repeated hour, end >1 day before (negative)
+var relativeTo = Temporal.PlainDateTime.from("2000-10-30T00:00").toZonedDateTime(timeZone);
+assert.sameValue(hours25.negated().total({
+ unit: "days",
+ relativeTo
+}), -1);
+assert.sameValue(oneDay.negated().total({
+ unit: "hours",
+ relativeTo
+}), -25);
+
+// start before repeated hour, end <1 day after
+var totalDays = hours12.total({
+ unit: "days",
+ relativeTo: repeatedHourDay
+});
+assert(Math.abs(totalDays - 12 / 25) < Number.EPSILON);
+
+// start after repeated hour, end <1 day before (negative)
+var relativeTo = Temporal.PlainDateTime.from("2000-10-29T12:00").toZonedDateTime(timeZone);
+var totalDays = hours12.negated().total({
+ unit: "days",
+ relativeTo
+});
+assert(Math.abs(totalDays - -12 / 25) < Number.EPSILON);
+
+// Samoa skipped 24 hours
+var fakeSamoa = TemporalHelpers.crossDateLineTimeZone();
+var relativeTo = Temporal.PlainDateTime.from("2011-12-29T12:00").toZonedDateTime(fakeSamoa);
+var totalDays = hours25.total({
+ unit: "days",
+ relativeTo
+});
+assert(Math.abs(totalDays - (2 + 1 / 24)) < Number.EPSILON);
+assert.sameValue(Temporal.Duration.from({ hours: 48 }).total({
+ unit: "days",
+ relativeTo
+}), 3);
+assert.sameValue(Temporal.Duration.from({ days: 2 }).total({
+ unit: "hours",
+ relativeTo
+}), 24);
+assert.sameValue(Temporal.Duration.from({ days: 3 }).total({
+ unit: "hours",
+ relativeTo
+}), 48);
+
+// totaling back up to days
+var relativeTo = Temporal.PlainDateTime.from("2000-10-28T00:00").toZonedDateTime(timeZone);
+assert.sameValue(Temporal.Duration.from({ hours: 48 }).total({ unit: "days" }), 2);
+var totalDays = Temporal.Duration.from({ hours: 48 }).total({
+ unit: "days",
+ relativeTo
+});
+assert(Math.abs(totalDays - (1 + 24 / 25)) < Number.EPSILON);
+
+// casts relativeTo to ZonedDateTime if possible
+assert.sameValue(oneDay.total({
+ unit: "hours",
+ relativeTo: {
+ year: 2000,
+ month: 10,
+ day: 29,
+ timeZone
+ }
+}), 25);
+
+// balances up to the next unit after rounding
+var almostWeek = Temporal.Duration.from({
+ days: 6,
+ hours: 20
+});
+var totalWeeks = almostWeek.total({
+ unit: "weeks",
+ relativeTo: "2020-01-01"
+});
+assert(Math.abs(totalWeeks - (6 + 20 / 24) / 7) < Number.EPSILON);
+
+// balances up to the next unit after rounding (negative)
+var almostWeek = Temporal.Duration.from({
+ days: -6,
+ hours: -20
+});
+var totalWeeks = almostWeek.total({
+ unit: "weeks",
+ relativeTo: "2020-01-01"
+});
+assert(Math.abs(totalWeeks - -((6 + 20 / 24) / 7)) < Number.EPSILON);
+
+// balances days up to both years and months
+var twoYears = Temporal.Duration.from({
+ months: 11,
+ days: 396
+});
+assert.sameValue(twoYears.total({
+ unit: "years",
+ relativeTo: "2017-01-01"
+}), 2);
+
+// balances days up to both years and months (negative)
+var twoYears = Temporal.Duration.from({
+ months: -11,
+ days: -396
+});
+assert.sameValue(twoYears.total({
+ unit: "years",
+ relativeTo: "2017-01-01"
+}), -2);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/Duration/shell.js b/js/src/tests/test262/staging/Temporal/Duration/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Duration/shell.js
diff --git a/js/src/tests/test262/staging/Temporal/Instant/browser.js b/js/src/tests/test262/staging/Temporal/Instant/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Instant/browser.js
diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/add.js b/js/src/tests/test262/staging/Temporal/Instant/old/add.js
new file mode 100644
index 0000000000..b2e2b05831
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Instant/old/add.js
@@ -0,0 +1,35 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-instant-objects
+description: Temporal.Instant.add works
+features: [Temporal]
+---*/
+
+var inst = Temporal.Instant.from("1969-12-25T12:23:45.678901234Z");
+
+// cross epoch in ms
+var one = inst.subtract({
+ hours: 240,
+ nanoseconds: 800
+});
+var two = inst.add({
+ hours: 240,
+ nanoseconds: 800
+});
+var three = two.subtract({
+ hours: 480,
+ nanoseconds: 1600
+});
+var four = one.add({
+ hours: 480,
+ nanoseconds: 1600
+});
+assert.sameValue(`${ one }`, "1969-12-15T12:23:45.678900434Z", `(${ inst }).subtract({ hours: 240, nanoseconds: 800 }) = ${ one }`);
+assert.sameValue(`${ two }`, "1970-01-04T12:23:45.678902034Z", `(${ inst }).add({ hours: 240, nanoseconds: 800 }) = ${ two }`);
+assert(three.equals(one), `(${ two }).subtract({ hours: 480, nanoseconds: 1600 }) = ${ one }`);
+assert(four.equals(two), `(${ one }).add({ hours: 480, nanoseconds: 1600 }) = ${ two }`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/browser.js b/js/src/tests/test262/staging/Temporal/Instant/old/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Instant/old/browser.js
diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/compare.js b/js/src/tests/test262/staging/Temporal/Instant/old/compare.js
new file mode 100644
index 0000000000..aab0b9bcfc
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Instant/old/compare.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-instant-objects
+description: Temporal.Instant.compare works
+features: [Temporal]
+---*/
+
+var i1 = Temporal.Instant.from("1963-02-13T09:36:29.123456789Z");
+var i2 = Temporal.Instant.from("1976-11-18T15:23:30.123456789Z");
+var i3 = Temporal.Instant.from("1981-12-15T14:34:31.987654321Z");
+
+// pre epoch equal
+assert.sameValue(Temporal.Instant.compare(i1, Temporal.Instant.from(i1)), 0)
+
+// epoch equal
+assert.sameValue(Temporal.Instant.compare(i2, Temporal.Instant.from(i2)), 0)
+
+// cross epoch smaller/larger
+assert.sameValue(Temporal.Instant.compare(i1, i2), -1)
+
+// cross epoch larger/smaller
+assert.sameValue(Temporal.Instant.compare(i2, i1), 1)
+
+// epoch smaller/larger
+assert.sameValue(Temporal.Instant.compare(i2, i3), -1)
+
+// epoch larger/smaller
+assert.sameValue(Temporal.Instant.compare(i3, i2), 1)
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/equals.js b/js/src/tests/test262/staging/Temporal/Instant/old/equals.js
new file mode 100644
index 0000000000..2df0d8db50
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Instant/old/equals.js
@@ -0,0 +1,27 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-instant-objects
+description: Temporal.Instant.equals works
+features: [Temporal]
+---*/
+
+var i1 = Temporal.Instant.from("1963-02-13T09:36:29.123456789Z");
+var i2 = Temporal.Instant.from("1976-11-18T15:23:30.123456789Z");
+var i3 = Temporal.Instant.from("1981-12-15T14:34:31.987654321Z");
+
+// pre epoch equal
+assert(i1.equals(i1))
+
+// epoch equal
+assert(i2.equals(i2))
+
+// cross epoch unequal
+assert(!i1.equals(i2))
+
+// epoch unequal
+assert(!i2.equals(i3))
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/limits.js b/js/src/tests/test262/staging/Temporal/Instant/old/limits.js
new file mode 100644
index 0000000000..adeeaaf508
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Instant/old/limits.js
@@ -0,0 +1,33 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-instant-objects
+description: Min/max range
+features: [Temporal]
+---*/
+
+
+// constructing from ns
+var limit = 8640000000000000000000n;
+assert.throws(RangeError, () => new Temporal.Instant(-limit - 1n));
+assert.throws(RangeError, () => new Temporal.Instant(limit + 1n));
+assert.sameValue(`${ new Temporal.Instant(-limit) }`, "-271821-04-20T00:00:00Z");
+assert.sameValue(`${ new Temporal.Instant(limit) }`, "+275760-09-13T00:00:00Z");
+
+// constructing from ms
+var limit = 8640000000000000;
+assert.throws(RangeError, () => Temporal.Instant.fromEpochMilliseconds(-limit - 1));
+assert.throws(RangeError, () => Temporal.Instant.fromEpochMilliseconds(limit + 1));
+assert.sameValue(`${ Temporal.Instant.fromEpochMilliseconds(-limit) }`, "-271821-04-20T00:00:00Z");
+assert.sameValue(`${ Temporal.Instant.fromEpochMilliseconds(limit) }`, "+275760-09-13T00:00:00Z");
+
+// converting from DateTime
+var min = Temporal.PlainDateTime.from("-271821-04-19T00:00:00.000000001");
+var max = Temporal.PlainDateTime.from("+275760-09-13T23:59:59.999999999");
+var utc = Temporal.TimeZone.from("UTC");
+assert.throws(RangeError, () => utc.getInstantFor(min));
+assert.throws(RangeError, () => utc.getInstantFor(max));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/round.js b/js/src/tests/test262/staging/Temporal/Instant/old/round.js
new file mode 100644
index 0000000000..6a5d41c878
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Instant/old/round.js
@@ -0,0 +1,131 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-instant-objects
+description: Temporal.Instant.round works
+features: [Temporal]
+---*/
+
+var inst = Temporal.Instant.from("1976-11-18T14:23:30.123456789Z");
+
+// throws without required smallestUnit parameter
+assert.throws(RangeError, () => inst.round({}));
+assert.throws(RangeError, () => inst.round({
+ roundingIncrement: 1,
+ roundingMode: "ceil"
+}));
+
+// rounds to an increment of hours
+assert.sameValue(`${ inst.round({
+ smallestUnit: "hour",
+ roundingIncrement: 4
+}) }`, "1976-11-18T16:00:00Z");
+
+// rounds to an increment of minutes
+assert.sameValue(`${ inst.round({
+ smallestUnit: "minute",
+ roundingIncrement: 15
+}) }`, "1976-11-18T14:30:00Z");
+
+// rounds to an increment of seconds
+assert.sameValue(`${ inst.round({
+ smallestUnit: "second",
+ roundingIncrement: 30
+}) }`, "1976-11-18T14:23:30Z");
+
+// rounds to an increment of milliseconds
+assert.sameValue(`${ inst.round({
+ smallestUnit: "millisecond",
+ roundingIncrement: 10
+}) }`, "1976-11-18T14:23:30.12Z");
+
+// rounds to an increment of microseconds
+assert.sameValue(`${ inst.round({
+ smallestUnit: "microsecond",
+ roundingIncrement: 10
+}) }`, "1976-11-18T14:23:30.12346Z");
+
+// rounds to an increment of nanoseconds
+assert.sameValue(`${ inst.round({
+ smallestUnit: "nanosecond",
+ roundingIncrement: 10
+}) }`, "1976-11-18T14:23:30.12345679Z");
+
+// rounds to days by specifying increment of 86400 seconds in various units
+var expected = "1976-11-19T00:00:00Z";
+assert.sameValue(`${ inst.round({
+ smallestUnit: "hour",
+ roundingIncrement: 24
+}) }`, expected);
+assert.sameValue(`${ inst.round({
+ smallestUnit: "minute",
+ roundingIncrement: 1440
+}) }`, expected);
+assert.sameValue(`${ inst.round({
+ smallestUnit: "second",
+ roundingIncrement: 86400
+}) }`, expected);
+assert.sameValue(`${ inst.round({
+ smallestUnit: "millisecond",
+ roundingIncrement: 86400000
+}) }`, expected);
+
+// allows increments that divide evenly into solar days
+assert(inst.round({
+ smallestUnit: "second",
+ roundingIncrement: 864
+}) instanceof Temporal.Instant);
+
+// throws on increments that do not divide evenly into solar days
+assert.throws(RangeError, () => inst.round({
+ smallestUnit: "hour",
+ roundingIncrement: 7
+}));
+assert.throws(RangeError, () => inst.round({
+ smallestUnit: "minute",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => inst.round({
+ smallestUnit: "second",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => inst.round({
+ smallestUnit: "millisecond",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => inst.round({
+ smallestUnit: "microsecond",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => inst.round({
+ smallestUnit: "nanosecond",
+ roundingIncrement: 29
+}));
+
+// accepts plural units
+[
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond"
+].forEach(smallestUnit => {
+ assert(inst.round({ smallestUnit }).equals(inst.round({ smallestUnit: `${ smallestUnit }s` })));
+});
+
+// accepts string parameter as shortcut for {smallestUnit}
+[
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond",
+ "nanosecond"
+].forEach(smallestUnit => {
+ assert(inst.round(smallestUnit).equals(inst.round({ smallestUnit })));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/shell.js b/js/src/tests/test262/staging/Temporal/Instant/old/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Instant/old/shell.js
diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/since.js b/js/src/tests/test262/staging/Temporal/Instant/old/since.js
new file mode 100644
index 0000000000..6dbc8bc0e1
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Instant/old/since.js
@@ -0,0 +1,249 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-instant-objects
+description: Temporal.Instant.since() works
+features: [Temporal]
+---*/
+
+var earlier = Temporal.Instant.from("1976-11-18T15:23:30.123456789Z");
+var later = Temporal.Instant.from("2019-10-29T10:46:38.271986102Z");
+var diff = later.since(earlier);
+assert.sameValue(`${ earlier.since(later) }`, `${ diff.negated() }`)
+assert.sameValue(`${ earlier.until(later) }`, `${ diff }`)
+assert(earlier.add(diff).equals(later))
+assert(later.subtract(diff).equals(earlier))
+var feb20 = Temporal.Instant.from("2020-02-01T00:00Z");
+var feb21 = Temporal.Instant.from("2021-02-01T00:00Z");
+
+// can return minutes and hours
+assert.sameValue(`${ feb21.since(feb20, { largestUnit: "hours" }) }`, "PT8784H");
+assert.sameValue(`${ feb21.since(feb20, { largestUnit: "minutes" }) }`, "PT527040M");
+
+// can return subseconds
+var latersub = feb20.add({
+ hours: 24,
+ milliseconds: 250,
+ microseconds: 250,
+ nanoseconds: 250
+});
+var msDiff = latersub.since(feb20, { largestUnit: "milliseconds" });
+assert.sameValue(msDiff.seconds, 0);
+assert.sameValue(msDiff.milliseconds, 86400250);
+assert.sameValue(msDiff.microseconds, 250);
+assert.sameValue(msDiff.nanoseconds, 250);
+var µsDiff = latersub.since(feb20, { largestUnit: "microseconds" });
+assert.sameValue(µsDiff.milliseconds, 0);
+assert.sameValue(µsDiff.microseconds, 86400250250);
+assert.sameValue(µsDiff.nanoseconds, 250);
+var nsDiff = latersub.since(feb20, { largestUnit: "nanoseconds" });
+assert.sameValue(nsDiff.microseconds, 0);
+assert.sameValue(nsDiff.nanoseconds, 86400250250250);
+
+// options may be a function object
+assert.sameValue(`${ feb21.since(feb20, () => {
+}) }`, "PT31622400S");
+
+// assumes a different default for largestUnit if smallestUnit is larger than seconds
+assert.sameValue(`${ later.since(earlier, {
+ smallestUnit: "hours",
+ roundingMode: "halfExpand"
+}) }`, "PT376435H");
+assert.sameValue(`${ later.since(earlier, {
+ smallestUnit: "minutes",
+ roundingMode: "halfExpand"
+}) }`, "PT22586123M");
+var largestUnit = "hours";
+
+// rounds to an increment of hours
+assert.sameValue(`${ later.since(earlier, {
+ largestUnit,
+ smallestUnit: "hours",
+ roundingIncrement: 3,
+ roundingMode: "halfExpand"
+}) }`, "PT376434H");
+
+// rounds to an increment of minutes
+assert.sameValue(`${ later.since(earlier, {
+ largestUnit,
+ smallestUnit: "minutes",
+ roundingIncrement: 30,
+ roundingMode: "halfExpand"
+}) }`, "PT376435H30M");
+
+// rounds to an increment of seconds
+assert.sameValue(`${ later.since(earlier, {
+ largestUnit,
+ smallestUnit: "seconds",
+ roundingIncrement: 15,
+ roundingMode: "halfExpand"
+}) }`, "PT376435H23M15S");
+
+// rounds to an increment of milliseconds
+assert.sameValue(`${ later.since(earlier, {
+ largestUnit,
+ smallestUnit: "milliseconds",
+ roundingIncrement: 10,
+ roundingMode: "halfExpand"
+}) }`, "PT376435H23M8.15S");
+
+// rounds to an increment of microseconds
+assert.sameValue(`${ later.since(earlier, {
+ largestUnit,
+ smallestUnit: "microseconds",
+ roundingIncrement: 10,
+ roundingMode: "halfExpand"
+}) }`, "PT376435H23M8.14853S");
+
+// rounds to an increment of nanoseconds
+assert.sameValue(`${ later.since(earlier, {
+ largestUnit,
+ smallestUnit: "nanoseconds",
+ roundingIncrement: 10,
+ roundingMode: "halfExpand"
+}) }`, "PT376435H23M8.14852931S");
+
+// valid hour increments divide into 24
+[
+ 1,
+ 2,
+ 3,
+ 4,
+ 6,
+ 8,
+ 12
+].forEach(roundingIncrement => {
+ var options = {
+ largestUnit,
+ smallestUnit: "hours",
+ roundingIncrement
+ };
+ assert(later.since(earlier, options) instanceof Temporal.Duration);
+});
+
+// valid increments divide into 60
+[
+ "minutes",
+ "seconds"
+].forEach(smallestUnit => {
+ [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 10,
+ 12,
+ 15,
+ 20,
+ 30
+ ].forEach(roundingIncrement => {
+ var options = {
+ largestUnit,
+ smallestUnit,
+ roundingIncrement
+ };
+ assert(later.since(earlier, options) instanceof Temporal.Duration);
+ });
+});
+
+// valid increments divide into 1000
+[
+ "milliseconds",
+ "microseconds",
+ "nanoseconds"
+].forEach(smallestUnit => {
+ [
+ 1,
+ 2,
+ 4,
+ 5,
+ 8,
+ 10,
+ 20,
+ 25,
+ 40,
+ 50,
+ 100,
+ 125,
+ 200,
+ 250,
+ 500
+ ].forEach(roundingIncrement => {
+ var options = {
+ largestUnit,
+ smallestUnit,
+ roundingIncrement
+ };
+ assert(later.since(earlier, options) instanceof Temporal.Duration);
+ });
+});
+
+// throws on increments that do not divide evenly into the next highest
+assert.throws(RangeError, () => later.since(earlier, {
+ largestUnit,
+ smallestUnit: "hours",
+ roundingIncrement: 11
+}));
+assert.throws(RangeError, () => later.since(earlier, {
+ largestUnit,
+ smallestUnit: "minutes",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => later.since(earlier, {
+ largestUnit,
+ smallestUnit: "seconds",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => later.since(earlier, {
+ largestUnit,
+ smallestUnit: "milliseconds",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => later.since(earlier, {
+ largestUnit,
+ smallestUnit: "microseconds",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => later.since(earlier, {
+ largestUnit,
+ smallestUnit: "nanoseconds",
+ roundingIncrement: 29
+}));
+
+// throws on increments that are equal to the next highest
+assert.throws(RangeError, () => later.since(earlier, {
+ largestUnit,
+ smallestUnit: "hours",
+ roundingIncrement: 24
+}));
+assert.throws(RangeError, () => later.since(earlier, {
+ largestUnit,
+ smallestUnit: "minutes",
+ roundingIncrement: 60
+}));
+assert.throws(RangeError, () => later.since(earlier, {
+ largestUnit,
+ smallestUnit: "seconds",
+ roundingIncrement: 60
+}));
+assert.throws(RangeError, () => later.since(earlier, {
+ largestUnit,
+ smallestUnit: "milliseconds",
+ roundingIncrement: 1000
+}));
+assert.throws(RangeError, () => later.since(earlier, {
+ largestUnit,
+ smallestUnit: "microseconds",
+ roundingIncrement: 1000
+}));
+assert.throws(RangeError, () => later.since(earlier, {
+ largestUnit,
+ smallestUnit: "nanoseconds",
+ roundingIncrement: 1000
+}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/toZonedDateTime.js b/js/src/tests/test262/staging/Temporal/Instant/old/toZonedDateTime.js
new file mode 100644
index 0000000000..85a7432434
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Instant/old/toZonedDateTime.js
@@ -0,0 +1,61 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-instant-objects
+description: Temporal.Instant.toZonedDateTime() works
+features: [Temporal]
+---*/
+
+var inst = Temporal.Instant.from("1976-11-18T14:23:30.123456789Z");
+
+// throws without parameter
+assert.throws(TypeError, () => inst.toZonedDateTime());
+
+// throws with a string parameter
+assert.throws(TypeError, () => inst.toZonedDateTime("UTC"));
+
+var fakeGregorian = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "gregory",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+// time zone parameter UTC
+var timeZone = Temporal.TimeZone.from("UTC");
+var zdt = inst.toZonedDateTime({
+ timeZone,
+ calendar: fakeGregorian,
+});
+assert.sameValue(inst.epochNanoseconds, zdt.epochNanoseconds);
+assert.sameValue(`${ zdt }`, "1976-11-18T14:23:30.123456789+00:00[UTC][u-ca=gregory]");
+
+// time zone parameter non-UTC
+var timeZone = Temporal.TimeZone.from("-05:00");
+var zdt = inst.toZonedDateTime({
+ timeZone,
+ calendar: fakeGregorian,
+});
+assert.sameValue(inst.epochNanoseconds, zdt.epochNanoseconds);
+assert.sameValue(`${ zdt }`, "1976-11-18T09:23:30.123456789-05:00[-05:00][u-ca=gregory]");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/toZonedDateTimeISO.js b/js/src/tests/test262/staging/Temporal/Instant/old/toZonedDateTimeISO.js
new file mode 100644
index 0000000000..2fbaad8b9d
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Instant/old/toZonedDateTimeISO.js
@@ -0,0 +1,28 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-instant-objects
+description: Temporal.Instant.toZonedDateTimeISO() works
+features: [Temporal]
+---*/
+
+var inst = Temporal.Instant.from("1976-11-18T14:23:30.123456789Z");
+
+// throws without parameter
+assert.throws(TypeError, () => inst.toZonedDateTimeISO());
+
+// time zone parameter UTC
+var tz = Temporal.TimeZone.from("UTC");
+var zdt = inst.toZonedDateTimeISO(tz);
+assert.sameValue(inst.epochNanoseconds, zdt.epochNanoseconds);
+assert.sameValue(`${ zdt }`, "1976-11-18T14:23:30.123456789+00:00[UTC]");
+
+// time zone parameter non-UTC
+var tz = Temporal.TimeZone.from("-05:00");
+var zdt = inst.toZonedDateTimeISO(tz);
+assert.sameValue(inst.epochNanoseconds, zdt.epochNanoseconds);
+assert.sameValue(`${ zdt }`, "1976-11-18T09:23:30.123456789-05:00[-05:00]");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/Instant/old/until.js b/js/src/tests/test262/staging/Temporal/Instant/old/until.js
new file mode 100644
index 0000000000..78f3c11189
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Instant/old/until.js
@@ -0,0 +1,249 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-instant-objects
+description: Temporal.Instant.until() works
+features: [Temporal]
+---*/
+
+var earlier = Temporal.Instant.from("1969-07-24T16:50:35.123456789Z");
+var later = Temporal.Instant.from("2019-10-29T10:46:38.271986102Z");
+var diff = earlier.until(later);
+assert.sameValue(`${ later.until(earlier) }`, `${ diff.negated() }`)
+assert.sameValue(`${ later.since(earlier) }`, `${ diff }`)
+assert(earlier.add(diff).equals(later))
+assert(later.subtract(diff).equals(earlier))
+var feb20 = Temporal.Instant.from("2020-02-01T00:00Z");
+var feb21 = Temporal.Instant.from("2021-02-01T00:00Z");
+
+// can return minutes and hours
+assert.sameValue(`${ feb20.until(feb21, { largestUnit: "hours" }) }`, "PT8784H");
+assert.sameValue(`${ feb20.until(feb21, { largestUnit: "minutes" }) }`, "PT527040M");
+
+// can return subseconds
+var latersub = feb20.add({
+ hours: 24,
+ milliseconds: 250,
+ microseconds: 250,
+ nanoseconds: 250
+});
+var msDiff = feb20.until(latersub, { largestUnit: "milliseconds" });
+assert.sameValue(msDiff.seconds, 0);
+assert.sameValue(msDiff.milliseconds, 86400250);
+assert.sameValue(msDiff.microseconds, 250);
+assert.sameValue(msDiff.nanoseconds, 250);
+var µsDiff = feb20.until(latersub, { largestUnit: "microseconds" });
+assert.sameValue(µsDiff.milliseconds, 0);
+assert.sameValue(µsDiff.microseconds, 86400250250);
+assert.sameValue(µsDiff.nanoseconds, 250);
+var nsDiff = feb20.until(latersub, { largestUnit: "nanoseconds" });
+assert.sameValue(nsDiff.microseconds, 0);
+assert.sameValue(nsDiff.nanoseconds, 86400250250250);
+
+// options may be a function object
+assert.sameValue(`${ feb20.until(feb21, () => {
+}) }`, "PT31622400S");
+
+// assumes a different default for largestUnit if smallestUnit is larger than seconds
+assert.sameValue(`${ earlier.until(later, {
+ smallestUnit: "hours",
+ roundingMode: "halfExpand"
+}) }`, "PT440610H");
+assert.sameValue(`${ earlier.until(later, {
+ smallestUnit: "minutes",
+ roundingMode: "halfExpand"
+}) }`, "PT26436596M");
+var largestUnit = "hours";
+
+// rounds to an increment of hours
+assert.sameValue(`${ earlier.until(later, {
+ largestUnit,
+ smallestUnit: "hours",
+ roundingIncrement: 4,
+ roundingMode: "halfExpand"
+}) }`, "PT440608H");
+
+// rounds to an increment of minutes
+assert.sameValue(`${ earlier.until(later, {
+ largestUnit,
+ smallestUnit: "minutes",
+ roundingIncrement: 30,
+ roundingMode: "halfExpand"
+}) }`, "PT440610H");
+
+// rounds to an increment of seconds
+assert.sameValue(`${ earlier.until(later, {
+ largestUnit,
+ smallestUnit: "seconds",
+ roundingIncrement: 15,
+ roundingMode: "halfExpand"
+}) }`, "PT440609H56M");
+
+// rounds to an increment of milliseconds
+assert.sameValue(`${ earlier.until(later, {
+ largestUnit,
+ smallestUnit: "milliseconds",
+ roundingIncrement: 10,
+ roundingMode: "halfExpand"
+}) }`, "PT440609H56M3.15S");
+
+// rounds to an increment of microseconds
+assert.sameValue(`${ earlier.until(later, {
+ largestUnit,
+ smallestUnit: "microseconds",
+ roundingIncrement: 10,
+ roundingMode: "halfExpand"
+}) }`, "PT440609H56M3.14853S");
+
+// rounds to an increment of nanoseconds
+assert.sameValue(`${ earlier.until(later, {
+ largestUnit,
+ smallestUnit: "nanoseconds",
+ roundingIncrement: 10,
+ roundingMode: "halfExpand"
+}) }`, "PT440609H56M3.14852931S");
+
+// valid hour increments divide into 24
+[
+ 1,
+ 2,
+ 3,
+ 4,
+ 6,
+ 8,
+ 12
+].forEach(roundingIncrement => {
+ var options = {
+ largestUnit,
+ smallestUnit: "hours",
+ roundingIncrement
+ };
+ assert(earlier.until(later, options) instanceof Temporal.Duration);
+});
+
+// valid increments divide into 60
+[
+ "minutes",
+ "seconds"
+].forEach(smallestUnit => {
+ [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 10,
+ 12,
+ 15,
+ 20,
+ 30
+ ].forEach(roundingIncrement => {
+ var options = {
+ largestUnit,
+ smallestUnit,
+ roundingIncrement
+ };
+ assert(earlier.until(later, options) instanceof Temporal.Duration);
+ });
+});
+
+// valid increments divide into 1000
+[
+ "milliseconds",
+ "microseconds",
+ "nanoseconds"
+].forEach(smallestUnit => {
+ [
+ 1,
+ 2,
+ 4,
+ 5,
+ 8,
+ 10,
+ 20,
+ 25,
+ 40,
+ 50,
+ 100,
+ 125,
+ 200,
+ 250,
+ 500
+ ].forEach(roundingIncrement => {
+ var options = {
+ largestUnit,
+ smallestUnit,
+ roundingIncrement
+ };
+ assert(earlier.until(later, options) instanceof Temporal.Duration);
+ });
+});
+
+// throws on increments that do not divide evenly into the next highest
+assert.throws(RangeError, () => earlier.until(later, {
+ largestUnit,
+ smallestUnit: "hours",
+ roundingIncrement: 11
+}));
+assert.throws(RangeError, () => earlier.until(later, {
+ largestUnit,
+ smallestUnit: "minutes",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => earlier.until(later, {
+ largestUnit,
+ smallestUnit: "seconds",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => earlier.until(later, {
+ largestUnit,
+ smallestUnit: "milliseconds",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => earlier.until(later, {
+ largestUnit,
+ smallestUnit: "microseconds",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => earlier.until(later, {
+ largestUnit,
+ smallestUnit: "nanoseconds",
+ roundingIncrement: 29
+}));
+
+// throws on increments that are equal to the next highest
+assert.throws(RangeError, () => earlier.until(later, {
+ largestUnit,
+ smallestUnit: "hours",
+ roundingIncrement: 24
+}));
+assert.throws(RangeError, () => earlier.until(later, {
+ largestUnit,
+ smallestUnit: "minutes",
+ roundingIncrement: 60
+}));
+assert.throws(RangeError, () => earlier.until(later, {
+ largestUnit,
+ smallestUnit: "seconds",
+ roundingIncrement: 60
+}));
+assert.throws(RangeError, () => earlier.until(later, {
+ largestUnit,
+ smallestUnit: "milliseconds",
+ roundingIncrement: 1000
+}));
+assert.throws(RangeError, () => earlier.until(later, {
+ largestUnit,
+ smallestUnit: "microseconds",
+ roundingIncrement: 1000
+}));
+assert.throws(RangeError, () => earlier.until(later, {
+ largestUnit,
+ smallestUnit: "nanoseconds",
+ roundingIncrement: 1000
+}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/Instant/shell.js b/js/src/tests/test262/staging/Temporal/Instant/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Instant/shell.js
diff --git a/js/src/tests/test262/staging/Temporal/Regex/browser.js b/js/src/tests/test262/staging/Temporal/Regex/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Regex/browser.js
diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/browser.js b/js/src/tests/test262/staging/Temporal/Regex/old/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Regex/old/browser.js
diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/duration.js b/js/src/tests/test262/staging/Temporal/Regex/old/duration.js
new file mode 100644
index 0000000000..8ea46a4202
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Regex/old/duration.js
@@ -0,0 +1,271 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-duration-objects
+description: Temporal.Duration works as expected
+features: [Temporal]
+---*/
+
+function test(isoString, components) {
+ var {y = 0, mon = 0, w = 0, d = 0, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0} = components;
+ var duration = Temporal.Duration.from(isoString);
+ assert.sameValue(duration.years, y);
+ assert.sameValue(duration.months, mon);
+ assert.sameValue(duration.weeks, w);
+ assert.sameValue(duration.days, d);
+ assert.sameValue(duration.hours, h);
+ assert.sameValue(duration.minutes, min);
+ assert.sameValue(duration.seconds, s);
+ assert.sameValue(duration.milliseconds, ms);
+ assert.sameValue(duration.microseconds, µs);
+ assert.sameValue(duration.nanoseconds, ns);
+}
+var day = [
+ [
+ "",
+ {}
+ ],
+ [
+ "1Y",
+ { y: 1 }
+ ],
+ [
+ "2M",
+ { mon: 2 }
+ ],
+ [
+ "4W",
+ { w: 4 }
+ ],
+ [
+ "3D",
+ { d: 3 }
+ ],
+ [
+ "1Y2M",
+ {
+ y: 1,
+ mon: 2
+ }
+ ],
+ [
+ "1Y3D",
+ {
+ y: 1,
+ d: 3
+ }
+ ],
+ [
+ "2M3D",
+ {
+ mon: 2,
+ d: 3
+ }
+ ],
+ [
+ "4W3D",
+ {
+ w: 4,
+ d: 3
+ }
+ ],
+ [
+ "1Y2M3D",
+ {
+ y: 1,
+ mon: 2,
+ d: 3
+ }
+ ],
+ [
+ "1Y2M4W3D",
+ {
+ y: 1,
+ mon: 2,
+ w: 4,
+ d: 3
+ }
+ ]
+];
+var times = [
+ [
+ "",
+ {}
+ ],
+ [
+ "4H",
+ { h: 4 }
+ ],
+ [
+ "5M",
+ { min: 5 }
+ ],
+ [
+ "4H5M",
+ {
+ h: 4,
+ min: 5
+ }
+ ]
+];
+var sec = [
+ [
+ "",
+ {}
+ ],
+ [
+ "6S",
+ { s: 6 }
+ ],
+ [
+ "7.1S",
+ {
+ s: 7,
+ ms: 100
+ }
+ ],
+ [
+ "7.12S",
+ {
+ s: 7,
+ ms: 120
+ }
+ ],
+ [
+ "7.123S",
+ {
+ s: 7,
+ ms: 123
+ }
+ ],
+ [
+ "8.1234S",
+ {
+ s: 8,
+ ms: 123,
+ µs: 400
+ }
+ ],
+ [
+ "8.12345S",
+ {
+ s: 8,
+ ms: 123,
+ µs: 450
+ }
+ ],
+ [
+ "8.123456S",
+ {
+ s: 8,
+ ms: 123,
+ µs: 456
+ }
+ ],
+ [
+ "9.1234567S",
+ {
+ s: 9,
+ ms: 123,
+ µs: 456,
+ ns: 700
+ }
+ ],
+ [
+ "9.12345678S",
+ {
+ s: 9,
+ ms: 123,
+ µs: 456,
+ ns: 780
+ }
+ ],
+ [
+ "9.123456789S",
+ {
+ s: 9,
+ ms: 123,
+ µs: 456,
+ ns: 789
+ }
+ ],
+ [
+ "0.123S",
+ { ms: 123 }
+ ],
+ [
+ "0,123S",
+ { ms: 123 }
+ ],
+ [
+ "0.123456S",
+ {
+ ms: 123,
+ µs: 456
+ }
+ ],
+ [
+ "0,123456S",
+ {
+ ms: 123,
+ µs: 456
+ }
+ ],
+ [
+ "0.123456789S",
+ {
+ ms: 123,
+ µs: 456,
+ ns: 789
+ }
+ ],
+ [
+ "0,123456789S",
+ {
+ ms: 123,
+ µs: 456,
+ ns: 789
+ }
+ ]
+];
+var tim = sec.reduce((arr, [s, add]) => arr.concat(times.map(([p, expect]) => [
+ `${ p }${ s }`,
+ {
+ ...expect,
+ ...add
+ }
+])), []).slice(1);
+day.slice(1).forEach(([p, expect]) => {
+ test(`P${ p }`, expect);
+ test(`p${ p }`, expect);
+ test(`p${ p.toLowerCase() }`, expect);
+});
+tim.forEach(([p, expect]) => {
+ test(`PT${ p }`, expect);
+ test(`Pt${ p }`, expect);
+ test(`pt${ p.toLowerCase() }`, expect);
+});
+for (var [d, dexpect] of day) {
+ for (var [t, texpect] of tim) {
+ test(`P${ d }T${ t }`, {
+ ...dexpect,
+ ...texpect
+ });
+ test(`p${ d }T${ t.toLowerCase() }`, {
+ ...dexpect,
+ ...texpect
+ });
+ test(`P${ d.toLowerCase() }t${ t }`, {
+ ...dexpect,
+ ...texpect
+ });
+ test(`p${ d.toLowerCase() }t${ t.toLowerCase() }`, {
+ ...dexpect,
+ ...texpect
+ });
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/instant.js b/js/src/tests/test262/staging/Temporal/Regex/old/instant.js
new file mode 100644
index 0000000000..39b03d4d1b
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Regex/old/instant.js
@@ -0,0 +1,255 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-instant-objects
+description: Temporal.Instant works as expected
+features: [Temporal]
+---*/
+
+function test(isoString, components) {
+ var [y, mon, d, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = components;
+ var instant = Temporal.Instant.from(isoString);
+ var utc = Temporal.TimeZone.from("UTC");
+ var datetime = utc.getPlainDateTimeFor(instant);
+ assert.sameValue(datetime.year, y);
+ assert.sameValue(datetime.month, mon);
+ assert.sameValue(datetime.day, d);
+ assert.sameValue(datetime.hour, h);
+ assert.sameValue(datetime.minute, min);
+ assert.sameValue(datetime.second, s);
+ assert.sameValue(datetime.millisecond, ms);
+ assert.sameValue(datetime.microsecond, µs);
+ assert.sameValue(datetime.nanosecond, ns);
+}
+function generateTest(dateTimeString, zoneString, components) {
+ test(`${ dateTimeString }${ zoneString }`, components.slice(0, 5));
+ test(`${ dateTimeString }:30${ zoneString }`, components.slice(0, 6));
+ test(`${ dateTimeString }:30.123456789${ zoneString }`, components);
+}
+// valid strings
+[
+ "+01:00",
+ "+01",
+ "+0100",
+].forEach(zoneString => {
+ generateTest("1976-11-18T15:23", `${ zoneString }[Europe/Vienna]`, [
+ 1976,
+ 11,
+ 18,
+ 14,
+ 23,
+ 30,
+ 123,
+ 456,
+ 789
+ ]);
+ generateTest("1976-11-18T15:23", `+01:00[${ zoneString }]`, [
+ 1976,
+ 11,
+ 18,
+ 14,
+ 23,
+ 30,
+ 123,
+ 456,
+ 789
+ ]);
+});
+[
+ "-04:00",
+ "-04",
+ "-0400",
+ "-04:00:00",
+ "-040000",
+ "-04:00:00.000000000",
+ "-040000.0"
+].forEach(zoneString => generateTest("1976-11-18T15:23", zoneString, [
+ 1976,
+ 11,
+ 18,
+ 19,
+ 23,
+ 30,
+ 123,
+ 456,
+ 789
+]));
+test("1976-11-18T15:23:30.1Z", [
+ 1976,
+ 11,
+ 18,
+ 15,
+ 23,
+ 30,
+ 100
+]);
+test("1976-11-18T15:23:30.12Z", [
+ 1976,
+ 11,
+ 18,
+ 15,
+ 23,
+ 30,
+ 120
+]);
+test("1976-11-18T15:23:30.123Z", [
+ 1976,
+ 11,
+ 18,
+ 15,
+ 23,
+ 30,
+ 123
+]);
+test("1976-11-18T15:23:30.1234Z", [
+ 1976,
+ 11,
+ 18,
+ 15,
+ 23,
+ 30,
+ 123,
+ 400
+]);
+test("1976-11-18T15:23:30.12345Z", [
+ 1976,
+ 11,
+ 18,
+ 15,
+ 23,
+ 30,
+ 123,
+ 450
+]);
+test("1976-11-18T15:23:30.123456Z", [
+ 1976,
+ 11,
+ 18,
+ 15,
+ 23,
+ 30,
+ 123,
+ 456
+]);
+test("1976-11-18T15:23:30.1234567Z", [
+ 1976,
+ 11,
+ 18,
+ 15,
+ 23,
+ 30,
+ 123,
+ 456,
+ 700
+]);
+test("1976-11-18T15:23:30.12345678Z", [
+ 1976,
+ 11,
+ 18,
+ 15,
+ 23,
+ 30,
+ 123,
+ 456,
+ 780
+]);
+generateTest("1976-11-18T15:23", "z", [
+ 1976,
+ 11,
+ 18,
+ 15,
+ 23,
+ 30,
+ 123,
+ 456,
+ 789
+]);
+test("1976-11-18T15:23:30,1234Z", [
+ 1976,
+ 11,
+ 18,
+ 15,
+ 23,
+ 30,
+ 123,
+ 400
+]);
+[
+ "\u221204:00",
+ "\u221204",
+ "\u22120400"
+].forEach(offset => test(`1976-11-18T15:23:30.1234${ offset }`, [
+ 1976,
+ 11,
+ 18,
+ 19,
+ 23,
+ 30,
+ 123,
+ 400
+]));
+test("\u2212009999-11-18T15:23:30.1234Z", [
+ -9999,
+ 11,
+ 18,
+ 15,
+ 23,
+ 30,
+ 123,
+ 400
+]);
+test("1976-11-18T152330Z", [
+ 1976,
+ 11,
+ 18,
+ 15,
+ 23,
+ 30
+]);
+test("1976-11-18T152330.1234Z", [
+ 1976,
+ 11,
+ 18,
+ 15,
+ 23,
+ 30,
+ 123,
+ 400
+]);
+test("19761118T15:23:30Z", [
+ 1976,
+ 11,
+ 18,
+ 15,
+ 23,
+ 30
+]);
+test("19761118T152330Z", [
+ 1976,
+ 11,
+ 18,
+ 15,
+ 23,
+ 30
+]);
+test("19761118T152330.1234Z", [
+ 1976,
+ 11,
+ 18,
+ 15,
+ 23,
+ 30,
+ 123,
+ 400
+]);
+test("1976-11-18T15Z", [
+ 1976,
+ 11,
+ 18,
+ 15
+]);
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/plaindate.js b/js/src/tests/test262/staging/Temporal/Regex/old/plaindate.js
new file mode 100644
index 0000000000..d757efe5be
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Regex/old/plaindate.js
@@ -0,0 +1,84 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-plainedate-objects
+description: Temporal.PlainDate works as expected
+features: [Temporal]
+---*/
+
+function test(isoString, components) {
+ var [y, m, d, cid = "iso8601"] = components;
+ var date = Temporal.PlainDate.from(isoString);
+ assert.sameValue(date.year, y);
+ assert.sameValue(date.month, m);
+ assert.sameValue(date.day, d);
+ assert.sameValue(date.calendarId, cid);
+}
+function generateTest(dateTimeString, zoneString) {
+ var components = [
+ 1976,
+ 11,
+ 18
+ ];
+ test(`${ dateTimeString }${ zoneString }`, components);
+ test(`${ dateTimeString }:30${ zoneString }`, components);
+ test(`${ dateTimeString }:30.123456789${ zoneString }`, components);
+}
+[
+ "+0100[Europe/Vienna]",
+ "[Europe/Vienna]",
+ "+01:00[Custom/Vienna]",
+ "-0400",
+ "-04:00:00.000000000",
+ "+01:00[+01:00]",
+ "+01:00[+0100]",
+ ""
+].forEach(zoneString => generateTest("1976-11-18T15:23", zoneString));
+[
+ "1",
+ "12",
+ "123",
+ "1234",
+ "12345",
+ "123456",
+ "1234567",
+ "12345678"
+].forEach(decimals => test(`1976-11-18T15:23:30.${ decimals }`, [
+ 1976,
+ 11,
+ 18
+]));
+test("1976-11-18T15:23:30,1234", [
+ 1976,
+ 11,
+ 18
+]);
+test("\u2212009999-11-18", [
+ -9999,
+ 11,
+ 18
+]);
+test("1976-11-18T15", [
+ 1976,
+ 11,
+ 18
+]);
+[
+ "",
+ "+01:00[Europe/Vienna]",
+ "[Europe/Vienna]",
+ "+01:00[Custom/Vienna]"
+].forEach(zoneString => test(`1976-11-18T15:23:30.123456789${ zoneString }[u-ca=iso8601]`, [
+ 1976,
+ 11,
+ 18
+]));
+test("1976-11-18[u-ca=iso8601]", [
+ 1976,
+ 11,
+ 18
+]);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/plaindatetime.js b/js/src/tests/test262/staging/Temporal/Regex/old/plaindatetime.js
new file mode 100644
index 0000000000..2e08edf249
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Regex/old/plaindatetime.js
@@ -0,0 +1,74 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-plaindatetime-objects
+description: Temporal.PlainDateTime works as expected
+features: [Temporal]
+---*/
+
+function test(isoString, components) {
+ var [y, mon, d, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0, cid = "iso8601"] = components;
+ var datetime = Temporal.PlainDateTime.from(isoString);
+ assert.sameValue(datetime.year, y);
+ assert.sameValue(datetime.month, mon);
+ assert.sameValue(datetime.day, d);
+ assert.sameValue(datetime.hour, h);
+ assert.sameValue(datetime.minute, min);
+ assert.sameValue(datetime.second, s);
+ assert.sameValue(datetime.millisecond, ms);
+ assert.sameValue(datetime.microsecond, µs);
+ assert.sameValue(datetime.nanosecond, ns);
+ assert.sameValue(datetime.calendarId, cid);
+}
+function generateTest(dateTimeString, zoneString) {
+ var components = [
+ 1976,
+ 11,
+ 18,
+ 15,
+ 23,
+ 30,
+ 123,
+ 456,
+ 789
+ ];
+ test(`${ dateTimeString }${ zoneString }`, components.slice(0, 5));
+ test(`${ dateTimeString }:30${ zoneString }`, components.slice(0, 6));
+ test(`${ dateTimeString }:30.123456789${ zoneString }`, components);
+}
+
+//valid strings
+[
+ "+0100[Europe/Vienna]",
+ "+01:00[Europe/Vienna]",
+ "[Europe/Vienna]",
+ "+01:00[Custom/Vienna]",
+ "-0400",
+ "-04:00",
+ "-04:00:00.000000000",
+ "+010000.0[Europe/Vienna]",
+ "+01:00[+01:00]",
+ "+01:00[+0100]",
+ ""
+].forEach(zoneString => generateTest("1976-11-18T15:23", zoneString));
+[
+ "",
+ "+01:00[Europe/Vienna]",
+ "+01:00[Custom/Vienna]",
+ "[Europe/Vienna]"
+].forEach(zoneString => test(`1976-11-18T15:23:30.123456789${ zoneString }[u-ca=iso8601]`, [
+ 1976,
+ 11,
+ 18,
+ 15,
+ 23,
+ 30,
+ 123,
+ 456,
+ 789
+]));
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/plainmonthday.js b/js/src/tests/test262/staging/Temporal/Regex/old/plainmonthday.js
new file mode 100644
index 0000000000..c6969e45d1
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Regex/old/plainmonthday.js
@@ -0,0 +1,78 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-plainmonthday-objects
+description: Temporal.PlainMonthDay works as expected
+features: [Temporal]
+---*/
+
+function test(isoString, components) {
+ var [m, d, cid = "iso8601"] = components;
+ var monthDay = Temporal.PlainMonthDay.from(isoString);
+ assert.sameValue(monthDay.monthCode, `M${ m.toString().padStart(2, "0") }`);
+ assert.sameValue(monthDay.day, d);
+ assert.sameValue(monthDay.calendarId, cid);
+}
+function generateTest(dateTimeString, zoneString) {
+ var components = [
+ 11,
+ 18
+ ];
+ test(`${ dateTimeString }${ zoneString }`, components);
+ test(`${ dateTimeString }:30${ zoneString }`, components);
+ test(`${ dateTimeString }:30.123456789${ zoneString }`, components);
+}
+[
+ "+0100[Europe/Vienna]",
+ "[Europe/Vienna]",
+ "+01:00[Custom/Vienna]",
+ "-0400",
+ "-04:00:00.000000000",
+ "+010000.0[Europe/Vienna]",
+ "+01:00[+01:00]",
+ "+01:00[+0100]",
+ ""
+].forEach(zoneString => generateTest("1976-11-18T15:23", zoneString));
+[
+ "1",
+ "12",
+ "123",
+ "1234",
+ "12345",
+ "123456",
+ "1234567",
+ "12345678"
+].forEach(decimals => test(`1976-11-18T15:23:30.${ decimals }`, [
+ 11,
+ 18
+]));
+[
+ "1976-11-18T15:23:30,1234",
+ "\u2212009999-11-18",
+ "19761118",
+ "+199999-11-18",
+ "+1999991118",
+ "-000300-11-18",
+ "-0003001118",
+ "15121118"
+].forEach(str => test(str, [
+ 11,
+ 18
+]));
+[
+ "",
+ "+01:00[Europe/Vienna]",
+ "[Europe/Vienna]",
+ "+01:00[Custom/Vienna]"
+].forEach(zoneString => test(`1976-11-18T15:23:30.123456789${ zoneString }[u-ca=iso8601]`, [
+ 11,
+ 18
+]));
+test("1972-11-18[u-ca=iso8601]", [
+ 11,
+ 18
+]);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/plaintime.js b/js/src/tests/test262/staging/Temporal/Regex/old/plaintime.js
new file mode 100644
index 0000000000..def35d7af2
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Regex/old/plaintime.js
@@ -0,0 +1,65 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-plaintime-objects
+description: Temporal.PlainTime works as expected
+features: [Temporal]
+---*/
+
+function test(isoString, components) {
+ var [h = 0, m = 0, s = 0, ms = 0, µs = 0, ns = 0] = components;
+ var time = Temporal.PlainTime.from(isoString);
+ assert.sameValue(time.hour, h);
+ assert.sameValue(time.minute, m);
+ assert.sameValue(time.second, s);
+ assert.sameValue(time.millisecond, ms);
+ assert.sameValue(time.microsecond, µs);
+ assert.sameValue(time.nanosecond, ns);
+}
+function generateTest(dateTimeString, zoneString) {
+ var components = [
+ 15,
+ 23,
+ 30,
+ 123,
+ 456,
+ 789
+ ];
+ test(`${ dateTimeString }${ zoneString }`, components.slice(0, 2));
+ test(`${ dateTimeString }:30${ zoneString }`, components.slice(0, 3));
+ test(`${ dateTimeString }:30.123456789${ zoneString }`, components);
+}
+[
+ "+0100[Europe/Vienna]",
+ "+01:00[Custom/Vienna]",
+ "-0400",
+ "-04:00:00.000000000",
+ "+010000.0[Europe/Vienna]",
+ "+01:00[+01:00]",
+ "+01:00[+0100]",
+ ""
+].forEach(zoneString => generateTest("1976-11-18T15:23", zoneString));
+[
+ "+01:00[Europe/Vienna]",
+ "[Europe/Vienna]",
+ "+01:00[Custom/Vienna]",
+ "-04:00",
+ ""
+].forEach(zoneStr => test(`15${ zoneStr }`, [15]));
+[
+ "",
+ "+01:00[Europe/Vienna]",
+ "[Europe/Vienna]",
+ "+01:00[Custom/Vienna]"
+].forEach(zoneString => test(`1976-11-18T15:23:30.123456789${ zoneString }[u-ca=iso8601]`, [
+ 15,
+ 23,
+ 30,
+ 123,
+ 456,
+ 789
+]));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/plainyearmonth.js b/js/src/tests/test262/staging/Temporal/Regex/old/plainyearmonth.js
new file mode 100644
index 0000000000..54fbd85e93
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Regex/old/plainyearmonth.js
@@ -0,0 +1,109 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-plainyearmonth-objects
+description: Temporal.PlainYearMonth works as expected
+features: [Temporal]
+---*/
+
+function test(isoString, components) {
+ var [y, m, cid = "iso8601"] = components;
+ var yearMonth = Temporal.PlainYearMonth.from(isoString);
+ assert.sameValue(yearMonth.year, y);
+ assert.sameValue(yearMonth.month, m);
+ assert.sameValue(yearMonth.calendarId, cid);
+}
+function generateTest(dateTimeString, zoneString) {
+ var components = [
+ 1976,
+ 11
+ ];
+ test(`${ dateTimeString }${ zoneString }`, components);
+ test(`${ dateTimeString }:30${ zoneString }`, components);
+ test(`${ dateTimeString }:30.123456789${ zoneString }`, components);
+}
+[
+ "+0100[Europe/Vienna]",
+ "[Europe/Vienna]",
+ "+01:00[Custom/Vienna]",
+ "-0400",
+ "-04:00:00.000000000",
+ "+01:00:00.0[Europe/Vienna]",
+ "+01:00[+01:00]",
+ "+01:00[+0100]",
+ ""
+].forEach(zoneString => generateTest("1976-11-18T15:23", zoneString));
+[
+ "1",
+ "12",
+ "123",
+ "1234",
+ "12345",
+ "123456",
+ "1234567",
+ "12345678"
+].forEach(decimals => test(`1976-11-18T15:23:30.${ decimals }`, [
+ 1976,
+ 11
+]));
+test("1976-11-18T15:23:30,1234", [
+ 1976,
+ 11
+]);
+test("19761118", [
+ 1976,
+ 11
+]);
+test("+199999-11-18", [
+ 199999,
+ 11
+]);
+test("+1999991118", [
+ 199999,
+ 11
+]);
+test("-000300-11-18", [
+ -300,
+ 11
+]);
+test("-0003001118", [
+ -300,
+ 11
+]);
+test("1512-11-18", [
+ 1512,
+ 11
+]);
+test("+199999-11", [
+ 199999,
+ 11
+]);
+test("+19999911", [
+ 199999,
+ 11
+]);
+test("-000300-11", [
+ -300,
+ 11
+]);
+test("-00030011", [
+ -300,
+ 11
+]);
+[
+ "",
+ "+01:00[Europe/Vienna]",
+ "[Europe/Vienna]",
+ "+01:00[Custom/Vienna]"
+].forEach(zoneString => test(`1976-11-18T15:23:30.123456789${ zoneString }[u-ca=iso8601]`, [
+ 1976,
+ 11
+]));
+test("1976-11-01[u-ca=iso8601]", [
+ 1976,
+ 11
+]);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/shell.js b/js/src/tests/test262/staging/Temporal/Regex/old/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Regex/old/shell.js
diff --git a/js/src/tests/test262/staging/Temporal/Regex/old/timezone.js b/js/src/tests/test262/staging/Temporal/Regex/old/timezone.js
new file mode 100644
index 0000000000..60ffd043bf
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Regex/old/timezone.js
@@ -0,0 +1,91 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-timezone-objects
+description: Temporal.TimeZone works as expected
+features: [Temporal]
+---*/
+
+function test(offsetString, expectedName) {
+ var timeZone = Temporal.TimeZone.from(offsetString);
+ assert.sameValue(timeZone.id, expectedName);
+}
+function generateTest(dateTimeString, zoneString, expectedName) {
+ test(`${ dateTimeString }${ zoneString }`, expectedName);
+ test(`${ dateTimeString }:30${ zoneString }`, expectedName);
+ test(`${ dateTimeString }:30.123456789${ zoneString }`, expectedName);
+}
+[
+ "+00:00",
+ "+00",
+ "+0000"
+].forEach(zoneString => {
+ generateTest("1976-11-18T15:23", `${ zoneString }[UTC]`, "UTC");
+ generateTest("1976-11-18T15:23", `+00:00[${ zoneString }]`, "+00:00");
+});
+[
+ "-04:00",
+ "-04",
+ "-0400"
+].forEach(zoneString => generateTest("1976-11-18T15:23", zoneString, "-04:00"));
+[
+ "1",
+ "12",
+ "123",
+ "1234",
+ "12345",
+ "123456",
+ "1234567",
+ "12345678"
+].forEach(decimals => {
+ test(`1976-11-18T15:23:30.${ decimals }Z`, "UTC");
+});
+generateTest("1976-11-18T15:23", "z", "UTC");
+test("1976-11-18T15:23:30,1234Z", "UTC");
+test("1976-11-18T15:23+000000,0[UTC]", "UTC");
+[
+ "\u221204:00",
+ "\u221204",
+ "\u22120400"
+].forEach(offset => test(`1976-11-18T15:23${ offset }`, "-04:00"));
+[
+ "1976-11-18T152330",
+ "1976-11-18T152330.1234",
+ "19761118T15:23:30",
+ "19761118T152330",
+ "19761118T152330.1234",
+ "1976-11-18T15"
+].forEach(dateTimeString => {
+ [
+ "+00:00",
+ "+00",
+ "+0000",
+ ""
+ ].forEach(zoneString => test(`${ dateTimeString }${ zoneString }[UTC]`, "UTC"));
+ [
+ "-04:00",
+ "-04",
+ "-0400"
+ ].forEach(zoneString => test(`${ dateTimeString }${ zoneString }`, "-04:00"));
+ test(`${ dateTimeString }Z`, "UTC");
+});
+test("-0000", "+00:00");
+test("-00:00", "+00:00");
+test("+00", "+00:00");
+test("-00", "+00:00");
+test("+03", "+03:00");
+test("-03", "-03:00");
+test("\u22120000", "+00:00");
+test("\u221200:00", "+00:00");
+test("\u221200", "+00:00");
+test("\u22120300", "-03:00");
+test("\u221203:00", "-03:00");
+test("\u221203", "-03:00");
+test("1976-11-18T15:23:30.123456789Z[u-ca=iso8601]", "UTC");
+test("1976-11-18T15:23:30.123456789-04:00[u-ca=iso8601]", "-04:00");
+test("1976-11-18T15:23:30.123456789[UTC][u-ca=iso8601]", "UTC");
+test("1976-11-18T15:23:30.123456789+00:00[UTC][u-ca=iso8601]", "UTC");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/Regex/shell.js b/js/src/tests/test262/staging/Temporal/Regex/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/Regex/shell.js
diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/browser.js b/js/src/tests/test262/staging/Temporal/TimeZone/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/TimeZone/browser.js
diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/browser.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/browser.js
diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/dst-change.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/dst-change.js
new file mode 100644
index 0000000000..8db7aa7e03
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/dst-change.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-timezone-objects
+description: with DST change
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+// clock moving forward
+var zone = TemporalHelpers.springForwardFallBackTimeZone();
+var dtm = new Temporal.PlainDateTime(2000, 4, 2, 2, 45);
+assert.sameValue(`${ zone.getInstantFor(dtm) }`, "2000-04-02T10:45:00Z");
+assert.sameValue(`${ zone.getInstantFor(dtm, { disambiguation: "earlier" }) }`, "2000-04-02T09:45:00Z");
+assert.sameValue(`${ zone.getInstantFor(dtm, { disambiguation: "later" }) }`, "2000-04-02T10:45:00Z");
+assert.sameValue(`${ zone.getInstantFor(dtm, { disambiguation: "compatible" }) }`, "2000-04-02T10:45:00Z");
+assert.throws(RangeError, () => zone.getInstantFor(dtm, { disambiguation: "reject" }));
+
+// clock moving backward
+var dtm = new Temporal.PlainDateTime(2000, 10, 29, 1, 45);
+assert.sameValue(`${ zone.getInstantFor(dtm) }`, "2000-10-29T08:45:00Z");
+assert.sameValue(`${ zone.getInstantFor(dtm, { disambiguation: "earlier" }) }`, "2000-10-29T08:45:00Z");
+assert.sameValue(`${ zone.getInstantFor(dtm, { disambiguation: "later" }) }`, "2000-10-29T09:45:00Z");
+assert.sameValue(`${ zone.getInstantFor(dtm, { disambiguation: "compatible" }) }`, "2000-10-29T08:45:00Z");
+assert.throws(RangeError, () => zone.getInstantFor(dtm, { disambiguation: "reject" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/getInstantFor-disambiguation.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/getInstantFor-disambiguation.js
new file mode 100644
index 0000000000..101a7d32a1
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/getInstantFor-disambiguation.js
@@ -0,0 +1,50 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-timezone-objects
+description: getInstantFor disambiguation
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+var dtm = new Temporal.PlainDateTime(2000, 10, 29, 1, 45);
+
+// with constant offset
+var zone = Temporal.TimeZone.from("+03:30");
+for (var disambiguation of [
+ undefined,
+ "compatible",
+ "earlier",
+ "later",
+ "reject"
+ ]) {
+ assert(zone.getInstantFor(dtm, { disambiguation }) instanceof Temporal.Instant);
+}
+
+// with daylight saving change - Fall
+var zone = TemporalHelpers.springForwardFallBackTimeZone();
+assert.sameValue(`${ zone.getInstantFor(dtm) }`, "2000-10-29T08:45:00Z");
+assert.sameValue(`${ zone.getInstantFor(dtm, { disambiguation: "earlier" }) }`, "2000-10-29T08:45:00Z");
+assert.sameValue(`${ zone.getInstantFor(dtm, { disambiguation: "later" }) }`, "2000-10-29T09:45:00Z");
+assert.sameValue(`${ zone.getInstantFor(dtm, { disambiguation: "compatible" }) }`, "2000-10-29T08:45:00Z");
+assert.throws(RangeError, () => zone.getInstantFor(dtm, { disambiguation: "reject" }));
+
+// with daylight saving change - Spring
+var dtmLA = new Temporal.PlainDateTime(2000, 4, 2, 2, 30);
+assert.sameValue(`${ zone.getInstantFor(dtmLA) }`, "2000-04-02T10:30:00Z");
+assert.sameValue(`${ zone.getInstantFor(dtmLA, { disambiguation: "earlier" }) }`, "2000-04-02T09:30:00Z");
+assert.sameValue(`${ zone.getInstantFor(dtmLA, { disambiguation: "later" }) }`, "2000-04-02T10:30:00Z");
+assert.sameValue(`${ zone.getInstantFor(dtmLA, { disambiguation: "compatible" }) }`, "2000-04-02T10:30:00Z");
+assert.throws(RangeError, () => zone.getInstantFor(dtmLA, { disambiguation: "reject" }));
+
+// throws on bad disambiguation
+var zone = Temporal.TimeZone.from("+03:30");
+[
+ "",
+ "EARLIER",
+ "test",
+].forEach(disambiguation => assert.throws(RangeError, () => zone.getInstantFor(dtm, { disambiguation })));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/getInstantFor.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/getInstantFor.js
new file mode 100644
index 0000000000..37c2be715f
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/getInstantFor.js
@@ -0,0 +1,61 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-timezone-objects
+description: Temporal.TimeZone.prototype.getInstantFor() works
+features: [Temporal]
+---*/
+
+
+// recent date
+var dt = Temporal.PlainDateTime.from("2019-10-29T10:46:38.271986102");
+var tz = Temporal.TimeZone.from("+01:00");
+assert.sameValue(`${ tz.getInstantFor(dt) }`, "2019-10-29T09:46:38.271986102Z");
+
+// year ≤ 99
+var dt = Temporal.PlainDateTime.from("0098-10-29T10:46:38.271986102");
+var tz = Temporal.TimeZone.from("+06:00");
+assert.sameValue(`${ tz.getInstantFor(dt) }`, "0098-10-29T04:46:38.271986102Z");
+dt = Temporal.PlainDateTime.from("+000098-10-29T10:46:38.271986102");
+assert.sameValue(`${ tz.getInstantFor(dt) }`, "0098-10-29T04:46:38.271986102Z");
+
+// year < 1
+var dt = Temporal.PlainDateTime.from("0000-10-29T10:46:38.271986102");
+var tz = Temporal.TimeZone.from("+06:00");
+assert.sameValue(`${ tz.getInstantFor(dt) }`, "0000-10-29T04:46:38.271986102Z");
+dt = Temporal.PlainDateTime.from("+000000-10-29T10:46:38.271986102");
+assert.sameValue(`${ tz.getInstantFor(dt) }`, "0000-10-29T04:46:38.271986102Z");
+dt = Temporal.PlainDateTime.from("-001000-10-29T10:46:38.271986102");
+assert.sameValue(`${ tz.getInstantFor(dt) }`, "-001000-10-29T04:46:38.271986102Z");
+
+// year 0 leap day
+var dt = Temporal.PlainDateTime.from("0000-02-29T00:00");
+var tz = Temporal.TimeZone.from("-00:01");
+assert.sameValue(`${ tz.getInstantFor(dt) }`, "0000-02-29T00:01:00Z");
+dt = Temporal.PlainDateTime.from("+000000-02-29T00:00");
+assert.sameValue(`${ tz.getInstantFor(dt) }`, "0000-02-29T00:01:00Z");
+
+// outside of Instant range
+var max = Temporal.PlainDateTime.from("+275760-09-13T23:59:59.999999999");
+var offsetTz = Temporal.TimeZone.from("-01:00");
+assert.throws(RangeError, () => offsetTz.getInstantFor(max));
+var namedTz = Temporal.TimeZone.from("Etc/GMT+12");
+assert.throws(RangeError, () => namedTz.getInstantFor(max));
+
+// casts argument
+var tz = Temporal.TimeZone.from("+01:00");
+assert.sameValue(`${ tz.getInstantFor("2019-10-29T10:46:38.271986102") }`, "2019-10-29T09:46:38.271986102Z");
+assert.sameValue(`${ tz.getInstantFor({
+ year: 2019,
+ month: 10,
+ day: 29,
+ hour: 10,
+ minute: 46,
+ second: 38
+}) }`, "2019-10-29T09:46:38Z");
+
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/getNextTransition.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/getNextTransition.js
new file mode 100644
index 0000000000..50e043ff83
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/getNextTransition.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-timezone-objects
+description: Temporal.TimeZone.prototype.getNextTransition() works as expected
+features: [Temporal]
+---*/
+
+var noTransitionTZ = Temporal.TimeZone.from("Etc/GMT+10");
+
+// should work for timezones with no scheduled transitions in the near future
+var start = Temporal.Instant.from("1945-10-15T13:00:00Z");
+assert.sameValue(noTransitionTZ.getNextTransition(start), null);
+
+// accepts string as argument
+assert.sameValue(noTransitionTZ.getNextTransition("2019-04-16T21:01Z"), null);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/getPossibleInstantsFor.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/getPossibleInstantsFor.js
new file mode 100644
index 0000000000..6eda88fdb0
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/getPossibleInstantsFor.js
@@ -0,0 +1,46 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-timezone-objects
+description: Temporal.TimeZone.prototype.getPossibleInstantsFor() works as expected
+includes: [deepEqual.js, temporalHelpers.js]
+features: [Temporal]
+---*/
+
+
+// with constant offset
+var zone = Temporal.TimeZone.from("+03:30");
+var dt = Temporal.PlainDateTime.from("2019-02-16T23:45");
+assert.deepEqual(zone.getPossibleInstantsFor(dt).map(a => `${ a }`), ["2019-02-16T20:15:00Z"]);
+
+// with clock moving forward
+var zone = TemporalHelpers.springForwardFallBackTimeZone();
+var dt = Temporal.PlainDateTime.from("2000-04-02T02:45");
+assert.deepEqual(zone.getPossibleInstantsFor(dt), []);
+
+// with clock moving backward
+var dt = Temporal.PlainDateTime.from("2000-10-29T01:45");
+assert.deepEqual(zone.getPossibleInstantsFor(dt).map(a => `${ a }`), [
+ "2000-10-29T08:45:00Z",
+ "2000-10-29T09:45:00Z"
+]);
+
+// casts argument
+var tz = Temporal.TimeZone.from("+03:30");
+assert.deepEqual(tz.getPossibleInstantsFor({
+ year: 2019,
+ month: 2,
+ day: 16,
+ hour: 23,
+ minute: 45,
+ second: 30
+}).map(a => `${ a }`), ["2019-02-16T20:15:30Z"]);
+assert.deepEqual(tz.getPossibleInstantsFor("2019-02-16T23:45:30").map(a => `${ a }`), ["2019-02-16T20:15:30Z"]);
+
+// object must contain at least the required properties
+var tz = Temporal.TimeZone.from("UTC");
+assert.throws(TypeError, () => tz.getPossibleInstantsFor({ year: 2019 }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/getPreviousTransition.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/getPreviousTransition.js
new file mode 100644
index 0000000000..6c6b703b58
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/getPreviousTransition.js
@@ -0,0 +1,20 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-timezone-objects
+description: Temporal.TimeZone.prototype.getPreviousTransition() works
+features: [Temporal]
+---*/
+
+var utc = Temporal.TimeZone.from("UTC");
+
+// no transitions without a TZDB
+var instant = Temporal.Instant.from("2020-06-11T21:01Z");
+assert.sameValue(utc.getPreviousTransition(instant), null);
+
+// accepts string as argument
+assert.sameValue(utc.getPreviousTransition("2020-06-11T21:01Z"), null);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/shell.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/shell.js
new file mode 100644
index 0000000000..eaa8761415
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/shell.js
@@ -0,0 +1,2487 @@
+// GENERATED, DO NOT EDIT
+// file: deepEqual.js
+// Copyright 2019 Ron Buckton. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: >
+ Compare two values structurally
+defines: [assert.deepEqual]
+---*/
+
+assert.deepEqual = function(actual, expected, message) {
+ var format = assert.deepEqual.format;
+ assert(
+ assert.deepEqual._compare(actual, expected),
+ `Expected ${format(actual)} to be structurally equal to ${format(expected)}. ${(message || '')}`
+ );
+};
+
+assert.deepEqual.format = function(value, seen) {
+ switch (typeof value) {
+ case 'string':
+ return typeof JSON !== "undefined" ? JSON.stringify(value) : `"${value}"`;
+ case 'number':
+ case 'boolean':
+ case 'symbol':
+ case 'bigint':
+ return value.toString();
+ case 'undefined':
+ return 'undefined';
+ case 'function':
+ return `[Function${value.name ? `: ${value.name}` : ''}]`;
+ case 'object':
+ if (value === null) return 'null';
+ if (value instanceof Date) return `Date "${value.toISOString()}"`;
+ if (value instanceof RegExp) return value.toString();
+ if (!seen) {
+ seen = {
+ counter: 0,
+ map: new Map()
+ };
+ }
+
+ let usage = seen.map.get(value);
+ if (usage) {
+ usage.used = true;
+ return `[Ref: #${usage.id}]`;
+ }
+
+ usage = { id: ++seen.counter, used: false };
+ seen.map.set(value, usage);
+
+ if (typeof Set !== "undefined" && value instanceof Set) {
+ return `Set {${Array.from(value).map(value => assert.deepEqual.format(value, seen)).join(', ')}}${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ if (typeof Map !== "undefined" && value instanceof Map) {
+ return `Map {${Array.from(value).map(pair => `${assert.deepEqual.format(pair[0], seen)} => ${assert.deepEqual.format(pair[1], seen)}}`).join(', ')}}${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ if (Array.isArray ? Array.isArray(value) : value instanceof Array) {
+ return `[${value.map(value => assert.deepEqual.format(value, seen)).join(', ')}]${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ let tag = Symbol.toStringTag in value ? value[Symbol.toStringTag] : 'Object';
+ if (tag === 'Object' && Object.getPrototypeOf(value) === null) {
+ tag = '[Object: null prototype]';
+ }
+ return `${tag ? `${tag} ` : ''}{ ${Object.keys(value).map(key => `${key.toString()}: ${assert.deepEqual.format(value[key], seen)}`).join(', ')} }${usage.used ? ` as #${usage.id}` : ''}`;
+ default:
+ return typeof value;
+ }
+};
+
+assert.deepEqual._compare = (function () {
+ var EQUAL = 1;
+ var NOT_EQUAL = -1;
+ var UNKNOWN = 0;
+
+ function deepEqual(a, b) {
+ return compareEquality(a, b) === EQUAL;
+ }
+
+ function compareEquality(a, b, cache) {
+ return compareIf(a, b, isOptional, compareOptionality)
+ || compareIf(a, b, isPrimitiveEquatable, comparePrimitiveEquality)
+ || compareIf(a, b, isObjectEquatable, compareObjectEquality, cache)
+ || NOT_EQUAL;
+ }
+
+ function compareIf(a, b, test, compare, cache) {
+ return !test(a)
+ ? !test(b) ? UNKNOWN : NOT_EQUAL
+ : !test(b) ? NOT_EQUAL : cacheComparison(a, b, compare, cache);
+ }
+
+ function tryCompareStrictEquality(a, b) {
+ return a === b ? EQUAL : UNKNOWN;
+ }
+
+ function tryCompareTypeOfEquality(a, b) {
+ return typeof a !== typeof b ? NOT_EQUAL : UNKNOWN;
+ }
+
+ function tryCompareToStringTagEquality(a, b) {
+ var aTag = Symbol.toStringTag in a ? a[Symbol.toStringTag] : undefined;
+ var bTag = Symbol.toStringTag in b ? b[Symbol.toStringTag] : undefined;
+ return aTag !== bTag ? NOT_EQUAL : UNKNOWN;
+ }
+
+ function isOptional(value) {
+ return value === undefined
+ || value === null;
+ }
+
+ function compareOptionality(a, b) {
+ return tryCompareStrictEquality(a, b)
+ || NOT_EQUAL;
+ }
+
+ function isPrimitiveEquatable(value) {
+ switch (typeof value) {
+ case 'string':
+ case 'number':
+ case 'bigint':
+ case 'boolean':
+ case 'symbol':
+ return true;
+ default:
+ return isBoxed(value);
+ }
+ }
+
+ function comparePrimitiveEquality(a, b) {
+ if (isBoxed(a)) a = a.valueOf();
+ if (isBoxed(b)) b = b.valueOf();
+ return tryCompareStrictEquality(a, b)
+ || tryCompareTypeOfEquality(a, b)
+ || compareIf(a, b, isNaNEquatable, compareNaNEquality)
+ || NOT_EQUAL;
+ }
+
+ function isNaNEquatable(value) {
+ return typeof value === 'number';
+ }
+
+ function compareNaNEquality(a, b) {
+ return isNaN(a) && isNaN(b) ? EQUAL : NOT_EQUAL;
+ }
+
+ function isObjectEquatable(value) {
+ return typeof value === 'object';
+ }
+
+ function compareObjectEquality(a, b, cache) {
+ if (!cache) cache = new Map();
+ return getCache(cache, a, b)
+ || setCache(cache, a, b, EQUAL) // consider equal for now
+ || cacheComparison(a, b, tryCompareStrictEquality, cache)
+ || cacheComparison(a, b, tryCompareToStringTagEquality, cache)
+ || compareIf(a, b, isValueOfEquatable, compareValueOfEquality)
+ || compareIf(a, b, isToStringEquatable, compareToStringEquality)
+ || compareIf(a, b, isArrayLikeEquatable, compareArrayLikeEquality, cache)
+ || compareIf(a, b, isStructurallyEquatable, compareStructuralEquality, cache)
+ || compareIf(a, b, isIterableEquatable, compareIterableEquality, cache)
+ || cacheComparison(a, b, fail, cache);
+ }
+
+ function isBoxed(value) {
+ return value instanceof String
+ || value instanceof Number
+ || value instanceof Boolean
+ || typeof Symbol === 'function' && value instanceof Symbol
+ || typeof BigInt === 'function' && value instanceof BigInt;
+ }
+
+ function isValueOfEquatable(value) {
+ return value instanceof Date;
+ }
+
+ function compareValueOfEquality(a, b) {
+ return compareIf(a.valueOf(), b.valueOf(), isPrimitiveEquatable, comparePrimitiveEquality)
+ || NOT_EQUAL;
+ }
+
+ function isToStringEquatable(value) {
+ return value instanceof RegExp;
+ }
+
+ function compareToStringEquality(a, b) {
+ return compareIf(a.toString(), b.toString(), isPrimitiveEquatable, comparePrimitiveEquality)
+ || NOT_EQUAL;
+ }
+
+ function isArrayLikeEquatable(value) {
+ return (Array.isArray ? Array.isArray(value) : value instanceof Array)
+ || (typeof Uint8Array === 'function' && value instanceof Uint8Array)
+ || (typeof Uint8ClampedArray === 'function' && value instanceof Uint8ClampedArray)
+ || (typeof Uint16Array === 'function' && value instanceof Uint16Array)
+ || (typeof Uint32Array === 'function' && value instanceof Uint32Array)
+ || (typeof Int8Array === 'function' && value instanceof Int8Array)
+ || (typeof Int16Array === 'function' && value instanceof Int16Array)
+ || (typeof Int32Array === 'function' && value instanceof Int32Array)
+ || (typeof Float32Array === 'function' && value instanceof Float32Array)
+ || (typeof Float64Array === 'function' && value instanceof Float64Array)
+ || (typeof BigUint64Array === 'function' && value instanceof BigUint64Array)
+ || (typeof BigInt64Array === 'function' && value instanceof BigInt64Array);
+ }
+
+ function compareArrayLikeEquality(a, b, cache) {
+ if (a.length !== b.length) return NOT_EQUAL;
+ for (var i = 0; i < a.length; i++) {
+ if (compareEquality(a[i], b[i], cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ }
+ return EQUAL;
+ }
+
+ function isStructurallyEquatable(value) {
+ return !(typeof Promise === 'function' && value instanceof Promise // only comparable by reference
+ || typeof WeakMap === 'function' && value instanceof WeakMap // only comparable by reference
+ || typeof WeakSet === 'function' && value instanceof WeakSet // only comparable by reference
+ || typeof Map === 'function' && value instanceof Map // comparable via @@iterator
+ || typeof Set === 'function' && value instanceof Set); // comparable via @@iterator
+ }
+
+ function compareStructuralEquality(a, b, cache) {
+ var aKeys = [];
+ for (var key in a) aKeys.push(key);
+
+ var bKeys = [];
+ for (var key in b) bKeys.push(key);
+
+ if (aKeys.length !== bKeys.length) {
+ return NOT_EQUAL;
+ }
+
+ aKeys.sort();
+ bKeys.sort();
+
+ for (var i = 0; i < aKeys.length; i++) {
+ var aKey = aKeys[i];
+ var bKey = bKeys[i];
+ if (compareEquality(aKey, bKey, cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ if (compareEquality(a[aKey], b[bKey], cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ }
+
+ return compareIf(a, b, isIterableEquatable, compareIterableEquality, cache)
+ || EQUAL;
+ }
+
+ function isIterableEquatable(value) {
+ return typeof Symbol === 'function'
+ && typeof value[Symbol.iterator] === 'function';
+ }
+
+ function compareIteratorEquality(a, b, cache) {
+ if (typeof Map === 'function' && a instanceof Map && b instanceof Map ||
+ typeof Set === 'function' && a instanceof Set && b instanceof Set) {
+ if (a.size !== b.size) return NOT_EQUAL; // exit early if we detect a difference in size
+ }
+
+ var ar, br;
+ while (true) {
+ ar = a.next();
+ br = b.next();
+ if (ar.done) {
+ if (br.done) return EQUAL;
+ if (b.return) b.return();
+ return NOT_EQUAL;
+ }
+ if (br.done) {
+ if (a.return) a.return();
+ return NOT_EQUAL;
+ }
+ if (compareEquality(ar.value, br.value, cache) === NOT_EQUAL) {
+ if (a.return) a.return();
+ if (b.return) b.return();
+ return NOT_EQUAL;
+ }
+ }
+ }
+
+ function compareIterableEquality(a, b, cache) {
+ return compareIteratorEquality(a[Symbol.iterator](), b[Symbol.iterator](), cache);
+ }
+
+ function cacheComparison(a, b, compare, cache) {
+ var result = compare(a, b, cache);
+ if (cache && (result === EQUAL || result === NOT_EQUAL)) {
+ setCache(cache, a, b, /** @type {EQUAL | NOT_EQUAL} */(result));
+ }
+ return result;
+ }
+
+ function fail() {
+ return NOT_EQUAL;
+ }
+
+ function setCache(cache, left, right, result) {
+ var otherCache;
+
+ otherCache = cache.get(left);
+ if (!otherCache) cache.set(left, otherCache = new Map());
+ otherCache.set(right, result);
+
+ otherCache = cache.get(right);
+ if (!otherCache) cache.set(right, otherCache = new Map());
+ otherCache.set(left, result);
+ }
+
+ function getCache(cache, left, right) {
+ var otherCache;
+ var result;
+
+ otherCache = cache.get(left);
+ result = otherCache && otherCache.get(right);
+ if (result) return result;
+
+ otherCache = cache.get(right);
+ result = otherCache && otherCache.get(left);
+ if (result) return result;
+
+ return UNKNOWN;
+ }
+
+ return deepEqual;
+})();
+
+// file: temporalHelpers.js
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: |
+ This defines helper objects and functions for testing Temporal.
+defines: [TemporalHelpers]
+features: [Symbol.species, Symbol.iterator, Temporal]
+---*/
+
+const ASCII_IDENTIFIER = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/u;
+
+function formatPropertyName(propertyKey, objectName = "") {
+ switch (typeof propertyKey) {
+ case "symbol":
+ if (Symbol.keyFor(propertyKey) !== undefined) {
+ return `${objectName}[Symbol.for('${Symbol.keyFor(propertyKey)}')]`;
+ } else if (propertyKey.description.startsWith('Symbol.')) {
+ return `${objectName}[${propertyKey.description}]`;
+ } else {
+ return `${objectName}[Symbol('${propertyKey.description}')]`
+ }
+ case "string":
+ if (propertyKey !== String(Number(propertyKey))) {
+ if (ASCII_IDENTIFIER.test(propertyKey)) {
+ return objectName ? `${objectName}.${propertyKey}` : propertyKey;
+ }
+ return `${objectName}['${propertyKey.replace(/'/g, "\\'")}']`
+ }
+ // fall through
+ default:
+ // integer or string integer-index
+ return `${objectName}[${propertyKey}]`;
+ }
+}
+
+const SKIP_SYMBOL = Symbol("Skip");
+
+var TemporalHelpers = {
+ /*
+ * Codes and maximum lengths of months in the ISO 8601 calendar.
+ */
+ ISOMonths: [
+ { month: 1, monthCode: "M01", daysInMonth: 31 },
+ { month: 2, monthCode: "M02", daysInMonth: 29 },
+ { month: 3, monthCode: "M03", daysInMonth: 31 },
+ { month: 4, monthCode: "M04", daysInMonth: 30 },
+ { month: 5, monthCode: "M05", daysInMonth: 31 },
+ { month: 6, monthCode: "M06", daysInMonth: 30 },
+ { month: 7, monthCode: "M07", daysInMonth: 31 },
+ { month: 8, monthCode: "M08", daysInMonth: 31 },
+ { month: 9, monthCode: "M09", daysInMonth: 30 },
+ { month: 10, monthCode: "M10", daysInMonth: 31 },
+ { month: 11, monthCode: "M11", daysInMonth: 30 },
+ { month: 12, monthCode: "M12", daysInMonth: 31 }
+ ],
+
+ /*
+ * assertDuration(duration, years, ..., nanoseconds[, description]):
+ *
+ * Shorthand for asserting that each field of a Temporal.Duration is equal to
+ * an expected value.
+ */
+ assertDuration(duration, years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(duration instanceof Temporal.Duration, `${prefix}instanceof`);
+ assert.sameValue(duration.years, years, `${prefix}years result:`);
+ assert.sameValue(duration.months, months, `${prefix}months result:`);
+ assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`);
+ assert.sameValue(duration.days, days, `${prefix}days result:`);
+ assert.sameValue(duration.hours, hours, `${prefix}hours result:`);
+ assert.sameValue(duration.minutes, minutes, `${prefix}minutes result:`);
+ assert.sameValue(duration.seconds, seconds, `${prefix}seconds result:`);
+ assert.sameValue(duration.milliseconds, milliseconds, `${prefix}milliseconds result:`);
+ assert.sameValue(duration.microseconds, microseconds, `${prefix}microseconds result:`);
+ assert.sameValue(duration.nanoseconds, nanoseconds, `${prefix}nanoseconds result`);
+ },
+
+ /*
+ * assertDateDuration(duration, years, months, weeks, days, [, description]):
+ *
+ * Shorthand for asserting that each date field of a Temporal.Duration is
+ * equal to an expected value.
+ */
+ assertDateDuration(duration, years, months, weeks, days, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(duration instanceof Temporal.Duration, `${prefix}instanceof`);
+ assert.sameValue(duration.years, years, `${prefix}years result:`);
+ assert.sameValue(duration.months, months, `${prefix}months result:`);
+ assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`);
+ assert.sameValue(duration.days, days, `${prefix}days result:`);
+ assert.sameValue(duration.hours, 0, `${prefix}hours result should be zero:`);
+ assert.sameValue(duration.minutes, 0, `${prefix}minutes result should be zero:`);
+ assert.sameValue(duration.seconds, 0, `${prefix}seconds result should be zero:`);
+ assert.sameValue(duration.milliseconds, 0, `${prefix}milliseconds result should be zero:`);
+ assert.sameValue(duration.microseconds, 0, `${prefix}microseconds result should be zero:`);
+ assert.sameValue(duration.nanoseconds, 0, `${prefix}nanoseconds result should be zero:`);
+ },
+
+ /*
+ * assertDurationsEqual(actual, expected[, description]):
+ *
+ * Shorthand for asserting that each field of a Temporal.Duration is equal to
+ * the corresponding field in another Temporal.Duration.
+ */
+ assertDurationsEqual(actual, expected, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(expected instanceof Temporal.Duration, `${prefix}expected value should be a Temporal.Duration`);
+ TemporalHelpers.assertDuration(actual, expected.years, expected.months, expected.weeks, expected.days, expected.hours, expected.minutes, expected.seconds, expected.milliseconds, expected.microseconds, expected.nanoseconds, description);
+ },
+
+ /*
+ * assertInstantsEqual(actual, expected[, description]):
+ *
+ * Shorthand for asserting that two Temporal.Instants are of the correct type
+ * and equal according to their equals() methods.
+ */
+ assertInstantsEqual(actual, expected, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(expected instanceof Temporal.Instant, `${prefix}expected value should be a Temporal.Instant`);
+ assert(actual instanceof Temporal.Instant, `${prefix}instanceof`);
+ assert(actual.equals(expected), `${prefix}equals method`);
+ },
+
+ /*
+ * assertPlainDate(date, year, ..., nanosecond[, description[, era, eraYear]]):
+ *
+ * Shorthand for asserting that each field of a Temporal.PlainDate is equal to
+ * an expected value. (Except the `calendar` property, since callers may want
+ * to assert either object equality with an object they put in there, or the
+ * value of date.calendarId.)
+ */
+ assertPlainDate(date, year, month, monthCode, day, description = "", era = undefined, eraYear = undefined) {
+ const prefix = description ? `${description}: ` : "";
+ assert(date instanceof Temporal.PlainDate, `${prefix}instanceof`);
+ assert.sameValue(date.era, era, `${prefix}era result:`);
+ assert.sameValue(date.eraYear, eraYear, `${prefix}eraYear result:`);
+ assert.sameValue(date.year, year, `${prefix}year result:`);
+ assert.sameValue(date.month, month, `${prefix}month result:`);
+ assert.sameValue(date.monthCode, monthCode, `${prefix}monthCode result:`);
+ assert.sameValue(date.day, day, `${prefix}day result:`);
+ },
+
+ /*
+ * assertPlainDateTime(datetime, year, ..., nanosecond[, description[, era, eraYear]]):
+ *
+ * Shorthand for asserting that each field of a Temporal.PlainDateTime is
+ * equal to an expected value. (Except the `calendar` property, since callers
+ * may want to assert either object equality with an object they put in there,
+ * or the value of datetime.calendarId.)
+ */
+ assertPlainDateTime(datetime, year, month, monthCode, day, hour, minute, second, millisecond, microsecond, nanosecond, description = "", era = undefined, eraYear = undefined) {
+ const prefix = description ? `${description}: ` : "";
+ assert(datetime instanceof Temporal.PlainDateTime, `${prefix}instanceof`);
+ assert.sameValue(datetime.era, era, `${prefix}era result:`);
+ assert.sameValue(datetime.eraYear, eraYear, `${prefix}eraYear result:`);
+ assert.sameValue(datetime.year, year, `${prefix}year result:`);
+ assert.sameValue(datetime.month, month, `${prefix}month result:`);
+ assert.sameValue(datetime.monthCode, monthCode, `${prefix}monthCode result:`);
+ assert.sameValue(datetime.day, day, `${prefix}day result:`);
+ assert.sameValue(datetime.hour, hour, `${prefix}hour result:`);
+ assert.sameValue(datetime.minute, minute, `${prefix}minute result:`);
+ assert.sameValue(datetime.second, second, `${prefix}second result:`);
+ assert.sameValue(datetime.millisecond, millisecond, `${prefix}millisecond result:`);
+ assert.sameValue(datetime.microsecond, microsecond, `${prefix}microsecond result:`);
+ assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`);
+ },
+
+ /*
+ * assertPlainDateTimesEqual(actual, expected[, description]):
+ *
+ * Shorthand for asserting that two Temporal.PlainDateTimes are of the correct
+ * type, equal according to their equals() methods, and additionally that
+ * their calendar internal slots are the same value.
+ */
+ assertPlainDateTimesEqual(actual, expected, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(expected instanceof Temporal.PlainDateTime, `${prefix}expected value should be a Temporal.PlainDateTime`);
+ assert(actual instanceof Temporal.PlainDateTime, `${prefix}instanceof`);
+ assert(actual.equals(expected), `${prefix}equals method`);
+ assert.sameValue(
+ actual.getISOFields().calendar,
+ expected.getISOFields().calendar,
+ `${prefix}calendar same value:`
+ );
+ },
+
+ /*
+ * assertPlainMonthDay(monthDay, monthCode, day[, description [, referenceISOYear]]):
+ *
+ * Shorthand for asserting that each field of a Temporal.PlainMonthDay is
+ * equal to an expected value. (Except the `calendar` property, since callers
+ * may want to assert either object equality with an object they put in there,
+ * or the value of monthDay.calendarId().)
+ */
+ assertPlainMonthDay(monthDay, monthCode, day, description = "", referenceISOYear = 1972) {
+ const prefix = description ? `${description}: ` : "";
+ assert(monthDay instanceof Temporal.PlainMonthDay, `${prefix}instanceof`);
+ assert.sameValue(monthDay.monthCode, monthCode, `${prefix}monthCode result:`);
+ assert.sameValue(monthDay.day, day, `${prefix}day result:`);
+ assert.sameValue(monthDay.getISOFields().isoYear, referenceISOYear, `${prefix}referenceISOYear result:`);
+ },
+
+ /*
+ * assertPlainTime(time, hour, ..., nanosecond[, description]):
+ *
+ * Shorthand for asserting that each field of a Temporal.PlainTime is equal to
+ * an expected value.
+ */
+ assertPlainTime(time, hour, minute, second, millisecond, microsecond, nanosecond, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(time instanceof Temporal.PlainTime, `${prefix}instanceof`);
+ assert.sameValue(time.hour, hour, `${prefix}hour result:`);
+ assert.sameValue(time.minute, minute, `${prefix}minute result:`);
+ assert.sameValue(time.second, second, `${prefix}second result:`);
+ assert.sameValue(time.millisecond, millisecond, `${prefix}millisecond result:`);
+ assert.sameValue(time.microsecond, microsecond, `${prefix}microsecond result:`);
+ assert.sameValue(time.nanosecond, nanosecond, `${prefix}nanosecond result:`);
+ },
+
+ /*
+ * assertPlainTimesEqual(actual, expected[, description]):
+ *
+ * Shorthand for asserting that two Temporal.PlainTimes are of the correct
+ * type and equal according to their equals() methods.
+ */
+ assertPlainTimesEqual(actual, expected, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(expected instanceof Temporal.PlainTime, `${prefix}expected value should be a Temporal.PlainTime`);
+ assert(actual instanceof Temporal.PlainTime, `${prefix}instanceof`);
+ assert(actual.equals(expected), `${prefix}equals method`);
+ },
+
+ /*
+ * assertPlainYearMonth(yearMonth, year, month, monthCode[, description[, era, eraYear, referenceISODay]]):
+ *
+ * Shorthand for asserting that each field of a Temporal.PlainYearMonth is
+ * equal to an expected value. (Except the `calendar` property, since callers
+ * may want to assert either object equality with an object they put in there,
+ * or the value of yearMonth.calendarId.)
+ */
+ assertPlainYearMonth(yearMonth, year, month, monthCode, description = "", era = undefined, eraYear = undefined, referenceISODay = 1) {
+ const prefix = description ? `${description}: ` : "";
+ assert(yearMonth instanceof Temporal.PlainYearMonth, `${prefix}instanceof`);
+ assert.sameValue(yearMonth.era, era, `${prefix}era result:`);
+ assert.sameValue(yearMonth.eraYear, eraYear, `${prefix}eraYear result:`);
+ assert.sameValue(yearMonth.year, year, `${prefix}year result:`);
+ assert.sameValue(yearMonth.month, month, `${prefix}month result:`);
+ assert.sameValue(yearMonth.monthCode, monthCode, `${prefix}monthCode result:`);
+ assert.sameValue(yearMonth.getISOFields().isoDay, referenceISODay, `${prefix}referenceISODay result:`);
+ },
+
+ /*
+ * assertZonedDateTimesEqual(actual, expected[, description]):
+ *
+ * Shorthand for asserting that two Temporal.ZonedDateTimes are of the correct
+ * type, equal according to their equals() methods, and additionally that
+ * their time zones and calendar internal slots are the same value.
+ */
+ assertZonedDateTimesEqual(actual, expected, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(expected instanceof Temporal.ZonedDateTime, `${prefix}expected value should be a Temporal.ZonedDateTime`);
+ assert(actual instanceof Temporal.ZonedDateTime, `${prefix}instanceof`);
+ assert(actual.equals(expected), `${prefix}equals method`);
+ assert.sameValue(actual.timeZone, expected.timeZone, `${prefix}time zone same value:`);
+ assert.sameValue(
+ actual.getISOFields().calendar,
+ expected.getISOFields().calendar,
+ `${prefix}calendar same value:`
+ );
+ },
+
+ /*
+ * assertUnreachable(description):
+ *
+ * Helper for asserting that code is not executed. This is useful for
+ * assertions that methods of user calendars and time zones are not called.
+ */
+ assertUnreachable(description) {
+ let message = "This code should not be executed";
+ if (description) {
+ message = `${message}: ${description}`;
+ }
+ throw new Test262Error(message);
+ },
+
+ /*
+ * checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls):
+ *
+ * When an options object with a largestUnit property is synthesized inside
+ * Temporal and passed to user code such as calendar.dateUntil(), the value of
+ * the largestUnit property should be in the singular form, even if the input
+ * was given in the plural form.
+ * (This doesn't apply when the options object is passed through verbatim.)
+ *
+ * func(calendar, largestUnit, index) is the operation under test. It's called
+ * with an instance of a calendar that keeps track of which largestUnit is
+ * passed to dateUntil(), each key of expectedLargestUnitCalls in turn, and
+ * the key's numerical index in case the function needs to generate test data
+ * based on the index. At the end, the actual values passed to dateUntil() are
+ * compared with the array values of expectedLargestUnitCalls.
+ */
+ checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls) {
+ const actual = [];
+
+ class DateUntilOptionsCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateUntil(earlier, later, options) {
+ actual.push(options.largestUnit);
+ return super.dateUntil(earlier, later, options);
+ }
+
+ toString() {
+ return "date-until-options";
+ }
+ }
+
+ const calendar = new DateUntilOptionsCalendar();
+ Object.entries(expectedLargestUnitCalls).forEach(([largestUnit, expected], index) => {
+ func(calendar, largestUnit, index);
+ assert.compareArray(actual, expected, `largestUnit passed to calendar.dateUntil() for largestUnit ${largestUnit}`);
+ actual.splice(0); // empty it for the next check
+ });
+ },
+
+ /*
+ * checkPlainDateTimeConversionFastPath(func):
+ *
+ * ToTemporalDate and ToTemporalTime should both, if given a
+ * Temporal.PlainDateTime instance, convert to the desired type by reading the
+ * PlainDateTime's internal slots, rather than calling any getters.
+ *
+ * func(datetime, calendar) is the actual operation to test, that must
+ * internally call the abstract operation ToTemporalDate or ToTemporalTime.
+ * It is passed a Temporal.PlainDateTime instance, as well as the instance's
+ * calendar object (so that it doesn't have to call the calendar getter itself
+ * if it wants to make any assertions about the calendar.)
+ */
+ checkPlainDateTimeConversionFastPath(func, message = "checkPlainDateTimeConversionFastPath") {
+ const actual = [];
+ const expected = [];
+
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+ const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDateTime.prototype);
+ ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => {
+ Object.defineProperty(datetime, property, {
+ get() {
+ actual.push(`get ${formatPropertyName(property)}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${formatPropertyName(property)}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${formatPropertyName(property)}`);
+ return value;
+ },
+ };
+ },
+ });
+ });
+ Object.defineProperty(datetime, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ func(datetime, calendar);
+ assert.compareArray(actual, expected, `${message}: property getters not called`);
+ },
+
+ /*
+ * Check that an options bag that accepts units written in the singular form,
+ * also accepts the same units written in the plural form.
+ * func(unit) should call the method with the appropriate options bag
+ * containing unit as a value. This will be called twice for each element of
+ * validSingularUnits, once with singular and once with plural, and the
+ * results of each pair should be the same (whether a Temporal object or a
+ * primitive value.)
+ */
+ checkPluralUnitsAccepted(func, validSingularUnits) {
+ const plurals = {
+ year: 'years',
+ month: 'months',
+ week: 'weeks',
+ day: 'days',
+ hour: 'hours',
+ minute: 'minutes',
+ second: 'seconds',
+ millisecond: 'milliseconds',
+ microsecond: 'microseconds',
+ nanosecond: 'nanoseconds',
+ };
+
+ validSingularUnits.forEach((unit) => {
+ const singularValue = func(unit);
+ const pluralValue = func(plurals[unit]);
+ const desc = `Plural ${plurals[unit]} produces the same result as singular ${unit}`;
+ if (singularValue instanceof Temporal.Duration) {
+ TemporalHelpers.assertDurationsEqual(pluralValue, singularValue, desc);
+ } else if (singularValue instanceof Temporal.Instant) {
+ TemporalHelpers.assertInstantsEqual(pluralValue, singularValue, desc);
+ } else if (singularValue instanceof Temporal.PlainDateTime) {
+ TemporalHelpers.assertPlainDateTimesEqual(pluralValue, singularValue, desc);
+ } else if (singularValue instanceof Temporal.PlainTime) {
+ TemporalHelpers.assertPlainTimesEqual(pluralValue, singularValue, desc);
+ } else if (singularValue instanceof Temporal.ZonedDateTime) {
+ TemporalHelpers.assertZonedDateTimesEqual(pluralValue, singularValue, desc);
+ } else {
+ assert.sameValue(pluralValue, singularValue);
+ }
+ });
+ },
+
+ /*
+ * checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc):
+ *
+ * Checks the type handling of the roundingIncrement option.
+ * checkFunc(roundingIncrement) is a function which takes the value of
+ * roundingIncrement to test, and calls the method under test with it,
+ * returning the result. assertTrueResultFunc(result, description) should
+ * assert that result is the expected result with roundingIncrement: true, and
+ * assertObjectResultFunc(result, description) should assert that result is
+ * the expected result with roundingIncrement being an object with a valueOf()
+ * method.
+ */
+ checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc) {
+ // null converts to 0, which is out of range
+ assert.throws(RangeError, () => checkFunc(null), "null");
+ // Booleans convert to either 0 or 1, and 1 is allowed
+ const trueResult = checkFunc(true);
+ assertTrueResultFunc(trueResult, "true");
+ assert.throws(RangeError, () => checkFunc(false), "false");
+ // Symbols and BigInts cannot convert to numbers
+ assert.throws(TypeError, () => checkFunc(Symbol()), "symbol");
+ assert.throws(TypeError, () => checkFunc(2n), "bigint");
+
+ // Objects prefer their valueOf() methods when converting to a number
+ assert.throws(RangeError, () => checkFunc({}), "plain object");
+
+ const expected = [
+ "get roundingIncrement.valueOf",
+ "call roundingIncrement.valueOf",
+ ];
+ const actual = [];
+ const observer = TemporalHelpers.toPrimitiveObserver(actual, 2, "roundingIncrement");
+ const objectResult = checkFunc(observer);
+ assertObjectResultFunc(objectResult, "object with valueOf");
+ assert.compareArray(actual, expected, "order of operations");
+ },
+
+ /*
+ * checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc):
+ *
+ * Checks the type handling of a string option, of which there are several in
+ * Temporal.
+ * propertyName is the name of the option, and value is the value that
+ * assertFunc should expect it to have.
+ * checkFunc(value) is a function which takes the value of the option to test,
+ * and calls the method under test with it, returning the result.
+ * assertFunc(result, description) should assert that result is the expected
+ * result with the option value being an object with a toString() method
+ * which returns the given value.
+ */
+ checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc) {
+ // null converts to the string "null", which is an invalid string value
+ assert.throws(RangeError, () => checkFunc(null), "null");
+ // Booleans convert to the strings "true" or "false", which are invalid
+ assert.throws(RangeError, () => checkFunc(true), "true");
+ assert.throws(RangeError, () => checkFunc(false), "false");
+ // Symbols cannot convert to strings
+ assert.throws(TypeError, () => checkFunc(Symbol()), "symbol");
+ // Numbers convert to strings which are invalid
+ assert.throws(RangeError, () => checkFunc(2), "number");
+ // BigInts convert to strings which are invalid
+ assert.throws(RangeError, () => checkFunc(2n), "bigint");
+
+ // Objects prefer their toString() methods when converting to a string
+ assert.throws(RangeError, () => checkFunc({}), "plain object");
+
+ const expected = [
+ `get ${propertyName}.toString`,
+ `call ${propertyName}.toString`,
+ ];
+ const actual = [];
+ const observer = TemporalHelpers.toPrimitiveObserver(actual, value, propertyName);
+ const result = checkFunc(observer);
+ assertFunc(result, "object with toString");
+ assert.compareArray(actual, expected, "order of operations");
+ },
+
+ /*
+ * checkSubclassingIgnored(construct, constructArgs, method, methodArgs,
+ * resultAssertions):
+ *
+ * Methods of Temporal classes that return a new instance of the same class,
+ * must not take the constructor of a subclass into account, nor the @@species
+ * property. This helper runs tests to ensure this.
+ *
+ * construct(...constructArgs) must yield a valid instance of the Temporal
+ * class. instance[method](...methodArgs) is the method call under test, which
+ * must also yield a valid instance of the same Temporal class, not a
+ * subclass. See below for the individual tests that this runs.
+ * resultAssertions() is a function that performs additional assertions on the
+ * instance returned by the method under test.
+ */
+ checkSubclassingIgnored(...args) {
+ this.checkSubclassConstructorNotObject(...args);
+ this.checkSubclassConstructorUndefined(...args);
+ this.checkSubclassConstructorThrows(...args);
+ this.checkSubclassConstructorNotCalled(...args);
+ this.checkSubclassSpeciesInvalidResult(...args);
+ this.checkSubclassSpeciesNotAConstructor(...args);
+ this.checkSubclassSpeciesNull(...args);
+ this.checkSubclassSpeciesUndefined(...args);
+ this.checkSubclassSpeciesThrows(...args);
+ },
+
+ /*
+ * Checks that replacing the 'constructor' property of the instance with
+ * various primitive values does not affect the returned new instance.
+ */
+ checkSubclassConstructorNotObject(construct, constructArgs, method, methodArgs, resultAssertions) {
+ function check(value, description) {
+ const instance = new construct(...constructArgs);
+ instance.constructor = value;
+ const result = instance[method](...methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
+ resultAssertions(result);
+ }
+
+ check(null, "null");
+ check(true, "true");
+ check("test", "string");
+ check(Symbol(), "Symbol");
+ check(7, "number");
+ check(7n, "bigint");
+ },
+
+ /*
+ * Checks that replacing the 'constructor' property of the subclass with
+ * undefined does not affect the returned new instance.
+ */
+ checkSubclassConstructorUndefined(construct, constructArgs, method, methodArgs, resultAssertions) {
+ let called = 0;
+
+ class MySubclass extends construct {
+ constructor() {
+ ++called;
+ super(...constructArgs);
+ }
+ }
+
+ const instance = new MySubclass();
+ assert.sameValue(called, 1);
+
+ MySubclass.prototype.constructor = undefined;
+
+ const result = instance[method](...methodArgs);
+ assert.sameValue(called, 1);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Checks that making the 'constructor' property of the instance throw when
+ * called does not affect the returned new instance.
+ */
+ checkSubclassConstructorThrows(construct, constructArgs, method, methodArgs, resultAssertions) {
+ function CustomError() {}
+ const instance = new construct(...constructArgs);
+ Object.defineProperty(instance, "constructor", {
+ get() {
+ throw new CustomError();
+ }
+ });
+ const result = instance[method](...methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Checks that when subclassing, the subclass constructor is not called by
+ * the method under test.
+ */
+ checkSubclassConstructorNotCalled(construct, constructArgs, method, methodArgs, resultAssertions) {
+ let called = 0;
+
+ class MySubclass extends construct {
+ constructor() {
+ ++called;
+ super(...constructArgs);
+ }
+ }
+
+ const instance = new MySubclass();
+ assert.sameValue(called, 1);
+
+ const result = instance[method](...methodArgs);
+ assert.sameValue(called, 1);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Check that the constructor's @@species property is ignored when it's a
+ * constructor that returns a non-object value.
+ */
+ checkSubclassSpeciesInvalidResult(construct, constructArgs, method, methodArgs, resultAssertions) {
+ function check(value, description) {
+ const instance = new construct(...constructArgs);
+ instance.constructor = {
+ [Symbol.species]: function() {
+ return value;
+ },
+ };
+ const result = instance[method](...methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
+ resultAssertions(result);
+ }
+
+ check(undefined, "undefined");
+ check(null, "null");
+ check(true, "true");
+ check("test", "string");
+ check(Symbol(), "Symbol");
+ check(7, "number");
+ check(7n, "bigint");
+ check({}, "plain object");
+ },
+
+ /*
+ * Check that the constructor's @@species property is ignored when it's not a
+ * constructor.
+ */
+ checkSubclassSpeciesNotAConstructor(construct, constructArgs, method, methodArgs, resultAssertions) {
+ function check(value, description) {
+ const instance = new construct(...constructArgs);
+ instance.constructor = {
+ [Symbol.species]: value,
+ };
+ const result = instance[method](...methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
+ resultAssertions(result);
+ }
+
+ check(true, "true");
+ check("test", "string");
+ check(Symbol(), "Symbol");
+ check(7, "number");
+ check(7n, "bigint");
+ check({}, "plain object");
+ },
+
+ /*
+ * Check that the constructor's @@species property is ignored when it's null.
+ */
+ checkSubclassSpeciesNull(construct, constructArgs, method, methodArgs, resultAssertions) {
+ let called = 0;
+
+ class MySubclass extends construct {
+ constructor() {
+ ++called;
+ super(...constructArgs);
+ }
+ }
+
+ const instance = new MySubclass();
+ assert.sameValue(called, 1);
+
+ MySubclass.prototype.constructor = {
+ [Symbol.species]: null,
+ };
+
+ const result = instance[method](...methodArgs);
+ assert.sameValue(called, 1);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Check that the constructor's @@species property is ignored when it's
+ * undefined.
+ */
+ checkSubclassSpeciesUndefined(construct, constructArgs, method, methodArgs, resultAssertions) {
+ let called = 0;
+
+ class MySubclass extends construct {
+ constructor() {
+ ++called;
+ super(...constructArgs);
+ }
+ }
+
+ const instance = new MySubclass();
+ assert.sameValue(called, 1);
+
+ MySubclass.prototype.constructor = {
+ [Symbol.species]: undefined,
+ };
+
+ const result = instance[method](...methodArgs);
+ assert.sameValue(called, 1);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Check that the constructor's @@species property is ignored when it throws,
+ * i.e. it is not called at all.
+ */
+ checkSubclassSpeciesThrows(construct, constructArgs, method, methodArgs, resultAssertions) {
+ function CustomError() {}
+
+ const instance = new construct(...constructArgs);
+ instance.constructor = {
+ get [Symbol.species]() {
+ throw new CustomError();
+ },
+ };
+
+ const result = instance[method](...methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ },
+
+ /*
+ * checkSubclassingIgnoredStatic(construct, method, methodArgs, resultAssertions):
+ *
+ * Static methods of Temporal classes that return a new instance of the class,
+ * must not use the this-value as a constructor. This helper runs tests to
+ * ensure this.
+ *
+ * construct[method](...methodArgs) is the static method call under test, and
+ * must yield a valid instance of the Temporal class, not a subclass. See
+ * below for the individual tests that this runs.
+ * resultAssertions() is a function that performs additional assertions on the
+ * instance returned by the method under test.
+ */
+ checkSubclassingIgnoredStatic(...args) {
+ this.checkStaticInvalidReceiver(...args);
+ this.checkStaticReceiverNotCalled(...args);
+ this.checkThisValueNotCalled(...args);
+ },
+
+ /*
+ * Check that calling the static method with a receiver that's not callable,
+ * still calls the intrinsic constructor.
+ */
+ checkStaticInvalidReceiver(construct, method, methodArgs, resultAssertions) {
+ function check(value, description) {
+ const result = construct[method].apply(value, methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ }
+
+ check(undefined, "undefined");
+ check(null, "null");
+ check(true, "true");
+ check("test", "string");
+ check(Symbol(), "symbol");
+ check(7, "number");
+ check(7n, "bigint");
+ check({}, "Non-callable object");
+ },
+
+ /*
+ * Check that calling the static method with a receiver that returns a value
+ * that's not callable, still calls the intrinsic constructor.
+ */
+ checkStaticReceiverNotCalled(construct, method, methodArgs, resultAssertions) {
+ function check(value, description) {
+ const receiver = function () {
+ return value;
+ };
+ const result = construct[method].apply(receiver, methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ }
+
+ check(undefined, "undefined");
+ check(null, "null");
+ check(true, "true");
+ check("test", "string");
+ check(Symbol(), "symbol");
+ check(7, "number");
+ check(7n, "bigint");
+ check({}, "Non-callable object");
+ },
+
+ /*
+ * Check that the receiver isn't called.
+ */
+ checkThisValueNotCalled(construct, method, methodArgs, resultAssertions) {
+ let called = false;
+
+ class MySubclass extends construct {
+ constructor(...args) {
+ called = true;
+ super(...args);
+ }
+ }
+
+ const result = MySubclass[method](...methodArgs);
+ assert.sameValue(called, false);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Check that any iterable returned from a custom time zone's
+ * getPossibleInstantsFor() method is exhausted.
+ * The custom time zone object is passed in to func().
+ * expected is an array of strings representing the expected calls to the
+ * getPossibleInstantsFor() method. The PlainDateTimes that it is called with,
+ * are compared (using their toString() results) with the array.
+ */
+ checkTimeZonePossibleInstantsIterable(func, expected) {
+ // A custom time zone that returns an iterable instead of an array from its
+ // getPossibleInstantsFor() method, and for testing purposes skips
+ // 00:00-01:00 UTC on January 1, 2030, and repeats 00:00-01:00 UTC+1 on
+ // January 3, 2030. Otherwise identical to the UTC time zone.
+ class TimeZonePossibleInstantsIterable extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ this.getPossibleInstantsForCallCount = 0;
+ this.getPossibleInstantsForCalledWith = [];
+ this.getPossibleInstantsForReturns = [];
+ this.iteratorExhausted = [];
+ }
+
+ toString() {
+ return "Custom/Iterable";
+ }
+
+ getOffsetNanosecondsFor(instant) {
+ if (Temporal.Instant.compare(instant, "2030-01-01T00:00Z") >= 0 &&
+ Temporal.Instant.compare(instant, "2030-01-03T01:00Z") < 0) {
+ return 3600_000_000_000;
+ } else {
+ return 0;
+ }
+ }
+
+ getPossibleInstantsFor(dateTime) {
+ this.getPossibleInstantsForCallCount++;
+ this.getPossibleInstantsForCalledWith.push(dateTime);
+
+ // Fake DST transition
+ let retval = super.getPossibleInstantsFor(dateTime);
+ if (dateTime.toPlainDate().equals("2030-01-01") && dateTime.hour === 0) {
+ retval = [];
+ } else if (dateTime.toPlainDate().equals("2030-01-03") && dateTime.hour === 0) {
+ retval.push(retval[0].subtract({ hours: 1 }));
+ } else if (dateTime.year === 2030 && dateTime.month === 1 && dateTime.day >= 1 && dateTime.day <= 2) {
+ retval[0] = retval[0].subtract({ hours: 1 });
+ }
+
+ this.getPossibleInstantsForReturns.push(retval);
+ this.iteratorExhausted.push(false);
+ return {
+ callIndex: this.getPossibleInstantsForCallCount - 1,
+ timeZone: this,
+ *[Symbol.iterator]() {
+ yield* this.timeZone.getPossibleInstantsForReturns[this.callIndex];
+ this.timeZone.iteratorExhausted[this.callIndex] = true;
+ },
+ };
+ }
+ }
+
+ const timeZone = new TimeZonePossibleInstantsIterable();
+ func(timeZone);
+
+ assert.sameValue(timeZone.getPossibleInstantsForCallCount, expected.length, "getPossibleInstantsFor() method called correct number of times");
+
+ for (let index = 0; index < expected.length; index++) {
+ assert.sameValue(timeZone.getPossibleInstantsForCalledWith[index].toString(), expected[index], "getPossibleInstantsFor() called with expected PlainDateTime");
+ assert(timeZone.iteratorExhausted[index], "iterated through the whole iterable");
+ }
+ },
+
+ /*
+ * Check that any calendar-carrying Temporal object has its [[Calendar]]
+ * internal slot read by ToTemporalCalendar, and does not fetch the calendar
+ * by calling getters.
+ * The custom calendar object is passed in to func() so that it can do its
+ * own additional assertions involving the calendar if necessary. (Sometimes
+ * there is nothing to assert as the calendar isn't stored anywhere that can
+ * be asserted about.)
+ */
+ checkToTemporalCalendarFastPath(func) {
+ class CalendarFastPathCheck extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateFromFields(...args) {
+ return super.dateFromFields(...args).withCalendar(this);
+ }
+
+ monthDayFromFields(...args) {
+ const { isoYear, isoMonth, isoDay } = super.monthDayFromFields(...args).getISOFields();
+ return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear);
+ }
+
+ yearMonthFromFields(...args) {
+ const { isoYear, isoMonth, isoDay } = super.yearMonthFromFields(...args).getISOFields();
+ return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay);
+ }
+
+ toString() {
+ return "fast-path-check";
+ }
+ }
+ const calendar = new CalendarFastPathCheck();
+
+ const plainDate = new Temporal.PlainDate(2000, 5, 2, calendar);
+ const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+ const plainMonthDay = new Temporal.PlainMonthDay(5, 2, calendar);
+ const plainYearMonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+ const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+
+ [plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((temporalObject) => {
+ const actual = [];
+ const expected = [];
+
+ Object.defineProperty(temporalObject, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ func(temporalObject, calendar);
+ assert.compareArray(actual, expected, "calendar getter not called");
+ });
+ },
+
+ checkToTemporalInstantFastPath(func) {
+ const actual = [];
+ const expected = [];
+
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ Object.defineProperty(datetime, 'toString', {
+ get() {
+ actual.push("get toString");
+ return function (options) {
+ actual.push("call toString");
+ return Temporal.ZonedDateTime.prototype.toString.call(this, options);
+ };
+ },
+ });
+
+ func(datetime);
+ assert.compareArray(actual, expected, "toString not called");
+ },
+
+ checkToTemporalPlainDateTimeFastPath(func) {
+ const actual = [];
+ const expected = [];
+
+ const calendar = new Temporal.Calendar("iso8601");
+ const date = new Temporal.PlainDate(2000, 5, 2, calendar);
+ const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDate.prototype);
+ ["year", "month", "monthCode", "day"].forEach((property) => {
+ Object.defineProperty(date, property, {
+ get() {
+ actual.push(`get ${formatPropertyName(property)}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return TemporalHelpers.toPrimitiveObserver(actual, value, property);
+ },
+ });
+ });
+ ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => {
+ Object.defineProperty(date, property, {
+ get() {
+ actual.push(`get ${formatPropertyName(property)}`);
+ return undefined;
+ },
+ });
+ });
+ Object.defineProperty(date, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ func(date, calendar);
+ assert.compareArray(actual, expected, "property getters not called");
+ },
+
+ /*
+ * A custom calendar used in prototype pollution checks. Verifies that the
+ * fromFields methods are always called with a null-prototype fields object.
+ */
+ calendarCheckFieldsPrototypePollution() {
+ class CalendarCheckFieldsPrototypePollution extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.dateFromFieldsCallCount = 0;
+ this.yearMonthFromFieldsCallCount = 0;
+ this.monthDayFromFieldsCallCount = 0;
+ }
+
+ // toString must remain "iso8601", so that some methods don't throw due to
+ // incompatible calendars
+
+ dateFromFields(fields, options = {}) {
+ this.dateFromFieldsCallCount++;
+ assert.sameValue(Object.getPrototypeOf(fields), null, "dateFromFields should be called with null-prototype fields object");
+ return super.dateFromFields(fields, options);
+ }
+
+ yearMonthFromFields(fields, options = {}) {
+ this.yearMonthFromFieldsCallCount++;
+ assert.sameValue(Object.getPrototypeOf(fields), null, "yearMonthFromFields should be called with null-prototype fields object");
+ return super.yearMonthFromFields(fields, options);
+ }
+
+ monthDayFromFields(fields, options = {}) {
+ this.monthDayFromFieldsCallCount++;
+ assert.sameValue(Object.getPrototypeOf(fields), null, "monthDayFromFields should be called with null-prototype fields object");
+ return super.monthDayFromFields(fields, options);
+ }
+ }
+
+ return new CalendarCheckFieldsPrototypePollution();
+ },
+
+ /*
+ * A custom calendar used in prototype pollution checks. Verifies that the
+ * mergeFields() method is always called with null-prototype fields objects.
+ */
+ calendarCheckMergeFieldsPrototypePollution() {
+ class CalendarCheckMergeFieldsPrototypePollution extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.mergeFieldsCallCount = 0;
+ }
+
+ toString() {
+ return "merge-fields-null-proto";
+ }
+
+ mergeFields(fields, additionalFields) {
+ this.mergeFieldsCallCount++;
+ assert.sameValue(Object.getPrototypeOf(fields), null, "mergeFields should be called with null-prototype fields object (first argument)");
+ assert.sameValue(Object.getPrototypeOf(additionalFields), null, "mergeFields should be called with null-prototype fields object (second argument)");
+ return super.mergeFields(fields, additionalFields);
+ }
+ }
+
+ return new CalendarCheckMergeFieldsPrototypePollution();
+ },
+
+ /*
+ * A custom calendar used in prototype pollution checks. Verifies that methods
+ * are always called with a null-prototype options object.
+ */
+ calendarCheckOptionsPrototypePollution() {
+ class CalendarCheckOptionsPrototypePollution extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.yearMonthFromFieldsCallCount = 0;
+ this.dateUntilCallCount = 0;
+ }
+
+ toString() {
+ return "options-null-proto";
+ }
+
+ yearMonthFromFields(fields, options) {
+ this.yearMonthFromFieldsCallCount++;
+ assert.sameValue(Object.getPrototypeOf(options), null, "yearMonthFromFields should be called with null-prototype options");
+ return super.yearMonthFromFields(fields, options);
+ }
+
+ dateUntil(one, two, options) {
+ this.dateUntilCallCount++;
+ assert.sameValue(Object.getPrototypeOf(options), null, "dateUntil should be called with null-prototype options");
+ return super.dateUntil(one, two, options);
+ }
+ }
+
+ return new CalendarCheckOptionsPrototypePollution();
+ },
+
+ /*
+ * A custom calendar that asserts its dateAdd() method is called with the
+ * options parameter having the value undefined.
+ */
+ calendarDateAddUndefinedOptions() {
+ class CalendarDateAddUndefinedOptions extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.dateAddCallCount = 0;
+ }
+
+ toString() {
+ return "dateadd-undef-options";
+ }
+
+ dateAdd(date, duration, options) {
+ this.dateAddCallCount++;
+ assert.sameValue(options, undefined, "dateAdd shouldn't be called with options");
+ return super.dateAdd(date, duration, options);
+ }
+ }
+ return new CalendarDateAddUndefinedOptions();
+ },
+
+ /*
+ * A custom calendar that asserts its dateAdd() method is called with a
+ * PlainDate instance. Optionally, it also asserts that the PlainDate instance
+ * is the specific object `this.specificPlainDate`, if it is set by the
+ * calling code.
+ */
+ calendarDateAddPlainDateInstance() {
+ class CalendarDateAddPlainDateInstance extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.dateAddCallCount = 0;
+ this.specificPlainDate = undefined;
+ }
+
+ toString() {
+ return "dateadd-plain-date-instance";
+ }
+
+ dateFromFields(...args) {
+ return super.dateFromFields(...args).withCalendar(this);
+ }
+
+ dateAdd(date, duration, options) {
+ this.dateAddCallCount++;
+ assert(date instanceof Temporal.PlainDate, "dateAdd() should be called with a PlainDate instance");
+ if (this.dateAddCallCount === 1 && this.specificPlainDate) {
+ assert.sameValue(date, this.specificPlainDate, `dateAdd() should be called first with the specific PlainDate instance ${this.specificPlainDate}`);
+ }
+ return super.dateAdd(date, duration, options).withCalendar(this);
+ }
+ }
+ return new CalendarDateAddPlainDateInstance();
+ },
+
+ /*
+ * A custom calendar that returns an iterable instead of an array from its
+ * fields() method, otherwise identical to the ISO calendar.
+ */
+ calendarFieldsIterable() {
+ class CalendarFieldsIterable extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.fieldsCallCount = 0;
+ this.fieldsCalledWith = [];
+ this.iteratorExhausted = [];
+ }
+
+ toString() {
+ return "fields-iterable";
+ }
+
+ fields(fieldNames) {
+ this.fieldsCallCount++;
+ this.fieldsCalledWith.push(fieldNames.slice());
+ this.iteratorExhausted.push(false);
+ return {
+ callIndex: this.fieldsCallCount - 1,
+ calendar: this,
+ *[Symbol.iterator]() {
+ yield* this.calendar.fieldsCalledWith[this.callIndex];
+ this.calendar.iteratorExhausted[this.callIndex] = true;
+ },
+ };
+ }
+ }
+ return new CalendarFieldsIterable();
+ },
+
+ /*
+ * A custom calendar that asserts its ...FromFields() methods are called with
+ * the options parameter having the value undefined.
+ */
+ calendarFromFieldsUndefinedOptions() {
+ class CalendarFromFieldsUndefinedOptions extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.dateFromFieldsCallCount = 0;
+ this.monthDayFromFieldsCallCount = 0;
+ this.yearMonthFromFieldsCallCount = 0;
+ }
+
+ toString() {
+ return "from-fields-undef-options";
+ }
+
+ dateFromFields(fields, options) {
+ this.dateFromFieldsCallCount++;
+ assert.sameValue(options, undefined, "dateFromFields shouldn't be called with options");
+ return super.dateFromFields(fields, options);
+ }
+
+ yearMonthFromFields(fields, options) {
+ this.yearMonthFromFieldsCallCount++;
+ assert.sameValue(options, undefined, "yearMonthFromFields shouldn't be called with options");
+ return super.yearMonthFromFields(fields, options);
+ }
+
+ monthDayFromFields(fields, options) {
+ this.monthDayFromFieldsCallCount++;
+ assert.sameValue(options, undefined, "monthDayFromFields shouldn't be called with options");
+ return super.monthDayFromFields(fields, options);
+ }
+ }
+ return new CalendarFromFieldsUndefinedOptions();
+ },
+
+ /*
+ * A custom calendar that modifies the fields object passed in to
+ * dateFromFields, sabotaging its time properties.
+ */
+ calendarMakeInfinityTime() {
+ class CalendarMakeInfinityTime extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateFromFields(fields, options) {
+ const retval = super.dateFromFields(fields, options);
+ fields.hour = Infinity;
+ fields.minute = Infinity;
+ fields.second = Infinity;
+ fields.millisecond = Infinity;
+ fields.microsecond = Infinity;
+ fields.nanosecond = Infinity;
+ return retval;
+ }
+ }
+ return new CalendarMakeInfinityTime();
+ },
+
+ /*
+ * A custom calendar that defines getters on the fields object passed into
+ * dateFromFields that throw, sabotaging its time properties.
+ */
+ calendarMakeInvalidGettersTime() {
+ class CalendarMakeInvalidGettersTime extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateFromFields(fields, options) {
+ const retval = super.dateFromFields(fields, options);
+ const throwingDescriptor = {
+ get() {
+ throw new Test262Error("reading a sabotaged time field");
+ },
+ };
+ Object.defineProperties(fields, {
+ hour: throwingDescriptor,
+ minute: throwingDescriptor,
+ second: throwingDescriptor,
+ millisecond: throwingDescriptor,
+ microsecond: throwingDescriptor,
+ nanosecond: throwingDescriptor,
+ });
+ return retval;
+ }
+ }
+ return new CalendarMakeInvalidGettersTime();
+ },
+
+ /*
+ * A custom calendar whose mergeFields() method returns a proxy object with
+ * all of its Get and HasProperty operations observable, as well as adding a
+ * "shouldNotBeCopied": true property.
+ */
+ calendarMergeFieldsGetters() {
+ class CalendarMergeFieldsGetters extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.mergeFieldsReturnOperations = [];
+ }
+
+ toString() {
+ return "merge-fields-getters";
+ }
+
+ dateFromFields(fields, options) {
+ assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied");
+ return super.dateFromFields(fields, options);
+ }
+
+ yearMonthFromFields(fields, options) {
+ assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied");
+ return super.yearMonthFromFields(fields, options);
+ }
+
+ monthDayFromFields(fields, options) {
+ assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied");
+ return super.monthDayFromFields(fields, options);
+ }
+
+ mergeFields(fields, additionalFields) {
+ const retval = super.mergeFields(fields, additionalFields);
+ retval._calendar = this;
+ retval.shouldNotBeCopied = true;
+ return new Proxy(retval, {
+ get(target, key) {
+ target._calendar.mergeFieldsReturnOperations.push(`get ${key}`);
+ const result = target[key];
+ if (result === undefined) {
+ return undefined;
+ }
+ return TemporalHelpers.toPrimitiveObserver(target._calendar.mergeFieldsReturnOperations, result, key);
+ },
+ has(target, key) {
+ target._calendar.mergeFieldsReturnOperations.push(`has ${key}`);
+ return key in target;
+ },
+ });
+ }
+ }
+ return new CalendarMergeFieldsGetters();
+ },
+
+ /*
+ * A custom calendar whose mergeFields() method returns a primitive value,
+ * given by @primitive, and which records the number of calls made to its
+ * dateFromFields(), yearMonthFromFields(), and monthDayFromFields() methods.
+ */
+ calendarMergeFieldsReturnsPrimitive(primitive) {
+ class CalendarMergeFieldsPrimitive extends Temporal.Calendar {
+ constructor(mergeFieldsReturnValue) {
+ super("iso8601");
+ this._mergeFieldsReturnValue = mergeFieldsReturnValue;
+ this.dateFromFieldsCallCount = 0;
+ this.monthDayFromFieldsCallCount = 0;
+ this.yearMonthFromFieldsCallCount = 0;
+ }
+
+ toString() {
+ return "merge-fields-primitive";
+ }
+
+ dateFromFields(fields, options) {
+ this.dateFromFieldsCallCount++;
+ return super.dateFromFields(fields, options);
+ }
+
+ yearMonthFromFields(fields, options) {
+ this.yearMonthFromFieldsCallCount++;
+ return super.yearMonthFromFields(fields, options);
+ }
+
+ monthDayFromFields(fields, options) {
+ this.monthDayFromFieldsCallCount++;
+ return super.monthDayFromFields(fields, options);
+ }
+
+ mergeFields() {
+ return this._mergeFieldsReturnValue;
+ }
+ }
+ return new CalendarMergeFieldsPrimitive(primitive);
+ },
+
+ /*
+ * A custom calendar whose fields() method returns the same value as the
+ * iso8601 calendar, with the addition of extraFields provided as parameter.
+ */
+ calendarWithExtraFields(fields) {
+ class CalendarWithExtraFields extends Temporal.Calendar {
+ constructor(extraFields) {
+ super("iso8601");
+ this._extraFields = extraFields;
+ }
+
+ fields(fieldNames) {
+ return super.fields(fieldNames).concat(this._extraFields);
+ }
+ }
+
+ return new CalendarWithExtraFields(fields);
+ },
+
+ /*
+ * crossDateLineTimeZone():
+ *
+ * This returns an instance of a custom time zone class that implements one
+ * single transition where the time zone moves from one side of the
+ * International Date Line to the other, for the purpose of testing time zone
+ * calculations without depending on system time zone data.
+ *
+ * The transition occurs at epoch second 1325239200 and goes from offset
+ * -10:00 to +14:00. In other words, the time zone skips the whole calendar
+ * day of 2011-12-30. This is the same as the real-life transition in the
+ * Pacific/Apia time zone.
+ */
+ crossDateLineTimeZone() {
+ const { compare } = Temporal.PlainDate;
+ const skippedDay = new Temporal.PlainDate(2011, 12, 30);
+ const transitionEpoch = 1325239200_000_000_000n;
+ const beforeOffset = new Temporal.TimeZone("-10:00");
+ const afterOffset = new Temporal.TimeZone("+14:00");
+
+ class CrossDateLineTimeZone extends Temporal.TimeZone {
+ constructor() {
+ super("+14:00");
+ }
+
+ getOffsetNanosecondsFor(instant) {
+ if (instant.epochNanoseconds < transitionEpoch) {
+ return beforeOffset.getOffsetNanosecondsFor(instant);
+ }
+ return afterOffset.getOffsetNanosecondsFor(instant);
+ }
+
+ getPossibleInstantsFor(datetime) {
+ const comparison = compare(datetime.toPlainDate(), skippedDay);
+ if (comparison === 0) {
+ return [];
+ }
+ if (comparison < 0) {
+ return [beforeOffset.getInstantFor(datetime)];
+ }
+ return [afterOffset.getInstantFor(datetime)];
+ }
+
+ getPreviousTransition(instant) {
+ if (instant.epochNanoseconds > transitionEpoch) return new Temporal.Instant(transitionEpoch);
+ return null;
+ }
+
+ getNextTransition(instant) {
+ if (instant.epochNanoseconds < transitionEpoch) return new Temporal.Instant(transitionEpoch);
+ return null;
+ }
+
+ toString() {
+ return "Custom/Date_Line";
+ }
+ }
+ return new CrossDateLineTimeZone();
+ },
+
+ /*
+ * observeProperty(calls, object, propertyName, value):
+ *
+ * Defines an own property @object.@propertyName with value @value, that
+ * will log any calls to its accessors to the array @calls.
+ */
+ observeProperty(calls, object, propertyName, value, objectName = "") {
+ Object.defineProperty(object, propertyName, {
+ get() {
+ calls.push(`get ${formatPropertyName(propertyName, objectName)}`);
+ return value;
+ },
+ set(v) {
+ calls.push(`set ${formatPropertyName(propertyName, objectName)}`);
+ }
+ });
+ },
+
+ /*
+ * observeMethod(calls, object, propertyName, value):
+ *
+ * Defines an own property @object.@propertyName with value @value, that
+ * will log any calls of @value to the array @calls.
+ */
+ observeMethod(calls, object, propertyName, objectName = "") {
+ const method = object[propertyName];
+ object[propertyName] = function () {
+ calls.push(`call ${formatPropertyName(propertyName, objectName)}`);
+ return method.apply(object, arguments);
+ };
+ },
+
+ /*
+ * Used for substituteMethod to indicate default behavior instead of a
+ * substituted value
+ */
+ SUBSTITUTE_SKIP: SKIP_SYMBOL,
+
+ /*
+ * substituteMethod(object, propertyName, values):
+ *
+ * Defines an own property @object.@propertyName that will, for each
+ * subsequent call to the method previously defined as
+ * @object.@propertyName:
+ * - Call the method, if no more values remain
+ * - Call the method, if the value in @values for the corresponding call
+ * is SUBSTITUTE_SKIP
+ * - Otherwise, return the corresponding value in @value
+ */
+ substituteMethod(object, propertyName, values) {
+ let calls = 0;
+ const method = object[propertyName];
+ object[propertyName] = function () {
+ if (calls >= values.length) {
+ return method.apply(object, arguments);
+ } else if (values[calls] === SKIP_SYMBOL) {
+ calls++;
+ return method.apply(object, arguments);
+ } else {
+ return values[calls++];
+ }
+ };
+ },
+
+ /*
+ * calendarObserver:
+ * A custom calendar that behaves exactly like the ISO 8601 calendar but
+ * tracks calls to any of its methods, and Get/Has operations on its
+ * properties, by appending messages to an array. This is for the purpose of
+ * testing order of operations that are observable from user code.
+ * objectName is used in the log.
+ */
+ calendarObserver(calls, objectName, methodOverrides = {}) {
+ function removeExtraHasPropertyChecks(objectName, calls) {
+ // Inserting the tracking calendar into the return values of methods
+ // that we chain up into the ISO calendar for, causes extra HasProperty
+ // checks, which we observe. This removes them so that we don't leak
+ // implementation details of the helper into the test code.
+ assert.sameValue(calls.pop(), `has ${objectName}.yearOfWeek`);
+ assert.sameValue(calls.pop(), `has ${objectName}.yearMonthFromFields`);
+ assert.sameValue(calls.pop(), `has ${objectName}.year`);
+ assert.sameValue(calls.pop(), `has ${objectName}.weekOfYear`);
+ assert.sameValue(calls.pop(), `has ${objectName}.monthsInYear`);
+ assert.sameValue(calls.pop(), `has ${objectName}.monthDayFromFields`);
+ assert.sameValue(calls.pop(), `has ${objectName}.monthCode`);
+ assert.sameValue(calls.pop(), `has ${objectName}.month`);
+ assert.sameValue(calls.pop(), `has ${objectName}.mergeFields`);
+ assert.sameValue(calls.pop(), `has ${objectName}.inLeapYear`);
+ assert.sameValue(calls.pop(), `has ${objectName}.id`);
+ assert.sameValue(calls.pop(), `has ${objectName}.fields`);
+ assert.sameValue(calls.pop(), `has ${objectName}.daysInYear`);
+ assert.sameValue(calls.pop(), `has ${objectName}.daysInWeek`);
+ assert.sameValue(calls.pop(), `has ${objectName}.daysInMonth`);
+ assert.sameValue(calls.pop(), `has ${objectName}.dayOfYear`);
+ assert.sameValue(calls.pop(), `has ${objectName}.dayOfWeek`);
+ assert.sameValue(calls.pop(), `has ${objectName}.day`);
+ assert.sameValue(calls.pop(), `has ${objectName}.dateUntil`);
+ assert.sameValue(calls.pop(), `has ${objectName}.dateFromFields`);
+ assert.sameValue(calls.pop(), `has ${objectName}.dateAdd`);
+ }
+
+ const iso8601 = new Temporal.Calendar("iso8601");
+ const trackingMethods = {
+ dateFromFields(...args) {
+ calls.push(`call ${objectName}.dateFromFields`);
+ if ('dateFromFields' in methodOverrides) {
+ const value = methodOverrides.dateFromFields;
+ return typeof value === "function" ? value(...args) : value;
+ }
+ const originalResult = iso8601.dateFromFields(...args);
+ // Replace the calendar in the result with the call-tracking calendar
+ const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
+ const result = new Temporal.PlainDate(isoYear, isoMonth, isoDay, this);
+ removeExtraHasPropertyChecks(objectName, calls);
+ return result;
+ },
+ yearMonthFromFields(...args) {
+ calls.push(`call ${objectName}.yearMonthFromFields`);
+ if ('yearMonthFromFields' in methodOverrides) {
+ const value = methodOverrides.yearMonthFromFields;
+ return typeof value === "function" ? value(...args) : value;
+ }
+ const originalResult = iso8601.yearMonthFromFields(...args);
+ // Replace the calendar in the result with the call-tracking calendar
+ const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
+ const result = new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay);
+ removeExtraHasPropertyChecks(objectName, calls);
+ return result;
+ },
+ monthDayFromFields(...args) {
+ calls.push(`call ${objectName}.monthDayFromFields`);
+ if ('monthDayFromFields' in methodOverrides) {
+ const value = methodOverrides.monthDayFromFields;
+ return typeof value === "function" ? value(...args) : value;
+ }
+ const originalResult = iso8601.monthDayFromFields(...args);
+ // Replace the calendar in the result with the call-tracking calendar
+ const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
+ const result = new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear);
+ removeExtraHasPropertyChecks(objectName, calls);
+ return result;
+ },
+ dateAdd(...args) {
+ calls.push(`call ${objectName}.dateAdd`);
+ if ('dateAdd' in methodOverrides) {
+ const value = methodOverrides.dateAdd;
+ return typeof value === "function" ? value(...args) : value;
+ }
+ const originalResult = iso8601.dateAdd(...args);
+ const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
+ const result = new Temporal.PlainDate(isoYear, isoMonth, isoDay, this);
+ removeExtraHasPropertyChecks(objectName, calls);
+ return result;
+ },
+ id: "iso8601",
+ };
+ // Automatically generate the other methods that don't need any custom code
+ [
+ "dateUntil",
+ "day",
+ "dayOfWeek",
+ "dayOfYear",
+ "daysInMonth",
+ "daysInWeek",
+ "daysInYear",
+ "era",
+ "eraYear",
+ "fields",
+ "inLeapYear",
+ "mergeFields",
+ "month",
+ "monthCode",
+ "monthsInYear",
+ "toString",
+ "weekOfYear",
+ "year",
+ "yearOfWeek",
+ ].forEach((methodName) => {
+ trackingMethods[methodName] = function (...args) {
+ calls.push(`call ${formatPropertyName(methodName, objectName)}`);
+ if (methodName in methodOverrides) {
+ const value = methodOverrides[methodName];
+ return typeof value === "function" ? value(...args) : value;
+ }
+ return iso8601[methodName](...args);
+ };
+ });
+ return new Proxy(trackingMethods, {
+ get(target, key, receiver) {
+ const result = Reflect.get(target, key, receiver);
+ calls.push(`get ${formatPropertyName(key, objectName)}`);
+ return result;
+ },
+ has(target, key) {
+ calls.push(`has ${formatPropertyName(key, objectName)}`);
+ return Reflect.has(target, key);
+ },
+ });
+ },
+
+ /*
+ * A custom calendar that does not allow any of its methods to be called, for
+ * the purpose of asserting that a particular operation does not call into
+ * user code.
+ */
+ calendarThrowEverything() {
+ class CalendarThrowEverything extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ toString() {
+ TemporalHelpers.assertUnreachable("toString should not be called");
+ }
+ dateFromFields() {
+ TemporalHelpers.assertUnreachable("dateFromFields should not be called");
+ }
+ yearMonthFromFields() {
+ TemporalHelpers.assertUnreachable("yearMonthFromFields should not be called");
+ }
+ monthDayFromFields() {
+ TemporalHelpers.assertUnreachable("monthDayFromFields should not be called");
+ }
+ dateAdd() {
+ TemporalHelpers.assertUnreachable("dateAdd should not be called");
+ }
+ dateUntil() {
+ TemporalHelpers.assertUnreachable("dateUntil should not be called");
+ }
+ era() {
+ TemporalHelpers.assertUnreachable("era should not be called");
+ }
+ eraYear() {
+ TemporalHelpers.assertUnreachable("eraYear should not be called");
+ }
+ year() {
+ TemporalHelpers.assertUnreachable("year should not be called");
+ }
+ month() {
+ TemporalHelpers.assertUnreachable("month should not be called");
+ }
+ monthCode() {
+ TemporalHelpers.assertUnreachable("monthCode should not be called");
+ }
+ day() {
+ TemporalHelpers.assertUnreachable("day should not be called");
+ }
+ fields() {
+ TemporalHelpers.assertUnreachable("fields should not be called");
+ }
+ mergeFields() {
+ TemporalHelpers.assertUnreachable("mergeFields should not be called");
+ }
+ }
+
+ return new CalendarThrowEverything();
+ },
+
+ /*
+ * oneShiftTimeZone(shiftInstant, shiftNanoseconds):
+ *
+ * In the case of a spring-forward time zone offset transition (skipped time),
+ * and disambiguation === 'earlier', BuiltinTimeZoneGetInstantFor subtracts a
+ * negative number of nanoseconds from a PlainDateTime, which should balance
+ * with the microseconds field.
+ *
+ * This returns an instance of a custom time zone class which skips a length
+ * of time equal to shiftNanoseconds (a number), at the Temporal.Instant
+ * shiftInstant. Before shiftInstant, it's identical to UTC, and after
+ * shiftInstant it's a constant-offset time zone.
+ *
+ * It provides a getPossibleInstantsForCalledWith member which is an array
+ * with the result of calling toString() on any PlainDateTimes passed to
+ * getPossibleInstantsFor().
+ */
+ oneShiftTimeZone(shiftInstant, shiftNanoseconds) {
+ class OneShiftTimeZone extends Temporal.TimeZone {
+ constructor(shiftInstant, shiftNanoseconds) {
+ super("+00:00");
+ this._shiftInstant = shiftInstant;
+ this._epoch1 = shiftInstant.epochNanoseconds;
+ this._epoch2 = this._epoch1 + BigInt(shiftNanoseconds);
+ this._shiftNanoseconds = shiftNanoseconds;
+ this._shift = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, this._shiftNanoseconds);
+ this.getPossibleInstantsForCalledWith = [];
+ }
+
+ _isBeforeShift(instant) {
+ return instant.epochNanoseconds < this._epoch1;
+ }
+
+ getOffsetNanosecondsFor(instant) {
+ return this._isBeforeShift(instant) ? 0 : this._shiftNanoseconds;
+ }
+
+ getPossibleInstantsFor(plainDateTime) {
+ this.getPossibleInstantsForCalledWith.push(plainDateTime.toString({ calendarName: "never" }));
+ const [instant] = super.getPossibleInstantsFor(plainDateTime);
+ if (this._shiftNanoseconds > 0) {
+ if (this._isBeforeShift(instant)) return [instant];
+ if (instant.epochNanoseconds < this._epoch2) return [];
+ return [instant.subtract(this._shift)];
+ }
+ if (instant.epochNanoseconds < this._epoch2) return [instant];
+ const shifted = instant.subtract(this._shift);
+ if (this._isBeforeShift(instant)) return [instant, shifted];
+ return [shifted];
+ }
+
+ getNextTransition(instant) {
+ return this._isBeforeShift(instant) ? this._shiftInstant : null;
+ }
+
+ getPreviousTransition(instant) {
+ return this._isBeforeShift(instant) ? null : this._shiftInstant;
+ }
+
+ toString() {
+ return "Custom/One_Shift";
+ }
+ }
+ return new OneShiftTimeZone(shiftInstant, shiftNanoseconds);
+ },
+
+ /*
+ * propertyBagObserver():
+ * Returns an object that behaves like the given propertyBag but tracks Get
+ * and Has operations on any of its properties, by appending messages to an
+ * array. If the value of a property in propertyBag is a primitive, the value
+ * of the returned object's property will additionally be a
+ * TemporalHelpers.toPrimitiveObserver that will track calls to its toString
+ * and valueOf methods in the same array. This is for the purpose of testing
+ * order of operations that are observable from user code. objectName is used
+ * in the log.
+ */
+ propertyBagObserver(calls, propertyBag, objectName) {
+ return new Proxy(propertyBag, {
+ ownKeys(target) {
+ calls.push(`ownKeys ${objectName}`);
+ return Reflect.ownKeys(target);
+ },
+ getOwnPropertyDescriptor(target, key) {
+ calls.push(`getOwnPropertyDescriptor ${formatPropertyName(key, objectName)}`);
+ return Reflect.getOwnPropertyDescriptor(target, key);
+ },
+ get(target, key, receiver) {
+ calls.push(`get ${formatPropertyName(key, objectName)}`);
+ const result = Reflect.get(target, key, receiver);
+ if (result === undefined) {
+ return undefined;
+ }
+ if ((result !== null && typeof result === "object") || typeof result === "function") {
+ return result;
+ }
+ return TemporalHelpers.toPrimitiveObserver(calls, result, `${formatPropertyName(key, objectName)}`);
+ },
+ has(target, key) {
+ calls.push(`has ${formatPropertyName(key, objectName)}`);
+ return Reflect.has(target, key);
+ },
+ });
+ },
+
+ /*
+ * specificOffsetTimeZone():
+ *
+ * This returns an instance of a custom time zone class, which returns a
+ * specific custom value from its getOffsetNanosecondsFrom() method. This is
+ * for the purpose of testing the validation of what this method returns.
+ *
+ * It also returns an empty array from getPossibleInstantsFor(), so as to
+ * trigger calls to getOffsetNanosecondsFor() when used from the
+ * BuiltinTimeZoneGetInstantFor operation.
+ */
+ specificOffsetTimeZone(offsetValue) {
+ class SpecificOffsetTimeZone extends Temporal.TimeZone {
+ constructor(offsetValue) {
+ super("UTC");
+ this._offsetValue = offsetValue;
+ }
+
+ getOffsetNanosecondsFor() {
+ return this._offsetValue;
+ }
+
+ getPossibleInstantsFor(dt) {
+ if (typeof this._offsetValue !== 'number' || Math.abs(this._offsetValue) >= 86400e9 || isNaN(this._offsetValue)) return [];
+ const zdt = dt.toZonedDateTime("UTC").add({ nanoseconds: -this._offsetValue });
+ return [zdt.toInstant()];
+ }
+
+ get id() {
+ return this.getOffsetStringFor(new Temporal.Instant(0n));
+ }
+ }
+ return new SpecificOffsetTimeZone(offsetValue);
+ },
+
+ /*
+ * springForwardFallBackTimeZone():
+ *
+ * This returns an instance of a custom time zone class that implements one
+ * single spring-forward/fall-back transition, for the purpose of testing the
+ * disambiguation option, without depending on system time zone data.
+ *
+ * The spring-forward occurs at epoch second 954669600 (2000-04-02T02:00
+ * local) and goes from offset -08:00 to -07:00.
+ *
+ * The fall-back occurs at epoch second 972810000 (2000-10-29T02:00 local) and
+ * goes from offset -07:00 to -08:00.
+ */
+ springForwardFallBackTimeZone() {
+ const { compare } = Temporal.PlainDateTime;
+ const springForwardLocal = new Temporal.PlainDateTime(2000, 4, 2, 2);
+ const springForwardEpoch = 954669600_000_000_000n;
+ const fallBackLocal = new Temporal.PlainDateTime(2000, 10, 29, 1);
+ const fallBackEpoch = 972810000_000_000_000n;
+ const winterOffset = new Temporal.TimeZone('-08:00');
+ const summerOffset = new Temporal.TimeZone('-07:00');
+
+ class SpringForwardFallBackTimeZone extends Temporal.TimeZone {
+ constructor() {
+ super("-08:00");
+ }
+
+ getOffsetNanosecondsFor(instant) {
+ if (instant.epochNanoseconds < springForwardEpoch ||
+ instant.epochNanoseconds >= fallBackEpoch) {
+ return winterOffset.getOffsetNanosecondsFor(instant);
+ }
+ return summerOffset.getOffsetNanosecondsFor(instant);
+ }
+
+ getPossibleInstantsFor(datetime) {
+ if (compare(datetime, springForwardLocal) >= 0 && compare(datetime, springForwardLocal.add({ hours: 1 })) < 0) {
+ return [];
+ }
+ if (compare(datetime, fallBackLocal) >= 0 && compare(datetime, fallBackLocal.add({ hours: 1 })) < 0) {
+ return [summerOffset.getInstantFor(datetime), winterOffset.getInstantFor(datetime)];
+ }
+ if (compare(datetime, springForwardLocal) < 0 || compare(datetime, fallBackLocal) >= 0) {
+ return [winterOffset.getInstantFor(datetime)];
+ }
+ return [summerOffset.getInstantFor(datetime)];
+ }
+
+ getPreviousTransition(instant) {
+ if (instant.epochNanoseconds > fallBackEpoch) return new Temporal.Instant(fallBackEpoch);
+ if (instant.epochNanoseconds > springForwardEpoch) return new Temporal.Instant(springForwardEpoch);
+ return null;
+ }
+
+ getNextTransition(instant) {
+ if (instant.epochNanoseconds < springForwardEpoch) return new Temporal.Instant(springForwardEpoch);
+ if (instant.epochNanoseconds < fallBackEpoch) return new Temporal.Instant(fallBackEpoch);
+ return null;
+ }
+
+ get id() {
+ return "Custom/Spring_Fall";
+ }
+
+ toString() {
+ return "Custom/Spring_Fall";
+ }
+ }
+ return new SpringForwardFallBackTimeZone();
+ },
+
+ /*
+ * timeZoneObserver:
+ * A custom calendar that behaves exactly like the UTC time zone but tracks
+ * calls to any of its methods, and Get/Has operations on its properties, by
+ * appending messages to an array. This is for the purpose of testing order of
+ * operations that are observable from user code. objectName is used in the
+ * log. methodOverrides is an optional object containing properties with the
+ * same name as Temporal.TimeZone methods. If the property value is a function
+ * it will be called with the proper arguments instead of the UTC method.
+ * Otherwise, the property value will be returned directly.
+ */
+ timeZoneObserver(calls, objectName, methodOverrides = {}) {
+ const utc = new Temporal.TimeZone("UTC");
+ const trackingMethods = {
+ id: "UTC",
+ };
+ // Automatically generate the methods
+ ["getOffsetNanosecondsFor", "getPossibleInstantsFor", "toString"].forEach((methodName) => {
+ trackingMethods[methodName] = function (...args) {
+ calls.push(`call ${formatPropertyName(methodName, objectName)}`);
+ if (methodName in methodOverrides) {
+ const value = methodOverrides[methodName];
+ return typeof value === "function" ? value(...args) : value;
+ }
+ return utc[methodName](...args);
+ };
+ });
+ return new Proxy(trackingMethods, {
+ get(target, key, receiver) {
+ const result = Reflect.get(target, key, receiver);
+ calls.push(`get ${formatPropertyName(key, objectName)}`);
+ return result;
+ },
+ has(target, key) {
+ calls.push(`has ${formatPropertyName(key, objectName)}`);
+ return Reflect.has(target, key);
+ },
+ });
+ },
+
+ /*
+ * A custom time zone that does not allow any of its methods to be called, for
+ * the purpose of asserting that a particular operation does not call into
+ * user code.
+ */
+ timeZoneThrowEverything() {
+ class TimeZoneThrowEverything extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ }
+ getOffsetNanosecondsFor() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be called");
+ }
+ getPossibleInstantsFor() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be called");
+ }
+ toString() {
+ TemporalHelpers.assertUnreachable("toString should not be called");
+ }
+ }
+
+ return new TimeZoneThrowEverything();
+ },
+
+ /*
+ * Returns an object that will append logs of any Gets or Calls of its valueOf
+ * or toString properties to the array calls. Both valueOf and toString will
+ * return the actual primitiveValue. propertyName is used in the log.
+ */
+ toPrimitiveObserver(calls, primitiveValue, propertyName) {
+ return {
+ get valueOf() {
+ calls.push(`get ${propertyName}.valueOf`);
+ return function () {
+ calls.push(`call ${propertyName}.valueOf`);
+ return primitiveValue;
+ };
+ },
+ get toString() {
+ calls.push(`get ${propertyName}.toString`);
+ return function () {
+ calls.push(`call ${propertyName}.toString`);
+ if (primitiveValue === undefined) return undefined;
+ return primitiveValue.toString();
+ };
+ },
+ };
+ },
+
+ /*
+ * An object containing further methods that return arrays of ISO strings, for
+ * testing parsers.
+ */
+ ISO: {
+ /*
+ * PlainMonthDay strings that are not valid.
+ */
+ plainMonthDayStringsInvalid() {
+ return [
+ "11-18junk",
+ "11-18[u-ca=gregory]",
+ "11-18[u-ca=hebrew]",
+ ];
+ },
+
+ /*
+ * PlainMonthDay strings that are valid and that should produce October 1st.
+ */
+ plainMonthDayStringsValid() {
+ return [
+ "10-01",
+ "1001",
+ "1965-10-01",
+ "1976-10-01T152330.1+00:00",
+ "19761001T15:23:30.1+00:00",
+ "1976-10-01T15:23:30.1+0000",
+ "1976-10-01T152330.1+0000",
+ "19761001T15:23:30.1+0000",
+ "19761001T152330.1+00:00",
+ "19761001T152330.1+0000",
+ "+001976-10-01T152330.1+00:00",
+ "+0019761001T15:23:30.1+00:00",
+ "+001976-10-01T15:23:30.1+0000",
+ "+001976-10-01T152330.1+0000",
+ "+0019761001T15:23:30.1+0000",
+ "+0019761001T152330.1+00:00",
+ "+0019761001T152330.1+0000",
+ "1976-10-01T15:23:00",
+ "1976-10-01T15:23",
+ "1976-10-01T15",
+ "1976-10-01",
+ "--10-01",
+ "--1001",
+ ];
+ },
+
+ /*
+ * PlainTime strings that may be mistaken for PlainMonthDay or
+ * PlainYearMonth strings, and so require a time designator.
+ */
+ plainTimeStringsAmbiguous() {
+ const ambiguousStrings = [
+ "2021-12", // ambiguity between YYYY-MM and HHMM-UU
+ "2021-12[-12:00]", // ditto, TZ does not disambiguate
+ "1214", // ambiguity between MMDD and HHMM
+ "0229", // ditto, including MMDD that doesn't occur every year
+ "1130", // ditto, including DD that doesn't occur in every month
+ "12-14", // ambiguity between MM-DD and HH-UU
+ "12-14[-14:00]", // ditto, TZ does not disambiguate
+ "202112", // ambiguity between YYYYMM and HHMMSS
+ "202112[UTC]", // ditto, TZ does not disambiguate
+ ];
+ // Adding a calendar annotation to one of these strings must not cause
+ // disambiguation in favour of time.
+ const stringsWithCalendar = ambiguousStrings.map((s) => s + '[u-ca=iso8601]');
+ return ambiguousStrings.concat(stringsWithCalendar);
+ },
+
+ /*
+ * PlainTime strings that are of similar form to PlainMonthDay and
+ * PlainYearMonth strings, but are not ambiguous due to components that
+ * aren't valid as months or days.
+ */
+ plainTimeStringsUnambiguous() {
+ return [
+ "2021-13", // 13 is not a month
+ "202113", // ditto
+ "2021-13[-13:00]", // ditto
+ "202113[-13:00]", // ditto
+ "0000-00", // 0 is not a month
+ "000000", // ditto
+ "0000-00[UTC]", // ditto
+ "000000[UTC]", // ditto
+ "1314", // 13 is not a month
+ "13-14", // ditto
+ "1232", // 32 is not a day
+ "0230", // 30 is not a day in February
+ "0631", // 31 is not a day in June
+ "0000", // 0 is neither a month nor a day
+ "00-00", // ditto
+ ];
+ },
+
+ /*
+ * PlainYearMonth-like strings that are not valid.
+ */
+ plainYearMonthStringsInvalid() {
+ return [
+ "2020-13",
+ ];
+ },
+
+ /*
+ * PlainYearMonth-like strings that are valid and should produce November
+ * 1976 in the ISO 8601 calendar.
+ */
+ plainYearMonthStringsValid() {
+ return [
+ "1976-11",
+ "1976-11-10",
+ "1976-11-01T09:00:00+00:00",
+ "1976-11-01T00:00:00+05:00",
+ "197611",
+ "+00197611",
+ "1976-11-18T15:23:30.1\u221202:00",
+ "1976-11-18T152330.1+00:00",
+ "19761118T15:23:30.1+00:00",
+ "1976-11-18T15:23:30.1+0000",
+ "1976-11-18T152330.1+0000",
+ "19761118T15:23:30.1+0000",
+ "19761118T152330.1+00:00",
+ "19761118T152330.1+0000",
+ "+001976-11-18T152330.1+00:00",
+ "+0019761118T15:23:30.1+00:00",
+ "+001976-11-18T15:23:30.1+0000",
+ "+001976-11-18T152330.1+0000",
+ "+0019761118T15:23:30.1+0000",
+ "+0019761118T152330.1+00:00",
+ "+0019761118T152330.1+0000",
+ "1976-11-18T15:23",
+ "1976-11-18T15",
+ "1976-11-18",
+ ];
+ },
+
+ /*
+ * PlainYearMonth-like strings that are valid and should produce November of
+ * the ISO year -9999.
+ */
+ plainYearMonthStringsValidNegativeYear() {
+ return [
+ "\u2212009999-11",
+ ];
+ },
+ }
+};
diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/timezone-offset.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/timezone-offset.js
new file mode 100644
index 0000000000..422507917a
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/timezone-offset.js
@@ -0,0 +1,24 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-timezone-objects
+description: +01:00
+features: [Temporal]
+---*/
+
+var zone = new Temporal.TimeZone("+01:00");
+var inst = Temporal.Instant.fromEpochSeconds(Math.floor(Math.random() * 1000000000));
+var dtm = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+assert.sameValue(zone.id, `${ zone }`)
+assert.sameValue(zone.getOffsetNanosecondsFor(inst), 3600000000000)
+assert(zone.getPlainDateTimeFor(inst) instanceof Temporal.PlainDateTime)
+assert(zone.getInstantFor(dtm) instanceof Temporal.Instant)
+assert.sameValue(zone.getNextTransition(inst), null)
+assert.sameValue(zone.getPreviousTransition(inst), null)
+
+// wraps around to the next day
+assert.sameValue(`${ zone.getPlainDateTimeFor(Temporal.Instant.from("2020-02-06T23:59Z")) }`, "2020-02-07T00:59:00")
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/old/timezone-utc-offset.js b/js/src/tests/test262/staging/Temporal/TimeZone/old/timezone-utc-offset.js
new file mode 100644
index 0000000000..7d8a75e581
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/TimeZone/old/timezone-utc-offset.js
@@ -0,0 +1,21 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-timezone-objects
+description: UTC
+features: [Temporal]
+---*/
+
+var zone = new Temporal.TimeZone("UTC");
+var inst = Temporal.Instant.fromEpochSeconds(Math.floor(Math.random() * 1000000000));
+var dtm = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+assert.sameValue(zone.id, `${ zone }`)
+assert.sameValue(zone.getOffsetNanosecondsFor(inst), 0)
+assert(zone.getPlainDateTimeFor(inst) instanceof Temporal.PlainDateTime)
+assert(zone.getInstantFor(dtm) instanceof Temporal.Instant)
+assert.sameValue(zone.getNextTransition(inst), null)
+assert.sameValue(zone.getPreviousTransition(inst), null)
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/TimeZone/shell.js b/js/src/tests/test262/staging/Temporal/TimeZone/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/TimeZone/shell.js
diff --git a/js/src/tests/test262/staging/Temporal/UserCalendar/browser.js b/js/src/tests/test262/staging/Temporal/UserCalendar/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/UserCalendar/browser.js
diff --git a/js/src/tests/test262/staging/Temporal/UserCalendar/old/browser.js b/js/src/tests/test262/staging/Temporal/UserCalendar/old/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/UserCalendar/old/browser.js
diff --git a/js/src/tests/test262/staging/Temporal/UserCalendar/old/calendar-extra-fields.js b/js/src/tests/test262/staging/Temporal/UserCalendar/old/calendar-extra-fields.js
new file mode 100644
index 0000000000..b9b3c0bc56
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/UserCalendar/old/calendar-extra-fields.js
@@ -0,0 +1,160 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: calendar with extra fields
+features: [Temporal, Array.prototype.includes]
+---*/
+
+class SeasonCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ get id() {
+ return "season";
+ }
+ toString() {
+ return "season";
+ }
+ month(date) {
+ var {isoMonth} = date.getISOFields();
+ return (isoMonth - 1) % 3 + 1;
+ }
+ monthCode(date) {
+ return `M${ this.month(date).toString().padStart(2, "0") }`;
+ }
+ season(date) {
+ var {isoMonth} = date.getISOFields();
+ return Math.floor((isoMonth - 1) / 3) + 1;
+ }
+ _isoMonthCode(fields) {
+ var month = fields.month || +fields.monthCode.slice(1);
+ return `M${ ((fields.season - 1) * 3 + month).toString().padStart(2, "0") }`;
+ }
+ dateFromFields(fields, options) {
+ var monthCode = this._isoMonthCode(fields);
+ delete fields.month;
+ return super.dateFromFields({
+ ...fields,
+ monthCode
+ }, options).withCalendar(this);
+ }
+ yearMonthFromFields(fields, options) {
+ var monthCode = this._isoMonthCode(fields);
+ delete fields.month;
+ const { isoYear, isoMonth, isoDay } = super.yearMonthFromFields({
+ ...fields,
+ monthCode
+ }, options).getISOFields();
+ return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay);
+ }
+ monthDayFromFields(fields, options) {
+ var monthCode = this._isoMonthCode(fields);
+ delete fields.month;
+ const { isoYear, isoMonth, isoDay } = super.monthDayFromFields({
+ ...fields,
+ monthCode
+ }, options).getISOFields();
+ return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear);
+ }
+ fields(fields) {
+ fields = fields.slice();
+ if (fields.includes("month") || fields.includes("monthCode"))
+ fields.push("season");
+ return fields;
+ }
+}
+var calendar = new SeasonCalendar();
+var datetime = new Temporal.PlainDateTime(2019, 9, 15, 0, 0, 0, 0, 0, 0, calendar);
+var date = new Temporal.PlainDate(2019, 9, 15, calendar);
+var yearmonth = new Temporal.PlainYearMonth(2019, 9, calendar);
+var monthday = new Temporal.PlainMonthDay(9, 15, calendar);
+var zoned = new Temporal.ZonedDateTime(1568505600000000000n, "UTC", calendar);
+var propDesc = {
+ get() {
+ return this.getCalendar().season(this);
+ },
+ configurable: true
+};
+Object.defineProperty(Temporal.PlainDateTime.prototype, "season", propDesc);
+Object.defineProperty(Temporal.PlainDate.prototype, "season", propDesc);
+Object.defineProperty(Temporal.PlainYearMonth.prototype, "season", propDesc);
+Object.defineProperty(Temporal.PlainMonthDay.prototype, "season", propDesc);
+Object.defineProperty(Temporal.ZonedDateTime.prototype, "season", propDesc);
+
+// property getter works
+assert.sameValue(datetime.season, 3);
+assert.sameValue(datetime.month, 3);
+assert.sameValue(datetime.monthCode, "M03");
+assert.sameValue(date.season, 3);
+assert.sameValue(date.month, 3);
+assert.sameValue(date.monthCode, "M03");
+assert.sameValue(yearmonth.season, 3);
+assert.sameValue(yearmonth.month, 3);
+assert.sameValue(yearmonth.monthCode, "M03");
+assert.sameValue(monthday.season, 3);
+assert.sameValue(monthday.monthCode, "M03");
+assert.sameValue(zoned.season, 3);
+assert.sameValue(zoned.month, 3);
+assert.sameValue(zoned.monthCode, "M03");
+
+// accepts season in from()
+assert.sameValue(`${ Temporal.PlainDateTime.from({
+ year: 2019,
+ season: 3,
+ month: 3,
+ day: 15,
+ calendar
+}) }`, "2019-09-15T00:00:00[u-ca=season]");
+assert.sameValue(`${ Temporal.PlainDate.from({
+ year: 2019,
+ season: 3,
+ month: 3,
+ day: 15,
+ calendar
+}) }`, "2019-09-15[u-ca=season]");
+assert.sameValue(`${ Temporal.PlainYearMonth.from({
+ year: 2019,
+ season: 3,
+ month: 3,
+ calendar
+}) }`, "2019-09-01[u-ca=season]");
+assert.sameValue(`${ Temporal.PlainMonthDay.from({
+ season: 3,
+ monthCode: "M03",
+ day: 15,
+ calendar
+}) }`, "1972-09-15[u-ca=season]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from({
+ year: 2019,
+ season: 3,
+ month: 3,
+ day: 15,
+ timeZone: "UTC",
+ calendar
+}) }`, "2019-09-15T00:00:00+00:00[UTC][u-ca=season]");
+
+// accepts season in with()
+assert.sameValue(`${ datetime.with({ season: 2 }) }`, "2019-06-15T00:00:00[u-ca=season]");
+assert.sameValue(`${ date.with({ season: 2 }) }`, "2019-06-15[u-ca=season]");
+assert.sameValue(`${ yearmonth.with({ season: 2 }) }`, "2019-06-01[u-ca=season]");
+assert.sameValue(`${ monthday.with({ season: 2 }) }`, "1972-06-15[u-ca=season]");
+assert.sameValue(`${ zoned.with({ season: 2 }) }`, "2019-06-15T00:00:00+00:00[UTC][u-ca=season]");
+
+// translates month correctly in with()
+assert.sameValue(`${ datetime.with({ month: 2 }) }`, "2019-08-15T00:00:00[u-ca=season]");
+assert.sameValue(`${ date.with({ month: 2 }) }`, "2019-08-15[u-ca=season]");
+assert.sameValue(`${ yearmonth.with({ month: 2 }) }`, "2019-08-01[u-ca=season]");
+assert.sameValue(`${ monthday.with({ monthCode: "M02" }) }`, "1972-08-15[u-ca=season]");
+assert.sameValue(`${ zoned.with({ month: 2 }) }`, "2019-08-15T00:00:00+00:00[UTC][u-ca=season]");
+
+delete Temporal.PlainDateTime.prototype.season;
+delete Temporal.PlainDate.prototype.season;
+delete Temporal.PlainYearMonth.prototype.season;
+delete Temporal.PlainMonthDay.prototype.season;
+delete Temporal.ZonedDateTime.prototype.season;
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/UserCalendar/old/calendar-non-trivial-mergefields.js b/js/src/tests/test262/staging/Temporal/UserCalendar/old/calendar-non-trivial-mergefields.js
new file mode 100644
index 0000000000..a3a836525f
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/UserCalendar/old/calendar-non-trivial-mergefields.js
@@ -0,0 +1,154 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: calendar with nontrivial mergeFields implementation
+features: [Temporal, Array.prototype.includes]
+---*/
+
+class CenturyCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ get id() {
+ return 'century';
+ }
+ toString() {
+ return "century";
+ }
+ century(date) {
+ var {isoYear} = date.getISOFields();
+ return Math.ceil(isoYear / 100);
+ }
+ centuryYear(date) {
+ var {isoYear} = date.getISOFields();
+ return isoYear % 100;
+ }
+ _validateFields(fields) {
+ var {year, century, centuryYear} = fields;
+ if (century === undefined !== (centuryYear === undefined)) {
+ throw new TypeError("pass either both or neither of century and centuryYear");
+ }
+ if (year === undefined)
+ return (century - 1) * 100 + centuryYear;
+ if (century !== undefined) {
+ var centuryCalculatedYear = (century - 1) * 100 + centuryYear;
+ if (year !== centuryCalculatedYear) {
+ throw new RangeError("year must agree with century/centuryYear if both given");
+ }
+ }
+ return year;
+ }
+ dateFromFields(fields, options) {
+ var isoYear = this._validateFields(fields);
+ return super.dateFromFields({
+ ...fields,
+ year: isoYear
+ }, options).withCalendar(this);
+ }
+ yearMonthFromFields(fields, options) {
+ var year = this._validateFields(fields);
+ const { isoYear, isoMonth, isoDay } = super.yearMonthFromFields({
+ ...fields,
+ year,
+ }, options).getISOFields();
+ return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay);
+ }
+ monthDayFromFields(fields, options) {
+ var year = this._validateFields(fields);
+ const { isoYear, isoMonth, isoDay } = super.monthDayFromFields({
+ ...fields,
+ year,
+ }, options).getISOFields();
+ return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear);
+ }
+ fields(fields) {
+ fields = fields.slice();
+ if (fields.includes("year"))
+ fields.push("century", "centuryYear");
+ return fields;
+ }
+ mergeFields(fields, additionalFields) {
+ var {year, century, centuryYear, ...original} = fields;
+ var {
+ year: newYear,
+ century: newCentury,
+ centuryYear: newCenturyYear
+ } = additionalFields;
+ if (newYear === undefined) {
+ original.century = century;
+ original.centuryYear = centuryYear;
+ }
+ if (newCentury === undefined && newCenturyYear === undefined) {
+ original.year === year;
+ }
+ return {
+ ...original,
+ ...additionalFields
+ };
+ }
+}
+var calendar = new CenturyCalendar();
+var datetime = new Temporal.PlainDateTime(2019, 9, 15, 0, 0, 0, 0, 0, 0, calendar);
+var date = new Temporal.PlainDate(2019, 9, 15, calendar);
+var yearmonth = new Temporal.PlainYearMonth(2019, 9, calendar);
+var zoned = new Temporal.ZonedDateTime(1568505600000000000n, "UTC", calendar);
+var propDesc = {
+ century: {
+ get() {
+ return this.getCalendar().century(this);
+ },
+ configurable: true
+ },
+ centuryYear: {
+ get() {
+ return this.getCalendar().centuryYear(this);
+ },
+ configurable: true
+ }
+};
+Object.defineProperties(Temporal.PlainDateTime.prototype, propDesc);
+Object.defineProperties(Temporal.PlainDate.prototype, propDesc);
+Object.defineProperties(Temporal.PlainYearMonth.prototype, propDesc);
+Object.defineProperties(Temporal.ZonedDateTime.prototype, propDesc);
+
+// property getters work
+assert.sameValue(datetime.century, 21);
+assert.sameValue(datetime.centuryYear, 19);
+assert.sameValue(date.century, 21);
+assert.sameValue(date.centuryYear, 19);
+assert.sameValue(yearmonth.century, 21);
+assert.sameValue(yearmonth.centuryYear, 19);
+assert.sameValue(zoned.century, 21);
+assert.sameValue(zoned.centuryYear, 19);
+
+// correctly resolves century in with()
+assert.sameValue(`${ datetime.with({ century: 20 }) }`, "1919-09-15T00:00:00[u-ca=century]");
+assert.sameValue(`${ date.with({ century: 20 }) }`, "1919-09-15[u-ca=century]");
+assert.sameValue(`${ yearmonth.with({ century: 20 }) }`, "1919-09-01[u-ca=century]");
+assert.sameValue(`${ zoned.with({ century: 20 }) }`, "1919-09-15T00:00:00+00:00[UTC][u-ca=century]");
+
+// correctly resolves centuryYear in with()
+assert.sameValue(`${ datetime.with({ centuryYear: 5 }) }`, "2005-09-15T00:00:00[u-ca=century]");
+assert.sameValue(`${ date.with({ centuryYear: 5 }) }`, "2005-09-15[u-ca=century]");
+assert.sameValue(`${ yearmonth.with({ centuryYear: 5 }) }`, "2005-09-01[u-ca=century]");
+assert.sameValue(`${ zoned.with({ centuryYear: 5 }) }`, "2005-09-15T00:00:00+00:00[UTC][u-ca=century]");
+
+// correctly resolves year in with()
+assert.sameValue(`${ datetime.with({ year: 1974 }) }`, "1974-09-15T00:00:00[u-ca=century]");
+assert.sameValue(`${ date.with({ year: 1974 }) }`, "1974-09-15[u-ca=century]");
+assert.sameValue(`${ yearmonth.with({ year: 1974 }) }`, "1974-09-01[u-ca=century]");
+assert.sameValue(`${ zoned.with({ year: 1974 }) }`, "1974-09-15T00:00:00+00:00[UTC][u-ca=century]");
+
+delete Temporal.PlainDateTime.prototype.century;
+delete Temporal.PlainDateTime.prototype.centuryYear;
+delete Temporal.PlainDate.prototype.century;
+delete Temporal.PlainDate.prototype.centuryYear;
+delete Temporal.PlainYearMonth.prototype.century;
+delete Temporal.PlainYearMonth.prototype.centuryYear;
+delete Temporal.ZonedDateTime.prototype.century;
+delete Temporal.ZonedDateTime.prototype.centuryYear;
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/UserCalendar/old/shell.js b/js/src/tests/test262/staging/Temporal/UserCalendar/old/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/UserCalendar/old/shell.js
diff --git a/js/src/tests/test262/staging/Temporal/UserCalendar/old/trivial-protocol-implementation.js b/js/src/tests/test262/staging/Temporal/UserCalendar/old/trivial-protocol-implementation.js
new file mode 100644
index 0000000000..b3bfd43e09
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/UserCalendar/old/trivial-protocol-implementation.js
@@ -0,0 +1,231 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: Trivial protocol implementation
+features: [Temporal]
+---*/
+
+function decimalToISO(year, month, day, overflow = "constrain") {
+ if (overflow === "constrain") {
+ if (month < 1)
+ month = 1;
+ if (month > 10)
+ month = 10;
+ if (day < 1)
+ day = 1;
+ if (day > 10)
+ day = 10;
+ } else if (overflow === "reject") {
+ if (month < 1 || month > 10 || day < 1 || day > 10) {
+ throw new RangeError("invalid value");
+ }
+ }
+ var days = year * 100 + (month - 1) * 10 + (day - 1);
+ return new Temporal.PlainDate(1970, 1, 1, "iso8601").add({ days });
+}
+function isoToDecimal(date) {
+ var {isoYear, isoMonth, isoDay} = date.getISOFields();
+ var isoDate = new Temporal.PlainDate(isoYear, isoMonth, isoDay);
+ var {days} = isoDate.since(new Temporal.PlainDate(1970, 1, 1), { largestUnit: "days" });
+ var year = Math.floor(days / 100);
+ days %= 100;
+ return {
+ year,
+ days
+ };
+}
+var obj = {
+ id: 'decimal',
+ toString() {
+ return "decimal";
+ },
+ fields(fieldNames) { return fieldNames; },
+ dateFromFields(fields, options) {
+ var {
+ overflow = "constrain"
+ } = options ? options : {};
+ var {month, monthCode} = fields;
+ if (month === undefined)
+ month = +monthCode.slice(1);
+ var isoDate = decimalToISO(fields.year, month, fields.day, 0, 0, 0, overflow);
+ return new Temporal.PlainDate(isoDate.year, isoDate.month, isoDate.day, this);
+ },
+ yearMonthFromFields(fields, options) {
+ var {
+ overflow = "constrain"
+ } = options ? options : {};
+ var {month, monthCode} = fields;
+ if (month === undefined)
+ month = +monthCode.slice(1);
+ var isoDate = decimalToISO(fields.year, month, 1, 0, 0, 0, overflow);
+ return new Temporal.PlainYearMonth(isoDate.year, isoDate.month, this, isoDate.day);
+ },
+ monthDayFromFields(fields, options) {
+ var {
+ overflow = "constrain"
+ } = options ? options : {};
+ var {month, monthCode} = fields;
+ if (month === undefined)
+ month = +monthCode.slice(1);
+ var isoDate = decimalToISO(0, month, fields.day, 0, 0, 0, overflow);
+ return new Temporal.PlainMonthDay(isoDate.month, isoDate.day, this, isoDate.year);
+ },
+ year(date) {
+ return isoToDecimal(date).year;
+ },
+ month(date) {
+ var {days} = isoToDecimal(date);
+ return Math.floor(days / 10) + 1;
+ },
+ monthCode(date) {
+ return `M${ this.month(date).toString().padStart(2, "0") }`;
+ },
+ day(date) {
+ var {days} = isoToDecimal(date);
+ return days % 10 + 1;
+ },
+ dateAdd() {}, // left as an exercise for the reader
+ dateUntil() {}, // ditto
+ dayOfWeek() {
+ throw new Error('no weeks');
+ },
+ dayOfYear(date) {
+ return isoToDecimal(date).days;
+ },
+ daysInMonth() {
+ return 10;
+ },
+ daysInWeek() {
+ throw new Error('no weeks');
+ },
+ daysInYear() {
+ return 100;
+ },
+ fields(fields) {
+ return fields;
+ },
+ inLeapYear() {
+ return false;
+ },
+ mergeFields(fields, additional) {
+ return new Temporal.Calendar("iso8601").mergeFields(fields, additional)
+ },
+ monthsInYear() {
+ return 10;
+ },
+ weekOfYear() {
+ throw new Error('no weeks');
+ },
+ yearOfWeek(date) {
+ throw new Error('no weeks');
+ },
+};
+var date = Temporal.PlainDate.from({
+ year: 184,
+ month: 2,
+ day: 9,
+ calendar: obj
+});
+var dt = Temporal.PlainDateTime.from({
+ year: 184,
+ month: 2,
+ day: 9,
+ hour: 12,
+ calendar: obj
+});
+var ym = Temporal.PlainYearMonth.from({
+ year: 184,
+ month: 2,
+ calendar: obj
+});
+var md = Temporal.PlainMonthDay.from({
+ monthCode: "M02",
+ day: 9,
+ calendar: obj
+});
+
+// is a calendar
+assert.sameValue(typeof obj, "object")
+
+// .id is not available in from()
+assert.throws(RangeError, () => Temporal.Calendar.from("decimal"));
+assert.throws(RangeError, () => Temporal.Calendar.from("2020-06-05T09:34-00:00[UTC][u-ca=decimal]"));
+
+// Temporal.PlainDate.from()
+assert.sameValue(`${ date }`, "2020-06-05[u-ca=decimal]")
+
+// Temporal.PlainDate fields
+assert.sameValue(date.year, 184);
+assert.sameValue(date.month, 2);
+assert.sameValue(date.day, 9);
+
+// date.with()
+var date2 = date.with({ year: 0 });
+assert.sameValue(date2.year, 0);
+
+// date.withCalendar()
+var date2 = Temporal.PlainDate.from("2020-06-05T12:00");
+assert(date2.withCalendar(obj).equals(date));
+
+// Temporal.PlainDateTime.from()
+assert.sameValue(`${ dt }`, "2020-06-05T12:00:00[u-ca=decimal]")
+
+// Temporal.PlainDateTime fields
+assert.sameValue(dt.year, 184);
+assert.sameValue(dt.month, 2);
+assert.sameValue(dt.day, 9);
+assert.sameValue(dt.hour, 12);
+assert.sameValue(dt.minute, 0);
+assert.sameValue(dt.second, 0);
+assert.sameValue(dt.millisecond, 0);
+assert.sameValue(dt.microsecond, 0);
+assert.sameValue(dt.nanosecond, 0);
+
+// datetime.with()
+var dt2 = dt.with({ year: 0 });
+assert.sameValue(dt2.year, 0);
+
+// datetime.withCalendar()
+var dt2 = Temporal.PlainDateTime.from("2020-06-05T12:00");
+assert(dt2.withCalendar(obj).equals(dt));
+
+// Temporal.PlainYearMonth.from()
+assert.sameValue(`${ ym }`, "2020-05-28[u-ca=decimal]")
+
+// Temporal.PlainYearMonth fields
+assert.sameValue(dt.year, 184);
+assert.sameValue(dt.month, 2);
+
+// yearmonth.with()
+var ym2 = ym.with({ year: 0 });
+assert.sameValue(ym2.year, 0);
+
+// Temporal.PlainMonthDay.from()
+assert.sameValue(`${ md }`, "1970-01-19[u-ca=decimal]")
+
+// Temporal.PlainMonthDay fields
+assert.sameValue(md.monthCode, "M02");
+assert.sameValue(md.day, 9);
+
+// monthday.with()
+var md2 = md.with({ monthCode: "M01" });
+assert.sameValue(md2.monthCode, "M01");
+
+// timezone.getPlainDateTimeFor()
+var tz = Temporal.TimeZone.from("UTC");
+var inst = Temporal.Instant.fromEpochSeconds(0);
+var dt = tz.getPlainDateTimeFor(inst, obj);
+assert.sameValue(dt.getCalendar(), obj);
+
+// Temporal.Now.plainDateTime()
+var nowDateTime = Temporal.Now.plainDateTime(obj, "UTC");
+assert.sameValue(nowDateTime.getCalendar(), obj);
+
+// Temporal.Now.plainDate()
+var nowDate = Temporal.Now.plainDate(obj, "UTC");
+assert.sameValue(nowDate.getCalendar(), obj);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/UserCalendar/old/trivial-subclass.js b/js/src/tests/test262/staging/Temporal/UserCalendar/old/trivial-subclass.js
new file mode 100644
index 0000000000..b96e75c95c
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/UserCalendar/old/trivial-subclass.js
@@ -0,0 +1,167 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: Trivial subclass
+features: [Temporal]
+---*/
+
+class TwoBasedCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ get id() {
+ return "two-based";
+ }
+ toString() {
+ return "two-based";
+ }
+ dateFromFields(fields, options) {
+ var {year, month, monthCode, day} = fields;
+ if (month === undefined)
+ month = +monthCode.slice(1);
+ return super.dateFromFields({
+ year,
+ monthCode: `M${ (month - 1).toString().padStart(2, "0") }`,
+ day
+ }, options).withCalendar(this);
+ }
+ yearMonthFromFields(fields, options) {
+ var {year, month, monthCode} = fields;
+ if (month === undefined)
+ month = +monthCode.slice(1);
+ const { isoYear, isoMonth, isoDay } = super.yearMonthFromFields({
+ year,
+ monthCode: `M${ (month - 1).toString().padStart(2, "0") }`
+ }, options).getISOFields();
+ return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay);
+ }
+ monthDayFromFields(fields, options) {
+ var {month, monthCode, day} = fields;
+ if (month === undefined)
+ month = +monthCode.slice(1);
+ const { isoYear, isoMonth, isoDay } = super.monthDayFromFields({
+ monthCode: `M${ (month - 1).toString().padStart(2, "0") }`,
+ day
+ }, options).getISOFields();
+ return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear);
+ }
+ month(date) {
+ return date.getISOFields().isoMonth + 1;
+ }
+ monthCode(date) {
+ return `M${ this.month(date).toString().padStart(2, "0") }`;
+ }
+}
+var obj = new TwoBasedCalendar();
+var date = Temporal.PlainDate.from({
+ year: 2020,
+ month: 5,
+ day: 5,
+ calendar: obj
+});
+var dt = Temporal.PlainDateTime.from({
+ year: 2020,
+ month: 5,
+ day: 5,
+ hour: 12,
+ calendar: obj
+});
+var ym = Temporal.PlainYearMonth.from({
+ year: 2020,
+ month: 5,
+ calendar: obj
+});
+var md = Temporal.PlainMonthDay.from({
+ monthCode: "M05",
+ day: 5,
+ calendar: obj
+});
+
+// is a calendar
+assert.sameValue(typeof obj, "object")
+
+// .id property
+assert.sameValue(obj.id, "two-based")
+
+// .id is not available in from()
+assert.throws(RangeError, () => Temporal.Calendar.from("two-based"));
+assert.throws(RangeError, () => Temporal.Calendar.from("2020-06-05T09:34-00:00[UTC][u-ca=two-based]"));
+
+// Temporal.PlainDate.from()
+assert.sameValue(`${ date }`, "2020-04-05[u-ca=two-based]")
+
+// Temporal.PlainDate fields
+assert.sameValue(date.year, 2020);
+assert.sameValue(date.month, 5);
+assert.sameValue(date.day, 5);
+
+// date.with()
+var date2 = date.with({ month: 2 });
+assert.sameValue(date2.month, 2);
+
+// date.withCalendar()
+var date2 = Temporal.PlainDate.from("2020-04-05");
+assert(date2.withCalendar(obj).equals(date));
+
+// Temporal.PlainDateTime.from()
+assert.sameValue(`${ dt }`, "2020-04-05T12:00:00[u-ca=two-based]")
+
+// Temporal.PlainDateTime fields
+assert.sameValue(dt.year, 2020);
+assert.sameValue(dt.month, 5);
+assert.sameValue(dt.day, 5);
+assert.sameValue(dt.hour, 12);
+assert.sameValue(dt.minute, 0);
+assert.sameValue(dt.second, 0);
+assert.sameValue(dt.millisecond, 0);
+assert.sameValue(dt.microsecond, 0);
+assert.sameValue(dt.nanosecond, 0);
+
+// datetime.with()
+var dt2 = dt.with({ month: 2 });
+assert.sameValue(dt2.month, 2);
+
+// datetime.withCalendar()
+var dt2 = Temporal.PlainDateTime.from("2020-04-05T12:00");
+assert(dt2.withCalendar(obj).equals(dt));
+
+// Temporal.PlainYearMonth.from()
+assert.sameValue(`${ ym }`, "2020-04-01[u-ca=two-based]")
+
+// Temporal.PlainYearMonth fields
+assert.sameValue(dt.year, 2020);
+assert.sameValue(dt.month, 5);
+
+// yearmonth.with()
+var ym2 = ym.with({ month: 2 });
+assert.sameValue(ym2.month, 2);
+
+// Temporal.PlainMonthDay.from()
+assert.sameValue(`${ md }`, "1972-04-05[u-ca=two-based]")
+
+// Temporal.PlainMonthDay fields
+assert.sameValue(md.monthCode, "M05");
+assert.sameValue(md.day, 5);
+
+// monthday.with()
+var md2 = md.with({ monthCode: "M02" });
+assert.sameValue(md2.monthCode, "M02");
+
+// timezone.getPlainDateTimeFor()
+var tz = Temporal.TimeZone.from("UTC");
+var instant = Temporal.Instant.fromEpochSeconds(0);
+var dt = tz.getPlainDateTimeFor(instant, obj);
+assert.sameValue(dt.getCalendar(), obj);
+
+// Temporal.Now.plainDateTime()
+var nowDateTime = Temporal.Now.plainDateTime(obj, "UTC");
+assert.sameValue(nowDateTime.getCalendar(), obj);
+
+// Temporal.Now.plainDate()
+var nowDate = Temporal.Now.plainDate(obj, "UTC");
+assert.sameValue(nowDate.getCalendar(), obj);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/UserCalendar/shell.js b/js/src/tests/test262/staging/Temporal/UserCalendar/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/UserCalendar/shell.js
diff --git a/js/src/tests/test262/staging/Temporal/UserTimezone/browser.js b/js/src/tests/test262/staging/Temporal/UserTimezone/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/UserTimezone/browser.js
diff --git a/js/src/tests/test262/staging/Temporal/UserTimezone/old/browser.js b/js/src/tests/test262/staging/Temporal/UserTimezone/old/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/UserTimezone/old/browser.js
diff --git a/js/src/tests/test262/staging/Temporal/UserTimezone/old/shell.js b/js/src/tests/test262/staging/Temporal/UserTimezone/old/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/UserTimezone/old/shell.js
diff --git a/js/src/tests/test262/staging/Temporal/UserTimezone/old/subminute-offset.js b/js/src/tests/test262/staging/Temporal/UserTimezone/old/subminute-offset.js
new file mode 100644
index 0000000000..52d11f3548
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/UserTimezone/old/subminute-offset.js
@@ -0,0 +1,106 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: sub-minute offset
+features: [Temporal]
+---*/
+
+class SubminuteTimeZone extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ }
+ get id() {
+ return "Custom/Subminute";
+ }
+ toString() {
+ return "Custom/Subminute";
+ }
+ getOffsetNanosecondsFor() {
+ return -1111111111;
+ }
+ getPossibleInstantsFor(dateTime) {
+ var utc = Temporal.TimeZone.from("UTC");
+ var instant = utc.getInstantFor(dateTime);
+ return [instant.add({ nanoseconds: 1111111111 })];
+ }
+ getNextTransition() {
+ return null;
+ }
+ getPreviousTransition() {
+ return null;
+ }
+}
+var obj = new SubminuteTimeZone();
+var inst = Temporal.Instant.fromEpochNanoseconds(0n);
+var dt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+
+// is a time zone
+assert.sameValue(typeof obj, "object")
+
+// .id property
+assert.sameValue(obj.id, "Custom/Subminute")
+
+// .id is not available in from()
+assert.throws(RangeError, () => Temporal.TimeZone.from("Custom/Subminute"));
+assert.throws(RangeError, () => Temporal.TimeZone.from("2020-05-26T16:02:46.251163036-00:00:01.111111111[Custom/Subminute]"));
+
+// has offset string -00:00:01.111111111
+assert.sameValue(obj.getOffsetStringFor(inst), "-00:00:01.111111111")
+
+// converts to DateTime
+var fakeGregorian = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "gregory",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+assert.sameValue(`${ obj.getPlainDateTimeFor(inst) }`, "1969-12-31T23:59:58.888888889");
+assert.sameValue(`${ obj.getPlainDateTimeFor(inst, fakeGregorian) }`, "1969-12-31T23:59:58.888888889[u-ca=gregory]");
+
+// converts to Instant
+assert.sameValue(`${ obj.getInstantFor(dt) }`, "1976-11-18T15:23:31.2345679Z");
+
+// converts to string
+assert.sameValue(`${ obj }`, obj.id)
+
+// offset prints with minute precision in instant.toString
+assert.sameValue(inst.toString({ timeZone: obj }), "1969-12-31T23:59:58.888888889+00:00")
+
+// offset prints with minute precision prints in zdt.toString
+var zdt = new Temporal.ZonedDateTime(0n, obj);
+assert.sameValue(zdt.toString(), "1969-12-31T23:59:58.888888889+00:00[Custom/Subminute]");
+
+// has no next transitions
+assert.sameValue(obj.getNextTransition(), null)
+
+// has no previous transitions
+assert.sameValue(obj.getPreviousTransition(), null)
+
+// works in Temporal.Now
+assert(Temporal.Now.plainDateTimeISO(obj) instanceof Temporal.PlainDateTime);
+assert(Temporal.Now.plainDateTime(fakeGregorian, obj) instanceof Temporal.PlainDateTime);
+assert(Temporal.Now.plainDateISO(obj) instanceof Temporal.PlainDate);
+assert(Temporal.Now.plainDate(fakeGregorian, obj) instanceof Temporal.PlainDate);
+assert(Temporal.Now.plainTimeISO(obj) instanceof Temporal.PlainTime);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/UserTimezone/old/trivial-protocol.js b/js/src/tests/test262/staging/Temporal/UserTimezone/old/trivial-protocol.js
new file mode 100644
index 0000000000..e11e798f68
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/UserTimezone/old/trivial-protocol.js
@@ -0,0 +1,63 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: Trivial protocol implementation
+features: [Temporal]
+---*/
+
+var obj = {
+ getOffsetNanosecondsFor() {
+ return 0;
+ },
+ getPossibleInstantsFor(dateTime) {
+ var {year, month, day, hour, minute, second, millisecond, microsecond, nanosecond} = dateTime;
+ var dayNum = MakeDay(year, month, day);
+ var time = MakeTime(hour, minute, second, millisecond, microsecond, nanosecond);
+ var epochNs = MakeDate(dayNum, time);
+ return [new Temporal.Instant(epochNs)];
+ },
+ id: "Etc/Custom/UTC_Protocol",
+};
+var inst = Temporal.Instant.fromEpochNanoseconds(0n);
+
+// offset prints in instant.toString
+assert.sameValue(inst.toString({ timeZone: obj }), "1970-01-01T00:00:00+00:00")
+
+// prints in zdt.toString
+var zdt = new Temporal.ZonedDateTime(0n, obj);
+assert.sameValue(zdt.toString(), "1970-01-01T00:00:00+00:00[Etc/Custom/UTC_Protocol]");
+
+// works in Temporal.Now
+var fakeGregorian = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "gregory",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+assert(Temporal.Now.plainDateTimeISO(obj) instanceof Temporal.PlainDateTime);
+assert(Temporal.Now.plainDateTime(fakeGregorian, obj) instanceof Temporal.PlainDateTime);
+assert(Temporal.Now.plainDateISO(obj) instanceof Temporal.PlainDate);
+assert(Temporal.Now.plainDate(fakeGregorian, obj) instanceof Temporal.PlainDate);
+assert(Temporal.Now.plainTimeISO(obj) instanceof Temporal.PlainTime);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/UserTimezone/old/trivial-subclass.js b/js/src/tests/test262/staging/Temporal/UserTimezone/old/trivial-subclass.js
new file mode 100644
index 0000000000..6b6db93e35
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/UserTimezone/old/trivial-subclass.js
@@ -0,0 +1,144 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: Trivial subclass
+features: [Temporal]
+---*/
+
+class CustomUTCSubclass extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ }
+ get id() {
+ return "Etc/Custom/UTC_Subclass";
+ }
+ toString() {
+ return "Etc/Custom/UTC_Subclass";
+ }
+ getOffsetNanosecondsFor() {
+ return 0;
+ }
+ getPossibleInstantsFor(dateTime) {
+ var {year, month, day, hour, minute, second, millisecond, microsecond, nanosecond} = dateTime;
+ var dayNum = MakeDay(year, month, day);
+ var time = MakeTime(hour, minute, second, millisecond, microsecond, nanosecond);
+ var epochNs = MakeDate(dayNum, time);
+ return [new Temporal.Instant(epochNs)];
+ }
+ getNextTransition() {
+ return null;
+ }
+ getPreviousTransition() {
+ return null;
+ }
+}
+
+const nsPerDay = 86400_000_000_000n;
+const nsPerMillisecond = 1_000_000n;
+
+function Day(t) {
+ return t / nsPerDay;
+}
+
+function MakeDate(day, time) {
+ return day * nsPerDay + time;
+}
+
+function MakeDay(year, month, day) {
+ const m = month - 1;
+ const ym = year + Math.floor(m / 12);
+ const mn = m % 12;
+ const t = BigInt(Date.UTC(ym, mn, 1)) * nsPerMillisecond;
+ return Day(t) + BigInt(day) - 1n;
+}
+
+function MakeTime(h, min, s, ms, µs, ns) {
+ const MinutesPerHour = 60n;
+ const SecondsPerMinute = 60n;
+ const nsPerSecond = 1_000_000_000n;
+ const nsPerMinute = nsPerSecond * SecondsPerMinute;
+ const nsPerHour = nsPerMinute * MinutesPerHour;
+ return (
+ BigInt(h) * nsPerHour +
+ BigInt(min) * nsPerMinute +
+ BigInt(s) * nsPerSecond +
+ BigInt(ms) * nsPerMillisecond +
+ BigInt(µs) * 1000n +
+ BigInt(ns)
+ );
+}
+
+var obj = new CustomUTCSubclass();
+var inst = Temporal.Instant.fromEpochNanoseconds(0n);
+var dt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789);
+
+// is a time zone
+assert.sameValue(typeof obj, "object")
+
+// .id property
+assert.sameValue(obj.id, "Etc/Custom/UTC_Subclass")
+
+// .id is not available in from()
+assert.throws(RangeError, () => Temporal.TimeZone.from("Etc/Custom/UTC_Subclass"));
+assert.throws(RangeError, () => Temporal.TimeZone.from("2020-05-26T16:02:46.251163036+00:00[Etc/Custom/UTC_Subclass]"));
+
+// has offset string +00:00
+assert.sameValue(obj.getOffsetStringFor(inst), "+00:00")
+
+// converts to DateTime
+var fakeGregorian = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "gregory",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+assert.sameValue(`${ obj.getPlainDateTimeFor(inst) }`, "1970-01-01T00:00:00");
+assert.sameValue(`${ obj.getPlainDateTimeFor(inst, fakeGregorian) }`, "1970-01-01T00:00:00[u-ca=gregory]");
+
+// converts to Instant
+assert.sameValue(`${ obj.getInstantFor(dt) }`, "1976-11-18T15:23:30.123456789Z");
+
+// converts to string
+assert.sameValue(`${ obj }`, obj.id)
+
+// offset prints in instant.toString
+assert.sameValue(inst.toString({ timeZone: obj }), "1970-01-01T00:00:00+00:00")
+
+// prints in zdt.toString
+var zdt = new Temporal.ZonedDateTime(0n, obj);
+assert.sameValue(zdt.toString(), "1970-01-01T00:00:00+00:00[Etc/Custom/UTC_Subclass]");
+
+// has no next transitions
+assert.sameValue(obj.getNextTransition(), null)
+
+// has no previous transitions
+assert.sameValue(obj.getPreviousTransition(), null)
+
+// works in Temporal.Now
+assert(Temporal.Now.plainDateTimeISO(obj) instanceof Temporal.PlainDateTime);
+assert(Temporal.Now.plainDateTime(fakeGregorian, obj) instanceof Temporal.PlainDateTime);
+assert(Temporal.Now.plainDateISO(obj) instanceof Temporal.PlainDate);
+assert(Temporal.Now.plainDate(fakeGregorian, obj) instanceof Temporal.PlainDate);
+assert(Temporal.Now.plainTimeISO(obj) instanceof Temporal.PlainTime);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/UserTimezone/shell.js b/js/src/tests/test262/staging/Temporal/UserTimezone/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/UserTimezone/shell.js
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/browser.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/browser.js
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/add.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/add.js
new file mode 100644
index 0000000000..e1fdbd9ac5
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/add.js
@@ -0,0 +1,53 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: Temporal.ZonedDateTime.prototype.add()
+features: [Temporal]
+---*/
+
+var zdt = Temporal.ZonedDateTime.from("1969-12-25T12:23:45.678901234+00:00[UTC]");
+// cross epoch in ms
+ var one = zdt.subtract({
+ hours: 240,
+ nanoseconds: 800
+ });
+ var two = zdt.add({
+ hours: 240,
+ nanoseconds: 800
+ });
+ var three = two.subtract({
+ hours: 480,
+ nanoseconds: 1600
+ });
+ var four = one.add({
+ hours: 480,
+ nanoseconds: 1600
+ });
+assert.sameValue(`${ one }`, "1969-12-15T12:23:45.678900434+00:00[UTC]");
+assert.sameValue(`${ two }`, "1970-01-04T12:23:45.678902034+00:00[UTC]");
+assert(three.equals(one));
+assert(four.equals(two));
+
+// zdt.add(durationObj)
+var later = zdt.add(Temporal.Duration.from("PT240H0.000000800S"));
+assert.sameValue(`${ later }`, "1970-01-04T12:23:45.678902034+00:00[UTC]");
+
+// casts argument
+assert.sameValue(`${ zdt.add("PT240H0.000000800S") }`, "1970-01-04T12:23:45.678902034+00:00[UTC]");
+var jan31 = Temporal.ZonedDateTime.from("2020-01-31T15:00-08:00[-08:00]");
+
+// constrain when ambiguous result
+assert.sameValue(`${ jan31.add({ months: 1 }) }`, "2020-02-29T15:00:00-08:00[-08:00]");
+assert.sameValue(`${ jan31.add({ months: 1 }, { overflow: "constrain" }) }`, "2020-02-29T15:00:00-08:00[-08:00]");
+
+// symmetrical with regard to negative durations in the time part
+assert.sameValue(`${ jan31.add({ minutes: -30 }) }`, "2020-01-31T14:30:00-08:00[-08:00]");
+assert.sameValue(`${ jan31.add({ seconds: -30 }) }`, "2020-01-31T14:59:30-08:00[-08:00]");
+
+// throw when ambiguous result with reject
+assert.throws(RangeError, () => jan31.add({ months: 1 }, { overflow: "reject" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/browser.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/browser.js
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/compare.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/compare.js
new file mode 100644
index 0000000000..7345c962b5
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/compare.js
@@ -0,0 +1,146 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: Temporal.ZonedDateTime.compare()
+features: [Temporal]
+---*/
+
+var zdt1 = Temporal.ZonedDateTime.from("1976-11-18T15:23:30.123456789+01:00[+01:00]");
+var zdt2 = Temporal.ZonedDateTime.from("2019-10-29T10:46:38.271986102+01:00[+01:00]");
+
+// equal
+assert.sameValue(Temporal.ZonedDateTime.compare(zdt1, zdt1), 0)
+
+// smaller/larger
+assert.sameValue(Temporal.ZonedDateTime.compare(zdt1, zdt2), -1)
+
+// larger/smaller
+assert.sameValue(Temporal.ZonedDateTime.compare(zdt2, zdt1), 1)
+
+// casts first argument
+assert.sameValue(Temporal.ZonedDateTime.compare({
+ year: 1976,
+ month: 11,
+ day: 18,
+ hour: 15,
+ timeZone: "+01:00"
+}, zdt2), -1);
+assert.sameValue(Temporal.ZonedDateTime.compare("1976-11-18T15:23:30.123456789+01:00[+01:00]", zdt2), -1);
+
+// casts second argument
+assert.sameValue(Temporal.ZonedDateTime.compare(zdt1, {
+ year: 2019,
+ month: 10,
+ day: 29,
+ hour: 10,
+ timeZone: "+01:00"
+}), -1);
+assert.sameValue(Temporal.ZonedDateTime.compare(zdt1, "2019-10-29T10:46:38.271986102+01:00[+01:00]"), -1);
+
+// object must contain at least the required properties
+assert.sameValue(Temporal.ZonedDateTime.compare({
+ year: 1976,
+ month: 11,
+ day: 18,
+ timeZone: "+01:00"
+}, zdt2), -1);
+assert.throws(TypeError, () => Temporal.ZonedDateTime.compare({
+ month: 11,
+ day: 18,
+ timeZone: "+01:00"
+}, zdt2));
+assert.throws(TypeError, () => Temporal.ZonedDateTime.compare({
+ year: 1976,
+ day: 18,
+ timeZone: "+01:00"
+}, zdt2));
+assert.throws(TypeError, () => Temporal.ZonedDateTime.compare({
+ year: 1976,
+ month: 11,
+ timeZone: "+01:00"
+}, zdt2));
+assert.throws(TypeError, () => Temporal.ZonedDateTime.compare({
+ year: 1976,
+ month: 11,
+ day: 18
+}, zdt2));
+assert.throws(TypeError, () => Temporal.ZonedDateTime.compare({
+ years: 1976,
+ months: 11,
+ days: 19,
+ hours: 15,
+ timeZone: "+01:00"
+}, zdt2));
+assert.sameValue(Temporal.ZonedDateTime.compare(zdt1, {
+ year: 2019,
+ month: 10,
+ day: 29,
+ timeZone: "+01:00"
+}), -1);
+assert.throws(TypeError, () => Temporal.ZonedDateTime.compare(zdt1, {
+ month: 10,
+ day: 29,
+ timeZone: "+01:00"
+}));
+assert.throws(TypeError, () => Temporal.ZonedDateTime.compare(zdt1, {
+ year: 2019,
+ day: 29,
+ timeZone: "+01:00"
+}));
+assert.throws(TypeError, () => Temporal.ZonedDateTime.compare(zdt1, {
+ year: 2019,
+ month: 10,
+ timeZone: "+01:00"
+}));
+assert.throws(TypeError, () => Temporal.ZonedDateTime.compare(zdt1, {
+ year: 2019,
+ month: 10,
+ day: 29
+}));
+assert.throws(TypeError, () => Temporal.ZonedDateTime.compare(zdt1, {
+ years: 2019,
+ months: 10,
+ days: 29,
+ hours: 10,
+ timeZone: "+01:00"
+}));
+
+// disregards time zone IDs if exact times are equal
+assert.sameValue(Temporal.ZonedDateTime.compare(zdt1, zdt1.withTimeZone("+05:30")), 0);
+
+// disregards calendar IDs if exact times and time zones are equal
+var fakeJapanese = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "japanese",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+assert.sameValue(Temporal.ZonedDateTime.compare(zdt1, zdt1.withCalendar(fakeJapanese)), 0);
+
+// compares exact time, not clock time
+var clockBefore = Temporal.ZonedDateTime.from("1999-12-31T23:30-08:00[-08:00]");
+var clockAfter = Temporal.ZonedDateTime.from("2000-01-01T01:30-04:00[-04:00]");
+assert.sameValue(Temporal.ZonedDateTime.compare(clockBefore, clockAfter), 1);
+assert.sameValue(Temporal.PlainDateTime.compare(clockBefore.toPlainDateTime(), clockAfter.toPlainDateTime()), -1);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/construction-and-properties.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/construction-and-properties.js
new file mode 100644
index 0000000000..eb039a9ee5
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/construction-and-properties.js
@@ -0,0 +1,117 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: Construction and properties
+features: [Temporal]
+---*/
+
+var tz = new Temporal.TimeZone("-08:00");
+var epochMillis = Date.UTC(1976, 10, 18, 15, 23, 30, 123);
+var epochNanos = BigInt(epochMillis) * BigInt(1000000) + BigInt(456789);
+
+// works
+var zdt = new Temporal.ZonedDateTime(epochNanos, tz);
+assert(zdt instanceof Temporal.ZonedDateTime);
+assert.sameValue(typeof zdt, "object");
+assert.sameValue(zdt.toInstant().epochSeconds, Math.floor(Date.UTC(1976, 10, 18, 15, 23, 30, 123) / 1000), "epochSeconds");
+assert.sameValue(zdt.toInstant().epochMilliseconds, Date.UTC(1976, 10, 18, 15, 23, 30, 123), "epochMilliseconds");
+
+// Temporal.ZonedDateTime for (1976, 11, 18, 15, 23, 30, 123, 456, 789)"
+ var zdt = new Temporal.ZonedDateTime(epochNanos, "UTC");
+// can be constructed
+assert(zdt instanceof Temporal.ZonedDateTime);
+assert.sameValue(typeof zdt, "object");
+
+assert.sameValue(zdt.year, 1976)
+assert.sameValue(zdt.month, 11);
+assert.sameValue(zdt.monthCode, "M11");
+assert.sameValue(zdt.day, 18);
+assert.sameValue(zdt.hour, 15);
+assert.sameValue(zdt.minute, 23);
+assert.sameValue(zdt.second, 30);
+assert.sameValue(zdt.millisecond, 123);
+assert.sameValue(zdt.microsecond, 456);
+assert.sameValue(zdt.nanosecond, 789);
+assert.sameValue(zdt.epochSeconds, 217178610);
+assert.sameValue(zdt.epochMilliseconds, 217178610123);
+assert.sameValue(zdt.epochMicroseconds, 217178610123456n);
+assert.sameValue(zdt.epochNanoseconds, 217178610123456789n);
+assert.sameValue(zdt.dayOfWeek, 4);
+assert.sameValue(zdt.dayOfYear, 323);
+assert.sameValue(zdt.weekOfYear, 47);
+assert.sameValue(zdt.daysInWeek, 7);
+assert.sameValue(zdt.daysInMonth, 30);
+assert.sameValue(zdt.daysInYear, 366);
+assert.sameValue(zdt.monthsInYear, 12);
+assert.sameValue(zdt.inLeapYear, true);
+assert.sameValue(zdt.offset, "+00:00");
+assert.sameValue(zdt.offsetNanoseconds, 0);
+assert.sameValue(`${ zdt }`, "1976-11-18T15:23:30.123456789+00:00[UTC]");
+
+// Temporal.ZonedDateTime with non-UTC time zone and non-ISO calendar
+// can be constructed
+var fakeGregorian = {
+ era() { return "ce"; },
+ year(date) { return date.withCalendar("iso8601").year; },
+ month(date) { return date.withCalendar("iso8601").month; },
+ monthCode(date) { return date.withCalendar("iso8601").monthCode; },
+ day(date) { return date.withCalendar("iso8601").day; },
+ dayOfWeek(date) { return date.withCalendar("iso8601").dayOfWeek; },
+ dayOfYear(date) { return date.withCalendar("iso8601").dayOfYear; },
+ weekOfYear(date) { return date.withCalendar("iso8601").weekOfYear; },
+ daysInWeek(date) { return date.withCalendar("iso8601").daysInWeek; },
+ daysInMonth(date) { return date.withCalendar("iso8601").daysInMonth; },
+ daysInYear(date) { return date.withCalendar("iso8601").daysInYear; },
+ monthsInYear(date) { return date.withCalendar("iso8601").monthsInYear; },
+ inLeapYear(date) { return date.withCalendar("iso8601").inLeapYear; },
+ id: "gregory",
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ fields() {},
+ mergeFields() {},
+ monthDayFromFields() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+var fakeVienna = {
+ getOffsetNanosecondsFor() { return 3600_000_000_000; },
+ getPossibleInstantsFor(datetime) { return [datetime.toZonedDateTime("+01:00").toInstant()]; },
+ id: "Europe/Vienna",
+}
+var zdt = new Temporal.ZonedDateTime(epochNanos, fakeVienna, fakeGregorian);
+assert(zdt instanceof Temporal.ZonedDateTime);
+assert.sameValue(typeof zdt, "object");
+
+assert.sameValue(zdt.era, "ce");
+assert.sameValue(zdt.year, 1976);
+assert.sameValue(zdt.month, 11);
+assert.sameValue(zdt.monthCode, "M11");
+assert.sameValue(zdt.day, 18);
+assert.sameValue(zdt.hour, 16);
+assert.sameValue(zdt.minute, 23);
+assert.sameValue(zdt.second, 30);
+assert.sameValue(zdt.millisecond, 123);
+assert.sameValue(zdt.microsecond, 456);
+assert.sameValue(zdt.nanosecond, 789);
+assert.sameValue(zdt.epochSeconds, 217178610);
+assert.sameValue(zdt.epochMilliseconds, 217178610123);
+assert.sameValue(zdt.epochMicroseconds, 217178610123456n);
+assert.sameValue(zdt.epochNanoseconds, 217178610123456789n);
+assert.sameValue(zdt.dayOfWeek, 4);
+assert.sameValue(zdt.dayOfYear, 323);
+assert.sameValue(zdt.weekOfYear, 47);
+assert.sameValue(zdt.daysInWeek, 7);
+assert.sameValue(zdt.daysInMonth, 30);
+assert.sameValue(zdt.daysInYear, 366);
+assert.sameValue(zdt.monthsInYear, 12);
+assert.sameValue(zdt.inLeapYear, true);
+assert.sameValue(zdt.offset, "+01:00");
+assert.sameValue(zdt.offsetNanoseconds, 3600000000000);
+assert.sameValue(`${ zdt }`, "1976-11-18T16:23:30.123456789+01:00[Europe/Vienna][u-ca=gregory]");
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/date-time-hours-overflow.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/date-time-hours-overflow.js
new file mode 100644
index 0000000000..5c1f644c1b
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/date-time-hours-overflow.js
@@ -0,0 +1,26 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: hours overflow
+features: [Temporal]
+---*/
+
+
+// subtract result
+var later = Temporal.ZonedDateTime.from("2019-10-29T10:46:38.271986102-03:00[-03:00]");
+var earlier = later.subtract({ hours: 12 });
+assert.sameValue(`${ earlier }`, "2019-10-28T22:46:38.271986102-03:00[-03:00]");
+
+// add result
+var earlier = Temporal.ZonedDateTime.from("2020-05-31T23:12:38.271986102-04:00[-04:00]");
+var later = earlier.add({ hours: 2 });
+assert.sameValue(`${ later }`, "2020-06-01T01:12:38.271986102-04:00[-04:00]");
+
+// symmetrical with regard to negative durations
+assert.sameValue(`${ Temporal.ZonedDateTime.from("2019-10-29T10:46:38.271986102-03:00[-03:00]").add({ hours: -12 }) }`, "2019-10-28T22:46:38.271986102-03:00[-03:00]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from("2020-05-31T23:12:38.271986102-04:00[-04:00]").subtract({ hours: -2 }) }`, "2020-06-01T01:12:38.271986102-04:00[-04:00]");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/dst-math.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/dst-math.js
new file mode 100644
index 0000000000..6e8879ee65
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/dst-math.js
@@ -0,0 +1,279 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: math around DST
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+var tz = TemporalHelpers.springForwardFallBackTimeZone();
+var hourBeforeDstStart = new Temporal.PlainDateTime(2000, 4, 2, 1).toZonedDateTime(tz);
+var dayBeforeDstStart = new Temporal.PlainDateTime(2000, 4, 1, 2, 30).toZonedDateTime(tz);
+
+// add 1 hour to get to DST start
+var added = hourBeforeDstStart.add({ hours: 1 });
+assert.sameValue(added.hour, 3);
+var diff = hourBeforeDstStart.until(added, { largestUnit: "hours" });
+assert.sameValue(`${ diff }`, "PT1H");
+assert.sameValue(`${ diff }`, `${ added.since(hourBeforeDstStart, { largestUnit: "hours" }) }`);
+var undo = added.subtract(diff);
+assert.sameValue(`${ undo }`, `${ hourBeforeDstStart }`);
+
+// add 2 hours to get to DST start +1
+var added = hourBeforeDstStart.add({ hours: 2 });
+assert.sameValue(added.hour, 4);
+var diff = hourBeforeDstStart.until(added, { largestUnit: "hours" });
+assert.sameValue(`${ diff }`, "PT2H");
+assert.sameValue(`${ diff }`, `${ added.since(hourBeforeDstStart, { largestUnit: "hours" }) }`);
+var undo = added.subtract(diff);
+assert.sameValue(`${ undo }`, `${ hourBeforeDstStart }`);
+
+// add 1.5 hours to get to 0.5 hours after DST start
+var added = hourBeforeDstStart.add({
+ hours: 1,
+ minutes: 30
+});
+assert.sameValue(added.hour, 3);
+assert.sameValue(added.minute, 30);
+var diff = hourBeforeDstStart.until(added, { largestUnit: "hours" });
+assert.sameValue(`${ diff }`, "PT1H30M");
+assert.sameValue(`${ diff }`, `${ added.since(hourBeforeDstStart, { largestUnit: "hours" }) }`);
+var undo = added.subtract(diff);
+assert.sameValue(`${ undo }`, `${ hourBeforeDstStart }`);
+
+// Samoa date line change (add): 10:00PM 29 Dec 2011 -> 11:00PM 31 Dec 2011
+var timeZone = TemporalHelpers.crossDateLineTimeZone();
+var dayBeforeSamoaDateLineChangeAbs = timeZone.getInstantFor(new Temporal.PlainDateTime(2011, 12, 29, 22));
+var start = dayBeforeSamoaDateLineChangeAbs.toZonedDateTimeISO(timeZone);
+var added = start.add({
+ days: 1,
+ hours: 1
+});
+assert.sameValue(added.day, 31);
+assert.sameValue(added.hour, 23);
+assert.sameValue(added.minute, 0);
+var diff = start.until(added, { largestUnit: "days" });
+assert.sameValue(`${ diff }`, "P2DT1H");
+var undo = added.subtract(diff);
+assert.sameValue(`${ undo }`, `${ start }`);
+
+// Samoa date line change (subtract): 11:00PM 31 Dec 2011 -> 10:00PM 29 Dec 2011
+var dayAfterSamoaDateLineChangeAbs = timeZone.getInstantFor(new Temporal.PlainDateTime(2011, 12, 31, 23));
+var start = dayAfterSamoaDateLineChangeAbs.toZonedDateTimeISO(timeZone);
+var skipped = start.subtract({
+ days: 1,
+ hours: 1
+});
+assert.sameValue(skipped.day, 31);
+assert.sameValue(skipped.hour, 22);
+assert.sameValue(skipped.minute, 0);
+var end = start.subtract({
+ days: 2,
+ hours: 1
+});
+assert.sameValue(end.day, 29);
+assert.sameValue(end.hour, 22);
+assert.sameValue(end.minute, 0);
+var diff = end.since(start, { largestUnit: "days" });
+assert.sameValue(`${ diff }`, "-P2DT1H");
+var undo = start.add(diff);
+assert.sameValue(`${ undo }`, `${ end }`);
+
+// 3:30 day before DST start -> 3:30 day of DST start
+var start = dayBeforeDstStart.add({ hours: 1 });
+var added = start.add({ days: 1 });
+assert.sameValue(added.day, 2);
+assert.sameValue(added.hour, 3);
+assert.sameValue(added.minute, 30);
+var diff = start.until(added, { largestUnit: "days" });
+assert.sameValue(`${ diff }`, "P1D");
+var undo = added.subtract(diff);
+assert.sameValue(`${ undo }`, `${ start }`);
+
+// 2:30 day before DST start -> 3:30 day of DST start
+var added = dayBeforeDstStart.add({ days: 1 });
+assert.sameValue(added.day, 2);
+assert.sameValue(added.hour, 3);
+assert.sameValue(added.minute, 30);
+var diff = dayBeforeDstStart.until(added, { largestUnit: "days" });
+assert.sameValue(`${ diff }`, "P1D");
+var undo = dayBeforeDstStart.add(diff);
+assert.sameValue(`${ undo }`, `${ added }`);
+
+// 1:30 day DST starts -> 4:30 day DST starts
+var start = dayBeforeDstStart.add({ hours: 23 });
+var added = start.add({ hours: 2 });
+assert.sameValue(added.day, 2);
+assert.sameValue(added.hour, 4);
+assert.sameValue(added.minute, 30);
+var diff = start.until(added, { largestUnit: "days" });
+assert.sameValue(`${ diff }`, "PT2H");
+var undo = added.subtract(diff);
+assert.sameValue(`${ undo }`, `${ start }`);
+
+// 2:00 day before DST starts -> 3:00 day DST starts
+var start = hourBeforeDstStart.subtract({ days: 1 }).add({ hours: 1 });
+var added = start.add({ days: 1 });
+assert.sameValue(added.day, 2);
+assert.sameValue(added.hour, 3);
+assert.sameValue(added.minute, 0);
+var diff = start.until(added, { largestUnit: "days" });
+assert.sameValue(`${ diff }`, "P1D");
+var undo = start.add(diff);
+assert.sameValue(`${ undo }`, `${ added }`);
+
+// 1:00AM day DST starts -> (add 24 hours) -> 2:00AM day after DST starts
+var start = hourBeforeDstStart;
+var added = start.add({ hours: 24 });
+assert.sameValue(added.day, 3);
+assert.sameValue(added.hour, 2);
+assert.sameValue(added.minute, 0);
+var diff = start.until(added, { largestUnit: "days" });
+assert.sameValue(`${ diff }`, "P1DT1H");
+var undo = added.subtract(diff);
+assert.sameValue(`${ undo }`, `${ start }`);
+
+// 12:00AM day DST starts -> (add 24 hours) -> 1:00AM day after DST starts
+var start = hourBeforeDstStart.subtract({ hours: 1 });
+var added = start.add({ hours: 24 });
+assert.sameValue(added.day, 3);
+assert.sameValue(added.hour, 1);
+assert.sameValue(added.minute, 0);
+var diff = start.until(added, { largestUnit: "days" });
+assert.sameValue(`${ diff }`, "P1DT1H");
+var undo = added.subtract(diff);
+assert.sameValue(`${ undo }`, `${ start }`);
+
+// Difference can return day length > 24 hours
+var start = Temporal.PlainDateTime.from("2000-10-27T01:45").toZonedDateTime(tz);
+var end = Temporal.PlainDateTime.from("2000-10-30T01:15").toZonedDateTime(tz);
+var diff = start.until(end, { largestUnit: "days" });
+assert.sameValue(`${ diff }`, "P2DT24H30M");
+var undo = start.add(diff);
+assert.sameValue(`${ undo }`, `${ end }`);
+
+// Difference rounding (nearest day) is DST-aware
+var start = Temporal.PlainDateTime.from("2000-04-04T02:30").toZonedDateTime(tz);
+var end = Temporal.PlainDateTime.from("2000-04-01T14:15").toZonedDateTime(tz);
+var diff = start.until(end, {
+ smallestUnit: "days",
+ roundingMode: "halfExpand"
+});
+assert.sameValue(`${ diff }`, "-P3D");
+
+// Difference rounding (ceil day) is DST-aware
+var diff = start.until(end, {
+ smallestUnit: "days",
+ roundingMode: "ceil"
+});
+assert.sameValue(`${ diff }`, "-P2D");
+
+// Difference rounding (trunc day) is DST-aware
+var diff = start.until(end, {
+ smallestUnit: "days",
+ roundingMode: "trunc"
+});
+assert.sameValue(`${ diff }`, "-P2D");
+
+// Difference rounding (floor day) is DST-aware
+var diff = start.until(end, {
+ smallestUnit: "days",
+ roundingMode: "floor"
+});
+assert.sameValue(`${ diff }`, "-P3D");
+
+// Difference rounding (nearest hour) is DST-aware
+var diff = start.until(end, {
+ largestUnit: "days",
+ smallestUnit: "hours",
+ roundingMode: "halfExpand"
+});
+assert.sameValue(`${ diff }`, "-P2DT12H");
+
+// Difference rounding (ceil hour) is DST-aware
+var diff = start.until(end, {
+ largestUnit: "days",
+ smallestUnit: "hours",
+ roundingMode: "ceil"
+});
+assert.sameValue(`${ diff }`, "-P2DT12H");
+
+// Difference rounding (trunc hour) is DST-aware
+var diff = start.until(end, {
+ largestUnit: "days",
+ smallestUnit: "hours",
+ roundingMode: "trunc"
+});
+assert.sameValue(`${ diff }`, "-P2DT12H");
+
+// Difference rounding (floor hour) is DST-aware
+var diff = start.until(end, {
+ largestUnit: "days",
+ smallestUnit: "hours",
+ roundingMode: "floor"
+});
+assert.sameValue(`${ diff }`, "-P2DT13H");
+
+// Difference when date portion ends inside a DST-skipped period
+var start = Temporal.PlainDateTime.from("2000-04-01T02:30").toZonedDateTime(tz);
+var end = Temporal.PlainDateTime.from("2000-04-02T03:15").toZonedDateTime(tz);
+var diff = start.until(end, { largestUnit: "days" });
+assert.sameValue(`${ diff }`, "PT23H45M");
+
+// Difference when date portion ends inside day skipped by Samoa's 24hr 2011 transition
+var end = Temporal.PlainDateTime.from("2011-12-31T05:00").toZonedDateTime(timeZone);
+var start = Temporal.PlainDateTime.from("2011-12-28T10:00").toZonedDateTime(timeZone);
+var diff = start.until(end, { largestUnit: "days" });
+assert.sameValue(`${ diff }`, "P1DT19H");
+
+// Rounding up to hours causes one more day of overflow (positive)
+var start = Temporal.ZonedDateTime.from("2020-01-01T00:00-08:00[-08:00]");
+var end = Temporal.ZonedDateTime.from("2020-01-03T23:59-08:00[-08:00]");
+var diff = start.until(end, {
+ largestUnit: "days",
+ smallestUnit: "hours",
+ roundingMode: "halfExpand"
+});
+assert.sameValue(`${ diff }`, "P3D");
+
+// Rounding up to hours causes one more day of overflow (negative)
+var start = Temporal.ZonedDateTime.from("2020-01-01T00:00-08:00[-08:00]");
+var end = Temporal.ZonedDateTime.from("2020-01-03T23:59-08:00[-08:00]");
+var diff = end.until(start, {
+ largestUnit: "days",
+ smallestUnit: "hours",
+ roundingMode: "halfExpand"
+});
+assert.sameValue(`${ diff }`, "-P3D");
+
+// addition and difference work near DST start
+var stepsPerHour = 2;
+var minutesPerStep = 60 / stepsPerHour;
+var hoursUntilEnd = 26;
+var startHourRange = 3;
+for (var i = 0; i < startHourRange * stepsPerHour; i++) {
+ var start = hourBeforeDstStart.add({ minutes: minutesPerStep * i });
+ for (var j = 0; j < hoursUntilEnd * stepsPerHour; j++) {
+ var end = start.add({ minutes: j * minutesPerStep });
+ var diff = start.until(end, { largestUnit: "days" });
+ var expectedMinutes = minutesPerStep * (j % stepsPerHour);
+ assert.sameValue(diff.minutes, expectedMinutes);
+ var diff60 = Math.floor(j / stepsPerHour);
+ if (i >= stepsPerHour) {
+ var expectedDays = diff60 < 24 ? 0 : diff60 < 48 ? 1 : 2;
+ var expectedHours = diff60 < 24 ? diff60 : diff60 < 48 ? diff60 - 24 : diff60 - 48;
+ assert.sameValue(diff.hours, expectedHours);
+ assert.sameValue(diff.days, expectedDays);
+ } else {
+ var expectedDays = diff60 < 23 ? 0 : diff60 < 47 ? 1 : 2;
+ var expectedHours = diff60 < 23 ? diff60 : diff60 < 47 ? diff60 - 23 : diff60 - 47;
+ assert.sameValue(diff.hours, expectedHours);
+ assert.sameValue(diff.days, expectedDays);
+ }
+ }
+}
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/dst-properties.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/dst-properties.js
new file mode 100644
index 0000000000..fbd5d37197
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/dst-properties.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: properties around DST
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+var tz = TemporalHelpers.springForwardFallBackTimeZone();
+var hourBeforeDstStart = new Temporal.PlainDateTime(2000, 4, 2, 1).toZonedDateTime(tz);
+var dayBeforeDstStart = new Temporal.PlainDateTime(2000, 4, 1, 2, 30).toZonedDateTime(tz);
+
+// hoursInDay works with DST start
+assert.sameValue(hourBeforeDstStart.hoursInDay, 23);
+
+// hoursInDay works with non-DST days
+assert.sameValue(dayBeforeDstStart.hoursInDay, 24);
+
+// hoursInDay works with DST end
+var dstEnd = Temporal.PlainDateTime.from("2000-10-29T01:00").toZonedDateTime(tz);
+assert.sameValue(dstEnd.hoursInDay, 25);
+
+// startOfDay works
+var start = dayBeforeDstStart.startOfDay();
+assert.sameValue(`${ start.toPlainDate() }`, `${ dayBeforeDstStart.toPlainDate() }`);
+assert.sameValue(`${ start.toPlainTime() }`, "00:00:00");
+
+var samoa = TemporalHelpers.crossDateLineTimeZone();
+var dayAfterSamoaDateLineChange = Temporal.PlainDateTime.from("2011-12-31T22:00").toZonedDateTime(samoa);
+var dayBeforeSamoaDateLineChange = Temporal.PlainDateTime.from("2011-12-29T22:00").toZonedDateTime(samoa);
+
+// startOfDay works after Samoa date line change
+var start = dayAfterSamoaDateLineChange.startOfDay();
+assert.sameValue(`${ start.toPlainTime() }`, "00:00:00");
+
+// hoursInDay works after Samoa date line change
+assert.sameValue(dayAfterSamoaDateLineChange.hoursInDay, 24);
+
+// hoursInDay works before Samoa date line change
+assert.sameValue(dayBeforeSamoaDateLineChange.hoursInDay, 24);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/equals.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/equals.js
new file mode 100644
index 0000000000..9f3eb5515e
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/equals.js
@@ -0,0 +1,111 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: Temporal.ZonedDateTime.prototype.equals()
+features: [Temporal]
+---*/
+
+var tz = {
+ getOffsetNanosecondsFor() { return -5 * 3600_000_000_000; },
+ getPossibleInstantsFor(pdt) { return Temporal.TimeZone.from("-05:00").getPossibleInstantsFor(pdt); },
+ id: "America/New_York",
+};
+var cal = {
+ dateFromFields(...args) { return Temporal.Calendar.from("iso8601").dateFromFields(...args); },
+ id: "gregory",
+ dateAdd() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields(fieldNames) { return fieldNames; },
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+var zdt = new Temporal.ZonedDateTime(0n, tz, cal);
+
+// constructed from equivalent parameters are equal
+var zdt2 = Temporal.ZonedDateTime.from({
+ year: 1969,
+ month: 12,
+ day: 31,
+ hour: 19,
+ timeZone: tz,
+ calendar: cal,
+});
+assert(zdt.equals(zdt2));
+assert(zdt2.equals(zdt));
+
+// different instant not equal
+var zdt2 = new Temporal.ZonedDateTime(1n, tz, cal);
+assert(!zdt.equals(zdt2));
+
+// different time zone not equal
+var zdt2 = new Temporal.ZonedDateTime(0n, "UTC", cal);
+assert(!zdt.equals(zdt2));
+
+// different calendar not equal
+var zdt2 = new Temporal.ZonedDateTime(0n, tz, "iso8601");
+assert(!zdt.equals(zdt2));
+
+// casts its argument
+var instance = new Temporal.ZonedDateTime(0n, "UTC", "iso8601");
+assert(instance.equals("1970-01-01T00:00+00:00[UTC][u-ca=iso8601]"));
+assert(instance.equals({
+ year: 1970,
+ month: 1,
+ day: 1,
+ timeZone: "UTC",
+ calendar: "iso8601",
+}));
+
+// at least the required properties must be present
+assert(!zdt.equals({
+ year: 1969,
+ month: 12,
+ day: 31,
+ timeZone: tz
+}));
+assert.throws(TypeError, () => zdt.equals({
+ month: 12,
+ day: 31,
+ timeZone: tz
+}));
+assert.throws(TypeError, () => zdt.equals({
+ year: 1969,
+ day: 31,
+ timeZone: tz
+}));
+assert.throws(TypeError, () => zdt.equals({
+ year: 1969,
+ month: 12,
+ timeZone: tz
+}));
+assert.throws(TypeError, () => zdt.equals({
+ year: 1969,
+ month: 12,
+ day: 31
+}));
+assert.throws(TypeError, () => zdt.equals({
+ years: 1969,
+ months: 12,
+ days: 31,
+ timeZone: tz,
+ calendarName: "gregory"
+}));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/order-of-operations.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/order-of-operations.js
new file mode 100644
index 0000000000..406ba04865
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/order-of-operations.js
@@ -0,0 +1,82 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: math order of operations and options
+features: [Temporal]
+---*/
+
+var breakoutUnits = (op, zdt, d, options) => zdt[op]({ years: d.years }, options)[op]({ months: d.months }, options)[op]({ weeks: d.weeks }, options)[op]({ days: d.days }, options)[op]({
+ hours: d.hours,
+ minutes: d.minutes,
+ seconds: d.seconds,
+ milliseconds: d.milliseconds,
+ microseconds: d.microseconds,
+ nanoseconds: d.nanoseconds
+}, options);
+
+// order of operations: add / none
+var zdt = Temporal.ZonedDateTime.from("2020-01-31T00:00-08:00[-08:00]");
+var d = Temporal.Duration.from({
+ months: 1,
+ days: 1
+});
+var options = undefined;
+var result = zdt.add(d, options);
+assert.sameValue(result.toString(), "2020-03-01T00:00:00-08:00[-08:00]");
+assert.sameValue(breakoutUnits("add", zdt, d, options).toString(), result.toString());
+
+// order of operations: add / constrain
+var zdt = Temporal.ZonedDateTime.from("2020-01-31T00:00-08:00[-08:00]");
+var d = Temporal.Duration.from({
+ months: 1,
+ days: 1
+});
+var options = { overflow: "constrain" };
+var result = zdt.add(d, options);
+assert.sameValue(result.toString(), "2020-03-01T00:00:00-08:00[-08:00]");
+assert.sameValue(breakoutUnits("add", zdt, d, options).toString(), result.toString());
+
+// order of operations: add / reject
+var zdt = Temporal.ZonedDateTime.from("2020-01-31T00:00-08:00[-08:00]");
+var d = Temporal.Duration.from({
+ months: 1,
+ days: 1
+});
+var options = { overflow: "reject" };
+assert.throws(RangeError, () => zdt.add(d, options));
+
+// order of operations: subtract / none
+var zdt = Temporal.ZonedDateTime.from("2020-03-31T00:00-08:00[-08:00]");
+var d = Temporal.Duration.from({
+ months: 1,
+ days: 1
+});
+var options = undefined;
+var result = zdt.subtract(d, options);
+assert.sameValue(result.toString(), "2020-02-28T00:00:00-08:00[-08:00]");
+assert.sameValue(breakoutUnits("subtract", zdt, d, options).toString(), result.toString());
+
+// order of operations: subtract / constrain
+var zdt = Temporal.ZonedDateTime.from("2020-03-31T00:00-08:00[-08:00]");
+var d = Temporal.Duration.from({
+ months: 1,
+ days: 1
+});
+var options = { overflow: "constrain" };
+var result = zdt.subtract(d, options);
+assert.sameValue(result.toString(), "2020-02-28T00:00:00-08:00[-08:00]");
+assert.sameValue(breakoutUnits("subtract", zdt, d, options).toString(), result.toString());
+
+// order of operations: subtract / reject
+var zdt = Temporal.ZonedDateTime.from("2020-03-31T00:00-08:00[-08:00]");
+var d = Temporal.Duration.from({
+ months: 1,
+ days: 1
+});
+var options = { overflow: "reject" };
+assert.throws(RangeError, () => zdt.subtract(d, options));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/property-bags.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/property-bags.js
new file mode 100644
index 0000000000..e308c33e23
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/property-bags.js
@@ -0,0 +1,296 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: property bags
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+var lagos = Temporal.TimeZone.from("+01:00");
+
+// can be constructed with monthCode and without month
+assert.sameValue(`${ Temporal.ZonedDateTime.from({
+ year: 1976,
+ monthCode: "M11",
+ day: 18,
+ timeZone: lagos
+}) }`, "1976-11-18T00:00:00+01:00[+01:00]");
+
+// can be constructed with month and without monthCode
+assert.sameValue(`${ Temporal.ZonedDateTime.from({
+ year: 1976,
+ month: 11,
+ day: 18,
+ timeZone: lagos
+}) }`, "1976-11-18T00:00:00+01:00[+01:00]");
+
+// month and monthCode must agree
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from({
+ year: 1976,
+ month: 11,
+ monthCode: "M12",
+ day: 18,
+ timeZone: lagos
+}));
+
+// Temporal.ZonedDateTime.from({}) throws
+assert.throws(TypeError, () => Temporal.ZonedDateTime.from({}))
+
+// Temporal.ZonedDateTime.from(required prop undefined) throws
+assert.throws(TypeError, () => Temporal.ZonedDateTime.from({
+ year: 1976,
+ month: undefined,
+ monthCode: undefined,
+ day: 18,
+ timeZone: lagos
+}))
+
+// options may be a function object
+assert.sameValue(`${ Temporal.ZonedDateTime.from({
+ year: 1976,
+ month: 11,
+ day: 18,
+ timeZone: lagos
+}, () => {
+}) }`, "1976-11-18T00:00:00+01:00[+01:00]");
+
+// object must contain at least the required correctly-spelled properties
+assert.throws(TypeError, () => Temporal.ZonedDateTime.from({
+ years: 1976,
+ months: 11,
+ days: 18,
+ timeZone: lagos
+}));
+
+// incorrectly-spelled properties are ignored
+assert.sameValue(`${ Temporal.ZonedDateTime.from({
+ year: 1976,
+ month: 11,
+ day: 18,
+ timeZone: lagos,
+ hours: 12
+}) }`, "1976-11-18T00:00:00+01:00[+01:00]");
+
+// does not accept non-string offset property
+[
+ null,
+ true,
+ 1000,
+ 1000n,
+ Symbol(),
+ {}
+].forEach(offset => {
+ assert.throws(
+ typeof offset === "string" || (typeof offset === "object" && offset !== null) || typeof offset === "function"
+ ? RangeError
+ : TypeError,
+ () => Temporal.ZonedDateTime.from({
+ year: 1976,
+ month: 11,
+ day: 18,
+ offset: offset,
+ timeZone: Temporal.TimeZone.from("+10:00")
+ })
+ )
+});
+
+
+// overflow options
+var bad = {
+ year: 2019,
+ month: 1,
+ day: 32,
+ timeZone: lagos
+};
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(bad, { overflow: "reject" }));
+assert.sameValue(`${ Temporal.ZonedDateTime.from(bad) }`, "2019-01-31T00:00:00+01:00[+01:00]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from(bad, { overflow: "constrain" }) }`, "2019-01-31T00:00:00+01:00[+01:00]");
+
+// Offset options
+
+// { offset: 'reject' } throws if offset does not match offset time zone
+var obj = {
+ year: 2020,
+ month: 3,
+ day: 8,
+ hour: 1,
+ offset: "-04:00",
+ timeZone: "-08:00"
+};
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(obj));
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(obj, { offset: "reject" }));
+
+// { offset: 'reject' } throws if offset does not match IANA time zone
+var obj = {
+ year: 2020,
+ month: 3,
+ day: 8,
+ hour: 1,
+ offset: "-04:00",
+ timeZone: "UTC"
+};
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(obj));
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(obj, { offset: "reject" }));
+
+var cali = TemporalHelpers.springForwardFallBackTimeZone();
+var date = {
+year: 2000,
+month: 10,
+day: 29,
+timeZone: cali
+};
+// { offset: 'prefer' } if offset matches time zone (first 1:30 when DST ends)
+var obj = {
+ ...date,
+ hour: 1,
+ minute: 30,
+ offset: "-07:00"
+};
+assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { offset: "prefer" }) }`, "2000-10-29T01:30:00-07:00[Custom/Spring_Fall]");
+
+// { offset: 'prefer' } if offset matches time zone (second 1:30 when DST ends)
+var obj = {
+ ...date,
+ hour: 1,
+ minute: 30,
+ offset: "-08:00"
+};
+assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { offset: "prefer" }) }`, "2000-10-29T01:30:00-08:00[Custom/Spring_Fall]");
+
+// { offset: 'prefer' } if offset does not match time zone"
+var obj = {
+ ...date,
+ hour: 4,
+ offset: "-07:00"
+};
+assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { offset: "prefer" }) }`, "2000-10-29T04:00:00-08:00[Custom/Spring_Fall]");
+
+// { offset: 'ignore' } uses time zone only
+var obj = {
+ ...date,
+ hour: 4,
+ offset: "-12:00"
+};
+assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { offset: "ignore" }) }`, "2000-10-29T04:00:00-08:00[Custom/Spring_Fall]");
+
+// { offset: 'use' } uses offset only
+var obj = {
+ ...date,
+ hour: 4,
+ offset: "-07:00"
+};
+assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { offset: "use" }) }`, "2000-10-29T03:00:00-08:00[Custom/Spring_Fall]");
+
+// Disambiguation options
+
+// plain datetime with multiple instants - Fall DST
+var obj = {
+ year: 2000,
+ month: 10,
+ day: 29,
+ hour: 1,
+ minute: 45,
+ timeZone: cali
+};
+assert.sameValue(`${ Temporal.ZonedDateTime.from(obj) }`, "2000-10-29T01:45:00-07:00[Custom/Spring_Fall]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { disambiguation: "compatible" }) }`, "2000-10-29T01:45:00-07:00[Custom/Spring_Fall]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { disambiguation: "earlier" }) }`, "2000-10-29T01:45:00-07:00[Custom/Spring_Fall]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { disambiguation: "later" }) }`, "2000-10-29T01:45:00-08:00[Custom/Spring_Fall]");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(obj, { disambiguation: "reject" }));
+
+// plain datetime with multiple instants - Spring DST
+var obj = {
+ year: 2000,
+ month: 4,
+ day: 2,
+ hour: 2,
+ minute: 30,
+ timeZone: cali
+};
+assert.sameValue(`${ Temporal.ZonedDateTime.from(obj) }`, "2000-04-02T03:30:00-07:00[Custom/Spring_Fall]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { disambiguation: "compatible" }) }`, "2000-04-02T03:30:00-07:00[Custom/Spring_Fall]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { disambiguation: "earlier" }) }`, "2000-04-02T01:30:00-08:00[Custom/Spring_Fall]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { disambiguation: "later" }) }`, "2000-04-02T03:30:00-07:00[Custom/Spring_Fall]");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(obj, { disambiguation: "reject" }));
+
+// uses disambiguation if offset is ignored
+var obj = {
+ year: 2000,
+ month: 4,
+ day: 2,
+ hour: 2,
+ minute: 30,
+ timeZone: cali
+};
+var offset = "ignore";
+assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { offset }) }`, "2000-04-02T03:30:00-07:00[Custom/Spring_Fall]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, {
+ offset,
+ disambiguation: "compatible"
+}) }`, "2000-04-02T03:30:00-07:00[Custom/Spring_Fall]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, {
+ offset,
+ disambiguation: "earlier"
+}) }`, "2000-04-02T01:30:00-08:00[Custom/Spring_Fall]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, {
+ offset,
+ disambiguation: "later"
+}) }`, "2000-04-02T03:30:00-07:00[Custom/Spring_Fall]");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(obj, { disambiguation: "reject" }));
+
+// uses disambiguation if offset is wrong and option is prefer
+var obj = {
+ year: 2000,
+ month: 4,
+ day: 2,
+ hour: 2,
+ minute: 30,
+ offset: "-23:59",
+ timeZone: cali
+};
+var offset = "prefer";
+assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, { offset }) }`, "2000-04-02T03:30:00-07:00[Custom/Spring_Fall]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, {
+ offset,
+ disambiguation: "compatible"
+}) }`, "2000-04-02T03:30:00-07:00[Custom/Spring_Fall]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, {
+ offset,
+ disambiguation: "earlier"
+}) }`, "2000-04-02T01:30:00-08:00[Custom/Spring_Fall]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from(obj, {
+ offset,
+ disambiguation: "later"
+}) }`, "2000-04-02T03:30:00-07:00[Custom/Spring_Fall]");
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from(obj, {
+ offset,
+ disambiguation: "reject"
+}));
+
+// throw when bad disambiguation
+[
+ "",
+ "EARLIER",
+ "balance",
+ 3,
+ null
+].forEach(disambiguation => {
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.from("2020-11-01T04:00[UTC]", { disambiguation }));
+});
+
+// sub-minute time zone offsets
+
+// does not truncate offset property to minutes
+var zdt = Temporal.ZonedDateTime.from({
+ year: 1971,
+ month: 1,
+ day: 1,
+ hour: 12,
+ timeZone: TemporalHelpers.specificOffsetTimeZone(-2.67e12) // -00:44:30 in nanoseconds
+});
+assert.sameValue(zdt.offset, "-00:44:30");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/reversibility-of-differences.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/reversibility-of-differences.js
new file mode 100644
index 0000000000..909d7b6b61
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/reversibility-of-differences.js
@@ -0,0 +1,40 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: Reversibility of differences
+features: [Temporal]
+---*/
+
+var earlier = Temporal.ZonedDateTime.from("1976-11-18T15:23:30.123456789-03:00[-03:00]");
+var later = Temporal.ZonedDateTime.from("2019-10-29T10:46:38.271986102-03:00[-03:00]");
+[
+ "hours",
+ "minutes",
+ "seconds"
+].forEach(largestUnit => {
+ var diff = later.since(earlier, { largestUnit });
+assert.sameValue(`${ earlier.since(later, { largestUnit }) }`, `${ diff.negated() }`);
+assert.sameValue(`${ earlier.until(later, { largestUnit }) }`, `${ diff }`);
+// difference symmetrical with regard to negative durations
+ assert(earlier.subtract(diff.negated()).equals(later));
+ assert(later.add(diff.negated()).equals(earlier));
+ });
+[
+ "years",
+ "months",
+ "weeks",
+ "days",
+ "hours",
+ "minutes",
+ "seconds"
+].forEach(largestUnit => {
+ var diff1 = earlier.until(later, { largestUnit });
+ var diff2 = later.since(earlier, { largestUnit });
+ assert(earlier.add(diff1).equals(later));
+ assert(later.subtract(diff2).equals(earlier));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/round.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/round.js
new file mode 100644
index 0000000000..3dfb21989d
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/round.js
@@ -0,0 +1,242 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: Temporal.ZonedDateTime.prototype.round()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+var zdt = Temporal.ZonedDateTime.from("1976-11-18T15:23:30.123456789+01:00[+01:00]");
+
+// throws without parameter
+assert.throws(TypeError, () => zdt.round());
+
+// throws without required smallestUnit parameter
+assert.throws(RangeError, () => zdt.round({}));
+assert.throws(RangeError, () => zdt.round({
+ roundingIncrement: 1,
+ roundingMode: "ceil"
+}));
+
+// throws on disallowed or invalid smallestUnit (string param)
+[
+ "era",
+ "year",
+ "month",
+ "week",
+ "years",
+ "months",
+ "weeks",
+ "nonsense"
+].forEach(smallestUnit => {
+ assert.throws(RangeError, () => zdt.round(smallestUnit));
+});
+
+// rounds to an increment of hours
+assert.sameValue(`${ zdt.round({
+ smallestUnit: "hour",
+ roundingIncrement: 4
+}) }`, "1976-11-18T16:00:00+01:00[+01:00]");
+
+// rounds to an increment of minutes
+assert.sameValue(`${ zdt.round({
+ smallestUnit: "minute",
+ roundingIncrement: 15
+}) }`, "1976-11-18T15:30:00+01:00[+01:00]");
+
+// rounds to an increment of seconds
+assert.sameValue(`${ zdt.round({
+ smallestUnit: "second",
+ roundingIncrement: 30
+}) }`, "1976-11-18T15:23:30+01:00[+01:00]");
+
+// rounds to an increment of milliseconds
+assert.sameValue(`${ zdt.round({
+ smallestUnit: "millisecond",
+ roundingIncrement: 10
+}) }`, "1976-11-18T15:23:30.12+01:00[+01:00]");
+
+// rounds to an increment of microseconds
+assert.sameValue(`${ zdt.round({
+ smallestUnit: "microsecond",
+ roundingIncrement: 10
+}) }`, "1976-11-18T15:23:30.12346+01:00[+01:00]");
+
+// rounds to an increment of nanoseconds
+assert.sameValue(`${ zdt.round({
+ smallestUnit: "nanosecond",
+ roundingIncrement: 10
+}) }`, "1976-11-18T15:23:30.12345679+01:00[+01:00]");
+
+// 1 day is a valid increment
+assert.sameValue(`${ zdt.round({
+ smallestUnit: "day",
+ roundingIncrement: 1
+}) }`, "1976-11-19T00:00:00+01:00[+01:00]");
+
+// valid hour increments divide into 24
+var smallestUnit = "hour";
+[
+ 1,
+ 2,
+ 3,
+ 4,
+ 6,
+ 8,
+ 12
+].forEach(roundingIncrement => {
+ assert(zdt.round({
+ smallestUnit,
+ roundingIncrement
+ }) instanceof Temporal.ZonedDateTime);
+});
+[
+ "minute",
+ "second"
+].forEach(smallestUnit => {
+ // valid minutes/seconds increments divide into 60`, () => {
+ [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 10,
+ 12,
+ 15,
+ 20,
+ 30
+ ].forEach(roundingIncrement => {
+ assert(zdt.round({
+ smallestUnit,
+ roundingIncrement
+ }) instanceof Temporal.ZonedDateTime);
+ });
+ });
+[
+ "millisecond",
+ "microsecond",
+ "nanosecond"
+].forEach(smallestUnit => {
+ // valid increments divide into 1000`
+ [
+ 1,
+ 2,
+ 4,
+ 5,
+ 8,
+ 10,
+ 20,
+ 25,
+ 40,
+ 50,
+ 100,
+ 125,
+ 200,
+ 250,
+ 500
+ ].forEach(roundingIncrement => {
+ assert(zdt.round({
+ smallestUnit,
+ roundingIncrement
+ }) instanceof Temporal.ZonedDateTime);
+ });
+ });
+
+// throws on increments that do not divide evenly into the next highest
+assert.throws(RangeError, () => zdt.round({
+ smallestUnit: "day",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => zdt.round({
+ smallestUnit: "hour",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => zdt.round({
+ smallestUnit: "minute",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => zdt.round({
+ smallestUnit: "second",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => zdt.round({
+ smallestUnit: "millisecond",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => zdt.round({
+ smallestUnit: "microsecond",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => zdt.round({
+ smallestUnit: "nanosecond",
+ roundingIncrement: 29
+}));
+
+// throws on increments that are equal to the next highest
+assert.throws(RangeError, () => zdt.round({
+ smallestUnit: "hour",
+ roundingIncrement: 24
+}));
+assert.throws(RangeError, () => zdt.round({
+ smallestUnit: "minute",
+ roundingIncrement: 60
+}));
+assert.throws(RangeError, () => zdt.round({
+ smallestUnit: "second",
+ roundingIncrement: 60
+}));
+assert.throws(RangeError, () => zdt.round({
+ smallestUnit: "millisecond",
+ roundingIncrement: 1000
+}));
+assert.throws(RangeError, () => zdt.round({
+ smallestUnit: "microsecond",
+ roundingIncrement: 1000
+}));
+assert.throws(RangeError, () => zdt.round({
+ smallestUnit: "nanosecond",
+ roundingIncrement: 1000
+}));
+var bal = Temporal.ZonedDateTime.from("1976-11-18T23:59:59.999999999+01:00[+01:00]");
+[
+ "day",
+ "hour",
+ "minute",
+ "second",
+ "millisecond",
+ "microsecond"
+].forEach(smallestUnit => {
+ assert.sameValue(`${ bal.round({ smallestUnit }) }`, "1976-11-19T00:00:00+01:00[+01:00]");
+});
+
+var timeZone = TemporalHelpers.springForwardFallBackTimeZone();
+
+// rounds correctly to a 25-hour day
+var roundTo = { smallestUnit: "day" };
+var roundMeDown = Temporal.PlainDateTime.from("2000-10-29T12:29:59").toZonedDateTime(timeZone);
+assert.sameValue(`${ roundMeDown.round(roundTo) }`, "2000-10-29T00:00:00-07:00[Custom/Spring_Fall]");
+var roundMeUp = Temporal.PlainDateTime.from("2000-10-29T12:30:01").toZonedDateTime(timeZone);
+assert.sameValue(`${ roundMeUp.round(roundTo) }`, "2000-10-30T00:00:00-08:00[Custom/Spring_Fall]");
+
+// rounds correctly to a 23-hour day
+var roundTo = { smallestUnit: "day" };
+var roundMeDown = Temporal.PlainDateTime.from("2000-04-02T11:29:59").toZonedDateTime(timeZone);
+assert.sameValue(`${ roundMeDown.round(roundTo) }`, "2000-04-02T00:00:00-08:00[Custom/Spring_Fall]");
+var roundMeUp = Temporal.PlainDateTime.from("2000-04-02T11:30:01").toZonedDateTime(timeZone);
+assert.sameValue(`${ roundMeUp.round(roundTo) }`, "2000-04-03T00:00:00-07:00[Custom/Spring_Fall]");
+
+// rounding up to a nonexistent wall-clock time
+var almostSkipped = Temporal.PlainDateTime.from("2000-04-02T01:59:59.999999999").toZonedDateTime(timeZone);
+var rounded = almostSkipped.round({
+ smallestUnit: "microsecond",
+ roundingMode: "halfExpand"
+});
+assert.sameValue(`${ rounded }`, "2000-04-02T03:00:00-07:00[Custom/Spring_Fall]");
+assert.sameValue(rounded.epochNanoseconds - almostSkipped.epochNanoseconds, 1n);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/shell.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/shell.js
new file mode 100644
index 0000000000..60f74c2518
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/shell.js
@@ -0,0 +1,2158 @@
+// GENERATED, DO NOT EDIT
+// file: temporalHelpers.js
+// Copyright (C) 2021 Igalia, S.L. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: |
+ This defines helper objects and functions for testing Temporal.
+defines: [TemporalHelpers]
+features: [Symbol.species, Symbol.iterator, Temporal]
+---*/
+
+const ASCII_IDENTIFIER = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/u;
+
+function formatPropertyName(propertyKey, objectName = "") {
+ switch (typeof propertyKey) {
+ case "symbol":
+ if (Symbol.keyFor(propertyKey) !== undefined) {
+ return `${objectName}[Symbol.for('${Symbol.keyFor(propertyKey)}')]`;
+ } else if (propertyKey.description.startsWith('Symbol.')) {
+ return `${objectName}[${propertyKey.description}]`;
+ } else {
+ return `${objectName}[Symbol('${propertyKey.description}')]`
+ }
+ case "string":
+ if (propertyKey !== String(Number(propertyKey))) {
+ if (ASCII_IDENTIFIER.test(propertyKey)) {
+ return objectName ? `${objectName}.${propertyKey}` : propertyKey;
+ }
+ return `${objectName}['${propertyKey.replace(/'/g, "\\'")}']`
+ }
+ // fall through
+ default:
+ // integer or string integer-index
+ return `${objectName}[${propertyKey}]`;
+ }
+}
+
+const SKIP_SYMBOL = Symbol("Skip");
+
+var TemporalHelpers = {
+ /*
+ * Codes and maximum lengths of months in the ISO 8601 calendar.
+ */
+ ISOMonths: [
+ { month: 1, monthCode: "M01", daysInMonth: 31 },
+ { month: 2, monthCode: "M02", daysInMonth: 29 },
+ { month: 3, monthCode: "M03", daysInMonth: 31 },
+ { month: 4, monthCode: "M04", daysInMonth: 30 },
+ { month: 5, monthCode: "M05", daysInMonth: 31 },
+ { month: 6, monthCode: "M06", daysInMonth: 30 },
+ { month: 7, monthCode: "M07", daysInMonth: 31 },
+ { month: 8, monthCode: "M08", daysInMonth: 31 },
+ { month: 9, monthCode: "M09", daysInMonth: 30 },
+ { month: 10, monthCode: "M10", daysInMonth: 31 },
+ { month: 11, monthCode: "M11", daysInMonth: 30 },
+ { month: 12, monthCode: "M12", daysInMonth: 31 }
+ ],
+
+ /*
+ * assertDuration(duration, years, ..., nanoseconds[, description]):
+ *
+ * Shorthand for asserting that each field of a Temporal.Duration is equal to
+ * an expected value.
+ */
+ assertDuration(duration, years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(duration instanceof Temporal.Duration, `${prefix}instanceof`);
+ assert.sameValue(duration.years, years, `${prefix}years result:`);
+ assert.sameValue(duration.months, months, `${prefix}months result:`);
+ assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`);
+ assert.sameValue(duration.days, days, `${prefix}days result:`);
+ assert.sameValue(duration.hours, hours, `${prefix}hours result:`);
+ assert.sameValue(duration.minutes, minutes, `${prefix}minutes result:`);
+ assert.sameValue(duration.seconds, seconds, `${prefix}seconds result:`);
+ assert.sameValue(duration.milliseconds, milliseconds, `${prefix}milliseconds result:`);
+ assert.sameValue(duration.microseconds, microseconds, `${prefix}microseconds result:`);
+ assert.sameValue(duration.nanoseconds, nanoseconds, `${prefix}nanoseconds result`);
+ },
+
+ /*
+ * assertDateDuration(duration, years, months, weeks, days, [, description]):
+ *
+ * Shorthand for asserting that each date field of a Temporal.Duration is
+ * equal to an expected value.
+ */
+ assertDateDuration(duration, years, months, weeks, days, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(duration instanceof Temporal.Duration, `${prefix}instanceof`);
+ assert.sameValue(duration.years, years, `${prefix}years result:`);
+ assert.sameValue(duration.months, months, `${prefix}months result:`);
+ assert.sameValue(duration.weeks, weeks, `${prefix}weeks result:`);
+ assert.sameValue(duration.days, days, `${prefix}days result:`);
+ assert.sameValue(duration.hours, 0, `${prefix}hours result should be zero:`);
+ assert.sameValue(duration.minutes, 0, `${prefix}minutes result should be zero:`);
+ assert.sameValue(duration.seconds, 0, `${prefix}seconds result should be zero:`);
+ assert.sameValue(duration.milliseconds, 0, `${prefix}milliseconds result should be zero:`);
+ assert.sameValue(duration.microseconds, 0, `${prefix}microseconds result should be zero:`);
+ assert.sameValue(duration.nanoseconds, 0, `${prefix}nanoseconds result should be zero:`);
+ },
+
+ /*
+ * assertDurationsEqual(actual, expected[, description]):
+ *
+ * Shorthand for asserting that each field of a Temporal.Duration is equal to
+ * the corresponding field in another Temporal.Duration.
+ */
+ assertDurationsEqual(actual, expected, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(expected instanceof Temporal.Duration, `${prefix}expected value should be a Temporal.Duration`);
+ TemporalHelpers.assertDuration(actual, expected.years, expected.months, expected.weeks, expected.days, expected.hours, expected.minutes, expected.seconds, expected.milliseconds, expected.microseconds, expected.nanoseconds, description);
+ },
+
+ /*
+ * assertInstantsEqual(actual, expected[, description]):
+ *
+ * Shorthand for asserting that two Temporal.Instants are of the correct type
+ * and equal according to their equals() methods.
+ */
+ assertInstantsEqual(actual, expected, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(expected instanceof Temporal.Instant, `${prefix}expected value should be a Temporal.Instant`);
+ assert(actual instanceof Temporal.Instant, `${prefix}instanceof`);
+ assert(actual.equals(expected), `${prefix}equals method`);
+ },
+
+ /*
+ * assertPlainDate(date, year, ..., nanosecond[, description[, era, eraYear]]):
+ *
+ * Shorthand for asserting that each field of a Temporal.PlainDate is equal to
+ * an expected value. (Except the `calendar` property, since callers may want
+ * to assert either object equality with an object they put in there, or the
+ * value of date.calendarId.)
+ */
+ assertPlainDate(date, year, month, monthCode, day, description = "", era = undefined, eraYear = undefined) {
+ const prefix = description ? `${description}: ` : "";
+ assert(date instanceof Temporal.PlainDate, `${prefix}instanceof`);
+ assert.sameValue(date.era, era, `${prefix}era result:`);
+ assert.sameValue(date.eraYear, eraYear, `${prefix}eraYear result:`);
+ assert.sameValue(date.year, year, `${prefix}year result:`);
+ assert.sameValue(date.month, month, `${prefix}month result:`);
+ assert.sameValue(date.monthCode, monthCode, `${prefix}monthCode result:`);
+ assert.sameValue(date.day, day, `${prefix}day result:`);
+ },
+
+ /*
+ * assertPlainDateTime(datetime, year, ..., nanosecond[, description[, era, eraYear]]):
+ *
+ * Shorthand for asserting that each field of a Temporal.PlainDateTime is
+ * equal to an expected value. (Except the `calendar` property, since callers
+ * may want to assert either object equality with an object they put in there,
+ * or the value of datetime.calendarId.)
+ */
+ assertPlainDateTime(datetime, year, month, monthCode, day, hour, minute, second, millisecond, microsecond, nanosecond, description = "", era = undefined, eraYear = undefined) {
+ const prefix = description ? `${description}: ` : "";
+ assert(datetime instanceof Temporal.PlainDateTime, `${prefix}instanceof`);
+ assert.sameValue(datetime.era, era, `${prefix}era result:`);
+ assert.sameValue(datetime.eraYear, eraYear, `${prefix}eraYear result:`);
+ assert.sameValue(datetime.year, year, `${prefix}year result:`);
+ assert.sameValue(datetime.month, month, `${prefix}month result:`);
+ assert.sameValue(datetime.monthCode, monthCode, `${prefix}monthCode result:`);
+ assert.sameValue(datetime.day, day, `${prefix}day result:`);
+ assert.sameValue(datetime.hour, hour, `${prefix}hour result:`);
+ assert.sameValue(datetime.minute, minute, `${prefix}minute result:`);
+ assert.sameValue(datetime.second, second, `${prefix}second result:`);
+ assert.sameValue(datetime.millisecond, millisecond, `${prefix}millisecond result:`);
+ assert.sameValue(datetime.microsecond, microsecond, `${prefix}microsecond result:`);
+ assert.sameValue(datetime.nanosecond, nanosecond, `${prefix}nanosecond result:`);
+ },
+
+ /*
+ * assertPlainDateTimesEqual(actual, expected[, description]):
+ *
+ * Shorthand for asserting that two Temporal.PlainDateTimes are of the correct
+ * type, equal according to their equals() methods, and additionally that
+ * their calendar internal slots are the same value.
+ */
+ assertPlainDateTimesEqual(actual, expected, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(expected instanceof Temporal.PlainDateTime, `${prefix}expected value should be a Temporal.PlainDateTime`);
+ assert(actual instanceof Temporal.PlainDateTime, `${prefix}instanceof`);
+ assert(actual.equals(expected), `${prefix}equals method`);
+ assert.sameValue(
+ actual.getISOFields().calendar,
+ expected.getISOFields().calendar,
+ `${prefix}calendar same value:`
+ );
+ },
+
+ /*
+ * assertPlainMonthDay(monthDay, monthCode, day[, description [, referenceISOYear]]):
+ *
+ * Shorthand for asserting that each field of a Temporal.PlainMonthDay is
+ * equal to an expected value. (Except the `calendar` property, since callers
+ * may want to assert either object equality with an object they put in there,
+ * or the value of monthDay.calendarId().)
+ */
+ assertPlainMonthDay(monthDay, monthCode, day, description = "", referenceISOYear = 1972) {
+ const prefix = description ? `${description}: ` : "";
+ assert(monthDay instanceof Temporal.PlainMonthDay, `${prefix}instanceof`);
+ assert.sameValue(monthDay.monthCode, monthCode, `${prefix}monthCode result:`);
+ assert.sameValue(monthDay.day, day, `${prefix}day result:`);
+ assert.sameValue(monthDay.getISOFields().isoYear, referenceISOYear, `${prefix}referenceISOYear result:`);
+ },
+
+ /*
+ * assertPlainTime(time, hour, ..., nanosecond[, description]):
+ *
+ * Shorthand for asserting that each field of a Temporal.PlainTime is equal to
+ * an expected value.
+ */
+ assertPlainTime(time, hour, minute, second, millisecond, microsecond, nanosecond, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(time instanceof Temporal.PlainTime, `${prefix}instanceof`);
+ assert.sameValue(time.hour, hour, `${prefix}hour result:`);
+ assert.sameValue(time.minute, minute, `${prefix}minute result:`);
+ assert.sameValue(time.second, second, `${prefix}second result:`);
+ assert.sameValue(time.millisecond, millisecond, `${prefix}millisecond result:`);
+ assert.sameValue(time.microsecond, microsecond, `${prefix}microsecond result:`);
+ assert.sameValue(time.nanosecond, nanosecond, `${prefix}nanosecond result:`);
+ },
+
+ /*
+ * assertPlainTimesEqual(actual, expected[, description]):
+ *
+ * Shorthand for asserting that two Temporal.PlainTimes are of the correct
+ * type and equal according to their equals() methods.
+ */
+ assertPlainTimesEqual(actual, expected, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(expected instanceof Temporal.PlainTime, `${prefix}expected value should be a Temporal.PlainTime`);
+ assert(actual instanceof Temporal.PlainTime, `${prefix}instanceof`);
+ assert(actual.equals(expected), `${prefix}equals method`);
+ },
+
+ /*
+ * assertPlainYearMonth(yearMonth, year, month, monthCode[, description[, era, eraYear, referenceISODay]]):
+ *
+ * Shorthand for asserting that each field of a Temporal.PlainYearMonth is
+ * equal to an expected value. (Except the `calendar` property, since callers
+ * may want to assert either object equality with an object they put in there,
+ * or the value of yearMonth.calendarId.)
+ */
+ assertPlainYearMonth(yearMonth, year, month, monthCode, description = "", era = undefined, eraYear = undefined, referenceISODay = 1) {
+ const prefix = description ? `${description}: ` : "";
+ assert(yearMonth instanceof Temporal.PlainYearMonth, `${prefix}instanceof`);
+ assert.sameValue(yearMonth.era, era, `${prefix}era result:`);
+ assert.sameValue(yearMonth.eraYear, eraYear, `${prefix}eraYear result:`);
+ assert.sameValue(yearMonth.year, year, `${prefix}year result:`);
+ assert.sameValue(yearMonth.month, month, `${prefix}month result:`);
+ assert.sameValue(yearMonth.monthCode, monthCode, `${prefix}monthCode result:`);
+ assert.sameValue(yearMonth.getISOFields().isoDay, referenceISODay, `${prefix}referenceISODay result:`);
+ },
+
+ /*
+ * assertZonedDateTimesEqual(actual, expected[, description]):
+ *
+ * Shorthand for asserting that two Temporal.ZonedDateTimes are of the correct
+ * type, equal according to their equals() methods, and additionally that
+ * their time zones and calendar internal slots are the same value.
+ */
+ assertZonedDateTimesEqual(actual, expected, description = "") {
+ const prefix = description ? `${description}: ` : "";
+ assert(expected instanceof Temporal.ZonedDateTime, `${prefix}expected value should be a Temporal.ZonedDateTime`);
+ assert(actual instanceof Temporal.ZonedDateTime, `${prefix}instanceof`);
+ assert(actual.equals(expected), `${prefix}equals method`);
+ assert.sameValue(actual.timeZone, expected.timeZone, `${prefix}time zone same value:`);
+ assert.sameValue(
+ actual.getISOFields().calendar,
+ expected.getISOFields().calendar,
+ `${prefix}calendar same value:`
+ );
+ },
+
+ /*
+ * assertUnreachable(description):
+ *
+ * Helper for asserting that code is not executed. This is useful for
+ * assertions that methods of user calendars and time zones are not called.
+ */
+ assertUnreachable(description) {
+ let message = "This code should not be executed";
+ if (description) {
+ message = `${message}: ${description}`;
+ }
+ throw new Test262Error(message);
+ },
+
+ /*
+ * checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls):
+ *
+ * When an options object with a largestUnit property is synthesized inside
+ * Temporal and passed to user code such as calendar.dateUntil(), the value of
+ * the largestUnit property should be in the singular form, even if the input
+ * was given in the plural form.
+ * (This doesn't apply when the options object is passed through verbatim.)
+ *
+ * func(calendar, largestUnit, index) is the operation under test. It's called
+ * with an instance of a calendar that keeps track of which largestUnit is
+ * passed to dateUntil(), each key of expectedLargestUnitCalls in turn, and
+ * the key's numerical index in case the function needs to generate test data
+ * based on the index. At the end, the actual values passed to dateUntil() are
+ * compared with the array values of expectedLargestUnitCalls.
+ */
+ checkCalendarDateUntilLargestUnitSingular(func, expectedLargestUnitCalls) {
+ const actual = [];
+
+ class DateUntilOptionsCalendar extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateUntil(earlier, later, options) {
+ actual.push(options.largestUnit);
+ return super.dateUntil(earlier, later, options);
+ }
+
+ toString() {
+ return "date-until-options";
+ }
+ }
+
+ const calendar = new DateUntilOptionsCalendar();
+ Object.entries(expectedLargestUnitCalls).forEach(([largestUnit, expected], index) => {
+ func(calendar, largestUnit, index);
+ assert.compareArray(actual, expected, `largestUnit passed to calendar.dateUntil() for largestUnit ${largestUnit}`);
+ actual.splice(0); // empty it for the next check
+ });
+ },
+
+ /*
+ * checkPlainDateTimeConversionFastPath(func):
+ *
+ * ToTemporalDate and ToTemporalTime should both, if given a
+ * Temporal.PlainDateTime instance, convert to the desired type by reading the
+ * PlainDateTime's internal slots, rather than calling any getters.
+ *
+ * func(datetime, calendar) is the actual operation to test, that must
+ * internally call the abstract operation ToTemporalDate or ToTemporalTime.
+ * It is passed a Temporal.PlainDateTime instance, as well as the instance's
+ * calendar object (so that it doesn't have to call the calendar getter itself
+ * if it wants to make any assertions about the calendar.)
+ */
+ checkPlainDateTimeConversionFastPath(func, message = "checkPlainDateTimeConversionFastPath") {
+ const actual = [];
+ const expected = [];
+
+ const calendar = new Temporal.Calendar("iso8601");
+ const datetime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+ const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDateTime.prototype);
+ ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => {
+ Object.defineProperty(datetime, property, {
+ get() {
+ actual.push(`get ${formatPropertyName(property)}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return {
+ toString() {
+ actual.push(`toString ${formatPropertyName(property)}`);
+ return value.toString();
+ },
+ valueOf() {
+ actual.push(`valueOf ${formatPropertyName(property)}`);
+ return value;
+ },
+ };
+ },
+ });
+ });
+ Object.defineProperty(datetime, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ func(datetime, calendar);
+ assert.compareArray(actual, expected, `${message}: property getters not called`);
+ },
+
+ /*
+ * Check that an options bag that accepts units written in the singular form,
+ * also accepts the same units written in the plural form.
+ * func(unit) should call the method with the appropriate options bag
+ * containing unit as a value. This will be called twice for each element of
+ * validSingularUnits, once with singular and once with plural, and the
+ * results of each pair should be the same (whether a Temporal object or a
+ * primitive value.)
+ */
+ checkPluralUnitsAccepted(func, validSingularUnits) {
+ const plurals = {
+ year: 'years',
+ month: 'months',
+ week: 'weeks',
+ day: 'days',
+ hour: 'hours',
+ minute: 'minutes',
+ second: 'seconds',
+ millisecond: 'milliseconds',
+ microsecond: 'microseconds',
+ nanosecond: 'nanoseconds',
+ };
+
+ validSingularUnits.forEach((unit) => {
+ const singularValue = func(unit);
+ const pluralValue = func(plurals[unit]);
+ const desc = `Plural ${plurals[unit]} produces the same result as singular ${unit}`;
+ if (singularValue instanceof Temporal.Duration) {
+ TemporalHelpers.assertDurationsEqual(pluralValue, singularValue, desc);
+ } else if (singularValue instanceof Temporal.Instant) {
+ TemporalHelpers.assertInstantsEqual(pluralValue, singularValue, desc);
+ } else if (singularValue instanceof Temporal.PlainDateTime) {
+ TemporalHelpers.assertPlainDateTimesEqual(pluralValue, singularValue, desc);
+ } else if (singularValue instanceof Temporal.PlainTime) {
+ TemporalHelpers.assertPlainTimesEqual(pluralValue, singularValue, desc);
+ } else if (singularValue instanceof Temporal.ZonedDateTime) {
+ TemporalHelpers.assertZonedDateTimesEqual(pluralValue, singularValue, desc);
+ } else {
+ assert.sameValue(pluralValue, singularValue);
+ }
+ });
+ },
+
+ /*
+ * checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc):
+ *
+ * Checks the type handling of the roundingIncrement option.
+ * checkFunc(roundingIncrement) is a function which takes the value of
+ * roundingIncrement to test, and calls the method under test with it,
+ * returning the result. assertTrueResultFunc(result, description) should
+ * assert that result is the expected result with roundingIncrement: true, and
+ * assertObjectResultFunc(result, description) should assert that result is
+ * the expected result with roundingIncrement being an object with a valueOf()
+ * method.
+ */
+ checkRoundingIncrementOptionWrongType(checkFunc, assertTrueResultFunc, assertObjectResultFunc) {
+ // null converts to 0, which is out of range
+ assert.throws(RangeError, () => checkFunc(null), "null");
+ // Booleans convert to either 0 or 1, and 1 is allowed
+ const trueResult = checkFunc(true);
+ assertTrueResultFunc(trueResult, "true");
+ assert.throws(RangeError, () => checkFunc(false), "false");
+ // Symbols and BigInts cannot convert to numbers
+ assert.throws(TypeError, () => checkFunc(Symbol()), "symbol");
+ assert.throws(TypeError, () => checkFunc(2n), "bigint");
+
+ // Objects prefer their valueOf() methods when converting to a number
+ assert.throws(RangeError, () => checkFunc({}), "plain object");
+
+ const expected = [
+ "get roundingIncrement.valueOf",
+ "call roundingIncrement.valueOf",
+ ];
+ const actual = [];
+ const observer = TemporalHelpers.toPrimitiveObserver(actual, 2, "roundingIncrement");
+ const objectResult = checkFunc(observer);
+ assertObjectResultFunc(objectResult, "object with valueOf");
+ assert.compareArray(actual, expected, "order of operations");
+ },
+
+ /*
+ * checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc):
+ *
+ * Checks the type handling of a string option, of which there are several in
+ * Temporal.
+ * propertyName is the name of the option, and value is the value that
+ * assertFunc should expect it to have.
+ * checkFunc(value) is a function which takes the value of the option to test,
+ * and calls the method under test with it, returning the result.
+ * assertFunc(result, description) should assert that result is the expected
+ * result with the option value being an object with a toString() method
+ * which returns the given value.
+ */
+ checkStringOptionWrongType(propertyName, value, checkFunc, assertFunc) {
+ // null converts to the string "null", which is an invalid string value
+ assert.throws(RangeError, () => checkFunc(null), "null");
+ // Booleans convert to the strings "true" or "false", which are invalid
+ assert.throws(RangeError, () => checkFunc(true), "true");
+ assert.throws(RangeError, () => checkFunc(false), "false");
+ // Symbols cannot convert to strings
+ assert.throws(TypeError, () => checkFunc(Symbol()), "symbol");
+ // Numbers convert to strings which are invalid
+ assert.throws(RangeError, () => checkFunc(2), "number");
+ // BigInts convert to strings which are invalid
+ assert.throws(RangeError, () => checkFunc(2n), "bigint");
+
+ // Objects prefer their toString() methods when converting to a string
+ assert.throws(RangeError, () => checkFunc({}), "plain object");
+
+ const expected = [
+ `get ${propertyName}.toString`,
+ `call ${propertyName}.toString`,
+ ];
+ const actual = [];
+ const observer = TemporalHelpers.toPrimitiveObserver(actual, value, propertyName);
+ const result = checkFunc(observer);
+ assertFunc(result, "object with toString");
+ assert.compareArray(actual, expected, "order of operations");
+ },
+
+ /*
+ * checkSubclassingIgnored(construct, constructArgs, method, methodArgs,
+ * resultAssertions):
+ *
+ * Methods of Temporal classes that return a new instance of the same class,
+ * must not take the constructor of a subclass into account, nor the @@species
+ * property. This helper runs tests to ensure this.
+ *
+ * construct(...constructArgs) must yield a valid instance of the Temporal
+ * class. instance[method](...methodArgs) is the method call under test, which
+ * must also yield a valid instance of the same Temporal class, not a
+ * subclass. See below for the individual tests that this runs.
+ * resultAssertions() is a function that performs additional assertions on the
+ * instance returned by the method under test.
+ */
+ checkSubclassingIgnored(...args) {
+ this.checkSubclassConstructorNotObject(...args);
+ this.checkSubclassConstructorUndefined(...args);
+ this.checkSubclassConstructorThrows(...args);
+ this.checkSubclassConstructorNotCalled(...args);
+ this.checkSubclassSpeciesInvalidResult(...args);
+ this.checkSubclassSpeciesNotAConstructor(...args);
+ this.checkSubclassSpeciesNull(...args);
+ this.checkSubclassSpeciesUndefined(...args);
+ this.checkSubclassSpeciesThrows(...args);
+ },
+
+ /*
+ * Checks that replacing the 'constructor' property of the instance with
+ * various primitive values does not affect the returned new instance.
+ */
+ checkSubclassConstructorNotObject(construct, constructArgs, method, methodArgs, resultAssertions) {
+ function check(value, description) {
+ const instance = new construct(...constructArgs);
+ instance.constructor = value;
+ const result = instance[method](...methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
+ resultAssertions(result);
+ }
+
+ check(null, "null");
+ check(true, "true");
+ check("test", "string");
+ check(Symbol(), "Symbol");
+ check(7, "number");
+ check(7n, "bigint");
+ },
+
+ /*
+ * Checks that replacing the 'constructor' property of the subclass with
+ * undefined does not affect the returned new instance.
+ */
+ checkSubclassConstructorUndefined(construct, constructArgs, method, methodArgs, resultAssertions) {
+ let called = 0;
+
+ class MySubclass extends construct {
+ constructor() {
+ ++called;
+ super(...constructArgs);
+ }
+ }
+
+ const instance = new MySubclass();
+ assert.sameValue(called, 1);
+
+ MySubclass.prototype.constructor = undefined;
+
+ const result = instance[method](...methodArgs);
+ assert.sameValue(called, 1);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Checks that making the 'constructor' property of the instance throw when
+ * called does not affect the returned new instance.
+ */
+ checkSubclassConstructorThrows(construct, constructArgs, method, methodArgs, resultAssertions) {
+ function CustomError() {}
+ const instance = new construct(...constructArgs);
+ Object.defineProperty(instance, "constructor", {
+ get() {
+ throw new CustomError();
+ }
+ });
+ const result = instance[method](...methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Checks that when subclassing, the subclass constructor is not called by
+ * the method under test.
+ */
+ checkSubclassConstructorNotCalled(construct, constructArgs, method, methodArgs, resultAssertions) {
+ let called = 0;
+
+ class MySubclass extends construct {
+ constructor() {
+ ++called;
+ super(...constructArgs);
+ }
+ }
+
+ const instance = new MySubclass();
+ assert.sameValue(called, 1);
+
+ const result = instance[method](...methodArgs);
+ assert.sameValue(called, 1);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Check that the constructor's @@species property is ignored when it's a
+ * constructor that returns a non-object value.
+ */
+ checkSubclassSpeciesInvalidResult(construct, constructArgs, method, methodArgs, resultAssertions) {
+ function check(value, description) {
+ const instance = new construct(...constructArgs);
+ instance.constructor = {
+ [Symbol.species]: function() {
+ return value;
+ },
+ };
+ const result = instance[method](...methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
+ resultAssertions(result);
+ }
+
+ check(undefined, "undefined");
+ check(null, "null");
+ check(true, "true");
+ check("test", "string");
+ check(Symbol(), "Symbol");
+ check(7, "number");
+ check(7n, "bigint");
+ check({}, "plain object");
+ },
+
+ /*
+ * Check that the constructor's @@species property is ignored when it's not a
+ * constructor.
+ */
+ checkSubclassSpeciesNotAConstructor(construct, constructArgs, method, methodArgs, resultAssertions) {
+ function check(value, description) {
+ const instance = new construct(...constructArgs);
+ instance.constructor = {
+ [Symbol.species]: value,
+ };
+ const result = instance[method](...methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype, description);
+ resultAssertions(result);
+ }
+
+ check(true, "true");
+ check("test", "string");
+ check(Symbol(), "Symbol");
+ check(7, "number");
+ check(7n, "bigint");
+ check({}, "plain object");
+ },
+
+ /*
+ * Check that the constructor's @@species property is ignored when it's null.
+ */
+ checkSubclassSpeciesNull(construct, constructArgs, method, methodArgs, resultAssertions) {
+ let called = 0;
+
+ class MySubclass extends construct {
+ constructor() {
+ ++called;
+ super(...constructArgs);
+ }
+ }
+
+ const instance = new MySubclass();
+ assert.sameValue(called, 1);
+
+ MySubclass.prototype.constructor = {
+ [Symbol.species]: null,
+ };
+
+ const result = instance[method](...methodArgs);
+ assert.sameValue(called, 1);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Check that the constructor's @@species property is ignored when it's
+ * undefined.
+ */
+ checkSubclassSpeciesUndefined(construct, constructArgs, method, methodArgs, resultAssertions) {
+ let called = 0;
+
+ class MySubclass extends construct {
+ constructor() {
+ ++called;
+ super(...constructArgs);
+ }
+ }
+
+ const instance = new MySubclass();
+ assert.sameValue(called, 1);
+
+ MySubclass.prototype.constructor = {
+ [Symbol.species]: undefined,
+ };
+
+ const result = instance[method](...methodArgs);
+ assert.sameValue(called, 1);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Check that the constructor's @@species property is ignored when it throws,
+ * i.e. it is not called at all.
+ */
+ checkSubclassSpeciesThrows(construct, constructArgs, method, methodArgs, resultAssertions) {
+ function CustomError() {}
+
+ const instance = new construct(...constructArgs);
+ instance.constructor = {
+ get [Symbol.species]() {
+ throw new CustomError();
+ },
+ };
+
+ const result = instance[method](...methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ },
+
+ /*
+ * checkSubclassingIgnoredStatic(construct, method, methodArgs, resultAssertions):
+ *
+ * Static methods of Temporal classes that return a new instance of the class,
+ * must not use the this-value as a constructor. This helper runs tests to
+ * ensure this.
+ *
+ * construct[method](...methodArgs) is the static method call under test, and
+ * must yield a valid instance of the Temporal class, not a subclass. See
+ * below for the individual tests that this runs.
+ * resultAssertions() is a function that performs additional assertions on the
+ * instance returned by the method under test.
+ */
+ checkSubclassingIgnoredStatic(...args) {
+ this.checkStaticInvalidReceiver(...args);
+ this.checkStaticReceiverNotCalled(...args);
+ this.checkThisValueNotCalled(...args);
+ },
+
+ /*
+ * Check that calling the static method with a receiver that's not callable,
+ * still calls the intrinsic constructor.
+ */
+ checkStaticInvalidReceiver(construct, method, methodArgs, resultAssertions) {
+ function check(value, description) {
+ const result = construct[method].apply(value, methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ }
+
+ check(undefined, "undefined");
+ check(null, "null");
+ check(true, "true");
+ check("test", "string");
+ check(Symbol(), "symbol");
+ check(7, "number");
+ check(7n, "bigint");
+ check({}, "Non-callable object");
+ },
+
+ /*
+ * Check that calling the static method with a receiver that returns a value
+ * that's not callable, still calls the intrinsic constructor.
+ */
+ checkStaticReceiverNotCalled(construct, method, methodArgs, resultAssertions) {
+ function check(value, description) {
+ const receiver = function () {
+ return value;
+ };
+ const result = construct[method].apply(receiver, methodArgs);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ }
+
+ check(undefined, "undefined");
+ check(null, "null");
+ check(true, "true");
+ check("test", "string");
+ check(Symbol(), "symbol");
+ check(7, "number");
+ check(7n, "bigint");
+ check({}, "Non-callable object");
+ },
+
+ /*
+ * Check that the receiver isn't called.
+ */
+ checkThisValueNotCalled(construct, method, methodArgs, resultAssertions) {
+ let called = false;
+
+ class MySubclass extends construct {
+ constructor(...args) {
+ called = true;
+ super(...args);
+ }
+ }
+
+ const result = MySubclass[method](...methodArgs);
+ assert.sameValue(called, false);
+ assert.sameValue(Object.getPrototypeOf(result), construct.prototype);
+ resultAssertions(result);
+ },
+
+ /*
+ * Check that any iterable returned from a custom time zone's
+ * getPossibleInstantsFor() method is exhausted.
+ * The custom time zone object is passed in to func().
+ * expected is an array of strings representing the expected calls to the
+ * getPossibleInstantsFor() method. The PlainDateTimes that it is called with,
+ * are compared (using their toString() results) with the array.
+ */
+ checkTimeZonePossibleInstantsIterable(func, expected) {
+ // A custom time zone that returns an iterable instead of an array from its
+ // getPossibleInstantsFor() method, and for testing purposes skips
+ // 00:00-01:00 UTC on January 1, 2030, and repeats 00:00-01:00 UTC+1 on
+ // January 3, 2030. Otherwise identical to the UTC time zone.
+ class TimeZonePossibleInstantsIterable extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ this.getPossibleInstantsForCallCount = 0;
+ this.getPossibleInstantsForCalledWith = [];
+ this.getPossibleInstantsForReturns = [];
+ this.iteratorExhausted = [];
+ }
+
+ toString() {
+ return "Custom/Iterable";
+ }
+
+ getOffsetNanosecondsFor(instant) {
+ if (Temporal.Instant.compare(instant, "2030-01-01T00:00Z") >= 0 &&
+ Temporal.Instant.compare(instant, "2030-01-03T01:00Z") < 0) {
+ return 3600_000_000_000;
+ } else {
+ return 0;
+ }
+ }
+
+ getPossibleInstantsFor(dateTime) {
+ this.getPossibleInstantsForCallCount++;
+ this.getPossibleInstantsForCalledWith.push(dateTime);
+
+ // Fake DST transition
+ let retval = super.getPossibleInstantsFor(dateTime);
+ if (dateTime.toPlainDate().equals("2030-01-01") && dateTime.hour === 0) {
+ retval = [];
+ } else if (dateTime.toPlainDate().equals("2030-01-03") && dateTime.hour === 0) {
+ retval.push(retval[0].subtract({ hours: 1 }));
+ } else if (dateTime.year === 2030 && dateTime.month === 1 && dateTime.day >= 1 && dateTime.day <= 2) {
+ retval[0] = retval[0].subtract({ hours: 1 });
+ }
+
+ this.getPossibleInstantsForReturns.push(retval);
+ this.iteratorExhausted.push(false);
+ return {
+ callIndex: this.getPossibleInstantsForCallCount - 1,
+ timeZone: this,
+ *[Symbol.iterator]() {
+ yield* this.timeZone.getPossibleInstantsForReturns[this.callIndex];
+ this.timeZone.iteratorExhausted[this.callIndex] = true;
+ },
+ };
+ }
+ }
+
+ const timeZone = new TimeZonePossibleInstantsIterable();
+ func(timeZone);
+
+ assert.sameValue(timeZone.getPossibleInstantsForCallCount, expected.length, "getPossibleInstantsFor() method called correct number of times");
+
+ for (let index = 0; index < expected.length; index++) {
+ assert.sameValue(timeZone.getPossibleInstantsForCalledWith[index].toString(), expected[index], "getPossibleInstantsFor() called with expected PlainDateTime");
+ assert(timeZone.iteratorExhausted[index], "iterated through the whole iterable");
+ }
+ },
+
+ /*
+ * Check that any calendar-carrying Temporal object has its [[Calendar]]
+ * internal slot read by ToTemporalCalendar, and does not fetch the calendar
+ * by calling getters.
+ * The custom calendar object is passed in to func() so that it can do its
+ * own additional assertions involving the calendar if necessary. (Sometimes
+ * there is nothing to assert as the calendar isn't stored anywhere that can
+ * be asserted about.)
+ */
+ checkToTemporalCalendarFastPath(func) {
+ class CalendarFastPathCheck extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateFromFields(...args) {
+ return super.dateFromFields(...args).withCalendar(this);
+ }
+
+ monthDayFromFields(...args) {
+ const { isoYear, isoMonth, isoDay } = super.monthDayFromFields(...args).getISOFields();
+ return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear);
+ }
+
+ yearMonthFromFields(...args) {
+ const { isoYear, isoMonth, isoDay } = super.yearMonthFromFields(...args).getISOFields();
+ return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay);
+ }
+
+ toString() {
+ return "fast-path-check";
+ }
+ }
+ const calendar = new CalendarFastPathCheck();
+
+ const plainDate = new Temporal.PlainDate(2000, 5, 2, calendar);
+ const plainDateTime = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar);
+ const plainMonthDay = new Temporal.PlainMonthDay(5, 2, calendar);
+ const plainYearMonth = new Temporal.PlainYearMonth(2000, 5, calendar);
+ const zonedDateTime = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar);
+
+ [plainDate, plainDateTime, plainMonthDay, plainYearMonth, zonedDateTime].forEach((temporalObject) => {
+ const actual = [];
+ const expected = [];
+
+ Object.defineProperty(temporalObject, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ func(temporalObject, calendar);
+ assert.compareArray(actual, expected, "calendar getter not called");
+ });
+ },
+
+ checkToTemporalInstantFastPath(func) {
+ const actual = [];
+ const expected = [];
+
+ const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC");
+ Object.defineProperty(datetime, 'toString', {
+ get() {
+ actual.push("get toString");
+ return function (options) {
+ actual.push("call toString");
+ return Temporal.ZonedDateTime.prototype.toString.call(this, options);
+ };
+ },
+ });
+
+ func(datetime);
+ assert.compareArray(actual, expected, "toString not called");
+ },
+
+ checkToTemporalPlainDateTimeFastPath(func) {
+ const actual = [];
+ const expected = [];
+
+ const calendar = new Temporal.Calendar("iso8601");
+ const date = new Temporal.PlainDate(2000, 5, 2, calendar);
+ const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.PlainDate.prototype);
+ ["year", "month", "monthCode", "day"].forEach((property) => {
+ Object.defineProperty(date, property, {
+ get() {
+ actual.push(`get ${formatPropertyName(property)}`);
+ const value = prototypeDescrs[property].get.call(this);
+ return TemporalHelpers.toPrimitiveObserver(actual, value, property);
+ },
+ });
+ });
+ ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((property) => {
+ Object.defineProperty(date, property, {
+ get() {
+ actual.push(`get ${formatPropertyName(property)}`);
+ return undefined;
+ },
+ });
+ });
+ Object.defineProperty(date, "calendar", {
+ get() {
+ actual.push("get calendar");
+ return calendar;
+ },
+ });
+
+ func(date, calendar);
+ assert.compareArray(actual, expected, "property getters not called");
+ },
+
+ /*
+ * A custom calendar used in prototype pollution checks. Verifies that the
+ * fromFields methods are always called with a null-prototype fields object.
+ */
+ calendarCheckFieldsPrototypePollution() {
+ class CalendarCheckFieldsPrototypePollution extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.dateFromFieldsCallCount = 0;
+ this.yearMonthFromFieldsCallCount = 0;
+ this.monthDayFromFieldsCallCount = 0;
+ }
+
+ // toString must remain "iso8601", so that some methods don't throw due to
+ // incompatible calendars
+
+ dateFromFields(fields, options = {}) {
+ this.dateFromFieldsCallCount++;
+ assert.sameValue(Object.getPrototypeOf(fields), null, "dateFromFields should be called with null-prototype fields object");
+ return super.dateFromFields(fields, options);
+ }
+
+ yearMonthFromFields(fields, options = {}) {
+ this.yearMonthFromFieldsCallCount++;
+ assert.sameValue(Object.getPrototypeOf(fields), null, "yearMonthFromFields should be called with null-prototype fields object");
+ return super.yearMonthFromFields(fields, options);
+ }
+
+ monthDayFromFields(fields, options = {}) {
+ this.monthDayFromFieldsCallCount++;
+ assert.sameValue(Object.getPrototypeOf(fields), null, "monthDayFromFields should be called with null-prototype fields object");
+ return super.monthDayFromFields(fields, options);
+ }
+ }
+
+ return new CalendarCheckFieldsPrototypePollution();
+ },
+
+ /*
+ * A custom calendar used in prototype pollution checks. Verifies that the
+ * mergeFields() method is always called with null-prototype fields objects.
+ */
+ calendarCheckMergeFieldsPrototypePollution() {
+ class CalendarCheckMergeFieldsPrototypePollution extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.mergeFieldsCallCount = 0;
+ }
+
+ toString() {
+ return "merge-fields-null-proto";
+ }
+
+ mergeFields(fields, additionalFields) {
+ this.mergeFieldsCallCount++;
+ assert.sameValue(Object.getPrototypeOf(fields), null, "mergeFields should be called with null-prototype fields object (first argument)");
+ assert.sameValue(Object.getPrototypeOf(additionalFields), null, "mergeFields should be called with null-prototype fields object (second argument)");
+ return super.mergeFields(fields, additionalFields);
+ }
+ }
+
+ return new CalendarCheckMergeFieldsPrototypePollution();
+ },
+
+ /*
+ * A custom calendar used in prototype pollution checks. Verifies that methods
+ * are always called with a null-prototype options object.
+ */
+ calendarCheckOptionsPrototypePollution() {
+ class CalendarCheckOptionsPrototypePollution extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.yearMonthFromFieldsCallCount = 0;
+ this.dateUntilCallCount = 0;
+ }
+
+ toString() {
+ return "options-null-proto";
+ }
+
+ yearMonthFromFields(fields, options) {
+ this.yearMonthFromFieldsCallCount++;
+ assert.sameValue(Object.getPrototypeOf(options), null, "yearMonthFromFields should be called with null-prototype options");
+ return super.yearMonthFromFields(fields, options);
+ }
+
+ dateUntil(one, two, options) {
+ this.dateUntilCallCount++;
+ assert.sameValue(Object.getPrototypeOf(options), null, "dateUntil should be called with null-prototype options");
+ return super.dateUntil(one, two, options);
+ }
+ }
+
+ return new CalendarCheckOptionsPrototypePollution();
+ },
+
+ /*
+ * A custom calendar that asserts its dateAdd() method is called with the
+ * options parameter having the value undefined.
+ */
+ calendarDateAddUndefinedOptions() {
+ class CalendarDateAddUndefinedOptions extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.dateAddCallCount = 0;
+ }
+
+ toString() {
+ return "dateadd-undef-options";
+ }
+
+ dateAdd(date, duration, options) {
+ this.dateAddCallCount++;
+ assert.sameValue(options, undefined, "dateAdd shouldn't be called with options");
+ return super.dateAdd(date, duration, options);
+ }
+ }
+ return new CalendarDateAddUndefinedOptions();
+ },
+
+ /*
+ * A custom calendar that asserts its dateAdd() method is called with a
+ * PlainDate instance. Optionally, it also asserts that the PlainDate instance
+ * is the specific object `this.specificPlainDate`, if it is set by the
+ * calling code.
+ */
+ calendarDateAddPlainDateInstance() {
+ class CalendarDateAddPlainDateInstance extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.dateAddCallCount = 0;
+ this.specificPlainDate = undefined;
+ }
+
+ toString() {
+ return "dateadd-plain-date-instance";
+ }
+
+ dateFromFields(...args) {
+ return super.dateFromFields(...args).withCalendar(this);
+ }
+
+ dateAdd(date, duration, options) {
+ this.dateAddCallCount++;
+ assert(date instanceof Temporal.PlainDate, "dateAdd() should be called with a PlainDate instance");
+ if (this.dateAddCallCount === 1 && this.specificPlainDate) {
+ assert.sameValue(date, this.specificPlainDate, `dateAdd() should be called first with the specific PlainDate instance ${this.specificPlainDate}`);
+ }
+ return super.dateAdd(date, duration, options).withCalendar(this);
+ }
+ }
+ return new CalendarDateAddPlainDateInstance();
+ },
+
+ /*
+ * A custom calendar that returns an iterable instead of an array from its
+ * fields() method, otherwise identical to the ISO calendar.
+ */
+ calendarFieldsIterable() {
+ class CalendarFieldsIterable extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.fieldsCallCount = 0;
+ this.fieldsCalledWith = [];
+ this.iteratorExhausted = [];
+ }
+
+ toString() {
+ return "fields-iterable";
+ }
+
+ fields(fieldNames) {
+ this.fieldsCallCount++;
+ this.fieldsCalledWith.push(fieldNames.slice());
+ this.iteratorExhausted.push(false);
+ return {
+ callIndex: this.fieldsCallCount - 1,
+ calendar: this,
+ *[Symbol.iterator]() {
+ yield* this.calendar.fieldsCalledWith[this.callIndex];
+ this.calendar.iteratorExhausted[this.callIndex] = true;
+ },
+ };
+ }
+ }
+ return new CalendarFieldsIterable();
+ },
+
+ /*
+ * A custom calendar that asserts its ...FromFields() methods are called with
+ * the options parameter having the value undefined.
+ */
+ calendarFromFieldsUndefinedOptions() {
+ class CalendarFromFieldsUndefinedOptions extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.dateFromFieldsCallCount = 0;
+ this.monthDayFromFieldsCallCount = 0;
+ this.yearMonthFromFieldsCallCount = 0;
+ }
+
+ toString() {
+ return "from-fields-undef-options";
+ }
+
+ dateFromFields(fields, options) {
+ this.dateFromFieldsCallCount++;
+ assert.sameValue(options, undefined, "dateFromFields shouldn't be called with options");
+ return super.dateFromFields(fields, options);
+ }
+
+ yearMonthFromFields(fields, options) {
+ this.yearMonthFromFieldsCallCount++;
+ assert.sameValue(options, undefined, "yearMonthFromFields shouldn't be called with options");
+ return super.yearMonthFromFields(fields, options);
+ }
+
+ monthDayFromFields(fields, options) {
+ this.monthDayFromFieldsCallCount++;
+ assert.sameValue(options, undefined, "monthDayFromFields shouldn't be called with options");
+ return super.monthDayFromFields(fields, options);
+ }
+ }
+ return new CalendarFromFieldsUndefinedOptions();
+ },
+
+ /*
+ * A custom calendar that modifies the fields object passed in to
+ * dateFromFields, sabotaging its time properties.
+ */
+ calendarMakeInfinityTime() {
+ class CalendarMakeInfinityTime extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateFromFields(fields, options) {
+ const retval = super.dateFromFields(fields, options);
+ fields.hour = Infinity;
+ fields.minute = Infinity;
+ fields.second = Infinity;
+ fields.millisecond = Infinity;
+ fields.microsecond = Infinity;
+ fields.nanosecond = Infinity;
+ return retval;
+ }
+ }
+ return new CalendarMakeInfinityTime();
+ },
+
+ /*
+ * A custom calendar that defines getters on the fields object passed into
+ * dateFromFields that throw, sabotaging its time properties.
+ */
+ calendarMakeInvalidGettersTime() {
+ class CalendarMakeInvalidGettersTime extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+
+ dateFromFields(fields, options) {
+ const retval = super.dateFromFields(fields, options);
+ const throwingDescriptor = {
+ get() {
+ throw new Test262Error("reading a sabotaged time field");
+ },
+ };
+ Object.defineProperties(fields, {
+ hour: throwingDescriptor,
+ minute: throwingDescriptor,
+ second: throwingDescriptor,
+ millisecond: throwingDescriptor,
+ microsecond: throwingDescriptor,
+ nanosecond: throwingDescriptor,
+ });
+ return retval;
+ }
+ }
+ return new CalendarMakeInvalidGettersTime();
+ },
+
+ /*
+ * A custom calendar whose mergeFields() method returns a proxy object with
+ * all of its Get and HasProperty operations observable, as well as adding a
+ * "shouldNotBeCopied": true property.
+ */
+ calendarMergeFieldsGetters() {
+ class CalendarMergeFieldsGetters extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ this.mergeFieldsReturnOperations = [];
+ }
+
+ toString() {
+ return "merge-fields-getters";
+ }
+
+ dateFromFields(fields, options) {
+ assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied");
+ return super.dateFromFields(fields, options);
+ }
+
+ yearMonthFromFields(fields, options) {
+ assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied");
+ return super.yearMonthFromFields(fields, options);
+ }
+
+ monthDayFromFields(fields, options) {
+ assert.sameValue(fields.shouldNotBeCopied, undefined, "extra fields should not be copied");
+ return super.monthDayFromFields(fields, options);
+ }
+
+ mergeFields(fields, additionalFields) {
+ const retval = super.mergeFields(fields, additionalFields);
+ retval._calendar = this;
+ retval.shouldNotBeCopied = true;
+ return new Proxy(retval, {
+ get(target, key) {
+ target._calendar.mergeFieldsReturnOperations.push(`get ${key}`);
+ const result = target[key];
+ if (result === undefined) {
+ return undefined;
+ }
+ return TemporalHelpers.toPrimitiveObserver(target._calendar.mergeFieldsReturnOperations, result, key);
+ },
+ has(target, key) {
+ target._calendar.mergeFieldsReturnOperations.push(`has ${key}`);
+ return key in target;
+ },
+ });
+ }
+ }
+ return new CalendarMergeFieldsGetters();
+ },
+
+ /*
+ * A custom calendar whose mergeFields() method returns a primitive value,
+ * given by @primitive, and which records the number of calls made to its
+ * dateFromFields(), yearMonthFromFields(), and monthDayFromFields() methods.
+ */
+ calendarMergeFieldsReturnsPrimitive(primitive) {
+ class CalendarMergeFieldsPrimitive extends Temporal.Calendar {
+ constructor(mergeFieldsReturnValue) {
+ super("iso8601");
+ this._mergeFieldsReturnValue = mergeFieldsReturnValue;
+ this.dateFromFieldsCallCount = 0;
+ this.monthDayFromFieldsCallCount = 0;
+ this.yearMonthFromFieldsCallCount = 0;
+ }
+
+ toString() {
+ return "merge-fields-primitive";
+ }
+
+ dateFromFields(fields, options) {
+ this.dateFromFieldsCallCount++;
+ return super.dateFromFields(fields, options);
+ }
+
+ yearMonthFromFields(fields, options) {
+ this.yearMonthFromFieldsCallCount++;
+ return super.yearMonthFromFields(fields, options);
+ }
+
+ monthDayFromFields(fields, options) {
+ this.monthDayFromFieldsCallCount++;
+ return super.monthDayFromFields(fields, options);
+ }
+
+ mergeFields() {
+ return this._mergeFieldsReturnValue;
+ }
+ }
+ return new CalendarMergeFieldsPrimitive(primitive);
+ },
+
+ /*
+ * A custom calendar whose fields() method returns the same value as the
+ * iso8601 calendar, with the addition of extraFields provided as parameter.
+ */
+ calendarWithExtraFields(fields) {
+ class CalendarWithExtraFields extends Temporal.Calendar {
+ constructor(extraFields) {
+ super("iso8601");
+ this._extraFields = extraFields;
+ }
+
+ fields(fieldNames) {
+ return super.fields(fieldNames).concat(this._extraFields);
+ }
+ }
+
+ return new CalendarWithExtraFields(fields);
+ },
+
+ /*
+ * crossDateLineTimeZone():
+ *
+ * This returns an instance of a custom time zone class that implements one
+ * single transition where the time zone moves from one side of the
+ * International Date Line to the other, for the purpose of testing time zone
+ * calculations without depending on system time zone data.
+ *
+ * The transition occurs at epoch second 1325239200 and goes from offset
+ * -10:00 to +14:00. In other words, the time zone skips the whole calendar
+ * day of 2011-12-30. This is the same as the real-life transition in the
+ * Pacific/Apia time zone.
+ */
+ crossDateLineTimeZone() {
+ const { compare } = Temporal.PlainDate;
+ const skippedDay = new Temporal.PlainDate(2011, 12, 30);
+ const transitionEpoch = 1325239200_000_000_000n;
+ const beforeOffset = new Temporal.TimeZone("-10:00");
+ const afterOffset = new Temporal.TimeZone("+14:00");
+
+ class CrossDateLineTimeZone extends Temporal.TimeZone {
+ constructor() {
+ super("+14:00");
+ }
+
+ getOffsetNanosecondsFor(instant) {
+ if (instant.epochNanoseconds < transitionEpoch) {
+ return beforeOffset.getOffsetNanosecondsFor(instant);
+ }
+ return afterOffset.getOffsetNanosecondsFor(instant);
+ }
+
+ getPossibleInstantsFor(datetime) {
+ const comparison = compare(datetime.toPlainDate(), skippedDay);
+ if (comparison === 0) {
+ return [];
+ }
+ if (comparison < 0) {
+ return [beforeOffset.getInstantFor(datetime)];
+ }
+ return [afterOffset.getInstantFor(datetime)];
+ }
+
+ getPreviousTransition(instant) {
+ if (instant.epochNanoseconds > transitionEpoch) return new Temporal.Instant(transitionEpoch);
+ return null;
+ }
+
+ getNextTransition(instant) {
+ if (instant.epochNanoseconds < transitionEpoch) return new Temporal.Instant(transitionEpoch);
+ return null;
+ }
+
+ toString() {
+ return "Custom/Date_Line";
+ }
+ }
+ return new CrossDateLineTimeZone();
+ },
+
+ /*
+ * observeProperty(calls, object, propertyName, value):
+ *
+ * Defines an own property @object.@propertyName with value @value, that
+ * will log any calls to its accessors to the array @calls.
+ */
+ observeProperty(calls, object, propertyName, value, objectName = "") {
+ Object.defineProperty(object, propertyName, {
+ get() {
+ calls.push(`get ${formatPropertyName(propertyName, objectName)}`);
+ return value;
+ },
+ set(v) {
+ calls.push(`set ${formatPropertyName(propertyName, objectName)}`);
+ }
+ });
+ },
+
+ /*
+ * observeMethod(calls, object, propertyName, value):
+ *
+ * Defines an own property @object.@propertyName with value @value, that
+ * will log any calls of @value to the array @calls.
+ */
+ observeMethod(calls, object, propertyName, objectName = "") {
+ const method = object[propertyName];
+ object[propertyName] = function () {
+ calls.push(`call ${formatPropertyName(propertyName, objectName)}`);
+ return method.apply(object, arguments);
+ };
+ },
+
+ /*
+ * Used for substituteMethod to indicate default behavior instead of a
+ * substituted value
+ */
+ SUBSTITUTE_SKIP: SKIP_SYMBOL,
+
+ /*
+ * substituteMethod(object, propertyName, values):
+ *
+ * Defines an own property @object.@propertyName that will, for each
+ * subsequent call to the method previously defined as
+ * @object.@propertyName:
+ * - Call the method, if no more values remain
+ * - Call the method, if the value in @values for the corresponding call
+ * is SUBSTITUTE_SKIP
+ * - Otherwise, return the corresponding value in @value
+ */
+ substituteMethod(object, propertyName, values) {
+ let calls = 0;
+ const method = object[propertyName];
+ object[propertyName] = function () {
+ if (calls >= values.length) {
+ return method.apply(object, arguments);
+ } else if (values[calls] === SKIP_SYMBOL) {
+ calls++;
+ return method.apply(object, arguments);
+ } else {
+ return values[calls++];
+ }
+ };
+ },
+
+ /*
+ * calendarObserver:
+ * A custom calendar that behaves exactly like the ISO 8601 calendar but
+ * tracks calls to any of its methods, and Get/Has operations on its
+ * properties, by appending messages to an array. This is for the purpose of
+ * testing order of operations that are observable from user code.
+ * objectName is used in the log.
+ */
+ calendarObserver(calls, objectName, methodOverrides = {}) {
+ function removeExtraHasPropertyChecks(objectName, calls) {
+ // Inserting the tracking calendar into the return values of methods
+ // that we chain up into the ISO calendar for, causes extra HasProperty
+ // checks, which we observe. This removes them so that we don't leak
+ // implementation details of the helper into the test code.
+ assert.sameValue(calls.pop(), `has ${objectName}.yearOfWeek`);
+ assert.sameValue(calls.pop(), `has ${objectName}.yearMonthFromFields`);
+ assert.sameValue(calls.pop(), `has ${objectName}.year`);
+ assert.sameValue(calls.pop(), `has ${objectName}.weekOfYear`);
+ assert.sameValue(calls.pop(), `has ${objectName}.monthsInYear`);
+ assert.sameValue(calls.pop(), `has ${objectName}.monthDayFromFields`);
+ assert.sameValue(calls.pop(), `has ${objectName}.monthCode`);
+ assert.sameValue(calls.pop(), `has ${objectName}.month`);
+ assert.sameValue(calls.pop(), `has ${objectName}.mergeFields`);
+ assert.sameValue(calls.pop(), `has ${objectName}.inLeapYear`);
+ assert.sameValue(calls.pop(), `has ${objectName}.id`);
+ assert.sameValue(calls.pop(), `has ${objectName}.fields`);
+ assert.sameValue(calls.pop(), `has ${objectName}.daysInYear`);
+ assert.sameValue(calls.pop(), `has ${objectName}.daysInWeek`);
+ assert.sameValue(calls.pop(), `has ${objectName}.daysInMonth`);
+ assert.sameValue(calls.pop(), `has ${objectName}.dayOfYear`);
+ assert.sameValue(calls.pop(), `has ${objectName}.dayOfWeek`);
+ assert.sameValue(calls.pop(), `has ${objectName}.day`);
+ assert.sameValue(calls.pop(), `has ${objectName}.dateUntil`);
+ assert.sameValue(calls.pop(), `has ${objectName}.dateFromFields`);
+ assert.sameValue(calls.pop(), `has ${objectName}.dateAdd`);
+ }
+
+ const iso8601 = new Temporal.Calendar("iso8601");
+ const trackingMethods = {
+ dateFromFields(...args) {
+ calls.push(`call ${objectName}.dateFromFields`);
+ if ('dateFromFields' in methodOverrides) {
+ const value = methodOverrides.dateFromFields;
+ return typeof value === "function" ? value(...args) : value;
+ }
+ const originalResult = iso8601.dateFromFields(...args);
+ // Replace the calendar in the result with the call-tracking calendar
+ const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
+ const result = new Temporal.PlainDate(isoYear, isoMonth, isoDay, this);
+ removeExtraHasPropertyChecks(objectName, calls);
+ return result;
+ },
+ yearMonthFromFields(...args) {
+ calls.push(`call ${objectName}.yearMonthFromFields`);
+ if ('yearMonthFromFields' in methodOverrides) {
+ const value = methodOverrides.yearMonthFromFields;
+ return typeof value === "function" ? value(...args) : value;
+ }
+ const originalResult = iso8601.yearMonthFromFields(...args);
+ // Replace the calendar in the result with the call-tracking calendar
+ const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
+ const result = new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay);
+ removeExtraHasPropertyChecks(objectName, calls);
+ return result;
+ },
+ monthDayFromFields(...args) {
+ calls.push(`call ${objectName}.monthDayFromFields`);
+ if ('monthDayFromFields' in methodOverrides) {
+ const value = methodOverrides.monthDayFromFields;
+ return typeof value === "function" ? value(...args) : value;
+ }
+ const originalResult = iso8601.monthDayFromFields(...args);
+ // Replace the calendar in the result with the call-tracking calendar
+ const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
+ const result = new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear);
+ removeExtraHasPropertyChecks(objectName, calls);
+ return result;
+ },
+ dateAdd(...args) {
+ calls.push(`call ${objectName}.dateAdd`);
+ if ('dateAdd' in methodOverrides) {
+ const value = methodOverrides.dateAdd;
+ return typeof value === "function" ? value(...args) : value;
+ }
+ const originalResult = iso8601.dateAdd(...args);
+ const {isoYear, isoMonth, isoDay} = originalResult.getISOFields();
+ const result = new Temporal.PlainDate(isoYear, isoMonth, isoDay, this);
+ removeExtraHasPropertyChecks(objectName, calls);
+ return result;
+ },
+ id: "iso8601",
+ };
+ // Automatically generate the other methods that don't need any custom code
+ [
+ "dateUntil",
+ "day",
+ "dayOfWeek",
+ "dayOfYear",
+ "daysInMonth",
+ "daysInWeek",
+ "daysInYear",
+ "era",
+ "eraYear",
+ "fields",
+ "inLeapYear",
+ "mergeFields",
+ "month",
+ "monthCode",
+ "monthsInYear",
+ "toString",
+ "weekOfYear",
+ "year",
+ "yearOfWeek",
+ ].forEach((methodName) => {
+ trackingMethods[methodName] = function (...args) {
+ calls.push(`call ${formatPropertyName(methodName, objectName)}`);
+ if (methodName in methodOverrides) {
+ const value = methodOverrides[methodName];
+ return typeof value === "function" ? value(...args) : value;
+ }
+ return iso8601[methodName](...args);
+ };
+ });
+ return new Proxy(trackingMethods, {
+ get(target, key, receiver) {
+ const result = Reflect.get(target, key, receiver);
+ calls.push(`get ${formatPropertyName(key, objectName)}`);
+ return result;
+ },
+ has(target, key) {
+ calls.push(`has ${formatPropertyName(key, objectName)}`);
+ return Reflect.has(target, key);
+ },
+ });
+ },
+
+ /*
+ * A custom calendar that does not allow any of its methods to be called, for
+ * the purpose of asserting that a particular operation does not call into
+ * user code.
+ */
+ calendarThrowEverything() {
+ class CalendarThrowEverything extends Temporal.Calendar {
+ constructor() {
+ super("iso8601");
+ }
+ toString() {
+ TemporalHelpers.assertUnreachable("toString should not be called");
+ }
+ dateFromFields() {
+ TemporalHelpers.assertUnreachable("dateFromFields should not be called");
+ }
+ yearMonthFromFields() {
+ TemporalHelpers.assertUnreachable("yearMonthFromFields should not be called");
+ }
+ monthDayFromFields() {
+ TemporalHelpers.assertUnreachable("monthDayFromFields should not be called");
+ }
+ dateAdd() {
+ TemporalHelpers.assertUnreachable("dateAdd should not be called");
+ }
+ dateUntil() {
+ TemporalHelpers.assertUnreachable("dateUntil should not be called");
+ }
+ era() {
+ TemporalHelpers.assertUnreachable("era should not be called");
+ }
+ eraYear() {
+ TemporalHelpers.assertUnreachable("eraYear should not be called");
+ }
+ year() {
+ TemporalHelpers.assertUnreachable("year should not be called");
+ }
+ month() {
+ TemporalHelpers.assertUnreachable("month should not be called");
+ }
+ monthCode() {
+ TemporalHelpers.assertUnreachable("monthCode should not be called");
+ }
+ day() {
+ TemporalHelpers.assertUnreachable("day should not be called");
+ }
+ fields() {
+ TemporalHelpers.assertUnreachable("fields should not be called");
+ }
+ mergeFields() {
+ TemporalHelpers.assertUnreachable("mergeFields should not be called");
+ }
+ }
+
+ return new CalendarThrowEverything();
+ },
+
+ /*
+ * oneShiftTimeZone(shiftInstant, shiftNanoseconds):
+ *
+ * In the case of a spring-forward time zone offset transition (skipped time),
+ * and disambiguation === 'earlier', BuiltinTimeZoneGetInstantFor subtracts a
+ * negative number of nanoseconds from a PlainDateTime, which should balance
+ * with the microseconds field.
+ *
+ * This returns an instance of a custom time zone class which skips a length
+ * of time equal to shiftNanoseconds (a number), at the Temporal.Instant
+ * shiftInstant. Before shiftInstant, it's identical to UTC, and after
+ * shiftInstant it's a constant-offset time zone.
+ *
+ * It provides a getPossibleInstantsForCalledWith member which is an array
+ * with the result of calling toString() on any PlainDateTimes passed to
+ * getPossibleInstantsFor().
+ */
+ oneShiftTimeZone(shiftInstant, shiftNanoseconds) {
+ class OneShiftTimeZone extends Temporal.TimeZone {
+ constructor(shiftInstant, shiftNanoseconds) {
+ super("+00:00");
+ this._shiftInstant = shiftInstant;
+ this._epoch1 = shiftInstant.epochNanoseconds;
+ this._epoch2 = this._epoch1 + BigInt(shiftNanoseconds);
+ this._shiftNanoseconds = shiftNanoseconds;
+ this._shift = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, this._shiftNanoseconds);
+ this.getPossibleInstantsForCalledWith = [];
+ }
+
+ _isBeforeShift(instant) {
+ return instant.epochNanoseconds < this._epoch1;
+ }
+
+ getOffsetNanosecondsFor(instant) {
+ return this._isBeforeShift(instant) ? 0 : this._shiftNanoseconds;
+ }
+
+ getPossibleInstantsFor(plainDateTime) {
+ this.getPossibleInstantsForCalledWith.push(plainDateTime.toString({ calendarName: "never" }));
+ const [instant] = super.getPossibleInstantsFor(plainDateTime);
+ if (this._shiftNanoseconds > 0) {
+ if (this._isBeforeShift(instant)) return [instant];
+ if (instant.epochNanoseconds < this._epoch2) return [];
+ return [instant.subtract(this._shift)];
+ }
+ if (instant.epochNanoseconds < this._epoch2) return [instant];
+ const shifted = instant.subtract(this._shift);
+ if (this._isBeforeShift(instant)) return [instant, shifted];
+ return [shifted];
+ }
+
+ getNextTransition(instant) {
+ return this._isBeforeShift(instant) ? this._shiftInstant : null;
+ }
+
+ getPreviousTransition(instant) {
+ return this._isBeforeShift(instant) ? null : this._shiftInstant;
+ }
+
+ toString() {
+ return "Custom/One_Shift";
+ }
+ }
+ return new OneShiftTimeZone(shiftInstant, shiftNanoseconds);
+ },
+
+ /*
+ * propertyBagObserver():
+ * Returns an object that behaves like the given propertyBag but tracks Get
+ * and Has operations on any of its properties, by appending messages to an
+ * array. If the value of a property in propertyBag is a primitive, the value
+ * of the returned object's property will additionally be a
+ * TemporalHelpers.toPrimitiveObserver that will track calls to its toString
+ * and valueOf methods in the same array. This is for the purpose of testing
+ * order of operations that are observable from user code. objectName is used
+ * in the log.
+ */
+ propertyBagObserver(calls, propertyBag, objectName) {
+ return new Proxy(propertyBag, {
+ ownKeys(target) {
+ calls.push(`ownKeys ${objectName}`);
+ return Reflect.ownKeys(target);
+ },
+ getOwnPropertyDescriptor(target, key) {
+ calls.push(`getOwnPropertyDescriptor ${formatPropertyName(key, objectName)}`);
+ return Reflect.getOwnPropertyDescriptor(target, key);
+ },
+ get(target, key, receiver) {
+ calls.push(`get ${formatPropertyName(key, objectName)}`);
+ const result = Reflect.get(target, key, receiver);
+ if (result === undefined) {
+ return undefined;
+ }
+ if ((result !== null && typeof result === "object") || typeof result === "function") {
+ return result;
+ }
+ return TemporalHelpers.toPrimitiveObserver(calls, result, `${formatPropertyName(key, objectName)}`);
+ },
+ has(target, key) {
+ calls.push(`has ${formatPropertyName(key, objectName)}`);
+ return Reflect.has(target, key);
+ },
+ });
+ },
+
+ /*
+ * specificOffsetTimeZone():
+ *
+ * This returns an instance of a custom time zone class, which returns a
+ * specific custom value from its getOffsetNanosecondsFrom() method. This is
+ * for the purpose of testing the validation of what this method returns.
+ *
+ * It also returns an empty array from getPossibleInstantsFor(), so as to
+ * trigger calls to getOffsetNanosecondsFor() when used from the
+ * BuiltinTimeZoneGetInstantFor operation.
+ */
+ specificOffsetTimeZone(offsetValue) {
+ class SpecificOffsetTimeZone extends Temporal.TimeZone {
+ constructor(offsetValue) {
+ super("UTC");
+ this._offsetValue = offsetValue;
+ }
+
+ getOffsetNanosecondsFor() {
+ return this._offsetValue;
+ }
+
+ getPossibleInstantsFor(dt) {
+ if (typeof this._offsetValue !== 'number' || Math.abs(this._offsetValue) >= 86400e9 || isNaN(this._offsetValue)) return [];
+ const zdt = dt.toZonedDateTime("UTC").add({ nanoseconds: -this._offsetValue });
+ return [zdt.toInstant()];
+ }
+
+ get id() {
+ return this.getOffsetStringFor(new Temporal.Instant(0n));
+ }
+ }
+ return new SpecificOffsetTimeZone(offsetValue);
+ },
+
+ /*
+ * springForwardFallBackTimeZone():
+ *
+ * This returns an instance of a custom time zone class that implements one
+ * single spring-forward/fall-back transition, for the purpose of testing the
+ * disambiguation option, without depending on system time zone data.
+ *
+ * The spring-forward occurs at epoch second 954669600 (2000-04-02T02:00
+ * local) and goes from offset -08:00 to -07:00.
+ *
+ * The fall-back occurs at epoch second 972810000 (2000-10-29T02:00 local) and
+ * goes from offset -07:00 to -08:00.
+ */
+ springForwardFallBackTimeZone() {
+ const { compare } = Temporal.PlainDateTime;
+ const springForwardLocal = new Temporal.PlainDateTime(2000, 4, 2, 2);
+ const springForwardEpoch = 954669600_000_000_000n;
+ const fallBackLocal = new Temporal.PlainDateTime(2000, 10, 29, 1);
+ const fallBackEpoch = 972810000_000_000_000n;
+ const winterOffset = new Temporal.TimeZone('-08:00');
+ const summerOffset = new Temporal.TimeZone('-07:00');
+
+ class SpringForwardFallBackTimeZone extends Temporal.TimeZone {
+ constructor() {
+ super("-08:00");
+ }
+
+ getOffsetNanosecondsFor(instant) {
+ if (instant.epochNanoseconds < springForwardEpoch ||
+ instant.epochNanoseconds >= fallBackEpoch) {
+ return winterOffset.getOffsetNanosecondsFor(instant);
+ }
+ return summerOffset.getOffsetNanosecondsFor(instant);
+ }
+
+ getPossibleInstantsFor(datetime) {
+ if (compare(datetime, springForwardLocal) >= 0 && compare(datetime, springForwardLocal.add({ hours: 1 })) < 0) {
+ return [];
+ }
+ if (compare(datetime, fallBackLocal) >= 0 && compare(datetime, fallBackLocal.add({ hours: 1 })) < 0) {
+ return [summerOffset.getInstantFor(datetime), winterOffset.getInstantFor(datetime)];
+ }
+ if (compare(datetime, springForwardLocal) < 0 || compare(datetime, fallBackLocal) >= 0) {
+ return [winterOffset.getInstantFor(datetime)];
+ }
+ return [summerOffset.getInstantFor(datetime)];
+ }
+
+ getPreviousTransition(instant) {
+ if (instant.epochNanoseconds > fallBackEpoch) return new Temporal.Instant(fallBackEpoch);
+ if (instant.epochNanoseconds > springForwardEpoch) return new Temporal.Instant(springForwardEpoch);
+ return null;
+ }
+
+ getNextTransition(instant) {
+ if (instant.epochNanoseconds < springForwardEpoch) return new Temporal.Instant(springForwardEpoch);
+ if (instant.epochNanoseconds < fallBackEpoch) return new Temporal.Instant(fallBackEpoch);
+ return null;
+ }
+
+ get id() {
+ return "Custom/Spring_Fall";
+ }
+
+ toString() {
+ return "Custom/Spring_Fall";
+ }
+ }
+ return new SpringForwardFallBackTimeZone();
+ },
+
+ /*
+ * timeZoneObserver:
+ * A custom calendar that behaves exactly like the UTC time zone but tracks
+ * calls to any of its methods, and Get/Has operations on its properties, by
+ * appending messages to an array. This is for the purpose of testing order of
+ * operations that are observable from user code. objectName is used in the
+ * log. methodOverrides is an optional object containing properties with the
+ * same name as Temporal.TimeZone methods. If the property value is a function
+ * it will be called with the proper arguments instead of the UTC method.
+ * Otherwise, the property value will be returned directly.
+ */
+ timeZoneObserver(calls, objectName, methodOverrides = {}) {
+ const utc = new Temporal.TimeZone("UTC");
+ const trackingMethods = {
+ id: "UTC",
+ };
+ // Automatically generate the methods
+ ["getOffsetNanosecondsFor", "getPossibleInstantsFor", "toString"].forEach((methodName) => {
+ trackingMethods[methodName] = function (...args) {
+ calls.push(`call ${formatPropertyName(methodName, objectName)}`);
+ if (methodName in methodOverrides) {
+ const value = methodOverrides[methodName];
+ return typeof value === "function" ? value(...args) : value;
+ }
+ return utc[methodName](...args);
+ };
+ });
+ return new Proxy(trackingMethods, {
+ get(target, key, receiver) {
+ const result = Reflect.get(target, key, receiver);
+ calls.push(`get ${formatPropertyName(key, objectName)}`);
+ return result;
+ },
+ has(target, key) {
+ calls.push(`has ${formatPropertyName(key, objectName)}`);
+ return Reflect.has(target, key);
+ },
+ });
+ },
+
+ /*
+ * A custom time zone that does not allow any of its methods to be called, for
+ * the purpose of asserting that a particular operation does not call into
+ * user code.
+ */
+ timeZoneThrowEverything() {
+ class TimeZoneThrowEverything extends Temporal.TimeZone {
+ constructor() {
+ super("UTC");
+ }
+ getOffsetNanosecondsFor() {
+ TemporalHelpers.assertUnreachable("getOffsetNanosecondsFor should not be called");
+ }
+ getPossibleInstantsFor() {
+ TemporalHelpers.assertUnreachable("getPossibleInstantsFor should not be called");
+ }
+ toString() {
+ TemporalHelpers.assertUnreachable("toString should not be called");
+ }
+ }
+
+ return new TimeZoneThrowEverything();
+ },
+
+ /*
+ * Returns an object that will append logs of any Gets or Calls of its valueOf
+ * or toString properties to the array calls. Both valueOf and toString will
+ * return the actual primitiveValue. propertyName is used in the log.
+ */
+ toPrimitiveObserver(calls, primitiveValue, propertyName) {
+ return {
+ get valueOf() {
+ calls.push(`get ${propertyName}.valueOf`);
+ return function () {
+ calls.push(`call ${propertyName}.valueOf`);
+ return primitiveValue;
+ };
+ },
+ get toString() {
+ calls.push(`get ${propertyName}.toString`);
+ return function () {
+ calls.push(`call ${propertyName}.toString`);
+ if (primitiveValue === undefined) return undefined;
+ return primitiveValue.toString();
+ };
+ },
+ };
+ },
+
+ /*
+ * An object containing further methods that return arrays of ISO strings, for
+ * testing parsers.
+ */
+ ISO: {
+ /*
+ * PlainMonthDay strings that are not valid.
+ */
+ plainMonthDayStringsInvalid() {
+ return [
+ "11-18junk",
+ "11-18[u-ca=gregory]",
+ "11-18[u-ca=hebrew]",
+ ];
+ },
+
+ /*
+ * PlainMonthDay strings that are valid and that should produce October 1st.
+ */
+ plainMonthDayStringsValid() {
+ return [
+ "10-01",
+ "1001",
+ "1965-10-01",
+ "1976-10-01T152330.1+00:00",
+ "19761001T15:23:30.1+00:00",
+ "1976-10-01T15:23:30.1+0000",
+ "1976-10-01T152330.1+0000",
+ "19761001T15:23:30.1+0000",
+ "19761001T152330.1+00:00",
+ "19761001T152330.1+0000",
+ "+001976-10-01T152330.1+00:00",
+ "+0019761001T15:23:30.1+00:00",
+ "+001976-10-01T15:23:30.1+0000",
+ "+001976-10-01T152330.1+0000",
+ "+0019761001T15:23:30.1+0000",
+ "+0019761001T152330.1+00:00",
+ "+0019761001T152330.1+0000",
+ "1976-10-01T15:23:00",
+ "1976-10-01T15:23",
+ "1976-10-01T15",
+ "1976-10-01",
+ "--10-01",
+ "--1001",
+ ];
+ },
+
+ /*
+ * PlainTime strings that may be mistaken for PlainMonthDay or
+ * PlainYearMonth strings, and so require a time designator.
+ */
+ plainTimeStringsAmbiguous() {
+ const ambiguousStrings = [
+ "2021-12", // ambiguity between YYYY-MM and HHMM-UU
+ "2021-12[-12:00]", // ditto, TZ does not disambiguate
+ "1214", // ambiguity between MMDD and HHMM
+ "0229", // ditto, including MMDD that doesn't occur every year
+ "1130", // ditto, including DD that doesn't occur in every month
+ "12-14", // ambiguity between MM-DD and HH-UU
+ "12-14[-14:00]", // ditto, TZ does not disambiguate
+ "202112", // ambiguity between YYYYMM and HHMMSS
+ "202112[UTC]", // ditto, TZ does not disambiguate
+ ];
+ // Adding a calendar annotation to one of these strings must not cause
+ // disambiguation in favour of time.
+ const stringsWithCalendar = ambiguousStrings.map((s) => s + '[u-ca=iso8601]');
+ return ambiguousStrings.concat(stringsWithCalendar);
+ },
+
+ /*
+ * PlainTime strings that are of similar form to PlainMonthDay and
+ * PlainYearMonth strings, but are not ambiguous due to components that
+ * aren't valid as months or days.
+ */
+ plainTimeStringsUnambiguous() {
+ return [
+ "2021-13", // 13 is not a month
+ "202113", // ditto
+ "2021-13[-13:00]", // ditto
+ "202113[-13:00]", // ditto
+ "0000-00", // 0 is not a month
+ "000000", // ditto
+ "0000-00[UTC]", // ditto
+ "000000[UTC]", // ditto
+ "1314", // 13 is not a month
+ "13-14", // ditto
+ "1232", // 32 is not a day
+ "0230", // 30 is not a day in February
+ "0631", // 31 is not a day in June
+ "0000", // 0 is neither a month nor a day
+ "00-00", // ditto
+ ];
+ },
+
+ /*
+ * PlainYearMonth-like strings that are not valid.
+ */
+ plainYearMonthStringsInvalid() {
+ return [
+ "2020-13",
+ ];
+ },
+
+ /*
+ * PlainYearMonth-like strings that are valid and should produce November
+ * 1976 in the ISO 8601 calendar.
+ */
+ plainYearMonthStringsValid() {
+ return [
+ "1976-11",
+ "1976-11-10",
+ "1976-11-01T09:00:00+00:00",
+ "1976-11-01T00:00:00+05:00",
+ "197611",
+ "+00197611",
+ "1976-11-18T15:23:30.1\u221202:00",
+ "1976-11-18T152330.1+00:00",
+ "19761118T15:23:30.1+00:00",
+ "1976-11-18T15:23:30.1+0000",
+ "1976-11-18T152330.1+0000",
+ "19761118T15:23:30.1+0000",
+ "19761118T152330.1+00:00",
+ "19761118T152330.1+0000",
+ "+001976-11-18T152330.1+00:00",
+ "+0019761118T15:23:30.1+00:00",
+ "+001976-11-18T15:23:30.1+0000",
+ "+001976-11-18T152330.1+0000",
+ "+0019761118T15:23:30.1+0000",
+ "+0019761118T152330.1+00:00",
+ "+0019761118T152330.1+0000",
+ "1976-11-18T15:23",
+ "1976-11-18T15",
+ "1976-11-18",
+ ];
+ },
+
+ /*
+ * PlainYearMonth-like strings that are valid and should produce November of
+ * the ISO year -9999.
+ */
+ plainYearMonthStringsValidNegativeYear() {
+ return [
+ "\u2212009999-11",
+ ];
+ },
+ }
+};
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/since.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/since.js
new file mode 100644
index 0000000000..ff64fa3160
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/since.js
@@ -0,0 +1,310 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: Temporal.ZonedDateTime.prototype.since()
+features: [Temporal]
+---*/
+
+var zdt = Temporal.ZonedDateTime.from("1976-11-18T15:23:30.123456789+01:00[+01:00]");
+
+// zdt.since(earlier) === earlier.until(zdt) with default options
+var earlier = Temporal.ZonedDateTime.from({
+ year: 1966,
+ month: 3,
+ day: 3,
+ hour: 18,
+ timeZone: "+01:00"
+});
+assert.sameValue(`${ zdt.since(earlier) }`, `${ earlier.until(zdt) }`);
+
+// casts argument
+assert.sameValue(`${ zdt.since({
+ year: 2019,
+ month: 10,
+ day: 29,
+ hour: 10,
+ timeZone: "+01:00"
+}) }`, "-PT376434H36M29.876543211S");
+assert.sameValue(`${ zdt.since("2019-10-29T10:46:38.271986102+01:00[+01:00]") }`, "-PT376435H23M8.148529313S");
+var feb20 = Temporal.ZonedDateTime.from("2020-02-01T00:00+01:00[+01:00]");
+var feb21 = Temporal.ZonedDateTime.from("2021-02-01T00:00+01:00[+01:00]");
+
+// defaults to returning hours
+assert.sameValue(`${ feb21.since(feb20) }`, "PT8784H");
+assert.sameValue(`${ feb21.since(feb20, { largestUnit: "auto" }) }`, "PT8784H");
+assert.sameValue(`${ feb21.since(feb20, { largestUnit: "hours" }) }`, "PT8784H");
+assert.sameValue(`${ Temporal.ZonedDateTime.from("2021-02-01T00:00:00.000000001+01:00[+01:00]").since(feb20) }`, "PT8784H0.000000001S");
+assert.sameValue(`${ feb21.since(Temporal.ZonedDateTime.from("2020-02-01T00:00:00.000000001+01:00[+01:00]")) }`, "PT8783H59M59.999999999S");
+
+// can return lower or higher units
+assert.sameValue(`${ feb21.since(feb20, { largestUnit: "years" }) }`, "P1Y");
+assert.sameValue(`${ feb21.since(feb20, { largestUnit: "months" }) }`, "P12M");
+assert.sameValue(`${ feb21.since(feb20, { largestUnit: "weeks" }) }`, "P52W2D");
+assert.sameValue(`${ feb21.since(feb20, { largestUnit: "days" }) }`, "P366D");
+assert.sameValue(`${ feb21.since(feb20, { largestUnit: "minutes" }) }`, "PT527040M");
+assert.sameValue(`${ feb21.since(feb20, { largestUnit: "seconds" }) }`, "PT31622400S");
+
+// can return subseconds
+var later = feb20.add({
+ days: 1,
+ milliseconds: 250,
+ microseconds: 250,
+ nanoseconds: 250
+});
+var msDiff = later.since(feb20, { largestUnit: "milliseconds" });
+assert.sameValue(msDiff.seconds, 0);
+assert.sameValue(msDiff.milliseconds, 86400250);
+assert.sameValue(msDiff.microseconds, 250);
+assert.sameValue(msDiff.nanoseconds, 250);
+var µsDiff = later.since(feb20, { largestUnit: "microseconds" });
+assert.sameValue(µsDiff.milliseconds, 0);
+assert.sameValue(µsDiff.microseconds, 86400250250);
+assert.sameValue(µsDiff.nanoseconds, 250);
+var nsDiff = later.since(feb20, { largestUnit: "nanoseconds" });
+assert.sameValue(nsDiff.microseconds, 0);
+assert.sameValue(nsDiff.nanoseconds, 86400250250250);
+
+// does not include higher units than necessary
+var lastFeb20 = Temporal.ZonedDateTime.from("2020-02-29T00:00+01:00[+01:00]");
+var lastFeb21 = Temporal.ZonedDateTime.from("2021-02-28T00:00+01:00[+01:00]");
+assert.sameValue(`${ lastFeb21.since(lastFeb20) }`, "PT8760H");
+assert.sameValue(`${ lastFeb21.since(lastFeb20, { largestUnit: "months" }) }`, "P11M28D");
+assert.sameValue(`${ lastFeb21.since(lastFeb20, { largestUnit: "years" }) }`, "P11M28D");
+
+// weeks and months are mutually exclusive
+var laterDateTime = zdt.add({
+ days: 42,
+ hours: 3
+});
+var weeksDifference = laterDateTime.since(zdt, { largestUnit: "weeks" });
+assert.notSameValue(weeksDifference.weeks, 0);
+assert.sameValue(weeksDifference.months, 0);
+var monthsDifference = laterDateTime.since(zdt, { largestUnit: "months" });
+assert.sameValue(monthsDifference.weeks, 0);
+assert.notSameValue(monthsDifference.months, 0);
+
+// no two different calendars
+var zdt1 = new Temporal.ZonedDateTime(0n, "UTC");
+var fakeJapanese = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "japanese",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+var zdt2 = new Temporal.ZonedDateTime(0n, "UTC", fakeJapanese);
+assert.throws(RangeError, () => zdt1.since(zdt2));
+
+var earlier = Temporal.ZonedDateTime.from('2019-01-08T09:22:36.123456789+01:00[+01:00]');
+var later = Temporal.ZonedDateTime.from('2021-09-07T13:39:40.987654321+01:00[+01:00]');
+// assumes a different default for largestUnit if smallestUnit is larger than days
+assert.sameValue(`${ later.since(earlier, {
+ smallestUnit: "years",
+ roundingMode: "halfExpand"
+}) }`, "P3Y");
+assert.sameValue(`${ later.since(earlier, {
+ smallestUnit: "months",
+ roundingMode: "halfExpand"
+}) }`, "P32M");
+assert.sameValue(`${ later.since(earlier, {
+ smallestUnit: "weeks",
+ roundingMode: "halfExpand"
+}) }`, "P139W");
+
+// rounds to an increment of hours
+assert.sameValue(`${ later.since(earlier, {
+ smallestUnit: "hours",
+ roundingIncrement: 3,
+ roundingMode: "halfExpand"
+}) }`, "PT23355H");
+
+// rounds to an increment of minutes
+assert.sameValue(`${ later.since(earlier, {
+ smallestUnit: "minutes",
+ roundingIncrement: 30,
+ roundingMode: "halfExpand"
+}) }`, "PT23356H30M");
+
+// rounds to an increment of seconds
+assert.sameValue(`${ later.since(earlier, {
+ smallestUnit: "seconds",
+ roundingIncrement: 15,
+ roundingMode: "halfExpand"
+}) }`, "PT23356H17M");
+
+// rounds to an increment of milliseconds
+assert.sameValue(`${ later.since(earlier, {
+ smallestUnit: "milliseconds",
+ roundingIncrement: 10,
+ roundingMode: "halfExpand"
+}) }`, "PT23356H17M4.86S");
+
+// rounds to an increment of microseconds
+assert.sameValue(`${ later.since(earlier, {
+ smallestUnit: "microseconds",
+ roundingIncrement: 10,
+ roundingMode: "halfExpand"
+}) }`, "PT23356H17M4.8642S");
+
+// rounds to an increment of nanoseconds
+assert.sameValue(`${ later.since(earlier, {
+ smallestUnit: "nanoseconds",
+ roundingIncrement: 10,
+ roundingMode: "halfExpand"
+}) }`, "PT23356H17M4.86419753S");
+
+// valid hour increments divide into 24
+[
+ 1,
+ 2,
+ 3,
+ 4,
+ 6,
+ 8,
+ 12
+].forEach(roundingIncrement => {
+ var options = {
+ smallestUnit: "hours",
+ roundingIncrement
+ };
+ assert(later.since(earlier, options) instanceof Temporal.Duration);
+});
+[
+ "minutes",
+ "seconds"
+].forEach(smallestUnit => {
+ [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 10,
+ 12,
+ 15,
+ 20,
+ 30
+ ].forEach(roundingIncrement => {
+ var options = {
+ smallestUnit,
+ roundingIncrement
+ };
+ assert(later.since(earlier, options) instanceof Temporal.Duration);
+ });
+});
+[
+ "milliseconds",
+ "microseconds",
+ "nanoseconds"
+].forEach(smallestUnit => {
+ [
+ 1,
+ 2,
+ 4,
+ 5,
+ 8,
+ 10,
+ 20,
+ 25,
+ 40,
+ 50,
+ 100,
+ 125,
+ 200,
+ 250,
+ 500
+ ].forEach(roundingIncrement => {
+ var options = {
+ smallestUnit,
+ roundingIncrement
+ };
+ assert(later.since(earlier, options) instanceof Temporal.Duration);
+ });
+});
+
+// throws on increments that do not divide evenly into the next highest
+assert.throws(RangeError, () => later.since(earlier, {
+ smallestUnit: "hours",
+ roundingIncrement: 11
+}));
+assert.throws(RangeError, () => later.since(earlier, {
+ smallestUnit: "minutes",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => later.since(earlier, {
+ smallestUnit: "seconds",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => later.since(earlier, {
+ smallestUnit: "milliseconds",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => later.since(earlier, {
+ smallestUnit: "microseconds",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => later.since(earlier, {
+ smallestUnit: "nanoseconds",
+ roundingIncrement: 29
+}));
+
+// throws on increments that are equal to the next highest
+assert.throws(RangeError, () => later.since(earlier, {
+ smallestUnit: "hours",
+ roundingIncrement: 24
+}));
+assert.throws(RangeError, () => later.since(earlier, {
+ smallestUnit: "minutes",
+ roundingIncrement: 60
+}));
+assert.throws(RangeError, () => later.since(earlier, {
+ smallestUnit: "seconds",
+ roundingIncrement: 60
+}));
+assert.throws(RangeError, () => later.since(earlier, {
+ smallestUnit: "milliseconds",
+ roundingIncrement: 1000
+}));
+assert.throws(RangeError, () => later.since(earlier, {
+ smallestUnit: "microseconds",
+ roundingIncrement: 1000
+}));
+assert.throws(RangeError, () => later.since(earlier, {
+ smallestUnit: "nanoseconds",
+ roundingIncrement: 1000
+}));
+
+// rounds relative to the receiver
+var dt1 = Temporal.ZonedDateTime.from("2019-01-01T00:00+00:00[UTC]");
+var dt2 = Temporal.ZonedDateTime.from("2020-07-02T00:00+00:00[UTC]");
+assert.sameValue(`${ dt2.since(dt1, {
+ smallestUnit: "years",
+ roundingMode: "halfExpand"
+}) }`, "P1Y");
+assert.sameValue(`${ dt1.since(dt2, {
+ smallestUnit: "years",
+ roundingMode: "halfExpand"
+}) }`, "-P2Y");
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/string-parsing.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/string-parsing.js
new file mode 100644
index 0000000000..f17eca11fd
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/string-parsing.js
@@ -0,0 +1,71 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: string parsing
+features: [Temporal]
+---*/
+
+// any number of decimal places
+assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.1-08:00[-08:00]") }`, "1976-11-18T15:23:30.1-08:00[-08:00]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.12-08:00[-08:00]") }`, "1976-11-18T15:23:30.12-08:00[-08:00]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.123-08:00[-08:00]") }`, "1976-11-18T15:23:30.123-08:00[-08:00]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.1234-08:00[-08:00]") }`, "1976-11-18T15:23:30.1234-08:00[-08:00]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.12345-08:00[-08:00]") }`, "1976-11-18T15:23:30.12345-08:00[-08:00]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.123456-08:00[-08:00]") }`, "1976-11-18T15:23:30.123456-08:00[-08:00]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.1234567-08:00[-08:00]") }`, "1976-11-18T15:23:30.1234567-08:00[-08:00]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.12345678-08:00[-08:00]") }`, "1976-11-18T15:23:30.12345678-08:00[-08:00]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.123456789-08:00[-08:00]") }`, "1976-11-18T15:23:30.123456789-08:00[-08:00]");
+
+// variant decimal separator
+assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30,12-08:00[-08:00]") }`, "1976-11-18T15:23:30.12-08:00[-08:00]");
+
+// variant minus sign
+assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30.12\u221208:00[-08:00]") }`, "1976-11-18T15:23:30.12-08:00[-08:00]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from("\u2212009999-11-18T15:23:30.12+00:00[UTC]") }`, "-009999-11-18T15:23:30.12+00:00[UTC]");
+
+// mixture of basic and extended format
+[
+ "1976-11-18T152330.1-08:00[-08:00]",
+ "19761118T15:23:30.1-08:00[-08:00]",
+ "1976-11-18T15:23:30.1-0800[-08:00]",
+ "1976-11-18T152330.1-0800[-08:00]",
+ "19761118T15:23:30.1-0800[-08:00]",
+ "19761118T152330.1-08:00[-08:00]",
+ "19761118T152330.1-0800[-08:00]",
+ "+001976-11-18T152330.1-08:00[-08:00]",
+ "+0019761118T15:23:30.1-08:00[-08:00]",
+ "+001976-11-18T15:23:30.1-0800[-08:00]",
+ "+001976-11-18T152330.1-0800[-08:00]",
+ "+0019761118T15:23:30.1-0800[-08:00]",
+ "+0019761118T152330.1-08:00[-08:00]",
+ "+0019761118T152330.1-0800[-08:00]"
+].forEach(input => assert.sameValue(`${ Temporal.ZonedDateTime.from(input) }`, "1976-11-18T15:23:30.1-08:00[-08:00]"));
+
+// optional parts
+assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15:23:30-08[-08:00]") }`, "1976-11-18T15:23:30-08:00[-08:00]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from("1976-11-18T15-08:00[-08:00]") }`, "1976-11-18T15:00:00-08:00[-08:00]");
+assert.sameValue(`${ Temporal.ZonedDateTime.from("2020-01-01[+09:00]") }`, "2020-01-01T00:00:00+09:00[+09:00]");
+
+// no junk at end of string
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from("1976-11-18T15:23:30.123456789-08:00[-08:00]junk"))
+
+// constrain has no effect on invalid ISO string
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from("2020-13-34T24:60[-08:00]", { overflow: "constrain" }));
+
+// { offset: 'reject' } throws if offset does not match IANA time zone
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from("2020-03-08T01:00-04:00[UTC]"));
+assert.throws(RangeError, () => Temporal.ZonedDateTime.from("2020-03-08T01:00-04:00[UTC]", { offset: "reject" }));
+
+// throw when bad disambiguation
+[
+ "",
+ "EARLIER",
+ "balance"
+].forEach(disambiguation => {
+ assert.throws(RangeError, () => Temporal.ZonedDateTime.from("2020-11-01T04:00[-08:00]", { disambiguation }));
+});
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/subtract.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/subtract.js
new file mode 100644
index 0000000000..790dfbb9cb
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/subtract.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: Temporal.ZonedDateTime.prototype.subtract()
+features: [Temporal]
+---*/
+
+var zdt = Temporal.ZonedDateTime.from("1969-12-25T12:23:45.678901234+00:00[UTC]");
+
+// inst.subtract(durationObj)
+var earlier = zdt.subtract(Temporal.Duration.from("PT240H0.000000800S"));
+assert.sameValue(`${ earlier }`, "1969-12-15T12:23:45.678900434+00:00[UTC]");
+
+// casts argument
+assert.sameValue(`${ zdt.subtract("PT240H0.000000800S") }`, "1969-12-15T12:23:45.678900434+00:00[UTC]");
+var mar31 = Temporal.ZonedDateTime.from("2020-03-31T15:00+00:00[UTC]");
+
+// constrain when ambiguous result
+assert.sameValue(`${ mar31.subtract({ months: 1 }) }`, "2020-02-29T15:00:00+00:00[UTC]");
+assert.sameValue(`${ mar31.subtract({ months: 1 }, { overflow: "constrain" }) }`, "2020-02-29T15:00:00+00:00[UTC]");
+
+// symmetrical with regard to negative durations in the time part
+assert.sameValue(`${ mar31.subtract({ minutes: -30 }) }`, "2020-03-31T15:30:00+00:00[UTC]");
+assert.sameValue(`${ mar31.subtract({ seconds: -30 }) }`, "2020-03-31T15:00:30+00:00[UTC]");
+
+// throw when ambiguous result with reject
+assert.throws(RangeError, () => mar31.subtract({ months: 1 }, { overflow: "reject" }));
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toInstant.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toInstant.js
new file mode 100644
index 0000000000..0fcc34bb13
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toInstant.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: Temporal.ZonedDateTime.prototype.toInstant()
+features: [Temporal]
+---*/
+
+
+// recent date
+var zdt = Temporal.ZonedDateTime.from("2019-10-29T10:46:38.271986102+01:00[+01:00]");
+assert.sameValue(`${ zdt.toInstant() }`, "2019-10-29T09:46:38.271986102Z");
+
+// year ≤ 99
+var zdt = Temporal.ZonedDateTime.from("0098-10-29T10:46:38.271986102+00:00[UTC]");
+assert.sameValue(`${ zdt.toInstant() }`, "0098-10-29T10:46:38.271986102Z");
+zdt = Temporal.ZonedDateTime.from("+000098-10-29T10:46:38.271986102+00:00[UTC]");
+assert.sameValue(`${ zdt.toInstant() }`, "0098-10-29T10:46:38.271986102Z");
+
+// year < 1
+var zdt = Temporal.ZonedDateTime.from("0000-10-29T10:46:38.271986102+00:00[UTC]");
+assert.sameValue(`${ zdt.toInstant() }`, "0000-10-29T10:46:38.271986102Z");
+zdt = Temporal.ZonedDateTime.from("+000000-10-29T10:46:38.271986102+00:00[UTC]");
+assert.sameValue(`${ zdt.toInstant() }`, "0000-10-29T10:46:38.271986102Z");
+zdt = Temporal.ZonedDateTime.from("-001000-10-29T10:46:38.271986102+00:00[UTC]");
+assert.sameValue(`${ zdt.toInstant() }`, "-001000-10-29T10:46:38.271986102Z");
+
+// year 0 leap day
+var zdt = Temporal.ZonedDateTime.from("0000-02-29T00:00-00:01[-00:01]");
+assert.sameValue(`${ zdt.toInstant() }`, "0000-02-29T00:01:00Z");
+zdt = Temporal.ZonedDateTime.from("+000000-02-29T00:00-00:01[-00:01]");
+assert.sameValue(`${ zdt.toInstant() }`, "0000-02-29T00:01:00Z");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainDate.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainDate.js
new file mode 100644
index 0000000000..4b8e6aa54e
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainDate.js
@@ -0,0 +1,47 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: Temporal.ZonedDateTime.prototype.toPlainDate()
+features: [Temporal]
+---*/
+
+var tz = new Temporal.TimeZone("-07:00");
+
+// works
+var zdt = Temporal.Instant.from("2019-10-29T09:46:38.271986102Z").toZonedDateTimeISO(tz);
+assert.sameValue(`${ zdt.toPlainDate() }`, "2019-10-29");
+
+// preserves the calendar
+const fakeGregorian = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "gregory",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+var zdt = Temporal.Instant.from("2019-10-29T09:46:38.271986102Z").toZonedDateTime({
+ timeZone: tz,
+ calendar: fakeGregorian
+});
+assert.sameValue(zdt.toPlainDate().getCalendar(), fakeGregorian);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainMonthDay.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainMonthDay.js
new file mode 100644
index 0000000000..23d1248aec
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainMonthDay.js
@@ -0,0 +1,51 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: Temporal.ZonedDateTime.prototype.toPlainMonthDay()
+features: [Temporal]
+---*/
+
+var tz = new Temporal.TimeZone("-08:00");
+
+// works
+var zdt = Temporal.Instant.from("2019-10-29T09:46:38.271986102Z").toZonedDateTimeISO(tz);
+assert.sameValue(`${ zdt.toPlainMonthDay() }`, "10-29");
+
+// preserves the calendar
+var fakeGregorian = {
+ id: 'gregory',
+ monthDayFromFields(fields) {
+ var md = Temporal.Calendar.from("iso8601").monthDayFromFields(fields);
+ var {isoYear, isoMonth, isoDay} = md.getISOFields();
+ return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear);
+ },
+ monthCode(date) { return date.withCalendar("iso8601").monthCode; },
+ day(date) { return date.withCalendar("iso8601").day; },
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields(fieldNames) { return fieldNames; },
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+var zdt = Temporal.Instant.from("2019-10-29T09:46:38.271986102Z").toZonedDateTime({
+ timeZone: tz,
+ calendar: fakeGregorian
+});
+assert.sameValue(zdt.toPlainMonthDay().getCalendar(), fakeGregorian);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainTime.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainTime.js
new file mode 100644
index 0000000000..78e6e0bbae
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainTime.js
@@ -0,0 +1,17 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: Temporal.ZonedDateTime.prototype.toPlainTime()
+features: [Temporal]
+---*/
+
+var tz = new Temporal.TimeZone("-07:00");
+
+// works
+var zdt = Temporal.Instant.from("2019-10-29T09:46:38.271986102Z").toZonedDateTimeISO(tz);
+assert.sameValue(`${ zdt.toPlainTime() }`, "02:46:38.271986102");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainYearMonth.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainYearMonth.js
new file mode 100644
index 0000000000..983bc21bec
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toPlainYearMonth.js
@@ -0,0 +1,51 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: Temporal.ZonedDateTime.prototype.toPlainYearMonth()
+features: [Temporal]
+---*/
+
+var tz = new Temporal.TimeZone("-08:00");
+
+// works
+var zdt = Temporal.Instant.from("2019-10-29T09:46:38.271986102Z").toZonedDateTimeISO(tz);
+assert.sameValue(`${ zdt.toPlainYearMonth() }`, "2019-10");
+
+// preserves the calendar
+var fakeGregorian = {
+ id: 'gregory',
+ yearMonthFromFields(fields) {
+ var ym = Temporal.Calendar.from("iso8601").yearMonthFromFields(fields);
+ var {isoYear, isoMonth, isoDay} = ym.getISOFields();
+ return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay);
+ },
+ year(date) { return date.withCalendar("iso8601").year; },
+ monthCode(date) { return date.withCalendar("iso8601").monthCode; },
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields(fieldNames) { return fieldNames; },
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ yearOfWeek() {},
+};
+var zdt = Temporal.Instant.from("2019-10-29T09:46:38.271986102Z").toZonedDateTime({
+ timeZone: tz,
+ calendar: fakeGregorian
+});
+assert.sameValue(zdt.toPlainYearMonth().getCalendar(), fakeGregorian);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toString.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toString.js
new file mode 100644
index 0000000000..2e3751b520
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/toString.js
@@ -0,0 +1,74 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: Temporal.ZonedDateTime.prototype.toString()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+var zdt1 = Temporal.ZonedDateTime.from("1976-11-18T15:23+00:00[UTC]");
+var fakeGregorian = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "gregory",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+
+// shows offset if offset = auto
+assert.sameValue(zdt1.toString({ offset: "auto" }), "1976-11-18T15:23:00+00:00[UTC]");
+
+// omits offset if offset = never
+assert.sameValue(zdt1.toString({ offset: "never" }), "1976-11-18T15:23:00[UTC]");
+
+// combinations of calendar, time zone, and offset
+var zdt = zdt1.withCalendar(fakeGregorian);
+assert.sameValue(zdt.toString({
+ timeZoneName: "never",
+ calendarName: "never"
+}), "1976-11-18T15:23:00+00:00");
+assert.sameValue(zdt.toString({
+ offset: "never",
+ calendarName: "never"
+}), "1976-11-18T15:23:00[UTC]");
+assert.sameValue(zdt.toString({
+ offset: "never",
+ timeZoneName: "never"
+}), "1976-11-18T15:23:00[u-ca=gregory]");
+assert.sameValue(zdt.toString({
+ offset: "never",
+ timeZoneName: "never",
+ calendarName: "never"
+}), "1976-11-18T15:23:00");
+
+// rounding up to a nonexistent wall-clock time
+var dst = TemporalHelpers.springForwardFallBackTimeZone();
+var zdt5 = Temporal.PlainDateTime.from("2000-04-02T01:59:59.999999999").toZonedDateTime(dst);
+var roundedString = zdt5.toString({
+ fractionalSecondDigits: 8,
+ roundingMode: "halfExpand"
+});
+assert.sameValue(roundedString, "2000-04-02T03:00:00.00000000-07:00[Custom/Spring_Fall]");
+var zdt6 = Temporal.Instant.from(roundedString);
+assert.sameValue(zdt6.epochNanoseconds - zdt5.epochNanoseconds, 1n);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/until.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/until.js
new file mode 100644
index 0000000000..65fdcf2ffc
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/until.js
@@ -0,0 +1,314 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: Temporal.ZonedDateTime.prototype.until()
+features: [Temporal]
+---*/
+
+var zdt = Temporal.ZonedDateTime.from("1976-11-18T15:23:30.123456789+01:00[+01:00]");
+
+// zdt.until(later) === later.since(zdt) with default options
+var later = Temporal.ZonedDateTime.from({
+ year: 2016,
+ month: 3,
+ day: 3,
+ hour: 18,
+ timeZone: "+01:00"
+});
+assert.sameValue(`${ zdt.until(later) }`, `${ later.since(zdt) }`);
+
+// casts argument
+assert.sameValue(`${ zdt.until({
+ year: 2019,
+ month: 10,
+ day: 29,
+ hour: 10,
+ timeZone: "+01:00"
+}) }`, "PT376434H36M29.876543211S");
+assert.sameValue(`${ zdt.until("2019-10-29T10:46:38.271986102+01:00[+01:00]") }`, "PT376435H23M8.148529313S");
+var feb20 = Temporal.ZonedDateTime.from("2020-02-01T00:00+01:00[+01:00]");
+var feb21 = Temporal.ZonedDateTime.from("2021-02-01T00:00+01:00[+01:00]");
+
+// defaults to returning hours
+assert.sameValue(`${ feb20.until(feb21) }`, "PT8784H");
+assert.sameValue(`${ feb20.until(feb21, { largestUnit: "auto" }) }`, "PT8784H");
+assert.sameValue(`${ feb20.until(feb21, { largestUnit: "hours" }) }`, "PT8784H");
+assert.sameValue(`${ feb20.until(Temporal.ZonedDateTime.from("2021-02-01T00:00:00.000000001+01:00[+01:00]")) }`, "PT8784H0.000000001S");
+assert.sameValue(`${ Temporal.ZonedDateTime.from("2020-02-01T00:00:00.000000001+01:00[+01:00]").until(feb21) }`, "PT8783H59M59.999999999S");
+
+// can return lower or higher units
+assert.sameValue(`${ feb20.until(feb21, { largestUnit: "years" }) }`, "P1Y");
+assert.sameValue(`${ feb20.until(feb21, { largestUnit: "months" }) }`, "P12M");
+assert.sameValue(`${ feb20.until(feb21, { largestUnit: "weeks" }) }`, "P52W2D");
+assert.sameValue(`${ feb20.until(feb21, { largestUnit: "days" }) }`, "P366D");
+assert.sameValue(`${ feb20.until(feb21, { largestUnit: "minutes" }) }`, "PT527040M");
+assert.sameValue(`${ feb20.until(feb21, { largestUnit: "seconds" }) }`, "PT31622400S");
+
+// can return subseconds
+var later = feb20.add({
+ days: 1,
+ milliseconds: 250,
+ microseconds: 250,
+ nanoseconds: 250
+});
+var msDiff = feb20.until(later, { largestUnit: "milliseconds" });
+assert.sameValue(msDiff.seconds, 0);
+assert.sameValue(msDiff.milliseconds, 86400250);
+assert.sameValue(msDiff.microseconds, 250);
+assert.sameValue(msDiff.nanoseconds, 250);
+var µsDiff = feb20.until(later, { largestUnit: "microseconds" });
+assert.sameValue(µsDiff.milliseconds, 0);
+assert.sameValue(µsDiff.microseconds, 86400250250);
+assert.sameValue(µsDiff.nanoseconds, 250);
+var nsDiff = feb20.until(later, { largestUnit: "nanoseconds" });
+assert.sameValue(nsDiff.microseconds, 0);
+assert.sameValue(nsDiff.nanoseconds, 86400250250250);
+
+// does not include higher units than necessary
+var lastFeb20 = Temporal.ZonedDateTime.from("2020-02-29T00:00+01:00[+01:00]");
+var lastJan21 = Temporal.ZonedDateTime.from("2021-01-31T00:00+01:00[+01:00]");
+assert.sameValue(`${ lastFeb20.until(lastJan21) }`, "PT8088H");
+assert.sameValue(`${ lastFeb20.until(lastJan21, { largestUnit: "months" }) }`, "P11M2D");
+assert.sameValue(`${ lastFeb20.until(lastJan21, { largestUnit: "years" }) }`, "P11M2D");
+
+// weeks and months are mutually exclusive
+var laterDateTime = zdt.add({
+ days: 42,
+ hours: 3
+});
+var weeksDifference = zdt.until(laterDateTime, { largestUnit: "weeks" });
+assert.notSameValue(weeksDifference.weeks, 0);
+assert.sameValue(weeksDifference.months, 0);
+var monthsDifference = zdt.until(laterDateTime, { largestUnit: "months" });
+assert.sameValue(monthsDifference.weeks, 0);
+assert.notSameValue(monthsDifference.months, 0);
+
+// no two different calendars
+var zdt1 = new Temporal.ZonedDateTime(0n, "UTC");
+var fakeJapanese = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "japanese",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+var zdt2 = new Temporal.ZonedDateTime(0n, "UTC", fakeJapanese);
+assert.throws(RangeError, () => zdt1.until(zdt2));
+
+var earlier = Temporal.ZonedDateTime.from('2019-01-08T09:22:36.123456789+01:00[+01:00]');
+var later = Temporal.ZonedDateTime.from('2021-09-07T13:39:40.987654321+01:00[+01:00]');
+// assumes a different default for largestUnit if smallestUnit is larger than hours
+assert.sameValue(`${ earlier.until(later, {
+ smallestUnit: "years",
+ roundingMode: "halfExpand"
+}) }`, "P3Y");
+assert.sameValue(`${ earlier.until(later, {
+ smallestUnit: "months",
+ roundingMode: "halfExpand"
+}) }`, "P32M");
+assert.sameValue(`${ earlier.until(later, {
+ smallestUnit: "weeks",
+ roundingMode: "halfExpand"
+}) }`, "P139W");
+assert.sameValue(`${ earlier.until(later, {
+ smallestUnit: "days",
+ roundingMode: "halfExpand"
+}) }`, "P973D");
+
+// rounds to an increment of hours
+assert.sameValue(`${ earlier.until(later, {
+ smallestUnit: "hours",
+ roundingIncrement: 3,
+ roundingMode: "halfExpand"
+}) }`, "PT23355H");
+
+// rounds to an increment of minutes
+assert.sameValue(`${ earlier.until(later, {
+ smallestUnit: "minutes",
+ roundingIncrement: 30,
+ roundingMode: "halfExpand"
+}) }`, "PT23356H30M");
+
+// rounds to an increment of seconds
+assert.sameValue(`${ earlier.until(later, {
+ smallestUnit: "seconds",
+ roundingIncrement: 15,
+ roundingMode: "halfExpand"
+}) }`, "PT23356H17M");
+
+// rounds to an increment of milliseconds
+assert.sameValue(`${ earlier.until(later, {
+ smallestUnit: "milliseconds",
+ roundingIncrement: 10,
+ roundingMode: "halfExpand"
+}) }`, "PT23356H17M4.86S");
+
+// rounds to an increment of microseconds
+assert.sameValue(`${ earlier.until(later, {
+ smallestUnit: "microseconds",
+ roundingIncrement: 10,
+ roundingMode: "halfExpand"
+}) }`, "PT23356H17M4.8642S");
+
+// rounds to an increment of nanoseconds
+assert.sameValue(`${ earlier.until(later, {
+ smallestUnit: "nanoseconds",
+ roundingIncrement: 10,
+ roundingMode: "halfExpand"
+}) }`, "PT23356H17M4.86419753S");
+
+// valid hour increments divide into 24
+[
+ 1,
+ 2,
+ 3,
+ 4,
+ 6,
+ 8,
+ 12
+].forEach(roundingIncrement => {
+ var options = {
+ smallestUnit: "hours",
+ roundingIncrement
+ };
+ assert(earlier.until(later, options) instanceof Temporal.Duration);
+});
+[
+ "minutes",
+ "seconds"
+].forEach(smallestUnit => {
+ [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 10,
+ 12,
+ 15,
+ 20,
+ 30
+ ].forEach(roundingIncrement => {
+ var options = {
+ smallestUnit,
+ roundingIncrement
+ };
+ assert(earlier.until(later, options) instanceof Temporal.Duration);
+ });
+});
+[
+ "milliseconds",
+ "microseconds",
+ "nanoseconds"
+].forEach(smallestUnit => {
+ [
+ 1,
+ 2,
+ 4,
+ 5,
+ 8,
+ 10,
+ 20,
+ 25,
+ 40,
+ 50,
+ 100,
+ 125,
+ 200,
+ 250,
+ 500
+ ].forEach(roundingIncrement => {
+ var options = {
+ smallestUnit,
+ roundingIncrement
+ };
+ assert(earlier.until(later, options) instanceof Temporal.Duration);
+ });
+});
+
+// throws on increments that do not divide evenly into the next highest
+assert.throws(RangeError, () => earlier.until(later, {
+ smallestUnit: "hours",
+ roundingIncrement: 11
+}));
+assert.throws(RangeError, () => earlier.until(later, {
+ smallestUnit: "minutes",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => earlier.until(later, {
+ smallestUnit: "seconds",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => earlier.until(later, {
+ smallestUnit: "milliseconds",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => earlier.until(later, {
+ smallestUnit: "microseconds",
+ roundingIncrement: 29
+}));
+assert.throws(RangeError, () => earlier.until(later, {
+ smallestUnit: "nanoseconds",
+ roundingIncrement: 29
+}));
+
+// throws on increments that are equal to the next highest
+assert.throws(RangeError, () => earlier.until(later, {
+ smallestUnit: "hours",
+ roundingIncrement: 24
+}));
+assert.throws(RangeError, () => earlier.until(later, {
+ smallestUnit: "minutes",
+ roundingIncrement: 60
+}));
+assert.throws(RangeError, () => earlier.until(later, {
+ smallestUnit: "seconds",
+ roundingIncrement: 60
+}));
+assert.throws(RangeError, () => earlier.until(later, {
+ smallestUnit: "milliseconds",
+ roundingIncrement: 1000
+}));
+assert.throws(RangeError, () => earlier.until(later, {
+ smallestUnit: "microseconds",
+ roundingIncrement: 1000
+}));
+assert.throws(RangeError, () => earlier.until(later, {
+ smallestUnit: "nanoseconds",
+ roundingIncrement: 1000
+}));
+
+// rounds relative to the receiver
+var dt1 = Temporal.ZonedDateTime.from("2019-01-01T00:00+00:00[UTC]");
+var dt2 = Temporal.ZonedDateTime.from("2020-07-02T00:00+00:00[UTC]");
+assert.sameValue(`${ dt1.until(dt2, {
+ smallestUnit: "years",
+ roundingMode: "halfExpand"
+}) }`, "P2Y");
+assert.sameValue(`${ dt2.until(dt1, {
+ smallestUnit: "years",
+ roundingMode: "halfExpand"
+}) }`, "-P1Y");
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/with.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/with.js
new file mode 100644
index 0000000000..42eede22f9
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/with.js
@@ -0,0 +1,272 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: Temporal.ZonedDateTime.prototype.with()
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+var zdt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789).toZonedDateTime("UTC");
+
+// zdt.with({ year: 2019 } works
+assert.sameValue(`${ zdt.with({ year: 2019 }) }`, "2019-11-18T15:23:30.123456789+00:00[UTC]");
+
+// zdt.with({ month: 5 } works
+assert.sameValue(`${ zdt.with({ month: 5 }) }`, "1976-05-18T15:23:30.123456789+00:00[UTC]");
+
+// zdt.with({ monthCode: "M05" }) works
+assert.sameValue(`${ zdt.with({ monthCode: "M05" }) }`, "1976-05-18T15:23:30.123456789+00:00[UTC]");
+
+// month and monthCode must agree
+assert.throws(RangeError, () => zdt.with({
+ month: 5,
+ monthCode: "M06"
+}));
+
+// zdt.with({ day: 5 } works
+assert.sameValue(`${ zdt.with({ day: 5 }) }`, "1976-11-05T15:23:30.123456789+00:00[UTC]");
+
+// zdt.with({ hour: 5 } works
+assert.sameValue(`${ zdt.with({ hour: 5 }) }`, "1976-11-18T05:23:30.123456789+00:00[UTC]");
+
+// zdt.with({ minute: 5 } works
+assert.sameValue(`${ zdt.with({ minute: 5 }) }`, "1976-11-18T15:05:30.123456789+00:00[UTC]");
+
+// zdt.with({ second: 5 } works
+assert.sameValue(`${ zdt.with({ second: 5 }) }`, "1976-11-18T15:23:05.123456789+00:00[UTC]");
+
+// zdt.with({ millisecond: 5 } works
+assert.sameValue(`${ zdt.with({ millisecond: 5 }) }`, "1976-11-18T15:23:30.005456789+00:00[UTC]");
+
+// zdt.with({ microsecond: 5 } works
+assert.sameValue(`${ zdt.with({ microsecond: 5 }) }`, "1976-11-18T15:23:30.123005789+00:00[UTC]");
+
+// zdt.with({ nanosecond: 5 } works
+assert.sameValue(`${ zdt.with({ nanosecond: 5 }) }`, "1976-11-18T15:23:30.123456005+00:00[UTC]");
+
+// zdt.with({ month: 5, second: 15 } works
+assert.sameValue(`${ zdt.with({
+ month: 5,
+ second: 15
+}) }`, "1976-05-18T15:23:15.123456789+00:00[UTC]");
+
+// Overflow options
+// constrain
+var overflow = "constrain";
+assert.sameValue(`${ zdt.with({ month: 29 }, { overflow }) }`, "1976-12-18T15:23:30.123456789+00:00[UTC]");
+assert.sameValue(`${ zdt.with({ day: 31 }, { overflow }) }`, "1976-11-30T15:23:30.123456789+00:00[UTC]");
+assert.sameValue(`${ zdt.with({ hour: 29 }, { overflow }) }`, "1976-11-18T23:23:30.123456789+00:00[UTC]");
+assert.sameValue(`${ zdt.with({ nanosecond: 9000 }, { overflow }) }`, "1976-11-18T15:23:30.123456999+00:00[UTC]");
+
+// reject
+var overflow = "reject";
+assert.throws(RangeError, () => zdt.with({ month: 29 }, { overflow }));
+assert.throws(RangeError, () => zdt.with({ day: 31 }, { overflow }));
+assert.throws(RangeError, () => zdt.with({ hour: 29 }, { overflow }));
+assert.throws(RangeError, () => zdt.with({ nanosecond: 9000 }, { overflow }));
+
+var dst = TemporalHelpers.springForwardFallBackTimeZone();
+var dstStartDay = Temporal.PlainDateTime.from("2000-04-02T12:00:01").toZonedDateTime(dst);
+var dstEndDay = Temporal.PlainDateTime.from("2000-10-29T12:00:01").toZonedDateTime(dst);
+var oneThirty = {
+hour: 1,
+minute: 30
+};
+var twoThirty = {
+hour: 2,
+minute: 30
+};
+
+// Disambiguation options
+var offset = "ignore";
+// compatible, skipped wall time
+assert.sameValue(`${ dstStartDay.with(twoThirty, {
+ offset,
+ disambiguation: "compatible"
+}) }`, "2000-04-02T03:30:01-07:00[Custom/Spring_Fall]");
+
+// earlier, skipped wall time
+assert.sameValue(`${ dstStartDay.with(twoThirty, {
+ offset,
+ disambiguation: "earlier"
+}) }`, "2000-04-02T01:30:01-08:00[Custom/Spring_Fall]");
+
+// later, skipped wall time
+assert.sameValue(`${ dstStartDay.with(twoThirty, {
+ offset,
+ disambiguation: "later"
+}) }`, "2000-04-02T03:30:01-07:00[Custom/Spring_Fall]");
+
+// compatible, repeated wall time
+assert.sameValue(`${ dstEndDay.with(oneThirty, {
+ offset,
+ disambiguation: "compatible"
+}) }`, "2000-10-29T01:30:01-07:00[Custom/Spring_Fall]");
+
+// earlier, repeated wall time
+assert.sameValue(`${ dstEndDay.with(oneThirty, {
+ offset,
+ disambiguation: "earlier"
+}) }`, "2000-10-29T01:30:01-07:00[Custom/Spring_Fall]");
+
+// later, repeated wall time
+assert.sameValue(`${ dstEndDay.with(oneThirty, {
+ offset,
+ disambiguation: "later"
+}) }`, "2000-10-29T01:30:01-08:00[Custom/Spring_Fall]");
+
+// reject
+assert.throws(RangeError, () => dstStartDay.with(twoThirty, {
+ offset,
+ disambiguation: "reject"
+}));
+assert.throws(RangeError, () => dstEndDay.with(oneThirty, {
+ offset,
+ disambiguation: "reject"
+}));
+
+// compatible is the default
+assert.sameValue(`${ dstStartDay.with(twoThirty, { offset }) }`, `${ dstStartDay.with(twoThirty, {
+ offset,
+ disambiguation: "compatible"
+}) }`);
+assert.sameValue(`${ dstEndDay.with(twoThirty, { offset }) }`, `${ dstEndDay.with(twoThirty, {
+ offset,
+ disambiguation: "compatible"
+}) }`);
+
+// invalid disambiguation
+[
+ "",
+ "EARLIER",
+ "balance"
+].forEach(disambiguation => assert.throws(RangeError, () => zdt.with({ day: 5 }, { disambiguation })));
+
+// Offset options
+var bogus = {
+ ...twoThirty,
+ offset: "+23:59"
+};
+// use, with bogus offset, changes to the exact time with the offset
+var preserveExact = dstStartDay.with(bogus, { offset: "use" });
+assert.sameValue(`${ preserveExact }`, "2000-03-31T18:31:01-08:00[Custom/Spring_Fall]");
+assert.sameValue(preserveExact.epochNanoseconds, Temporal.Instant.from("2000-04-02T02:30:01+23:59").epochNanoseconds);
+
+// ignore, with bogus offset, defers to disambiguation option
+var offset = "ignore";
+assert.sameValue(`${ dstStartDay.with(bogus, {
+ offset,
+ disambiguation: "earlier"
+}) }`, "2000-04-02T01:30:01-08:00[Custom/Spring_Fall]");
+assert.sameValue(`${ dstStartDay.with(bogus, {
+ offset,
+ disambiguation: "later"
+}) }`, "2000-04-02T03:30:01-07:00[Custom/Spring_Fall]");
+
+// prefer, with bogus offset, defers to disambiguation option
+var offset = "prefer";
+assert.sameValue(`${ dstStartDay.with(bogus, {
+ offset,
+ disambiguation: "earlier"
+}) }`, "2000-04-02T01:30:01-08:00[Custom/Spring_Fall]");
+assert.sameValue(`${ dstStartDay.with(bogus, {
+ offset,
+ disambiguation: "later"
+}) }`, "2000-04-02T03:30:01-07:00[Custom/Spring_Fall]");
+
+// reject, with bogus offset, throws
+assert.throws(RangeError, () => dstStartDay.with({
+ ...twoThirty,
+ offset: "+23:59"
+}, { offset: "reject" }));
+
+var doubleTime = new Temporal.ZonedDateTime(972811801_000_000_000n, dst);
+// use changes to the exact time with the offset
+var preserveExact = doubleTime.with({ offset: "-07:00" }, { offset: "use" });
+assert.sameValue(preserveExact.offset, "-07:00");
+assert.sameValue(preserveExact.epochNanoseconds, Temporal.Instant.from("2000-10-29T01:30:01-07:00").epochNanoseconds);
+
+// ignore defers to disambiguation option
+var offset = "ignore";
+assert.sameValue(doubleTime.with({ offset: "-07:00" }, {
+ offset,
+ disambiguation: "earlier"
+}).offset, "-07:00");
+assert.sameValue(doubleTime.with({ offset: "-07:00" }, {
+ offset,
+ disambiguation: "later"
+}).offset, "-08:00");
+
+// prefer adjusts offset of repeated clock time
+assert.sameValue(doubleTime.with({ offset: "-07:00" }, { offset: "prefer" }).offset, "-07:00");
+
+// reject adjusts offset of repeated clock time
+assert.sameValue(doubleTime.with({ offset: "-07:00" }, { offset: "reject" }).offset, "-07:00");
+
+// use does not cause the offset to change when adjusting repeated clock time
+assert.sameValue(doubleTime.with({ minute: 31 }, { offset: "use" }).offset, "-08:00");
+
+// ignore may cause the offset to change when adjusting repeated clock time
+assert.sameValue(doubleTime.with({ minute: 31 }, { offset: "ignore" }).offset, "-07:00");
+
+// prefer does not cause the offset to change when adjusting repeated clock time
+assert.sameValue(doubleTime.with({ minute: 31 }, { offset: "prefer" }).offset, "-08:00");
+
+// reject does not cause the offset to change when adjusting repeated clock time
+assert.sameValue(doubleTime.with({ minute: 31 }, { offset: "reject" }).offset, "-08:00");
+
+// prefer is the default
+assert.sameValue(`${ dstStartDay.with(twoThirty) }`, `${ dstStartDay.with(twoThirty, { offset: "prefer" }) }`);
+assert.sameValue(`${ dstEndDay.with(twoThirty) }`, `${ dstEndDay.with(twoThirty, { offset: "prefer" }) }`);
+assert.sameValue(`${ doubleTime.with({ minute: 31 }) }`, `${ doubleTime.with({ minute: 31 }, { offset: "prefer" }) }`);
+
+// invalid offset
+[
+ "",
+ "PREFER",
+ "balance"
+].forEach(offset => assert.throws(RangeError, () => zdt.with({ day: 5 }, { offset })));
+
+// object must contain at least one correctly-spelled property
+assert.throws(TypeError, () => zdt.with({}));
+assert.throws(TypeError, () => zdt.with({ months: 12 }));
+
+// incorrectly-spelled properties are ignored
+assert.sameValue(`${ zdt.with({
+ month: 12,
+ days: 15
+}) }`, "1976-12-18T15:23:30.123456789+00:00[UTC]");
+
+// throws if timeZone is included
+assert.throws(TypeError, () => zdt.with({
+ month: 2,
+ timeZone: "UTC"
+}));
+
+// throws if given a Temporal object with a time zone
+assert.throws(TypeError, () => zdt.with(dstStartDay));
+
+// throws if calendarName is included
+assert.throws(TypeError, () => zdt.with({
+ month: 2,
+ calendar: "iso8601"
+}));
+
+// throws if given a Temporal object with a calendar
+assert.throws(TypeError, () => zdt.with(Temporal.PlainDateTime.from("1976-11-18T12:00")));
+assert.throws(TypeError, () => zdt.with(Temporal.PlainDate.from("1976-11-18")));
+assert.throws(TypeError, () => zdt.with(Temporal.PlainTime.from("12:00")));
+assert.throws(TypeError, () => zdt.with(Temporal.PlainYearMonth.from("1976-11")));
+assert.throws(TypeError, () => zdt.with(Temporal.PlainMonthDay.from("11-18")));
+
+// throws if given a string
+assert.throws(TypeError, () => zdt.with("1976-11-18T12:00+00:00[UTC]"));
+assert.throws(TypeError, () => zdt.with("1976-11-18"));
+assert.throws(TypeError, () => zdt.with("12:00"));
+assert.throws(TypeError, () => zdt.with("invalid"));
+
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withCalendar.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withCalendar.js
new file mode 100644
index 0000000000..380cbeb664
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withCalendar.js
@@ -0,0 +1,46 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: Temporal.ZonedDateTime.prototype.withCalendar()
+features: [Temporal]
+---*/
+
+var zdt = Temporal.ZonedDateTime.from("2019-11-18T15:23:30.123456789-08:00[-08:00]");
+
+// zonedDateTime.withCalendar(japanese) works
+var cal = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "japanese",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+assert.sameValue(`${ zdt.withCalendar(cal) }`, "2019-11-18T15:23:30.123456789-08:00[-08:00][u-ca=japanese]");
+
+// keeps instant and time zone the same
+var zdt = Temporal.ZonedDateTime.from("2019-11-18T15:23:30.123456789+01:00[+01:00][u-ca=iso8601]");
+var zdt2 = zdt.withCalendar(cal);
+assert.sameValue(zdt.epochNanoseconds, zdt2.epochNanoseconds);
+assert.sameValue(zdt2.getCalendar(), cal);
+assert.sameValue(zdt2.timeZoneId, "+01:00");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withPlainDate.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withPlainDate.js
new file mode 100644
index 0000000000..810ebd3350
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withPlainDate.js
@@ -0,0 +1,98 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: .withPlainDate manipulation
+includes: [temporalHelpers.js]
+features: [Temporal]
+---*/
+
+var dst = TemporalHelpers.springForwardFallBackTimeZone();
+var zdt = Temporal.PlainDateTime.from("1995-12-07T03:24:30").toZonedDateTime(dst);
+
+// withPlainDate({ year: 2000, month: 6, day: 1 }) works
+// and keeps wall time constant despite the UTC offset change
+assert.sameValue(`${ zdt.withPlainDate({
+ year: 2000,
+ month: 6,
+ day: 1
+}) }`, "2000-06-01T03:24:30-07:00[Custom/Spring_Fall]");
+
+// withPlainDate(plainDate) works
+var date = Temporal.PlainDate.from("2020-01-23");
+assert.sameValue(`${ zdt.withPlainDate(date) }`, "2020-01-23T03:24:30-08:00[Custom/Spring_Fall]");
+
+// withPlainDate('2018-09-15') works
+assert.sameValue(`${ zdt.withPlainDate("2018-09-15") }`, "2018-09-15T03:24:30-08:00[Custom/Spring_Fall]");
+
+// result contains a non-ISO calendar if present in the input
+var fakeJapanese = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "japanese",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+assert.sameValue(`${ zdt.withCalendar(fakeJapanese).withPlainDate("2008-09-06") }`, "2008-09-06T03:24:30-08:00[Custom/Spring_Fall][u-ca=japanese]");
+
+// calendar is unchanged if input has ISO calendar
+var date = new Temporal.PlainDate(2008, 9, 6, fakeJapanese);
+assert.sameValue(`${ zdt.withPlainDate(date) }`, "2008-09-06T03:24:30-08:00[Custom/Spring_Fall][u-ca=japanese]");
+
+// throws if both `this` and `other` have a non-ISO calendar
+var fakeGregorian = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "gregory",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+assert.throws(RangeError, () => zdt.withCalendar(fakeGregorian).withPlainDate(date));
+
+// object must contain at least one correctly-spelled property
+assert.throws(TypeError, () => zdt.withPlainDate({}));
+assert.throws(TypeError, () => zdt.withPlainDate({ months: 12 }));
+
+// incorrectly-spelled properties are ignored
+assert.sameValue(`${ zdt.withPlainDate({
+ year: 2000,
+ month: 6,
+ day: 1,
+ months: 123
+}) }`, "2000-06-01T03:24:30-07:00[Custom/Spring_Fall]");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withPlainTime.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withPlainTime.js
new file mode 100644
index 0000000000..9bd03c02fd
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withPlainTime.js
@@ -0,0 +1,29 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: .withPlainTime manipulation
+features: [Temporal]
+---*/
+
+var zdt = Temporal.ZonedDateTime.from("2015-12-07T03:24:30.000003500[-08:00]");
+
+// withPlainTime({ hour: 10 }) works
+assert.sameValue(`${ zdt.withPlainTime({ hour: 10 }) }`, "2015-12-07T10:00:00-08:00[-08:00]");
+
+// withPlainTime(time) works
+var time = Temporal.PlainTime.from("11:22");
+assert.sameValue(`${ zdt.withPlainTime(time) }`, "2015-12-07T11:22:00-08:00[-08:00]");
+
+// withPlainTime('12:34') works
+assert.sameValue(`${ zdt.withPlainTime("12:34") }`, "2015-12-07T12:34:00-08:00[-08:00]");
+
+// incorrectly-spelled properties are ignored
+assert.sameValue(`${ zdt.withPlainTime({
+ hour: 10,
+ seconds: 55
+}) }`, "2015-12-07T10:00:00-08:00[-08:00]");
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withTimezone.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withTimezone.js
new file mode 100644
index 0000000000..ae5951ff64
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/old/withTimezone.js
@@ -0,0 +1,42 @@
+// |reftest| skip-if(!this.hasOwnProperty('Temporal')) -- Temporal is not enabled unconditionally
+// Copyright (C) 2018 Bloomberg LP. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+esid: sec-temporal-zoneddatetime-objects
+description: Temporal.ZonedDateTime.prototype.withTimeZone()
+features: [Temporal]
+---*/
+
+// keeps instant and calendar the same
+var fakeGregorian = {
+ dateAdd() {},
+ dateFromFields() {},
+ dateUntil() {},
+ day() {},
+ dayOfWeek() {},
+ dayOfYear() {},
+ daysInMonth() {},
+ daysInWeek() {},
+ daysInYear() {},
+ fields() {},
+ id: "gregory",
+ inLeapYear() {},
+ mergeFields() {},
+ month() {},
+ monthCode() {},
+ monthDayFromFields() {},
+ monthsInYear() {},
+ weekOfYear() {},
+ year() {},
+ yearMonthFromFields() {},
+ yearOfWeek() {},
+};
+var zdt = Temporal.ZonedDateTime.from("2019-11-18T15:23:30.123456789+01:00[+01:00]").withCalendar(fakeGregorian);
+var zdt2 = zdt.withTimeZone("-08:00");
+assert.sameValue(zdt.epochNanoseconds, zdt2.epochNanoseconds);
+assert.sameValue(zdt2.getCalendar(), fakeGregorian);
+assert.sameValue(zdt2.timeZoneId, "-08:00");
+assert.notSameValue(`${ zdt.toPlainDateTime() }`, `${ zdt2.toPlainDateTime() }`);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/Temporal/ZonedDateTime/shell.js b/js/src/tests/test262/staging/Temporal/ZonedDateTime/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/ZonedDateTime/shell.js
diff --git a/js/src/tests/test262/staging/Temporal/browser.js b/js/src/tests/test262/staging/Temporal/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/browser.js
diff --git a/js/src/tests/test262/staging/Temporal/shell.js b/js/src/tests/test262/staging/Temporal/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/Temporal/shell.js
diff --git a/js/src/tests/test262/staging/browser.js b/js/src/tests/test262/staging/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/browser.js
diff --git a/js/src/tests/test262/staging/set-is-subset-on-set-like.js b/js/src/tests/test262/staging/set-is-subset-on-set-like.js
new file mode 100644
index 0000000000..dac869b822
--- /dev/null
+++ b/js/src/tests/test262/staging/set-is-subset-on-set-like.js
@@ -0,0 +1,27 @@
+// |reftest| skip -- set-methods is not supported
+// Copyright (C) 2023 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: Test isSubsetOf set method on a set like with equal size.
+features: [set-methods]
+---*/
+
+const SetLike = {
+ arr: [42, 44, 45],
+ size: 3,
+ keys() {
+ return this.arr[Symbol.iterator]();
+ },
+ has(key) {
+ return this.arr.indexOf(key) != -1;
+ }
+};
+
+const firstSet = new Set();
+firstSet.add(42);
+firstSet.add(43);
+firstSet.add(45);
+
+assert.sameValue(firstSet.isSubsetOf(SetLike), false);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/set-is-subset-table-receiver-cleared.js b/js/src/tests/test262/staging/set-is-subset-table-receiver-cleared.js
new file mode 100644
index 0000000000..927f57ac53
--- /dev/null
+++ b/js/src/tests/test262/staging/set-is-subset-table-receiver-cleared.js
@@ -0,0 +1,28 @@
+// |reftest| skip -- set-methods is not supported
+// Copyright (C) 2023 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: Test isSubsetOf set method receiver is cleared.
+features: [set-methods]
+---*/
+
+const firstSet = new Set();
+firstSet.add(42);
+firstSet.add(43);
+
+const otherSet = new Set();
+otherSet.add(42);
+otherSet.add(43);
+otherSet.add(47);
+
+Object.defineProperty(otherSet, 'size', {
+ get: function() {
+ firstSet.clear();
+ return 3;
+ },
+
+});
+
+assert.sameValue(firstSet.isSubsetOf(otherSet), true);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/set-is-subset-table-transition.js b/js/src/tests/test262/staging/set-is-subset-table-transition.js
new file mode 100644
index 0000000000..4ef9bf407e
--- /dev/null
+++ b/js/src/tests/test262/staging/set-is-subset-table-transition.js
@@ -0,0 +1,32 @@
+// |reftest| skip -- set-methods is not supported
+// Copyright (C) 2023 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: Test isSubsetOf set method after table transition in receiver.
+features: [set-methods]
+---*/
+
+const firstSet = new Set();
+firstSet.add(42);
+firstSet.add(43);
+firstSet.add(44);
+
+const setLike = {
+ size: 5,
+ keys() {
+ return [1, 2, 3, 4, 5].keys();
+ },
+ has(key) {
+ if (key == 42) {
+ // Cause a table transition in the receiver.
+ firstSet.clear();
+ }
+ // Return true so we keep iterating the transitioned receiver.
+ return true;
+ }
+};
+
+assert.sameValue(firstSet.isSubsetOf(setLike), true);
+assert.sameValue(firstSet.size, 0);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/set-methods/browser.js b/js/src/tests/test262/staging/set-methods/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/set-methods/browser.js
diff --git a/js/src/tests/test262/staging/set-methods/set-intersect-other-is-set-like.js b/js/src/tests/test262/staging/set-methods/set-intersect-other-is-set-like.js
new file mode 100644
index 0000000000..8d31996491
--- /dev/null
+++ b/js/src/tests/test262/staging/set-methods/set-intersect-other-is-set-like.js
@@ -0,0 +1,34 @@
+// |reftest| skip -- set-methods is not supported
+// Copyright (C) 2023 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: test intersection method when `other` is a set-like.
+features: [set-methods]
+includes: [compareArray.js]
+---*/
+
+const SetLike = {
+ arr: [42, 43, 45],
+ size: 3,
+ keys() {
+ return this.arr[Symbol.iterator]();
+ },
+ has(key) {
+ return this.arr.indexOf(key) != -1;
+ }
+ };
+
+const firstSet = new Set();
+firstSet.add(42);
+firstSet.add(43);
+
+const resultSet = new Set();
+resultSet.add(42);
+resultSet.add(43);
+
+const resultArray = Array.from(resultSet);
+const intersectionArray = Array.from(firstSet.intersection(SetLike));
+
+assert.compareArray(resultArray, intersectionArray);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/set-methods/set-intersection-other-is-map.js b/js/src/tests/test262/staging/set-methods/set-intersection-other-is-map.js
new file mode 100644
index 0000000000..e53671fa8a
--- /dev/null
+++ b/js/src/tests/test262/staging/set-methods/set-intersection-other-is-map.js
@@ -0,0 +1,27 @@
+// |reftest| skip -- set-methods is not supported
+// Copyright (C) 2023 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: test intersection method when `other` is a map.
+features: [set-methods]
+includes: [compareArray.js]
+---*/
+
+const firstSet = new Set();
+firstSet.add(42);
+firstSet.add(43);
+
+const other = new Map();
+other.set(42);
+other.set(46);
+other.set(47);
+
+const resultSet = new Set();
+resultSet.add(42);
+
+const resultArray = Array.from(resultSet);
+const intersectionArray = Array.from(firstSet.intersection(other));
+
+assert.compareArray(resultArray, intersectionArray);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/set-methods/set-intersection-other-is-set.js b/js/src/tests/test262/staging/set-methods/set-intersection-other-is-set.js
new file mode 100644
index 0000000000..b2cb955a11
--- /dev/null
+++ b/js/src/tests/test262/staging/set-methods/set-intersection-other-is-set.js
@@ -0,0 +1,27 @@
+// |reftest| skip -- set-methods is not supported
+// Copyright (C) 2023 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: test intersection method when `other` is a set.
+features: [set-methods]
+includes: [compareArray.js]
+---*/
+
+const firstSet = new Set();
+firstSet.add(42);
+firstSet.add(43);
+firstSet.add(44);
+
+const otherSet = new Set();
+otherSet.add(42);
+otherSet.add(45);
+
+const resultSet = new Set();
+resultSet.add(42);
+
+const resultArray = Array.from(resultSet);
+const intersectionArray = Array.from(firstSet.intersection(otherSet));
+
+assert.compareArray(resultArray, intersectionArray);
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/set-methods/shell.js b/js/src/tests/test262/staging/set-methods/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/set-methods/shell.js
diff --git a/js/src/tests/test262/staging/shell.js b/js/src/tests/test262/staging/shell.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/shell.js