diff --git a/.gitignore b/.gitignore
index 4f26a3928b29..a9cf906749cd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,7 @@
autoinstall.cache.d
project.xcworkspace
xcuserdata
+*.xcresult
DerivedData
.mailmap
results
@@ -96,4 +97,6 @@ CLAUDE.md
# Ignore tracing files
*.atrc
+*.profraw
+*.pas_stats.jsonl
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0bdbada6264f..2101982ccc09 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -7,42 +7,16 @@
# and loading the cross-compilation settings from CMAKE_TOOLCHAIN_FILE.
cmake_minimum_required(VERSION 3.20)
+set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/Source/cmake")
+
+if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
+ # Resolve the SDK and pin tools before the call to project().
+ include(WebKitXcodeSDK)
+endif ()
if (PORT STREQUAL "IOS")
+ # FIXME: Delete or merge into WebKitXcodeSDK logic.
set(CMAKE_SYSTEM_NAME iOS)
- if (CMAKE_IOS_SIMULATOR)
- set(_sdk_name "iphonesimulator.internal")
- set(_sdk_name_fallback "iphonesimulator")
- else ()
- set(_sdk_name "iphoneos.internal")
- set(_sdk_name_fallback "iphoneos")
- endif ()
- execute_process(COMMAND xcrun --sdk ${_sdk_name} --show-sdk-path
- OUTPUT_VARIABLE _ios_sysroot
- OUTPUT_STRIP_TRAILING_WHITESPACE
- RESULT_VARIABLE _internal_sdk_result
- ERROR_QUIET)
- if (NOT _internal_sdk_result EQUAL 0 OR NOT _ios_sysroot)
- set(_ios_sysroot "")
- if (CMAKE_OSX_SYSROOT AND EXISTS "${CMAKE_OSX_SYSROOT}")
- set(_ios_sysroot "${CMAKE_OSX_SYSROOT}")
- elseif (DEFINED ENV{SDKROOT} AND EXISTS "$ENV{SDKROOT}" AND "$ENV{SDKROOT}" MATCHES "iPhone")
- set(_ios_sysroot "$ENV{SDKROOT}")
- else ()
- execute_process(COMMAND xcrun --sdk ${_sdk_name_fallback} --show-sdk-path
- OUTPUT_VARIABLE _ios_sysroot
- OUTPUT_STRIP_TRAILING_WHITESPACE
- ERROR_QUIET)
- endif ()
- endif ()
- if (_ios_sysroot)
- set(CMAKE_OSX_SYSROOT "${_ios_sysroot}" CACHE PATH "iOS SDK path" FORCE)
- endif ()
- unset(_sdk_name)
- unset(_sdk_name_fallback)
- unset(_internal_sdk_result)
- unset(_ios_sysroot)
-
if (NOT CMAKE_OSX_ARCHITECTURES)
if (CMAKE_OSX_SYSROOT MATCHES "\\.Internal\\.sdk$" AND NOT CMAKE_OSX_SYSROOT MATCHES "[Ss]imulator")
execute_process(COMMAND uname -m
@@ -66,32 +40,11 @@ if (PORT STREQUAL "IOS")
endif ()
endif ()
-if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin" AND NOT CMAKE_SYSTEM_NAME STREQUAL "iOS" AND NOT CMAKE_OSX_SYSROOT)
- # Set CMAKE_OSX_SYSROOT before the project() call below. project() runs ABI
- # detection tests whose results are cached; having the wrong sysroot there
- # would cause those cached results to refer to a different SDK than actual
- # builds use. OptionsMac.cmake (which sets this via WEBKIT_XCRUN) is loaded
- # only after project() via include(WebKitCommon), so it is too late.
- # WEBKIT_XCRUN is also unavailable here because WebKitXcrun.cmake is
- # included by OptionsMac.cmake.
- execute_process(COMMAND xcrun --sdk macosx --show-sdk-path
- OUTPUT_VARIABLE _macos_sysroot
- OUTPUT_STRIP_TRAILING_WHITESPACE
- RESULT_VARIABLE _xcrun_result
- ERROR_QUIET)
- if (_xcrun_result EQUAL 0 AND _macos_sysroot)
- set(CMAKE_OSX_SYSROOT "${_macos_sysroot}" CACHE PATH "macOS SDK path" FORCE)
- endif ()
- unset(_macos_sysroot)
- unset(_xcrun_result)
-endif ()
-
project(WebKit LANGUAGES C CXX)
# -----------------------------------------------------------------------------
# Common configuration
#------------------------------------------------------------------------------
-set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/Source/cmake")
include(WebKitCommon)
# -----------------------------------------------------------------------------
@@ -160,6 +113,8 @@ if (DEVELOPER_MODE)
endif ()
endif ()
+WEBKIT_GENERATE_HEADER_MAPS()
+
# -----------------------------------------------------------------------------
# Print the features list last, for maximum visibility.
# -----------------------------------------------------------------------------
diff --git a/Configurations/CommonBase.xcconfig b/Configurations/CommonBase.xcconfig
index b9f977ef6e71..636cb626594e 100644
--- a/Configurations/CommonBase.xcconfig
+++ b/Configurations/CommonBase.xcconfig
@@ -129,7 +129,7 @@ WK_SWIFT_CLANG_DEPLOYMENT_TARGET = $(WK_SWIFT_CLANG_DEPLOYMENT_TARGET_$(WK_PLATF
WK_SWIFT_CLANG_DEPLOYMENT_TARGET_macosx = -clang-target $(CURRENT_ARCH)-apple-macos$(MACOSX_DEPLOYMENT_TARGET);
// rdar://170129992: remove -track-system-dependencies when resolved
-OTHER_SWIFT_FLAGS = $(inherited) -Xcc -fvisibility=hidden -Werror ExistentialAny -Werror NoUseUnstructuredThrowingTask -enable-upcoming-feature ExistentialAny $(WK_SWIFT_MEMORY_SAFETY_FLAGS) $(WK_SWIFT_MEMORY_SAFETY_ERROR_FLAGS) $(WK_SWIFT_CLANG_DEPLOYMENT_TARGET) $(WK_SANITIZER_OTHER_SWIFT_FLAGS) -enable-upcoming-feature InternalImportsByDefault -enable-upcoming-feature MemberImportVisibility -track-system-dependencies -enable-experimental-feature ImportCxxMembersLazily;
+OTHER_SWIFT_FLAGS = $(inherited) -Xcc -fvisibility=hidden -Werror ExistentialAny -Werror NoUseUnstructuredThrowingTask -Werror NoUsage -enable-upcoming-feature ExistentialAny $(WK_SWIFT_MEMORY_SAFETY_FLAGS) $(WK_SWIFT_MEMORY_SAFETY_ERROR_FLAGS) $(WK_SWIFT_CLANG_DEPLOYMENT_TARGET) $(WK_SANITIZER_OTHER_SWIFT_FLAGS) -enable-upcoming-feature InternalImportsByDefault -enable-upcoming-feature MemberImportVisibility -track-system-dependencies -enable-experimental-feature ImportCxxMembersLazily;
// Work around rdar://157581667 on affected toolchains.
OTHER_SWIFT_FLAGS[sdk=*26.2*] = $(inherited) -Xcc -fno-modulemap-allow-subdirectory-search;
OTHER_SWIFT_FLAGS[sdk=*26.3*] = $(inherited) -Xcc -fno-modulemap-allow-subdirectory-search;
diff --git a/Configurations/Version.xcconfig b/Configurations/Version.xcconfig
index 206a058fde57..dd7445552464 100644
--- a/Configurations/Version.xcconfig
+++ b/Configurations/Version.xcconfig
@@ -23,7 +23,7 @@
MAJOR_VERSION = 625;
MINOR_VERSION = 1;
-TINY_VERSION = 19;
+TINY_VERSION = 21;
MICRO_VERSION = 0;
NANO_VERSION = 0;
FULL_VERSION = $(MAJOR_VERSION).$(MINOR_VERSION).$(TINY_VERSION);
diff --git a/JSTests/microbenchmarks/class-fields-anonymous-arrow-function-fields.js b/JSTests/microbenchmarks/class-fields-anonymous-arrow-function-fields.js
new file mode 100644
index 000000000000..148d0941da05
--- /dev/null
+++ b/JSTests/microbenchmarks/class-fields-anonymous-arrow-function-fields.js
@@ -0,0 +1,28 @@
+// Stresses SetFunctionName for anonymous arrow function class fields.
+class ArrowFunctionFields {
+ render = (...args) => args;
+ setLayout = (l) => l;
+ getLayout = () => 0;
+ setRenderer = (r) => r;
+ header = (n, v) => v;
+ status = (s) => s;
+ set = (k, v) => v;
+ get = (k) => k;
+ newResponse = (...a) => a;
+ body = (d) => d;
+ text = (t) => t;
+ json = (o) => o;
+ html = (h) => h;
+ redirect = (l) => l;
+ notFound = () => null;
+}
+
+function bench(testClass) {
+ var instance;
+ for (var i = 0; i < 1e5; i++)
+ instance = new testClass();
+ return instance;
+}
+noInline(bench);
+
+bench(ArrowFunctionFields);
diff --git a/JSTests/microbenchmarks/regexp-empty-body-parens.js b/JSTests/microbenchmarks/regexp-empty-body-parens.js
new file mode 100644
index 000000000000..6b20c1d21bdb
--- /dev/null
+++ b/JSTests/microbenchmarks/regexp-empty-body-parens.js
@@ -0,0 +1,58 @@
+//@ skip if $model == "Apple Watch Series 3" # added by mark-jsc-stress-test.py
+// This benchmarks quantified parentheses whose body can match an empty
+// string, e.g. /(){3}/, /(?:){5}/, /(a?){3}/. The Yarr JIT compiles these
+// patterns and then bails to the interpreter at runtime via the
+// empty-match-detection branch in ParenthesesSubpattern[FixedCount]End.
+// This microbenchmark guards against regressions in either the JIT
+// compilation path or the interpreter fallback for the empty-body case.
+
+(function() {
+ var result = 0;
+ var n = 200000;
+
+ var str1 = "abc";
+ var str2 = "";
+ var str3 = "aaa";
+ var str4 = "aaab";
+ var str5 = "xx";
+
+ // Pure empty body — JIT bails to interpreter on the first iteration.
+ var re1 = /(){3}/; // capturing empty
+ var re2 = /(?:){3}/; // non-capturing empty
+ var re3 = /((?:)){3}/; // capturing of empty alternative
+
+ // Optional / alternation that matches empty for some inputs.
+ var re4 = /(a?){3}/; // optional content
+ var re5 = /(|x){3}/; // empty alternation
+ var re6 = /(x|){3}/; // empty alternation
+
+ // Empty inner inside a non-empty outer.
+ var re7 = /(a()){3}/; // outer non-empty, inner empty
+
+ // Empty paren followed by capturing content.
+ var re8 = /(){2}(a)b/;
+
+ for (var i = 0; i < n; ++i) {
+ if (re1.exec(str1))
+ ++result;
+ if (re2.exec(str1))
+ ++result;
+ if (re3.exec(str1))
+ ++result;
+ if (re4.exec(str2))
+ ++result;
+ if (re4.exec(str3))
+ ++result;
+ if (re5.exec(str2))
+ ++result;
+ if (re6.exec(str5))
+ ++result;
+ if (re7.exec(str4))
+ ++result;
+ if (re8.exec("ab"))
+ ++result;
+ }
+
+ if (result != n * 9)
+ throw "Error: bad result: " + result;
+})();
diff --git a/JSTests/microbenchmarks/regexp-fixed-count-capturing-parens.js b/JSTests/microbenchmarks/regexp-fixed-count-capturing-parens.js
new file mode 100644
index 000000000000..88142faa0c59
--- /dev/null
+++ b/JSTests/microbenchmarks/regexp-fixed-count-capturing-parens.js
@@ -0,0 +1,44 @@
+//@ skip if $model == "Apple Watch Series 3" # added by mark-jsc-stress-test.py
+// This benchmarks capturing parentheses with fixed-count quantifiers whose
+// body has a single alternative and contains nothing backtrackable, e.g.
+// /([0-9a-fA-F]){12}/ or /([0-9a-fA-F]{4}\.){2}/. These now route to the
+// ParenthesesSubpatternFixedCount fast path that no longer allocates a
+// ParenContext per iteration.
+
+(function() {
+ var result = 0;
+ var n = 500000;
+
+ // Test strings that will match
+ var str1 = "0123456789ab"; // for ([0-9a-fA-F]){12}
+ var str2 = "abcd.efff.0011"; // for ([0-9a-fA-F]{4}\.){2}([0-9a-fA-F]{4})
+ var str3 = "aaa"; // for ([a-z]){3}
+ var str4 = "abcabc"; // for ([a-z]{3}){2}
+
+ // Test strings that will fail (exercise the abort/backtrack path).
+ var fail1 = "0123456789ax";
+ var fail2 = "abcd.efgh.0011";
+
+ var re1 = /([0-9a-fA-F]){12}/;
+ var re2 = /([0-9a-fA-F]{4}\.){2}([0-9a-fA-F]{4})/;
+ var re3 = /([a-z]){3}/;
+ var re4 = /([a-z]{3}){2}/;
+
+ for (var i = 0; i < n; ++i) {
+ if (re1.exec(str1))
+ ++result;
+ if (re2.exec(str2))
+ ++result;
+ if (re3.exec(str3))
+ ++result;
+ if (re4.exec(str4))
+ ++result;
+ if (re1.exec(fail1) === null)
+ ++result;
+ if (re2.exec(fail2) === null)
+ ++result;
+ }
+
+ if (result != n * 6)
+ throw "Error: bad result: " + result;
+})();
diff --git a/JSTests/microbenchmarks/regexp-prototype-match-anchor.js b/JSTests/microbenchmarks/regexp-prototype-match-anchor.js
new file mode 100644
index 000000000000..541cd859ba92
--- /dev/null
+++ b/JSTests/microbenchmarks/regexp-prototype-match-anchor.js
@@ -0,0 +1,19 @@
+function shouldBe(actual, expected) {
+ if (JSON.stringify(actual) !== JSON.stringify(expected))
+ throw new Error(`Expected ${JSON.stringify(expected)} but got ${JSON.stringify(actual)}`);
+}
+
+const str = ("X".repeat(50) + "the_end").repeat(2);
+
+const re1 = /the_end$/;
+const re2 = /^X{50}the_end$/;
+const re3 = /the_end/g;
+
+for (let i = 0; i < 1e5; i++) {
+ shouldBe(re1[Symbol.match](str)[0], "the_end");
+ shouldBe(re2[Symbol.match](str), null);
+ shouldBe(re3[Symbol.match](str), ["the_end", "the_end"]);
+ shouldBe(str.match(re1)[0], "the_end");
+ shouldBe(str.match(re2), null);
+ shouldBe(str.match(re3), ["the_end", "the_end"]);
+}
diff --git a/JSTests/microbenchmarks/regexp-prototype-match-basic.js b/JSTests/microbenchmarks/regexp-prototype-match-basic.js
new file mode 100644
index 000000000000..092147602212
--- /dev/null
+++ b/JSTests/microbenchmarks/regexp-prototype-match-basic.js
@@ -0,0 +1,17 @@
+function shouldBe(a, b) {
+ if (JSON.stringify(a) !== JSON.stringify(b))
+ throw new Error(`Expected ${JSON.stringify(b)} but got ${JSON.stringify(a)}`);
+}
+
+const str = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzfoo";
+const re1 = /foo/;
+const re2 = /bar/;
+const re3 = /[a-z]/g;
+for (let i = 0; i < 1e4; i++) {
+ shouldBe(re1[Symbol.match](str)[0], "foo");
+ shouldBe(re2[Symbol.match](str), null);
+ shouldBe(re3[Symbol.match](str).length, 81);
+ shouldBe(str.match(re1)[0], "foo");
+ shouldBe(str.match(re2), null);
+ shouldBe(str.match(re3).length, 81);
+}
diff --git a/JSTests/microbenchmarks/regexp-prototype-match-complex-pattern.js b/JSTests/microbenchmarks/regexp-prototype-match-complex-pattern.js
new file mode 100644
index 000000000000..d29ec2d61ddb
--- /dev/null
+++ b/JSTests/microbenchmarks/regexp-prototype-match-complex-pattern.js
@@ -0,0 +1,19 @@
+function shouldBe(actual, expected) {
+ if (JSON.stringify(actual) !== JSON.stringify(expected))
+ throw new Error(`Expected ${JSON.stringify(expected)} but got ${JSON.stringify(actual)}`);
+}
+
+const str = "abc123XYZ_foo_bar_baz_987654321".repeat(2);
+
+const re1 = /abc\d+XYZ_foo_bar_baz_\d+/;
+const re2 = /abc\d+(?=XXX)(?:_foo_)(?:bar_)(?:baz_)\d+/;
+const re3 = /\d+/g;
+
+for (let i = 0; i < 1e4; i++) {
+ shouldBe(re1[Symbol.match](str)[0], "abc123XYZ_foo_bar_baz_987654321");
+ shouldBe(re2[Symbol.match](str), null);
+ shouldBe(re3[Symbol.match](str), ["123", "987654321", "123", "987654321"]);
+ shouldBe(str.match(re1)[0], "abc123XYZ_foo_bar_baz_987654321");
+ shouldBe(str.match(re2), null);
+ shouldBe(str.match(re3), ["123", "987654321", "123", "987654321"]);
+}
diff --git a/JSTests/microbenchmarks/regexp-prototype-match-no-strength-reduction.js b/JSTests/microbenchmarks/regexp-prototype-match-no-strength-reduction.js
new file mode 100644
index 000000000000..8b879859326a
--- /dev/null
+++ b/JSTests/microbenchmarks/regexp-prototype-match-no-strength-reduction.js
@@ -0,0 +1,9 @@
+function shouldBe(a, b) {
+ if (JSON.stringify(a) !== JSON.stringify(b))
+ throw new Error(`Expected ${JSON.stringify(b)} but got ${JSON.stringify(a)}`);
+}
+
+for (let i = 0; i < 1e5; i++) {
+ shouldBe(new RegExp("wor" + "ld")[Symbol.match]("hello world")[0], "world");
+ shouldBe(/hello/[Symbol.match]("hello" + "world" + Math.random())[0], "hello");
+}
diff --git a/JSTests/microbenchmarks/regexp-prototype-match-observable-side-effects.js b/JSTests/microbenchmarks/regexp-prototype-match-observable-side-effects.js
new file mode 100644
index 000000000000..34fdd4130202
--- /dev/null
+++ b/JSTests/microbenchmarks/regexp-prototype-match-observable-side-effects.js
@@ -0,0 +1,141 @@
+//@ skip if $model == "Apple Watch Series 3" # added by mark-jsc-stress-test.py
+//@ runNoFTL
+
+function assert(testedValue, msg) {
+ if (!testedValue)
+ throw Error(msg);
+}
+
+// RegExp subclass should not be able to override lastIndex via accessors
+// in the fast path; the C++ fast path reads it directly.
+(function () {
+ let accesses = [];
+ class SubRegExp extends RegExp {
+ get lastIndex() {
+ accesses.push("getLastIndex");
+ return super.lastIndex;
+ }
+ set lastIndex(newIndex) {
+ accesses.push("setLastIndex");
+ super.lastIndex = newIndex;
+ }
+ }
+
+ let obj = new SubRegExp(/rch/);
+
+ assert(accesses == "", "Should not be able to override lastIndex");
+ let result = RegExp.prototype[Symbol.match].call(obj, "searchme");
+ assert(accesses == "", "Should not be able to override lastIndex");
+ assert(result[0] === "rch", "Unexpected result");
+})();
+
+// RegExp subclass overriding exec should be observed (slow path).
+(function () {
+ let accesses = [];
+ class SubRegExp extends RegExp {
+ exec(str) {
+ accesses.push("exec");
+ return super.exec(str);
+ }
+ }
+
+ let obj = new SubRegExp(/rch/);
+
+ assert(accesses == "", "unexpected call to overridden props");
+ let result = RegExp.prototype[Symbol.match].call(obj, "searchme");
+ assert(accesses == "exec", "Property accesses do not match expectation");
+ assert(result[0] === "rch", "Unexpected result");
+})();
+
+// 2 levels of RegExp subclasses with the middle parent overriding exec.
+(function () {
+ let accesses = [];
+ class RegExpB extends RegExp {
+ exec(str) {
+ accesses.push("exec");
+ return super.exec(str);
+ }
+ }
+ class RegExpC extends RegExpB { }
+
+ assert(RegExpB.__proto__ == RegExp);
+ assert(RegExpC.__proto__ == RegExpB);
+
+ let obj = new RegExpC(/rch/);
+
+ assert(accesses == "", "unexpected call to overridden props");
+ let result = RegExp.prototype[Symbol.match].call(obj, "searchme");
+ assert(accesses == "exec", "Property accesses do not match expectation");
+ assert(result[0] === "rch", "Unexpected result");
+})();
+
+// RegExp subclass overriding flags is observed (the slow path reads `flags`).
+(function () {
+ let accesses = [];
+ class SubRegExp extends RegExp {
+ get flags() {
+ accesses.push("flags");
+ return super.flags;
+ }
+ }
+
+ let obj = new SubRegExp(/rch/, "g");
+
+ assert(accesses == "", "unexpected call to overridden props");
+ let result = RegExp.prototype[Symbol.match].call(obj, "rch_rch");
+ assert(accesses == "flags", "Property accesses do not match expectation: " + accesses);
+ assert(JSON.stringify(result) === JSON.stringify(["rch", "rch"]), "Unexpected result: " + result);
+})();
+
+// Proxied RegExp observing every get on a non-global regex.
+(function () {
+ let accesses = [];
+ let regexp = new RegExp(/rch/);
+ let proxy = new Proxy(regexp, {
+ get(obj, prop) {
+ accesses.push(prop.toString());
+ if (prop == "exec") {
+ return function(str) {
+ return obj.exec(str);
+ }
+ }
+ return obj[prop];
+ }
+ });
+
+ assert(accesses == "", "unexpected call to overridden props");
+ let result = RegExp.prototype[Symbol.match].call(proxy, "searchme");
+ // Non-global path: just calls flags getter and exec, no lastIndex access.
+ assert(accesses.toString() == "flags,exec", "Proxy not able to observe some gets, got: " + accesses);
+ assert(result[0] === "rch", "Unexpected result");
+})();
+
+// Proxied global RegExp: global path resets lastIndex to 0 then loops exec
+// until it returns null.
+(function () {
+ let accesses = [];
+ let regexp = /rch/g;
+ let proxy = new Proxy(regexp, {
+ get(obj, prop) {
+ accesses.push("get_" + prop.toString());
+ if (prop == "exec") {
+ return function(str) {
+ return obj.exec(str);
+ }
+ }
+ return obj[prop];
+ },
+ set(obj, prop, value) {
+ accesses.push("set_" + prop.toString() + ":" + value);
+ obj[prop] = value;
+ return true;
+ }
+ });
+
+ let result = RegExp.prototype[Symbol.match].call(proxy, "rch_rch");
+ assert(JSON.stringify(result) === JSON.stringify(["rch", "rch"]), "Unexpected result: " + result);
+ // flags read once; lastIndex set to 0 once; exec called 3 times (twice
+ // returning a match, once returning null).
+ assert(accesses[0] === "get_flags", "Expected first access to be flags, got: " + accesses[0]);
+ assert(accesses.includes("set_lastIndex:0"), "Expected lastIndex to be reset");
+})();
diff --git a/JSTests/microbenchmarks/regexp-prototype-match-short-string.js b/JSTests/microbenchmarks/regexp-prototype-match-short-string.js
new file mode 100644
index 000000000000..1d8be703281a
--- /dev/null
+++ b/JSTests/microbenchmarks/regexp-prototype-match-short-string.js
@@ -0,0 +1,16 @@
+function shouldBe(actual, expected) {
+ if (JSON.stringify(actual) !== JSON.stringify(expected))
+ throw new Error(`Expected ${JSON.stringify(expected)} but got ${JSON.stringify(actual)}`);
+}
+
+const str = "abc123XYZ";
+
+const re1 = /^([a-z]+)(\d+)([A-Z]+)$/;
+const re2 = /^([a-z]+)(\d+)([A-Z]+)foo$/;
+
+for (let i = 0; i < 1e5; i++) {
+ shouldBe(re1[Symbol.match](str)[0], "abc123XYZ");
+ shouldBe(re2[Symbol.match](str), null);
+ shouldBe(str.match(re1)[0], "abc123XYZ");
+ shouldBe(str.match(re2), null);
+}
diff --git a/JSTests/modules/import-defer-then-not-enumerable.js b/JSTests/modules/import-defer-then-not-enumerable.js
new file mode 100644
index 000000000000..62c492a9bb0f
--- /dev/null
+++ b/JSTests/modules/import-defer-then-not-enumerable.js
@@ -0,0 +1,23 @@
+//@ requireOptions("--useImportDefer=1")
+import { shouldBe } from "./resources/assert.js";
+
+import defer * as ns from "./import-defer/dep.js";
+
+// On a deferred namespace, [[GetOwnProperty]]("then") returns undefined (IsSymbolLikeNamespaceKey),
+// so enumerable-only listings must exclude "then" while [[OwnPropertyKeys]] still includes it.
+shouldBe(JSON.stringify(Object.keys(ns)), `["value"]`);
+shouldBe(JSON.stringify(Object.getOwnPropertyNames(ns)), `["then","value"]`);
+shouldBe(JSON.stringify(Reflect.ownKeys(ns).filter((key) => typeof key === "string")), `["then","value"]`);
+shouldBe(Object.getOwnPropertyDescriptor(ns, "then"), undefined);
+shouldBe(JSON.stringify(Object.entries(ns)), `[["value",1]]`);
+shouldBe(JSON.stringify(Object.values(ns)), `[1]`);
+shouldBe(JSON.stringify({ ...ns }), `{"value":1}`);
+
+const forIn = [];
+for (const key in ns)
+ forIn.push(key);
+shouldBe(JSON.stringify(forIn), `["value"]`);
+
+// Evaluation has been triggered above; the namespace stays deferred, so "then" remains hidden.
+shouldBe(ns.then, undefined);
+shouldBe(JSON.stringify(Object.keys(ns)), `["value"]`);
diff --git a/JSTests/stress/array-sort-nested-comparator-call-ignore-result.js b/JSTests/stress/array-sort-nested-comparator-call-ignore-result.js
new file mode 100644
index 000000000000..5bd221d0235f
--- /dev/null
+++ b/JSTests/stress/array-sort-nested-comparator-call-ignore-result.js
@@ -0,0 +1,7 @@
+function test() {
+ var arr = [3, 4, /Ῠ/iu];
+ arr.sort(function() { arr.sort(function() {}); });
+}
+
+for (var i = 0; i < testLoopCount; ++i)
+ test();
diff --git a/JSTests/stress/array-sort-nested-comparator-tail-call.js b/JSTests/stress/array-sort-nested-comparator-tail-call.js
new file mode 100644
index 000000000000..f05d5a59cd64
--- /dev/null
+++ b/JSTests/stress/array-sort-nested-comparator-tail-call.js
@@ -0,0 +1,7 @@
+function test() {
+ var arr = [3, 4, /Ῠ/iu];
+ return arr.sort(function() { arr.sort(function() {}); });
+}
+
+for (var i = 0; i < testLoopCount; ++i)
+ test();
diff --git a/JSTests/stress/async-generator-next-on-completed-reentrant.js b/JSTests/stress/async-generator-next-on-completed-reentrant.js
new file mode 100644
index 000000000000..c4413b761e84
--- /dev/null
+++ b/JSTests/stress/async-generator-next-on-completed-reentrant.js
@@ -0,0 +1,34 @@
+// https://bugs.webkit.org/show_bug.cgi?id=316447
+// %AsyncGeneratorPrototype%.next step 5: completed -> resolve { undefined, true } directly. Resolving reads
+// `then`, so a reentrant next() must still see the completed state and resolve inline (h2 before m1).
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`bad value: expected ${expected} but got ${actual}`);
+}
+
+let log = [];
+
+async function* gen() { }
+let it = gen();
+
+it.next().then(function () {
+ let phase = 1;
+ Object.defineProperty(Object.prototype, "then", {
+ configurable: true,
+ get() {
+ if (phase === 1) {
+ phase = 2;
+ it.next().then(() => { log.push("h2"); });
+ Promise.resolve().then(() => { log.push("m1"); });
+ }
+ return undefined;
+ },
+ });
+
+ it.next();
+});
+
+drainMicrotasks();
+
+shouldBe(log.join("|"), "h2|m1");
diff --git a/JSTests/stress/async-generator-reentrant-resume-during-resolve.js b/JSTests/stress/async-generator-reentrant-resume-during-resolve.js
new file mode 100644
index 000000000000..de999e6edda7
--- /dev/null
+++ b/JSTests/stress/async-generator-reentrant-resume-during-resolve.js
@@ -0,0 +1,58 @@
+// Regression test for https://bugs.webkit.org/show_bug.cgi?id=316132
+//
+// Resolving an async generator request reads `.then` on the iterator result object. A user-defined
+// `then` getter on Object.prototype can reentrantly call `iter.next()` while the generator is
+// suspended at a yield. The old driver resumed the generator nested inside that resolve and could
+// leave it awaiting, then resumed it again from the outer drain — asserting/crashing.
+//
+// After aligning the driver to the current spec (AsyncGeneratorDrainQueue / AsyncGeneratorResume /
+// AsyncGeneratorCompleteStep with a draining-queue state), the resolve in AsyncGeneratorYield runs
+// while the generator is in an execution state, so the reentrant next() only enqueues; the request
+// is then resumed inline. The observed order matches SpiderMonkey (the spec sequences CompleteStep,
+// which fulfils the first next()'s promise, before the inline resume re-suspends on the awaited
+// return value). It intentionally differs from V8's order; engines do not agree here.
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`bad value: expected ${expected} but got ${actual}`);
+}
+
+let log = [];
+let iter;
+let thenGets = 0;
+
+Object.defineProperty(Object.prototype, "then", {
+ get() {
+ log.push("then:" + thenGets);
+ if (thenGets++ === 0)
+ iter.next();
+ return undefined;
+ },
+ configurable: true,
+});
+
+async function* gen() {
+ log.push("start");
+ yield 1;
+ log.push("resume");
+ return 2;
+}
+
+let error = null;
+let resolvedWith = null;
+
+(async function main() {
+ iter = gen();
+ await iter.next();
+ log.push("await-first");
+ await 0;
+ await 0;
+ return 10;
+})().then(result => { resolvedWith = result; }, e => { error = e; });
+
+drainMicrotasks();
+
+if (error)
+ throw error;
+shouldBe(log.join("|"), "start|then:0|resume|await-first|then:1");
+shouldBe(resolvedWith, 10);
diff --git a/JSTests/stress/class-fields-anonymous-function-name-edge-cases.js b/JSTests/stress/class-fields-anonymous-function-name-edge-cases.js
new file mode 100644
index 000000000000..95dc83800b4a
--- /dev/null
+++ b/JSTests/stress/class-fields-anonymous-function-name-edge-cases.js
@@ -0,0 +1,158 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual + ' expected: ' + expected);
+}
+
+// The inferred class name is visible while static members evaluate, matching
+// NamedEvaluation order: SetFunctionName happens before static fields run.
+{
+ class O { h = class { static x = this.name; }; }
+ shouldBe(new O().h.x, 'h');
+ shouldBe(new O().h.name, 'h');
+}
+
+// A static "name" field overrides the inferred name, but static fields that
+// run before it still observe the inferred name.
+{
+ class O { n = class { static x = this.name; static name = 42; }; }
+ let n = new O().n;
+ shouldBe(n.x, 'n');
+ shouldBe(n.name, 42);
+}
+
+// Many class-valued fields in one class: each constructor must get its own
+// field name (stresses the lifetime of identifiers captured by the parser).
+{
+ class O {
+ alpha = class {};
+ beta = class {};
+ gamma = class {};
+ delta = class {};
+ epsilon = class {};
+ }
+ let o = new O();
+ shouldBe(o.alpha.name, 'alpha');
+ shouldBe(o.beta.name, 'beta');
+ shouldBe(o.gamma.name, 'gamma');
+ shouldBe(o.delta.name, 'delta');
+ shouldBe(o.epsilon.name, 'epsilon');
+}
+
+// "__proto__" as a field name is a regular field, and the function gets it as
+// a name.
+{
+ class O { "__proto__" = () => {}; }
+ let desc = Object.getOwnPropertyDescriptor(new O(), '__proto__');
+ shouldBe(desc.value.name, '__proto__');
+}
+
+// Parenthesized anonymous functions are still anonymous function definitions;
+// comma, ternary, and eval results are not.
+{
+ class O {
+ p = (() => {});
+ c = (0, () => {});
+ t = true ? () => {} : 0;
+ e = eval('() => {}');
+ }
+ let o = new O();
+ shouldBe(o.p.name, 'p');
+ shouldBe(o.c.name, '');
+ shouldBe(o.t.name, '');
+ shouldBe(o.e.name, '');
+}
+
+// All anonymous function flavors are inferred.
+{
+ class O {
+ a = async () => {};
+ g = function* () {};
+ ag = async function* () {};
+ af = async function () {};
+ }
+ let o = new O();
+ shouldBe(o.a.name, 'a');
+ shouldBe(o.g.name, 'g');
+ shouldBe(o.ag.name, 'ag');
+ shouldBe(o.af.name, 'af');
+}
+
+// Nested classes with their own function-valued fields.
+{
+ class O {
+ inner = class {
+ leaf = () => 1;
+ deep = class {};
+ };
+ }
+ let innerInstance = new (new O().inner)();
+ shouldBe(new O().inner.name, 'inner');
+ shouldBe(innerInstance.leaf.name, 'leaf');
+ shouldBe(innerInstance.deep.name, 'deep');
+}
+
+// Static and computed fields mix in the same class; computed names keep the
+// runtime SetFunctionName path.
+{
+ function make(key) {
+ class D {
+ fixed = () => 1;
+ [key] = () => 2;
+ other = class {};
+ }
+ return new D();
+ }
+ let d1 = make('k1');
+ let d2 = make('k2');
+ shouldBe(d1.fixed.name, 'fixed');
+ shouldBe(d1.k1.name, 'k1');
+ shouldBe(d1.other.name, 'other');
+ shouldBe(d2.k2.name, 'k2');
+}
+
+// Names stay correct as the field initializer tiers up.
+{
+ class O { f = () => 1; k = class {}; }
+ for (let i = 0; i < 1e5; ++i) {
+ let o = new O();
+ if (o.f.name !== 'f' || o.k.name !== 'k')
+ throw new Error('bad name at iteration ' + i);
+ }
+}
+
+// Repeatedly evaluating the class definition itself.
+{
+ function makeClass() {
+ return class { f = () => 1; k = class {}; };
+ }
+ for (let i = 0; i < 1e3; ++i) {
+ let K = makeClass();
+ let o = new K();
+ shouldBe(o.f.name, 'f');
+ shouldBe(o.k.name, 'k');
+ }
+}
+
+// toString still returns the original source text.
+{
+ class O { f = () => 1; }
+ shouldBe(new O().f.toString(), '() => 1');
+}
+
+// A Proxy observing the field definition sees the already-named function.
+{
+ let observedName;
+ class Base {
+ constructor() {
+ return new Proxy({}, {
+ defineProperty(target, key, desc) {
+ observedName = desc.value.name;
+ return Reflect.defineProperty(target, key, desc);
+ }
+ });
+ }
+ }
+ class O extends Base { f = () => 1; }
+ new O();
+ shouldBe(observedName, 'f');
+}
diff --git a/JSTests/stress/class-fields-anonymous-function-name-inference.js b/JSTests/stress/class-fields-anonymous-function-name-inference.js
new file mode 100644
index 000000000000..7efeb1600087
--- /dev/null
+++ b/JSTests/stress/class-fields-anonymous-function-name-inference.js
@@ -0,0 +1,130 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual + ' expected: ' + expected);
+}
+
+// Anonymous functions and classes in non-computed class field initializers
+// get their names inferred statically (no SetFunctionName at runtime).
+class C {
+ f = () => 1;
+ g = function () { return 2; };
+ h = class {};
+ #p = () => 3;
+ static s = () => 4;
+ "str x" = () => 5;
+ 0 = () => 6;
+ n = class { static name() { return 7; } };
+ getP() { return this.#p; }
+}
+
+for (var i = 0; i < 1e4; ++i) {
+ var c = new C();
+ shouldBe(c.f.name, 'f');
+ shouldBe(c.g.name, 'g');
+ shouldBe(c.h.name, 'h');
+ shouldBe(c.getP().name, '#p');
+ shouldBe(c['str x'].name, 'str x');
+ shouldBe(c[0].name, '0');
+}
+shouldBe(C.s.name, 's');
+
+// A static "name" method shadows the inferred class name.
+shouldBe(typeof new C().n.name, 'function');
+shouldBe(new C().n.name(), 7);
+
+// Property descriptor must match runtime SetFunctionName semantics.
+{
+ let c = new C();
+ let desc = Object.getOwnPropertyDescriptor(c.f, 'name');
+ shouldBe(desc.value, 'f');
+ shouldBe(desc.writable, false);
+ shouldBe(desc.enumerable, false);
+ shouldBe(desc.configurable, true);
+}
+
+// Own property names and their order.
+{
+ let c = new C();
+ shouldBe(Object.getOwnPropertyNames(c.f).join(','), 'length,name');
+ shouldBe(Object.keys(c.f).length, 0);
+ shouldBe(c.f.hasOwnProperty('name'), true);
+}
+
+// delete fn.name falls through to Function.prototype.name.
+{
+ let c = new C();
+ shouldBe(delete c.f.name, true);
+ shouldBe(c.f.name, '');
+ shouldBe(c.f.hasOwnProperty('name'), false);
+}
+
+// Writing is not allowed (ReadOnly), but defineProperty works (configurable).
+{
+ let c = new C();
+ let threw = false;
+ try {
+ (function () { 'use strict'; c.f.name = 'changed'; })();
+ } catch (e) {
+ threw = e instanceof TypeError;
+ }
+ shouldBe(threw, true);
+ shouldBe(c.f.name, 'f');
+ Object.defineProperty(c.f, 'name', { value: 'redefined' });
+ shouldBe(c.f.name, 'redefined');
+}
+
+// Instances are independent: reifying one must not affect another.
+{
+ let a = new C();
+ shouldBe(a.f.name, 'f');
+ let b = new C();
+ Object.defineProperty(a.f, 'name', { value: 'mutated' });
+ shouldBe(b.f.name, 'f');
+ shouldBe(a.f.name, 'mutated');
+}
+
+// length is still derived from the function itself, not the field.
+{
+ let c = new C();
+ shouldBe(c.g.length, 0);
+ shouldBe(c.f.length, 0);
+}
+
+// bind reads the inferred name.
+{
+ let c = new C();
+ shouldBe(c.f.bind(null).name, 'bound f');
+}
+
+// Error.stack shows the inferred name.
+{
+ class S { e = () => new Error(); }
+ let stack = new S().e().stack;
+ shouldBe(stack.split('\n')[0].split('@')[0], 'e');
+}
+
+// Computed field names still go through runtime SetFunctionName.
+{
+ function make(key) {
+ class D { [key] = () => 1; }
+ return new D();
+ }
+ shouldBe(make('alpha').alpha.name, 'alpha');
+ shouldBe(make('beta').beta.name, 'beta');
+ let sym = Symbol('x');
+ shouldBe(make(sym)[sym].name, '[x]');
+}
+
+// Named function expressions in field initializers keep their own name.
+{
+ class E { m = function inner() {}; }
+ shouldBe(new E().m.name, 'inner');
+}
+
+// Fields whose initializer is not a function are unaffected.
+{
+ class F { v = 42; w = 'hi'; }
+ let f = new F();
+ shouldBe(f.v, 42);
+ shouldBe(f.w, 'hi');
+}
diff --git a/JSTests/stress/intl-canonical-iana-time-zone.js b/JSTests/stress/intl-canonical-iana-time-zone.js
index 5591278f7a9a..2b7e92bc347e 100644
--- a/JSTests/stress/intl-canonical-iana-time-zone.js
+++ b/JSTests/stress/intl-canonical-iana-time-zone.js
@@ -90,21 +90,20 @@ for (const tz of legacy) {
// Temporal.ZonedDateTime uses the same hashmap-backed TimeZoneID lookup as Intl, so
// legacy aliases must also be accepted there. ZonedDateTime.prototype.equals() uses
// TimeZoneEquals internally, which recognises legacy ↔ primary as the same timezone.
-// FIXME: ZonedDateTime — test via Temporal.ZonedDateTime once implemented.
-// if (typeof Temporal !== "undefined") {
-// const pairs = [
-// ["Asia/Calcutta", "Asia/Kolkata"],
-// ["America/Buenos_Aires", "America/Argentina/Buenos_Aires"],
-// ["Europe/Kiev", "Europe/Kyiv"],
-// ["Asia/Katmandu", "Asia/Kathmandu"],
-// ["US/Pacific", "America/Los_Angeles"],
-// ["GB", "Europe/London"],
-// ["Brazil/East", "America/Sao_Paulo"],
-// ["UCT", "UTC"],
-// ["Etc/UTC", "UTC"],
-// ["Zulu", "UTC"],
-// ];
-// for (const [legacy, primary] of pairs) {
-// shouldBeTrue(new Temporal.ZonedDateTime(0n, legacy).equals(new Temporal.ZonedDateTime(0n, primary)));
-// }
-// }
+if (typeof Temporal !== "undefined") {
+ const pairs = [
+ ["Asia/Calcutta", "Asia/Kolkata"],
+ ["America/Buenos_Aires", "America/Argentina/Buenos_Aires"],
+ ["Europe/Kiev", "Europe/Kyiv"],
+ ["Asia/Katmandu", "Asia/Kathmandu"],
+ ["US/Pacific", "America/Los_Angeles"],
+ ["GB", "Europe/London"],
+ ["Brazil/East", "America/Sao_Paulo"],
+ ["UCT", "UTC"],
+ ["Etc/UTC", "UTC"],
+ ["Zulu", "UTC"],
+ ];
+ for (const [legacy, primary] of pairs) {
+ shouldBeTrue(new Temporal.ZonedDateTime(0n, legacy).equals(new Temporal.ZonedDateTime(0n, primary)));
+ }
+}
diff --git a/JSTests/stress/intl-datetimeformat-temporal-era-width.js b/JSTests/stress/intl-datetimeformat-temporal-era-width.js
new file mode 100644
index 000000000000..68dcbc33eff1
--- /dev/null
+++ b/JSTests/stress/intl-datetimeformat-temporal-era-width.js
@@ -0,0 +1,49 @@
+//@ requireOptions("--useTemporal=1")
+// Formatting a Temporal value must preserve the requested era width
+// (era: "long" / "short" / "narrow"), matching an equivalent legacy Date.
+// GetDateTimeFormat used to copy only a single 'G' into the format options,
+// collapsing era:"long" (GGGG) and era:"narrow" (GGGGG) to the short form.
+
+function shouldBe(actual, expected, msg) {
+ if (actual !== expected)
+ throw new Error(`${msg}: expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);
+}
+
+const plainDate = new Temporal.PlainDate(2026, 6, 2);
+const plainDateTime = new Temporal.PlainDateTime(2026, 6, 2, 10, 30, 45);
+const date = new Date(Date.UTC(2026, 5, 2, 10, 30, 45));
+
+const optionsList = [
+ { era: "long", year: "numeric", month: "long", day: "numeric" },
+ { era: "short", year: "numeric", month: "long", day: "numeric" },
+ { era: "narrow", year: "numeric", month: "long", day: "numeric" },
+ { era: "long", year: "numeric", month: "numeric", day: "numeric" },
+];
+
+const locales = ["ja", "zh", "ko", "en-US", "de"];
+
+for (const locale of locales) {
+ for (const options of optionsList) {
+ const dtf = new Intl.DateTimeFormat(locale, { ...options, timeZone: "UTC" });
+ const label = `${locale} ${JSON.stringify(options)}`;
+
+ // All requested fields are date fields, so PlainDate and PlainDateTime
+ // must format exactly like a legacy Date at the same calendar date.
+ shouldBe(dtf.format(plainDate), dtf.format(date), `${label} format(PlainDate)`);
+ shouldBe(dtf.format(plainDateTime), dtf.format(date), `${label} format(PlainDateTime)`);
+
+ const dateParts = JSON.stringify(dtf.formatToParts(date));
+ shouldBe(JSON.stringify(dtf.formatToParts(plainDate)), dateParts, `${label} formatToParts(PlainDate)`);
+
+ const laterPlainDate = new Temporal.PlainDate(2026, 7, 4);
+ const laterDate = new Date(Date.UTC(2026, 6, 4));
+ shouldBe(dtf.formatRange(plainDate, laterPlainDate), dtf.formatRange(date, laterDate), `${label} formatRange(PlainDate)`);
+ }
+}
+
+// era: "long" must not degrade to the short form ("AD") in ko.
+{
+ const dtf = new Intl.DateTimeFormat("ko", { era: "long", year: "numeric", month: "long", day: "numeric", timeZone: "UTC" });
+ const formatted = dtf.format(plainDate);
+ shouldBe(formatted.includes("서기"), true, `ko era long PlainDate contains 서기: ${formatted}`);
+}
diff --git a/JSTests/stress/intl-datetimeformat-temporal-non-default-calendar.js b/JSTests/stress/intl-datetimeformat-temporal-non-default-calendar.js
new file mode 100644
index 000000000000..f9cba9ae1f1c
--- /dev/null
+++ b/JSTests/stress/intl-datetimeformat-temporal-non-default-calendar.js
@@ -0,0 +1,105 @@
+//@ requireOptions("--useTemporal=1")
+// Temporal formatter pattern generation must request the calendar from ICU in BCP47
+// form via the -u-ca- extension (e.g. "ar-SA-u-ca-gregory"). Passing a BCP47 calendar
+// value through ICU's legacy @calendar= keyword (e.g. "ar-SA@calendar=gregory") is
+// ignored by ICU when the BCP47 name differs from the legacy name ("gregory" vs
+// "gregorian", "ethioaa" vs "ethiopic-amete-alem"), so pattern selection silently
+// falls back to the locale's default calendar. For ar-SA (default: islamic-umalqura)
+// that injects a spurious era field and long month names into gregorian output.
+
+function shouldBe(actual, expected, msg) {
+ if (actual !== expected)
+ throw new Error(`${msg}: expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);
+}
+
+function partTypes(parts) {
+ return parts.map((part) => part.type).join(",");
+}
+
+function hasEra(parts) {
+ return parts.some((part) => part.type === "era");
+}
+
+const plainDate = new Temporal.PlainDate(2024, 1, 15);
+const plainDateTime = new Temporal.PlainDateTime(2024, 1, 15, 10, 30, 45);
+const instant = new Temporal.Instant(BigInt(Date.UTC(2024, 0, 15, 10, 30, 45)) * 1_000_000n);
+const date = new Date(Date.UTC(2024, 0, 15));
+const dateTime = new Date(Date.UTC(2024, 0, 15, 10, 30, 45));
+
+// 1. GetDateTimeFormat path with default fields.
+// ar-SA's default calendar is islamic-umalqura. With ca=gregory, Temporal values
+// must be formatted with the gregorian pattern — identical to Date formatting.
+{
+ const fmt = new Intl.DateTimeFormat("ar-SA-u-ca-gregory", { timeZone: "UTC" });
+ shouldBe(fmt.resolvedOptions().calendar, "gregory", "resolved calendar");
+
+ shouldBe(hasEra(fmt.formatToParts(plainDate)), false,
+ "ar-SA-u-ca-gregory default fields: PlainDate must not include era");
+ shouldBe(partTypes(fmt.formatToParts(plainDate)), partTypes(fmt.formatToParts(date)),
+ "ar-SA-u-ca-gregory default fields: PlainDate parts vs Date parts");
+ shouldBe(fmt.format(plainDate), fmt.format(date),
+ "ar-SA-u-ca-gregory default fields: PlainDate vs Date");
+}
+
+// 2. GetDateTimeFormat path with explicit fields.
+{
+ const options = { year: "numeric", month: "numeric", day: "numeric", timeZone: "UTC" };
+ const fmt = new Intl.DateTimeFormat("ar-SA-u-ca-gregory", options);
+
+ shouldBe(hasEra(fmt.formatToParts(plainDate)), false,
+ "ar-SA-u-ca-gregory explicit fields: PlainDate must not include era");
+ shouldBe(partTypes(fmt.formatToParts(plainDate)), partTypes(fmt.formatToParts(date)),
+ "ar-SA-u-ca-gregory explicit fields: PlainDate parts vs Date parts");
+ shouldBe(fmt.format(plainDate), fmt.format(date),
+ "ar-SA-u-ca-gregory explicit fields: PlainDate vs Date");
+}
+
+// 3. GetDateTimeFormat path for other Temporal kinds.
+{
+ const fmt = new Intl.DateTimeFormat("ar-SA-u-ca-gregory", {
+ year: "numeric", month: "numeric", day: "numeric",
+ hour: "numeric", minute: "2-digit", second: "2-digit",
+ timeZone: "UTC",
+ });
+
+ shouldBe(hasEra(fmt.formatToParts(plainDateTime)), false,
+ "ar-SA-u-ca-gregory: PlainDateTime must not include era");
+ shouldBe(fmt.format(plainDateTime), fmt.format(dateTime),
+ "ar-SA-u-ca-gregory: PlainDateTime vs Date");
+
+ shouldBe(hasEra(fmt.formatToParts(instant)), false,
+ "ar-SA-u-ca-gregory: Instant must not include era");
+ shouldBe(fmt.format(instant), fmt.format(dateTime),
+ "ar-SA-u-ca-gregory: Instant vs Date");
+}
+
+// 4. AdjustDateTimeStyleFormat path: dateStyle+timeStyle formatting a PlainDate has
+// conflicting (time) fields, forcing a date-only pattern to be regenerated.
+{
+ for (const dateStyle of ["full", "long", "medium", "short"]) {
+ const fmt = new Intl.DateTimeFormat("ar-SA-u-ca-gregory",
+ { dateStyle, timeStyle: "short", timeZone: "UTC" });
+ shouldBe(hasEra(fmt.formatToParts(plainDate)), false,
+ `ar-SA-u-ca-gregory dateStyle=${dateStyle}+timeStyle: PlainDate must not include era`);
+ }
+}
+
+// 5. ethioaa is the other calendar whose BCP47 name differs from the ICU legacy name
+// ("ethiopic-amete-alem"). Temporal and Date formatting must agree.
+{
+ const fmt = new Intl.DateTimeFormat("en-u-ca-ethioaa", { timeZone: "UTC" });
+ shouldBe(fmt.resolvedOptions().calendar, "ethioaa", "resolved calendar");
+ shouldBe(partTypes(fmt.formatToParts(plainDate)), partTypes(fmt.formatToParts(date)),
+ "en-u-ca-ethioaa: PlainDate parts vs Date parts");
+ shouldBe(fmt.format(plainDate), fmt.format(date),
+ "en-u-ca-ethioaa: PlainDate vs Date");
+}
+
+// 6. Calendars whose BCP47 and ICU legacy names coincide must keep working.
+{
+ const fmt = new Intl.DateTimeFormat("en-u-ca-islamic-umalqura", { timeZone: "UTC" });
+ shouldBe(fmt.format(plainDate), fmt.format(date),
+ "en-u-ca-islamic-umalqura: PlainDate vs Date");
+ shouldBe(hasEra(fmt.formatToParts(plainDate)), true,
+ "en-u-ca-islamic-umalqura: PlainDate must include era");
+}
diff --git a/JSTests/stress/iterator-close-map-set-fast-path-mid-iteration-return.js b/JSTests/stress/iterator-close-map-set-fast-path-mid-iteration-return.js
new file mode 100644
index 000000000000..e6643ba2119a
--- /dev/null
+++ b/JSTests/stress/iterator-close-map-set-fast-path-mid-iteration-return.js
@@ -0,0 +1,184 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`bad value: ${String(actual)}, expected: ${String(expected)}`);
+}
+
+const error = new Error("boom");
+const mapIteratorPrototype = Object.getPrototypeOf(new Map()[Symbol.iterator]());
+const setIteratorPrototype = Object.getPrototypeOf(new Set()[Symbol.iterator]());
+
+// Iterator.prototype.forEach over a Map iterator: "return" installed on the iterator
+// mid-iteration must be invoked by IteratorClose when the callback throws.
+{
+ let returnCalled = 0;
+ let resumeValue = null;
+ const map = new Map([[1, 1], [2, 2], [3, 3]]);
+ const iterator = map.keys();
+ let caught = null;
+ try {
+ iterator.forEach(function (value) {
+ if (value === 2) {
+ iterator.return = function () {
+ returnCalled++;
+ resumeValue = this.next().value;
+ return { done: true };
+ };
+ throw error;
+ }
+ });
+ } catch (e) {
+ caught = e;
+ }
+ shouldBe(caught, error);
+ shouldBe(returnCalled, 1);
+ shouldBe(resumeValue, 3);
+}
+
+// Same for a Set iterator.
+{
+ let returnCalled = 0;
+ let resumeValue = null;
+ const set = new Set([1, 2, 3]);
+ const iterator = set.values();
+ let caught = null;
+ try {
+ iterator.forEach(function (value) {
+ if (value === 2) {
+ iterator.return = function () {
+ returnCalled++;
+ resumeValue = this.next().value;
+ return { done: true };
+ };
+ throw error;
+ }
+ });
+ } catch (e) {
+ caught = e;
+ }
+ shouldBe(caught, error);
+ shouldBe(returnCalled, 1);
+ shouldBe(resumeValue, 3);
+}
+
+// "return" installed on %MapIteratorPrototype% mid-iteration.
+{
+ let returnCalled = 0;
+ const map = new Map([[1, 10], [2, 20], [3, 30]]);
+ const iterator = map.entries();
+ let caught = null;
+ try {
+ iterator.forEach(function (entry) {
+ if (entry[0] === 2) {
+ mapIteratorPrototype.return = function () {
+ returnCalled++;
+ return { done: true };
+ };
+ throw error;
+ }
+ });
+ } catch (e) {
+ caught = e;
+ }
+ delete mapIteratorPrototype.return;
+ shouldBe(caught, error);
+ shouldBe(returnCalled, 1);
+}
+
+// "return" installed on %SetIteratorPrototype% mid-iteration.
+{
+ let returnCalled = 0;
+ const set = new Set([1, 2, 3]);
+ const iterator = set.keys();
+ let caught = null;
+ try {
+ iterator.forEach(function (value) {
+ if (value === 2) {
+ setIteratorPrototype.return = function () {
+ returnCalled++;
+ return { done: true };
+ };
+ throw error;
+ }
+ });
+ } catch (e) {
+ caught = e;
+ }
+ delete setIteratorPrototype.return;
+ shouldBe(caught, error);
+ shouldBe(returnCalled, 1);
+}
+
+// new Map(map) storage fast path: Map.prototype.set throwing mid-iteration must
+// trigger IteratorClose, invoking a "return" installed on %MapIteratorPrototype%,
+// with the iterator positioned where iteration stopped.
+{
+ let returnCalled = 0;
+ let resumeKey = null;
+ const map = new Map([[1, 10], [2, 20], [3, 30]]);
+ const originalSet = Map.prototype.set;
+ Map.prototype.set = function (key, value) {
+ if (key === 2) {
+ mapIteratorPrototype.return = function () {
+ returnCalled++;
+ resumeKey = this.next().value[0];
+ return { done: true };
+ };
+ throw error;
+ }
+ return originalSet.call(this, key, value);
+ };
+ let caught = null;
+ try {
+ new Map(map);
+ } catch (e) {
+ caught = e;
+ }
+ Map.prototype.set = originalSet;
+ delete mapIteratorPrototype.return;
+ shouldBe(caught, error);
+ shouldBe(returnCalled, 1);
+ shouldBe(resumeKey, 3);
+}
+
+// new Set(set) storage fast path: same via Set.prototype.add.
+{
+ let returnCalled = 0;
+ let resumeValue = null;
+ const set = new Set([1, 2, 3]);
+ const originalAdd = Set.prototype.add;
+ Set.prototype.add = function (value) {
+ if (value === 2) {
+ setIteratorPrototype.return = function () {
+ returnCalled++;
+ resumeValue = this.next().value;
+ return { done: true };
+ };
+ throw error;
+ }
+ return originalAdd.call(this, value);
+ };
+ let caught = null;
+ try {
+ new Set(set);
+ } catch (e) {
+ caught = e;
+ }
+ Set.prototype.add = originalAdd;
+ delete setIteratorPrototype.return;
+ shouldBe(caught, error);
+ shouldBe(returnCalled, 1);
+ shouldBe(resumeValue, 3);
+}
+
+// Normal exhaustion must not invoke "return".
+{
+ let returnCalled = 0;
+ const map = new Map([[1, 1], [2, 2]]);
+ const iterator = map.keys();
+ iterator.return = function () {
+ returnCalled++;
+ return { done: true };
+ };
+ iterator.forEach(function () { });
+ shouldBe(returnCalled, 0);
+}
diff --git a/JSTests/stress/multi-get-by-val-mixed-dfg.js b/JSTests/stress/multi-get-by-val-mixed-dfg.js
new file mode 100644
index 000000000000..4aaf2ba79fdb
--- /dev/null
+++ b/JSTests/stress/multi-get-by-val-mixed-dfg.js
@@ -0,0 +1,32 @@
+//@ runDefault("--useFTLJIT=0")
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual + ' (expected ' + expected + ')');
+}
+noInline(shouldBe);
+
+function test(array) {
+ return array[0] * array[0];
+}
+noInline(test);
+
+let jsInt32 = [42];
+let jsDouble = [5.5];
+let jsContiguous = ["x", 7];
+let f32array = new Float32Array([42]);
+let f64array = new Float64Array([5.5]);
+let i32array = new Int32Array([42]);
+let u8array = new Uint8Array([42]);
+let u8clamped = new Uint8ClampedArray([42]);
+
+for (let i = 0; i < testLoopCount; ++i) {
+ shouldBe(test(jsInt32), 42 * 42);
+ shouldBe(test(jsDouble), 5.5 * 5.5);
+ shouldBe(Number.isNaN(test(jsContiguous)), true);
+ shouldBe(test(f32array), 42 * 42);
+ shouldBe(test(f64array), 5.5 * 5.5);
+ shouldBe(test(i32array), 42 * 42);
+ shouldBe(test(u8array), 42 * 42);
+ shouldBe(test(u8clamped), 42 * 42);
+}
diff --git a/JSTests/stress/multi-get-by-val-mixed-oob-dfg.js b/JSTests/stress/multi-get-by-val-mixed-oob-dfg.js
new file mode 100644
index 000000000000..e55d7186184e
--- /dev/null
+++ b/JSTests/stress/multi-get-by-val-mixed-oob-dfg.js
@@ -0,0 +1,43 @@
+//@ runDefault("--useFTLJIT=0")
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual + ' (expected ' + expected + ')');
+}
+noInline(shouldBe);
+
+function test(array, index) {
+ return array[index];
+}
+noInline(test);
+
+let jsInt32 = [42];
+let jsDouble = [5.5];
+let jsContiguous = ["x"];
+let f32array = new Float32Array([42]);
+let f64array = new Float64Array([5.5]);
+let i32array = new Int32Array([42]);
+let u8array = new Uint8Array([42]);
+
+for (let i = 0; i < testLoopCount; ++i) {
+ shouldBe(test(jsInt32, 0), 42);
+ shouldBe(test(jsDouble, 0), 5.5);
+ shouldBe(test(jsContiguous, 0), "x");
+ shouldBe(test(f32array, 0), 42);
+ shouldBe(test(f64array, 0), 5.5);
+ shouldBe(test(i32array, 0), 42);
+ shouldBe(test(u8array, 0), 42);
+
+ shouldBe(test(jsInt32, 1), undefined);
+ shouldBe(test(jsDouble, 1), undefined);
+ shouldBe(test(jsContiguous, 1), undefined);
+ shouldBe(test(f32array, 1), undefined);
+ shouldBe(test(f64array, 1), undefined);
+ shouldBe(test(i32array, 1), undefined);
+ shouldBe(test(u8array, 1), undefined);
+
+ shouldBe(test(jsInt32, 100), undefined);
+ shouldBe(test(jsDouble, 100), undefined);
+ shouldBe(test(f32array, 100), undefined);
+ shouldBe(test(i32array, 100), undefined);
+}
diff --git a/JSTests/stress/multi-get-by-val-sane-chain-oob-dfg.js b/JSTests/stress/multi-get-by-val-sane-chain-oob-dfg.js
new file mode 100644
index 000000000000..d40189614483
--- /dev/null
+++ b/JSTests/stress/multi-get-by-val-sane-chain-oob-dfg.js
@@ -0,0 +1,37 @@
+//@ runDefault("--useFTLJIT=0")
+
+// Exercises the OutOfBoundsSaneChain path of MultiGetByVal in DFG.
+// Sane chain reads return undefined for OOB without going through the slow path,
+// after speculating the index is non-negative.
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual + ' (expected ' + expected + ')');
+}
+noInline(shouldBe);
+
+function test(array, index) {
+ return array[index];
+}
+noInline(test);
+
+let jsInt32 = [10, 20, 30];
+let jsDouble = [1.5, 2.5, 3.5];
+let jsContiguous = ["a", "b", "c"];
+
+for (let i = 0; i < testLoopCount; ++i) {
+ shouldBe(test(jsInt32, 0), 10);
+ shouldBe(test(jsInt32, 2), 30);
+ shouldBe(test(jsInt32, 3), undefined);
+ shouldBe(test(jsInt32, 100), undefined);
+
+ shouldBe(test(jsDouble, 0), 1.5);
+ shouldBe(test(jsDouble, 2), 3.5);
+ shouldBe(test(jsDouble, 3), undefined);
+ shouldBe(test(jsDouble, 100), undefined);
+
+ shouldBe(test(jsContiguous, 0), "a");
+ shouldBe(test(jsContiguous, 2), "c");
+ shouldBe(test(jsContiguous, 3), undefined);
+ shouldBe(test(jsContiguous, 100), undefined);
+}
diff --git a/JSTests/stress/multi-put-by-val-mixed-dfg.js b/JSTests/stress/multi-put-by-val-mixed-dfg.js
new file mode 100644
index 000000000000..05372f0f5f6c
--- /dev/null
+++ b/JSTests/stress/multi-put-by-val-mixed-dfg.js
@@ -0,0 +1,54 @@
+//@ runDefault("--useFTLJIT=0")
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual + ' (expected ' + expected + ')');
+}
+
+function shouldBeArray(actual, expected) {
+ shouldBe(actual.length, expected.length);
+ for (var i = 0; i < expected.length; ++i)
+ shouldBe(actual[i], expected[i]);
+}
+
+function test(array, index, value) {
+ array[index] = value;
+ return array;
+}
+noInline(test);
+
+for (let i = 0; i < testLoopCount; ++i) {
+ let int32 = [1, 2, 3, 4];
+ shouldBeArray(test(int32, 0, 42), [42, 2, 3, 4]);
+ shouldBeArray(test(int32, 3, 99), [42, 2, 3, 99]);
+
+ let double = [1.5, 2.5, 3.5];
+ shouldBeArray(test(double, 1, 7), [1.5, 7, 3.5]);
+
+ let contiguous = ["a", "b", "c"];
+ shouldBeArray(test(contiguous, 0, 11), [11, "b", "c"]);
+
+ let i32a = new Int32Array([0, 0, 0]);
+ test(i32a, 0, 42);
+ test(i32a, 2, -7);
+ shouldBeArray(i32a, [42, 0, -7]);
+
+ let u8a = new Uint8Array([0, 0, 0]);
+ test(u8a, 0, 200);
+ test(u8a, 1, 300); // wraps mod 256
+ shouldBeArray(u8a, [200, 44, 0]);
+
+ let u8c = new Uint8ClampedArray([0, 0, 0]);
+ test(u8c, 0, 300); // clamps to 255
+ test(u8c, 1, -5); // clamps to 0
+ test(u8c, 2, 100);
+ shouldBeArray(u8c, [255, 0, 100]);
+
+ let f32a = new Float32Array([0, 0, 0]);
+ test(f32a, 0, 7);
+ shouldBeArray(f32a, [7, 0, 0]);
+
+ let f64a = new Float64Array([0, 0, 0]);
+ test(f64a, 0, 13);
+ shouldBeArray(f64a, [13, 0, 0]);
+}
diff --git a/JSTests/stress/multi-put-by-val-oob-dfg.js b/JSTests/stress/multi-put-by-val-oob-dfg.js
new file mode 100644
index 000000000000..b5bb52e1e455
--- /dev/null
+++ b/JSTests/stress/multi-put-by-val-oob-dfg.js
@@ -0,0 +1,53 @@
+//@ runDefault("--useFTLJIT=0")
+
+// Exercises the OOB JSArray store path of MultiPutByVal in DFG, which extends
+// publicLength when the index is past it but still within vectorLength, and
+// falls into the operationPutByValBeyondArrayBounds slow path otherwise.
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual + ' (expected ' + expected + ')');
+}
+
+function shouldBeArray(actual, expected) {
+ shouldBe(actual.length, expected.length);
+ for (var i = 0; i < expected.length; ++i)
+ shouldBe(actual[i], expected[i]);
+}
+
+function test(array, index, value) {
+ array[index] = value;
+ return array;
+}
+noInline(test);
+
+// Warm up with both Int32 and Contiguous arrays plus OOB stores so the DFG
+// chooses MultiPutByVal with isOutOfBounds().
+let warmInt32 = [0, 0, 0];
+let warmContig = ["a", "b", "c"];
+for (let i = 0; i < testLoopCount; ++i) {
+ test(warmInt32, 0, 42);
+ test(warmContig, 0, "x");
+ test(warmInt32, 5, 7); // past publicLength -> exit site
+ test(warmContig, 5, "y");
+}
+
+for (let i = 0; i < testLoopCount; ++i) {
+ let arr = [1, 2, 3];
+ test(arr, 0, 11); // in bounds
+ shouldBeArray(arr, [11, 2, 3]);
+
+ test(arr, 3, 99); // grow by one (still likely in vectorLength)
+ shouldBeArray(arr, [11, 2, 3, 99]);
+
+ test(arr, 100, 7); // far OOB -> slow path
+ shouldBe(arr[100], 7);
+ shouldBe(arr.length, 101);
+
+ let cont = ["a", "b"];
+ test(cont, 1, "z");
+ shouldBe(cont[1], "z");
+ test(cont, 4, "q");
+ shouldBe(cont[4], "q");
+ shouldBe(cont.length, 5);
+}
diff --git a/JSTests/stress/promise-constructor-length.js b/JSTests/stress/promise-constructor-length.js
new file mode 100644
index 000000000000..b03061d9eb7a
--- /dev/null
+++ b/JSTests/stress/promise-constructor-length.js
@@ -0,0 +1,48 @@
+// The Promise constructor's "length" is no longer defined eagerly at creation time; it is
+// lazily reified from the builtin executable's parameter count. This locks down the
+// observable shape: value 1 with { writable: false, enumerable: false, configurable: true },
+// correct property ordering, and standard delete / redefine behavior.
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`FAIL: expected '${expected}' actual '${actual}'`);
+}
+
+shouldBe(Promise.length, 1);
+shouldBe(Object.prototype.hasOwnProperty.call(Promise, "length"), true);
+
+let desc = Object.getOwnPropertyDescriptor(Promise, "length");
+shouldBe(desc.value, 1);
+shouldBe(desc.writable, false);
+shouldBe(desc.enumerable, false);
+shouldBe(desc.configurable, true);
+
+// "length" and "name" must come first in own property order.
+let keys = Object.getOwnPropertyNames(Promise);
+shouldBe(keys[0], "length");
+shouldBe(keys[1], "name");
+shouldBe(Object.keys(Promise).length, 0);
+
+// writable: false. Strict mode write throws; the value is unchanged.
+shouldBe((() => {
+ "use strict";
+ try {
+ Promise.length = 42;
+ return "no throw";
+ } catch (error) {
+ return String(error.constructor === TypeError);
+ }
+})(), "true");
+shouldBe(Promise.length, 1);
+
+// Bound function lengths derive from the original length.
+shouldBe(Promise.bind(null).length, 1);
+shouldBe(Promise.bind(null, function() {}).length, 0);
+
+// configurable: true. delete and redefine work.
+shouldBe(delete Promise.length, true);
+shouldBe(Object.prototype.hasOwnProperty.call(Promise, "length"), false);
+shouldBe(Promise.length, 0); // Inherited from Function.prototype.length.
+
+Object.defineProperty(Promise, "length", { value: 7, writable: false, enumerable: false, configurable: true });
+shouldBe(Promise.length, 7);
diff --git a/JSTests/stress/promise-resolve-non-thenable-structure-cache-cross-realm-proto.js b/JSTests/stress/promise-resolve-non-thenable-structure-cache-cross-realm-proto.js
new file mode 100644
index 000000000000..4ed6eb7c861f
--- /dev/null
+++ b/JSTests/stress/promise-resolve-non-thenable-structure-cache-cross-realm-proto.js
@@ -0,0 +1,84 @@
+// The isDefinitelyNonThenable Structure cache may only cache NonThenable when the
+// prototype is the Object.prototype of the *structure's* realm: a cached `true` is
+// guarded by structure->realm()'s promiseThenWatchpointSet, which only watches that
+// realm's Object.prototype. This test mixes a structure from one realm with the
+// Object.prototype of another realm and verifies that adding `then` to the foreign
+// prototype is always observed.
+
+function shouldBe(actual, expected, msg) {
+ if (actual !== expected)
+ throw new Error(`bad value (${msg ?? ""}): got ${String(actual)}, expected ${String(expected)}`);
+}
+
+function asyncTest(fn) {
+ let done = false;
+ let error;
+ fn().then(() => { done = true; }, (e) => { error = e; done = true; });
+ drainMicrotasks();
+ if (!done)
+ throw new Error("async test did not settle: " + fn.name);
+ if (error)
+ throw error;
+}
+
+const other = createGlobalObject();
+
+// --- 1. Structure from this realm, prototype from the other realm. The other
+// realm warms the cache; poisoning the other realm's Object.prototype
+// must still be observed when resolving from this realm. ---
+asyncTest(async function localStructureForeignProto() {
+ const otherProto = other.Object.prototype;
+ function make() {
+ const o = { a: 1 };
+ Object.setPrototypeOf(o, otherProto);
+ return o;
+ }
+
+ // Warm up from the other realm: must not cache NonThenable on this realm's
+ // structure, since this realm's watchpoint set does not watch otherProto.
+ for (let i = 0; i < 100; i++)
+ other.Promise.resolve(make());
+ other.drainMicrotasks();
+
+ // Fires only the other realm's watchpoint.
+ other.Function(`
+ Object.prototype.then = function(resolve) { resolve("foreign-proto-poisoned"); };
+ `)();
+ try {
+ const v = await Promise.resolve(make());
+ shouldBe(v, "foreign-proto-poisoned", "localStructureForeignProto");
+ } finally {
+ other.Function("delete Object.prototype.then;")();
+ }
+});
+
+// --- 2. Mirror: structure from the other realm, prototype from this realm.
+// This realm warms the cache; poisoning this realm's Object.prototype
+// must still be observed when resolving from the other realm. ---
+asyncTest(async function foreignStructureLocalProto() {
+ const make = other.Function("proto", `
+ const o = { b: 2 };
+ Object.setPrototypeOf(o, proto);
+ return o;
+ `);
+
+ for (let i = 0; i < 100; i++)
+ Promise.resolve(make(Object.prototype));
+ drainMicrotasks();
+
+ const resolveInOther = other.Function("value", "onSettled", `
+ Promise.resolve(value).then(onSettled);
+ `);
+
+ // Fires only this realm's watchpoint.
+ Object.prototype.then = function(resolve) { resolve("local-proto-poisoned"); };
+ try {
+ let settled;
+ resolveInOther(make(Object.prototype), (v) => { settled = v; });
+ other.drainMicrotasks();
+ drainMicrotasks();
+ shouldBe(settled, "local-proto-poisoned", "foreignStructureLocalProto");
+ } finally {
+ delete Object.prototype.then;
+ }
+});
diff --git a/JSTests/stress/promise-resolving-functions-name-and-length.js b/JSTests/stress/promise-resolving-functions-name-and-length.js
new file mode 100644
index 000000000000..b16503729c68
--- /dev/null
+++ b/JSTests/stress/promise-resolving-functions-name-and-length.js
@@ -0,0 +1,96 @@
+// Regression test for https://bugs.webkit.org/show_bug.cgi?id=316443
+// NativeExecutable now holds name/length the same way FunctionExecutable does, and the
+// internal promise resolving / combinator / finally functions reify their "name" and "length"
+// from the NativeExecutable instead of receiving them eagerly at creation time. These functions
+// are spec-anonymous built-ins (CreateBuiltinFunction with name ""), so they must expose an
+// *own* "name" property whose value is "" (not undefined, not inherited from Function.prototype),
+// and an *own* "length" property. This locks that observable shape down.
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`FAIL: expected '${expected}' actual '${actual}'`);
+}
+
+function checkAnonymousBuiltin(label, f, expectedLength) {
+ shouldBe(typeof f, "function");
+
+ // "name" must be an own property (not merely inherited from Function.prototype.name === "").
+ shouldBe(Object.prototype.hasOwnProperty.call(f, "name"), true);
+ shouldBe(f.name, "");
+
+ let nameDesc = Object.getOwnPropertyDescriptor(f, "name");
+ shouldBe(nameDesc.value, "");
+ shouldBe(nameDesc.writable, false);
+ shouldBe(nameDesc.enumerable, false);
+ shouldBe(nameDesc.configurable, true);
+
+ // "length" must be an own property with the expected value.
+ shouldBe(Object.prototype.hasOwnProperty.call(f, "length"), true);
+ shouldBe(f.length, expectedLength);
+
+ let lengthDesc = Object.getOwnPropertyDescriptor(f, "length");
+ shouldBe(lengthDesc.value, expectedLength);
+ shouldBe(lengthDesc.writable, false);
+ shouldBe(lengthDesc.enumerable, false);
+ shouldBe(lengthDesc.configurable, true);
+
+ if (label) { /* keep label referenced for debugging */ }
+}
+
+// Resolve / reject functions handed to a Promise executor (CreateResolvingFunctions).
+{
+ let resolve, reject;
+ new Promise(function (res, rej) {
+ resolve = res;
+ reject = rej;
+ });
+ checkAnonymousBuiltin("resolve", resolve, 1);
+ checkAnonymousBuiltin("reject", reject, 1);
+}
+
+// Promise.withResolvers exposes the same pair of functions.
+{
+ let { resolve, reject } = Promise.withResolvers();
+ checkAnonymousBuiltin("withResolvers.resolve", resolve, 1);
+ checkAnonymousBuiltin("withResolvers.reject", reject, 1);
+}
+
+// Reading "name" first, then "length" (and vice versa) must both reify correctly and independently.
+{
+ let resolve, reject;
+ new Promise(function (res, rej) { resolve = res; reject = rej; });
+ // Force name reification before touching length.
+ shouldBe(resolve.name, "");
+ shouldBe(Object.prototype.hasOwnProperty.call(resolve, "length"), true);
+ shouldBe(resolve.length, 1);
+
+ // Force length reification before touching name on the other function.
+ shouldBe(reject.length, 1);
+ shouldBe(Object.prototype.hasOwnProperty.call(reject, "name"), true);
+ shouldBe(reject.name, "");
+}
+
+// The "name" property is configurable, so user code may redefine it; doing so must not
+// corrupt the originally-observed value on a freshly created pair.
+{
+ let resolve;
+ new Promise(function (res) { resolve = res; });
+ Object.defineProperty(resolve, "name", { value: "mutated", configurable: true });
+ shouldBe(resolve.name, "mutated");
+
+ let resolve2;
+ new Promise(function (res) { resolve2 = res; });
+ checkAnonymousBuiltin("resolve2", resolve2, 1);
+}
+
+// Run many times so the test covers all JIT tiers / repeated allocation paths.
+for (let i = 0; i < testLoopCount; ++i) {
+ let resolve, reject;
+ new Promise(function (res, rej) { resolve = res; reject = rej; });
+ shouldBe(resolve.name, "");
+ shouldBe(reject.name, "");
+ shouldBe(resolve.length, 1);
+ shouldBe(reject.length, 1);
+ shouldBe(Object.prototype.hasOwnProperty.call(resolve, "name"), true);
+ shouldBe(Object.prototype.hasOwnProperty.call(reject, "name"), true);
+}
diff --git a/JSTests/stress/regexp-greedy-varcount-reextend-after-content-backtrack.js b/JSTests/stress/regexp-greedy-varcount-reextend-after-content-backtrack.js
new file mode 100644
index 000000000000..3f8d191776bd
--- /dev/null
+++ b/JSTests/stress/regexp-greedy-varcount-reextend-after-content-backtrack.js
@@ -0,0 +1,56 @@
+// Regression test: after popping iterations below quantityMinCount and using
+// parenthesesDoBacktrack to find a new content distribution, the interpreter
+// must re-run the greedy extension loop up to quantityMaxCount. The new
+// distribution leaves the input at a different position, so the iteration
+// counts in (min, max] are an unexplored branch — silently skipping them led
+// to short matches and corrupted captures.
+//
+// All cases below force the YARR interpreter (lookbehind / backreference, or
+// --useRegExpJIT=0) and should match V8 behavior.
+
+function eq(actual, expected, label) {
+ if (actual === expected)
+ return;
+ if (Array.isArray(actual) && Array.isArray(expected) && actual.length === expected.length) {
+ let allEq = true;
+ for (let i = 0; i < actual.length; ++i) {
+ if (actual[i] !== expected[i]) {
+ allEq = false;
+ break;
+ }
+ }
+ if (allEq)
+ return;
+ }
+ throw new Error("FAIL " + label + ": got " + JSON.stringify(actual) + ", expected " + JSON.stringify(expected));
+}
+
+function check(re, input, expectedArr, expectedIndex, label) {
+ const m = re.exec(input);
+ if (expectedArr === null) {
+ if (m !== null)
+ throw new Error("FAIL " + label + ": got " + JSON.stringify(m) + ", expected null");
+ return;
+ }
+ if (m === null)
+ throw new Error("FAIL " + label + ": got null, expected " + JSON.stringify(expectedArr));
+ eq(Array.from(m), expectedArr, label + " (groups)");
+ eq(m.index, expectedIndex, label + " (index)");
+}
+
+// Greedy variable-count parens, anchored, multi-alt forces a content backtrack
+// that drops below min (=2). After refilling to min, the third iteration must
+// be re-attempted to satisfy `$` at end of input.
+check(/(?<=^)(a|aa|b){2,3}$/, "aaba", ["aaba", "a"], 0, "lookbehind aaba");
+
+// Same shape but with a backreference to force the interpreter, exercising the
+// capture restoration on backtrack as well.
+check(/((\w.)?\2+|c){2,}(.)+(..)/, "cbcbcbbb", ["cbcbcbbb", "c", undefined, "b", "bb"], 0, "backref cbcbcbbb");
+
+// A lookbehind variant that used to return null because the third iteration
+// was never attempted after the first iteration backtracked from "aa" to "a".
+check(/(?<=^)(aa|a){2,3}$/, "aaa", ["aaa", "a"], 0, "lookbehind aaa");
+
+// Min reachable only after redistributing earlier iterations across alts;
+// then max must still be reachable by extending the greedy tail.
+check(/(?<=^)(a|aa|b){2,4}b$/, "aaab", ["aaab", "a"], 0, "lookbehind reach max via extend");
diff --git a/JSTests/stress/regexp-prototype-symbol-match-edge-cases.js b/JSTests/stress/regexp-prototype-symbol-match-edge-cases.js
new file mode 100644
index 000000000000..9ad3ec6c1bf3
--- /dev/null
+++ b/JSTests/stress/regexp-prototype-symbol-match-edge-cases.js
@@ -0,0 +1,155 @@
+// Verifies spec edge cases for RegExp.prototype[@@match] now that it is
+// implemented in C++. Covers: ordering of ToString(string) vs flags lookup,
+// AdvanceStringIndex semantics, and behavior with custom exec returning
+// non-object/non-null values.
+
+function shouldBe(actual, expected) {
+ var a = JSON.stringify(actual);
+ var e = JSON.stringify(expected);
+ if (a !== e)
+ throw new Error("expected " + e + " but got " + a);
+}
+
+function shouldThrow(func, expected) {
+ var error;
+ try {
+ func();
+ } catch (e) {
+ error = e;
+ }
+ if (!error)
+ throw new Error("expected to throw");
+ if (String(error).indexOf(expected) === -1)
+ throw new Error("expected '" + expected + "' but got '" + error + "'");
+}
+
+// Spec step 3: ToString(string) happens BEFORE Get(rx, "flags").
+(function () {
+ var trace = [];
+ var re = /a/;
+ Object.defineProperty(re, "flags", {
+ configurable: true,
+ get() { trace.push("flags"); return ""; }
+ });
+ var arg = {
+ toString() { trace.push("toString"); return "aaa"; }
+ };
+ re[Symbol.match](arg);
+ shouldBe(trace, ["toString", "flags"]);
+})();
+
+// Spec step 6.b: For global regex, lastIndex is set to +0.
+(function () {
+ var re = /a/g;
+ re.lastIndex = 5;
+ re[Symbol.match]("aaa");
+ shouldBe(re.lastIndex, 0);
+})();
+
+// Spec 6.e.iii.3: Empty match advances via AdvanceStringIndex.
+(function () {
+ var re = /(?:)/g;
+ shouldBe(re[Symbol.match]("ab"), ["", "", ""]);
+})();
+(function () {
+ // With unicode flag, AdvanceStringIndex is surrogate-pair aware.
+ var re = /(?:)/gu;
+ shouldBe(re[Symbol.match]("😀x"), ["", "", ""]);
+})();
+(function () {
+ // With unicodeSets flag, AdvanceStringIndex is surrogate-pair aware.
+ var re = /(?:)/gv;
+ shouldBe(re[Symbol.match]("😀x"), ["", "", ""]);
+})();
+
+// Custom exec returning a non-object non-null value must throw.
+(function () {
+ var re = /a/g;
+ re.exec = function () { return 42; };
+ shouldThrow(function () { re[Symbol.match]("aaa"); }, "TypeError");
+})();
+
+// Custom exec returning an object with non-string [0] is coerced via ToString.
+(function () {
+ var re = /a/g;
+ var execCalls = 0;
+ re.exec = function () {
+ execCalls++;
+ if (execCalls === 1) return { 0: { toString() { return "x"; } } };
+ return null;
+ };
+ shouldBe(re[Symbol.match]("a"), ["x"]);
+})();
+
+// Custom exec returning an object with empty-string [0] must advance
+// lastIndex via AdvanceStringIndex.
+(function () {
+ var re = /(?:)/g;
+ var ranges = [];
+ re.exec = function (s) {
+ ranges.push(re.lastIndex);
+ if (re.lastIndex >= s.length) return null;
+ return { 0: "" };
+ };
+ shouldBe(re[Symbol.match]("abc"), ["", "", ""]);
+ shouldBe(ranges, [0, 1, 2, 3]);
+})();
+
+// Non-global path: result is whatever RegExpExec returns (can be an array
+// with named captures, etc).
+(function () {
+ var re = /(?a)/;
+ var r = re[Symbol.match]("abc");
+ shouldBe(r[0], "a");
+ shouldBe(r.index, 0);
+ shouldBe(r.groups.x, "a");
+})();
+
+// |this| not an object must throw a TypeError.
+(function () {
+ shouldThrow(function () { RegExp.prototype[Symbol.match].call(null, "x"); }, "TypeError");
+ shouldThrow(function () { RegExp.prototype[Symbol.match].call(undefined, "x"); }, "TypeError");
+ shouldThrow(function () { RegExp.prototype[Symbol.match].call(42, "x"); }, "TypeError");
+})();
+
+// |this| is an arbitrary object (not a RegExpObject): goes to slow path,
+// observes flags, exec, lastIndex via property access.
+(function () {
+ var trace = [];
+ var fakeRegExp = {
+ get flags() { trace.push("flags"); return ""; },
+ get lastIndex() { trace.push("lastIndex.get"); return 0; },
+ set lastIndex(v) { trace.push("lastIndex.set:" + v); },
+ exec(s) { trace.push("exec:" + s); return null; }
+ };
+ var r = RegExp.prototype[Symbol.match].call(fakeRegExp, "abc");
+ shouldBe(r, null);
+ // Non-global path: just calls exec once, no lastIndex writes.
+ shouldBe(trace, ["flags", "exec:abc"]);
+})();
+(function () {
+ var trace = [];
+ var matches = [{ 0: "a" }, { 0: "" }, null];
+ var fakeRegExp = {
+ get flags() { return "g"; },
+ get lastIndex() { trace.push("lastIndex.get"); return 1; },
+ set lastIndex(v) { trace.push("lastIndex.set:" + v); },
+ exec(s) {
+ trace.push("exec");
+ return matches.shift();
+ }
+ };
+ var r = RegExp.prototype[Symbol.match].call(fakeRegExp, "abc");
+ shouldBe(r, ["a", ""]);
+ // Global path: initial lastIndex set to 0, then exec, then for empty match
+ // do lastIndex.get -> AdvanceStringIndex -> lastIndex.set, then exec, then
+ // exec returns null and we exit.
+ shouldBe(trace, [
+ "lastIndex.set:0",
+ "exec",
+ "exec",
+ "lastIndex.get",
+ "lastIndex.set:2",
+ "exec"
+ ]);
+})();
diff --git a/JSTests/stress/regexp-prototype-symbol-match-watchpoint-invalidation.js b/JSTests/stress/regexp-prototype-symbol-match-watchpoint-invalidation.js
new file mode 100644
index 000000000000..0bd909d1c5ff
--- /dev/null
+++ b/JSTests/stress/regexp-prototype-symbol-match-watchpoint-invalidation.js
@@ -0,0 +1,195 @@
+// Verifies that direct calls to RegExp.prototype[@@match] honor dynamic mutations
+// after DFG/FTL has compiled the call. Each scenario warms the fast path with
+// testLoopCount iterations, then invalidates a watchpoint or per-instance
+// state and confirms the override is observed.
+
+function shouldBe(actual, expected) {
+ var a = JSON.stringify(actual);
+ var e = JSON.stringify(expected);
+ if (a !== e)
+ throw new Error("expected " + e + " but got " + a);
+}
+
+function shouldThrow(func, expected) {
+ var error;
+ try {
+ func();
+ } catch (e) {
+ error = e;
+ }
+ if (!error)
+ throw new Error("expected to throw");
+ if (String(error).indexOf(expected) === -1)
+ throw new Error("expected '" + expected + "' but got '" + error + "'");
+}
+
+// 1. Per-instance exec override after JIT compilation.
+// The DFG-emitted CheckIsConstant on regexp.exec must catch this and OSR-exit.
+(function () {
+ function match(re, s) { return re[Symbol.match](s); }
+ noInline(match);
+ var primordial = /[0-9]/g;
+ for (var i = 0; i < testLoopCount; ++i)
+ shouldBe(match(primordial, "a1b2c3"), ["1", "2", "3"]);
+ var custom = /[0-9]/g;
+ custom.exec = function () { return null; };
+ shouldBe(match(custom, "a1b2c3"), null);
+ shouldBe(match(primordial, "a1b2c3"), ["1", "2", "3"]);
+})();
+
+// 2. RegExp.prototype.exec replaced after JIT compilation.
+// The primordial properties watchpoint fires and the compiled code is invalidated.
+(function () {
+ function match(re, s) { return re[Symbol.match](s); }
+ noInline(match);
+ var re = /[0-9]/g;
+ for (var i = 0; i < testLoopCount; ++i)
+ shouldBe(match(re, "a1b2c3"), ["1", "2", "3"]);
+ var saved = RegExp.prototype.exec;
+ var execCount = 0;
+ RegExp.prototype.exec = function (s) {
+ execCount++;
+ return saved.call(this, s);
+ };
+ try {
+ shouldBe(match(re, "a1b2c3"), ["1", "2", "3"]);
+ if (execCount === 0)
+ throw new Error("custom RegExp.prototype.exec must be observed");
+ } finally {
+ RegExp.prototype.exec = saved;
+ }
+})();
+
+// 3. lastIndex set to a non-number after JIT compilation.
+// The fixup-time Check on lastIndex must OSR-exit and the C++
+// slow path must coerce it to a number per spec.
+(function () {
+ function match(re, s) { return re[Symbol.match](s); }
+ noInline(match);
+ var re = /[0-9]/g;
+ for (var i = 0; i < testLoopCount; ++i)
+ shouldBe(match(re, "a1b2c3"), ["1", "2", "3"]);
+ re.lastIndex = "garbage";
+ // For global regexps, [@@match] resets lastIndex to 0 first, so the result is
+ // unaffected by the prior value of lastIndex.
+ shouldBe(match(re, "a1b2c3"), ["1", "2", "3"]);
+})();
+
+// 4. lastIndex with side-effecting valueOf after JIT compilation.
+// For non-global regexps lastIndex is consulted by RegExpExec; the user's
+// valueOf must be observed.
+(function () {
+ function match(re, s) { return re[Symbol.match](s); }
+ noInline(match);
+ var re = /[0-9]/;
+ for (var i = 0; i < testLoopCount; ++i)
+ shouldBe(match(re, "a1b2c3"), ["1"]);
+ var valueOfCount = 0;
+ re.lastIndex = { valueOf() { valueOfCount++; return 0; } };
+ // exec is the primordial one and the regexp is non-global, so RegExpExec
+ // calls ToLength(lastIndex), which calls valueOf.
+ shouldBe(match(re, "a1b2c3"), ["1"]);
+ if (valueOfCount === 0)
+ throw new Error("lastIndex.valueOf was not observed");
+})();
+
+// 5. RegExp.prototype.global replaced after JIT compilation.
+(function () {
+ function match(re, s) { return re[Symbol.match](s); }
+ noInline(match);
+ var re = /[0-9]/g;
+ for (var i = 0; i < testLoopCount; ++i)
+ shouldBe(match(re, "a1b2c3"), ["1", "2", "3"]);
+ var savedDescriptor = Object.getOwnPropertyDescriptor(RegExp.prototype, "global");
+ Object.defineProperty(RegExp.prototype, "global", {
+ configurable: true,
+ get() { return false; }
+ });
+ try {
+ // With global=false, [@@match] takes the non-global path: a single RegExpExec.
+ shouldBe(match(re, "a1b2c3"), ["1"]);
+ } finally {
+ Object.defineProperty(RegExp.prototype, "global", savedDescriptor);
+ }
+})();
+
+// 6. RegExp.prototype.flags getter replaced after JIT compilation.
+// The watchpoint covers `flags` so the recompiled slow path observes the override.
+(function () {
+ function match(re, s) { return re[Symbol.match](s); }
+ noInline(match);
+ var re = /[0-9]/g;
+ for (var i = 0; i < testLoopCount; ++i)
+ shouldBe(match(re, "a1b2c3"), ["1", "2", "3"]);
+ var savedDescriptor = Object.getOwnPropertyDescriptor(RegExp.prototype, "flags");
+ Object.defineProperty(RegExp.prototype, "flags", {
+ configurable: true,
+ get() { return ""; }
+ });
+ try {
+ // flags="" → not global → single match.
+ shouldBe(match(re, "a1b2c3"), ["1"]);
+ } finally {
+ Object.defineProperty(RegExp.prototype, "flags", savedDescriptor);
+ }
+})();
+
+// 7. Subclass with primordial prototype properties stays on the slow path
+// (custom-properties guard) and still produces the correct result.
+(function () {
+ class MyRegExp extends RegExp {}
+ function match(re, s) { return re[Symbol.match](s); }
+ noInline(match);
+ var re = new MyRegExp("[0-9]", "g");
+ re.tag = "subclass";
+ for (var i = 0; i < testLoopCount; ++i)
+ shouldBe(match(re, "a1b2c3"), ["1", "2", "3"]);
+})();
+
+// 8. Setting prototype to a non-RegExp prototype after warmup must be observed.
+(function () {
+ function match(re, s) { return re[Symbol.match](s); }
+ noInline(match);
+ var re = /[0-9]/g;
+ for (var i = 0; i < testLoopCount; ++i)
+ shouldBe(match(re, "a1b2c3"), ["1", "2", "3"]);
+ var weird = /[0-9]/g;
+ Object.setPrototypeOf(weird, {
+ [Symbol.match](s) { return ["weird:" + s]; }
+ });
+ // Direct call goes through the user's [@@match].
+ shouldBe(weird[Symbol.match]("a1b2c3"), ["weird:a1b2c3"]);
+ // The fast path must keep working for normal regexps after one weird instance.
+ shouldBe(match(re, "a1b2c3"), ["1", "2", "3"]);
+})();
+
+// 9. Calling RegExp.prototype[@@match] on a non-object must throw.
+(function () {
+ var match = RegExp.prototype[Symbol.match];
+ shouldThrow(function () { match.call(null, "x"); }, "TypeError");
+ shouldThrow(function () { match.call(undefined, "x"); }, "TypeError");
+ shouldThrow(function () { match.call(42, "x"); }, "TypeError");
+ shouldThrow(function () { match.call("str", "x"); }, "TypeError");
+})();
+
+// 10. Empty-match advancement in the slow path with fullUnicode=true.
+// The empty-match branch must call AdvanceStringIndex; the C++ slow path
+// must surrogate-pair-aware advance to avoid infinite loops.
+(function () {
+ function match(re, s) { return re[Symbol.match](s); }
+ noInline(match);
+ var re = /(?:)/gu;
+ for (var i = 0; i < testLoopCount; ++i) {
+ var r = match(re, "😀x");
+ shouldBe(r, ["", "", ""]);
+ }
+})();
+
+// 11. Empty-match advancement without unicode flag.
+(function () {
+ function match(re, s) { return re[Symbol.match](s); }
+ noInline(match);
+ var re = /(?:)/g;
+ for (var i = 0; i < testLoopCount; ++i)
+ shouldBe(match(re, "ab"), ["", "", ""]);
+})();
diff --git a/JSTests/stress/regexp-string-list-empty-alternative.js b/JSTests/stress/regexp-string-list-empty-alternative.js
new file mode 100644
index 000000000000..6b8db1035a69
--- /dev/null
+++ b/JSTests/stress/regexp-string-list-empty-alternative.js
@@ -0,0 +1,83 @@
+// Test that the Yarr string list fast path correctly matches an empty
+// alternative that is not the last alternative, e.g. /^(?:c||b)/.
+
+function shouldBe(actual, expected, message) {
+ if (actual !== expected)
+ throw new Error((message ? message + ": " : "") + "expected " + JSON.stringify(expected) + " but got " + JSON.stringify(actual));
+}
+
+function shouldBeMatch(result, expectedStrings, expectedIndex, message) {
+ if (expectedStrings === null) {
+ shouldBe(result, null, message);
+ return;
+ }
+ if (result === null)
+ throw new Error((message ? message + ": " : "") + "expected a match but got null");
+ shouldBe(result.length, expectedStrings.length, message + " (length)");
+ for (var i = 0; i < expectedStrings.length; ++i)
+ shouldBe(result[i], expectedStrings[i], message + " [" + i + "]");
+ shouldBe(result.index, expectedIndex, message + " (index)");
+}
+
+// Empty alternative in the middle.
+shouldBeMatch(/^(?:c||b)/.exec("bccc"), [""], 0, "/^(?:c||b)/ vs \"bccc\"");
+shouldBeMatch(/^(?:c||b)/.exec("cccc"), ["c"], 0, "/^(?:c||b)/ vs \"cccc\"");
+shouldBeMatch(/^(?:c||b)/.exec("zzzz"), [""], 0, "/^(?:c||b)/ vs \"zzzz\"");
+shouldBeMatch(/^(?:c||b)/.exec(""), [""], 0, "/^(?:c||b)/ vs \"\"");
+
+// Empty alternative first.
+shouldBeMatch(/^(?:|c)/.exec("c"), [""], 0, "/^(?:|c)/ vs \"c\"");
+shouldBeMatch(/^(?:|foo|bar)/.exec("bar"), [""], 0, "/^(?:|foo|bar)/ vs \"bar\"");
+
+// Multiple empty alternatives.
+shouldBeMatch(/^(?:||b)/.exec("b"), [""], 0, "/^(?:||b)/ vs \"b\"");
+
+// Empty alternative last already worked; check for regressions.
+shouldBeMatch(/^(?:c|b|)/.exec("bccc"), ["b"], 0, "/^(?:c|b|)/ vs \"bccc\"");
+shouldBeMatch(/^(?:c|b|)/.exec("zzzz"), [""], 0, "/^(?:c|b|)/ vs \"zzzz\"");
+
+// Multi-character alternatives.
+shouldBeMatch(/^(?:foo||bar)/.exec("bar"), [""], 0, "/^(?:foo||bar)/ vs \"bar\"");
+shouldBeMatch(/^(?:foo||bar)/.exec("foofoo"), ["foo"], 0, "/^(?:foo||bar)/ vs \"foofoo\"");
+
+// EOL anchored string lists: alternatives after an empty one stay reachable.
+shouldBeMatch(/^(?:c||b)$/.exec("c"), ["c"], 0, "/^(?:c||b)$/ vs \"c\"");
+shouldBeMatch(/^(?:c||b)$/.exec("b"), ["b"], 0, "/^(?:c||b)$/ vs \"b\"");
+shouldBeMatch(/^(?:c||b)$/.exec(""), [""], 0, "/^(?:c||b)$/ vs \"\"");
+shouldBeMatch(/^(?:c||b)$/.exec("bc"), null, 0, "/^(?:c||b)$/ vs \"bc\"");
+
+// Not a string list: no BOL anchor.
+shouldBeMatch(/(?:c||b)/.exec("bccc"), [""], 0, "/(?:c||b)/ vs \"bccc\"");
+
+// Not a string list: capturing group.
+shouldBeMatch(/^(c||b)/.exec("bccc"), ["", ""], 0, "/^(c||b)/ vs \"bccc\"");
+
+// Not a string list: quantified group. A zero-length iteration is rejected
+// by RepeatMatcher, so the "b" alternative matches.
+shouldBeMatch(/^(?:c||b)?/.exec("bccc"), ["b"], 0, "/^(?:c||b)?/ vs \"bccc\"");
+
+// String list without empty alternatives: fast path regression check.
+shouldBeMatch(/^(?:foo|bar|baz)/.exec("barxx"), ["bar"], 0, "/^(?:foo|bar|baz)/ vs \"barxx\"");
+shouldBeMatch(/^(?:foo|bar|baz)/.exec("qux"), null, 0, "/^(?:foo|bar|baz)/ vs \"qux\"");
+
+// Flags.
+shouldBeMatch(/^(?:c||b)/i.exec("Bccc"), [""], 0, "/^(?:c||b)/i vs \"Bccc\"");
+shouldBeMatch(/^(?:c||b)/m.exec("x\nbc"), [""], 0, "/^(?:c||b)/m vs \"x\\nbc\"");
+
+var stickyRe = /^(?:c||b)/y;
+stickyRe.lastIndex = 0;
+shouldBeMatch(stickyRe.exec("bccc"), [""], 0, "/^(?:c||b)/y vs \"bccc\"");
+shouldBe(stickyRe.lastIndex, 0, "sticky lastIndex after zero-length match");
+
+var globalRe = /^(?:c||b)/g;
+globalRe.lastIndex = 0;
+shouldBeMatch(globalRe.exec("bccc"), [""], 0, "/^(?:c||b)/g vs \"bccc\"");
+shouldBe(globalRe.lastIndex, 0, "global lastIndex after zero-length match");
+
+// 16-bit strings.
+shouldBeMatch(/^(?:あ||い)/.exec("いい"), [""], 0, "/^(?:\\u3042||\\u3044)/ vs \"\\u3044\\u3044\"");
+shouldBeMatch(/^(?:あ||い)/.exec("ああ"), ["あ"], 0, "/^(?:\\u3042||\\u3044)/ vs \"\\u3042\\u3042\"");
+
+// test() and replace() use the same match.
+shouldBe(/^(?:c||b)/.test("zzz"), true, "/^(?:c||b)/.test(\"zzz\")");
+shouldBe("bccc".replace(/^(?:c||b)/, "X"), "Xbccc", "replace with /^(?:c||b)/");
diff --git a/JSTests/stress/string-split-regexp-fast-path-limit-side-effects.js b/JSTests/stress/string-split-regexp-fast-path-limit-side-effects.js
new file mode 100644
index 000000000000..83bb34eedafa
--- /dev/null
+++ b/JSTests/stress/string-split-regexp-fast-path-limit-side-effects.js
@@ -0,0 +1,26 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`bad value: ${actual}, expected ${expected}`);
+}
+
+function doSplit(string, separator, limit) {
+ return string.split(separator, limit);
+}
+noInline(doSplit);
+
+for (let i = 0; i < testLoopCount; ++i)
+ shouldBe(JSON.stringify(doSplit("a,b,c", /,/, 4)), '["a","b","c"]');
+
+let execCalled = false;
+let evil = {
+ valueOf() {
+ RegExp.prototype.exec = function() {
+ execCalled = true;
+ return null;
+ };
+ return 4;
+ }
+};
+let result = doSplit("a,b,c", /,/, evil);
+shouldBe(execCalled, true);
+shouldBe(JSON.stringify(result), '["a,b,c"]');
diff --git a/JSTests/stress/string-split-regexp-fast-path-tostring-side-effects.js b/JSTests/stress/string-split-regexp-fast-path-tostring-side-effects.js
new file mode 100644
index 000000000000..4ec279fc91c3
--- /dev/null
+++ b/JSTests/stress/string-split-regexp-fast-path-tostring-side-effects.js
@@ -0,0 +1,43 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`bad value: ${actual}, expected ${expected}`);
+}
+
+{
+ let constructed = false;
+ class MyRegExp extends RegExp {
+ constructor(...args) {
+ constructed = true;
+ super(...args);
+ }
+ }
+ let re = /,/;
+ let obj = {
+ toString() {
+ re.constructor = MyRegExp;
+ return "a,b,c";
+ }
+ };
+ let result = String.prototype.split.call(obj, re);
+ shouldBe(constructed, true);
+ shouldBe(JSON.stringify(result), '["a","b","c"]');
+}
+
+// Overriding RegExp.prototype.exec fires the primordial RegExp watchpoint, so
+// this must stay the last case in this file.
+{
+ let execCalled = false;
+ let re = /,/;
+ let obj = {
+ toString() {
+ RegExp.prototype.exec = function() {
+ execCalled = true;
+ return null;
+ };
+ return "a,b,c";
+ }
+ };
+ let result = String.prototype.split.call(obj, re);
+ shouldBe(execCalled, true);
+ shouldBe(JSON.stringify(result), '["a,b,c"]');
+}
diff --git a/JSTests/stress/stringProtoFuncAt-GCOwnedDataScope-atomstring-swap.js b/JSTests/stress/stringProtoFuncAt-GCOwnedDataScope-atomstring-swap.js
new file mode 100644
index 000000000000..708c82c6f1e5
--- /dev/null
+++ b/JSTests/stress/stringProtoFuncAt-GCOwnedDataScope-atomstring-swap.js
@@ -0,0 +1,25 @@
+const target = "A".repeat(128);
+const dummy = {};
+Reflect.set(dummy, target, 1);
+
+const freshRope = "A".repeat(128);
+
+let nonAtom = "D".repeat(64) + "D".repeat(64);
+String.prototype.at.call(nonAtom, 0);
+
+let thatObj = {
+ [Symbol.toPrimitive]() {
+ // Trigger atomization via Reflect.set to avoid Inline Cache holding the string
+ Reflect.set(dummy, freshRope, 1);
+
+ // Overwrite the VM's lastAtomizedIdentifierStringImpl cache
+ Reflect.set(dummy, nonAtom, 1);
+
+ gc();
+
+ return 0;
+ }
+};
+
+// Verify that GCOwnedDataScope/Heap keeps the StringImpl alive across the callback
+String.prototype.at.call(freshRope, thatObj);
diff --git a/JSTests/stress/stringProtoFuncEndsWith-GCOwnedDataScope-atomstring-swap.js b/JSTests/stress/stringProtoFuncEndsWith-GCOwnedDataScope-atomstring-swap.js
new file mode 100644
index 000000000000..b3a2d6e17ced
--- /dev/null
+++ b/JSTests/stress/stringProtoFuncEndsWith-GCOwnedDataScope-atomstring-swap.js
@@ -0,0 +1,23 @@
+const target = "A".repeat(128);
+const dummy = {};
+Reflect.set(dummy, target, 1);
+
+const freshRope = "A".repeat(128);
+
+let nonAtom = "D".repeat(64) + "D".repeat(64);
+String.prototype.endsWith.call(nonAtom, "E");
+
+let thatObj = {
+ toString() {
+ // Trigger atomization via Reflect.set to avoid Inline Cache holding the string
+ Reflect.set(dummy, freshRope, 1);
+ // Overwrite the VM's lastAtomizedIdentifierStringImpl cache
+ Reflect.set(dummy, nonAtom, 1);
+
+ gc();
+ return "B";
+ }
+};
+
+// Verify that GCOwnedDataScope/Heap keeps the StringImpl alive across the callback
+String.prototype.endsWith.call(freshRope, thatObj);
diff --git a/JSTests/stress/stringProtoFuncLocaleCompare-GCOwnedDataScope-atomstring-swap.js b/JSTests/stress/stringProtoFuncLocaleCompare-GCOwnedDataScope-atomstring-swap.js
new file mode 100644
index 000000000000..d29ad5730263
--- /dev/null
+++ b/JSTests/stress/stringProtoFuncLocaleCompare-GCOwnedDataScope-atomstring-swap.js
@@ -0,0 +1,23 @@
+const target = "A".repeat(128);
+const dummy = {};
+Reflect.set(dummy, target, 1);
+
+const freshRope = "A".repeat(128);
+
+let nonAtom = "D".repeat(64) + "D".repeat(64);
+String.prototype.localeCompare.call(nonAtom, "E");
+
+let thatObj = {
+ toString() {
+ // Trigger atomization via Reflect.set to avoid Inline Cache holding the string
+ Reflect.set(dummy, freshRope, 1);
+ // Overwrite the VM's lastAtomizedIdentifierStringImpl cache
+ Reflect.set(dummy, nonAtom, 1);
+
+ gc();
+ return "B";
+ }
+};
+
+// Verify that GCOwnedDataScope/Heap keeps the StringImpl alive across the callback
+String.prototype.localeCompare.call(freshRope, thatObj);
diff --git a/JSTests/stress/stringProtoFuncStartsWith-GCOwnedDataScope-atomstring-swap.js b/JSTests/stress/stringProtoFuncStartsWith-GCOwnedDataScope-atomstring-swap.js
new file mode 100644
index 000000000000..cccbc04bb32e
--- /dev/null
+++ b/JSTests/stress/stringProtoFuncStartsWith-GCOwnedDataScope-atomstring-swap.js
@@ -0,0 +1,23 @@
+const target = "A".repeat(128);
+const dummy = {};
+Reflect.set(dummy, target, 1);
+
+const freshRope = "A".repeat(128);
+
+let nonAtom = "D".repeat(64) + "D".repeat(64);
+String.prototype.startsWith.call(nonAtom, "E");
+
+let thatObj = {
+ toString() {
+ // Trigger atomization via Reflect.set to avoid Inline Cache holding the string
+ Reflect.set(dummy, freshRope, 1);
+ // Overwrite the VM's lastAtomizedIdentifierStringImpl cache
+ Reflect.set(dummy, nonAtom, 1);
+
+ gc();
+ return "B";
+ }
+};
+
+// Verify that GCOwnedDataScope/Heap keeps the StringImpl alive across the callback
+String.prototype.startsWith.call(freshRope, thatObj);
diff --git a/JSTests/stress/temporal-boundary-values.js b/JSTests/stress/temporal-boundary-values.js
new file mode 100644
index 000000000000..93beea6a99df
--- /dev/null
+++ b/JSTests/stress/temporal-boundary-values.js
@@ -0,0 +1,94 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(a, b, msg) {
+ if (a !== b) throw new Error(`${msg}: expected ${b}, got ${a}`);
+}
+
+function shouldThrow(fn, msg) {
+ try { fn(); throw new Error(`${msg}: should have thrown`); }
+ catch (e) { if (e.message.startsWith(msg)) throw e; }
+}
+
+{
+ const maxNs = 8640000000000000000000n;
+ const zdt = new Temporal.ZonedDateTime(maxNs, "UTC");
+ shouldBe(zdt.epochNanoseconds, maxNs, "max epoch ns");
+}
+{
+ const minNs = -8640000000000000000000n;
+ const zdt = new Temporal.ZonedDateTime(minNs, "UTC");
+ shouldBe(zdt.epochNanoseconds, minNs, "min epoch ns");
+}
+{
+ shouldThrow(() => new Temporal.ZonedDateTime(8640000000000000000001n, "UTC"), "Beyond max epoch");
+}
+{
+ shouldThrow(() => new Temporal.ZonedDateTime(-8640000000000000000001n, "UTC"), "Beyond min epoch");
+}
+
+
+{
+ const pd = new Temporal.PlainDate(275760, 9, 13);
+ shouldBe(pd.year, 275760, "max year");
+}
+
+{
+ const pd = new Temporal.PlainDate(-271821, 4, 20);
+ shouldBe(pd.year, -271821, "min year");
+}
+
+{
+ const d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 1000000000);
+ shouldBe(d.nanoseconds, 1000000000, "large nanoseconds");
+ shouldBe(d.sign, 1, "positive sign");
+}
+
+{
+ const d = new Temporal.Duration();
+ shouldBe(d.blank, true, "zero duration is blank");
+ shouldBe(d.sign, 0, "zero duration sign");
+}
+
+{
+ const d = new Temporal.Duration(-1, -2, -3);
+ shouldBe(d.sign, -1, "negative duration sign");
+ shouldBe(d.blank, false, "negative duration not blank");
+ const neg = d.negated();
+ shouldBe(neg.years, 1, "negated years");
+ shouldBe(neg.sign, 1, "negated sign");
+}
+
+{
+ const d = new Temporal.Duration(-5, 0, 0, -3);
+ const a = d.abs();
+ shouldBe(a.years, 5, "abs years");
+ shouldBe(a.days, 3, "abs days");
+ shouldBe(a.sign, 1, "abs sign");
+}
+
+// --- Instant boundaries ---
+{
+ const maxNs = 8640000000000000000000n;
+ const inst = Temporal.Instant.fromEpochNanoseconds(maxNs);
+ shouldBe(inst.epochNanoseconds, maxNs, "instant max ns");
+}
+
+{
+ shouldThrow(
+ () => Temporal.Instant.fromEpochNanoseconds(8640000000000000000001n),
+ "instant beyond max"
+ );
+}
+
+{
+ const t = new Temporal.PlainTime(23, 59, 59, 999, 999, 999);
+ shouldBe(t.hour, 23, "max hour");
+ shouldBe(t.nanosecond, 999, "max nanosecond");
+}
+
+{
+ shouldThrow(
+ () => new Temporal.PlainTime(24, 0, 0),
+ "hour 24 invalid"
+ );
+}
diff --git a/JSTests/stress/temporal-calendar-daymath-weeks.js b/JSTests/stress/temporal-calendar-daymath-weeks.js
new file mode 100644
index 000000000000..0dd86aaeacdd
--- /dev/null
+++ b/JSTests/stress/temporal-calendar-daymath-weeks.js
@@ -0,0 +1,55 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`expected ${JSON.stringify(expected)} but got ${JSON.stringify(actual)}`);
+}
+
+// Bug: calendarDateUntil for Chinese/Dangi (lunisolar) calendars with largestUnit='days'
+// or 'weeks' used ICU epoch-ms arithmetic. ICU's Chinese calendar approximation for
+// dates far beyond ~year 2100 gives wrong epoch ms, causing incorrect day counts.
+// e.g. 2020-01-01 until +275760-01-01 in Chinese calendar: expected 99981482 days,
+// got 99999744 days (18262 extra ≈ 50 years).
+//
+// Fix: like ICU4X's fast path (calendar_arithmetic.rs:934-958), use pure ISO day count
+// (diffISODate) for days/weeks regardless of calendar — day count is calendar-independent.
+
+const ISO_ONE = Temporal.PlainDate.from("2020-01-01");
+const ISO_TWO = Temporal.PlainDate.from("+275760-01-01");
+
+// ISO reference
+const isoWeeks = ISO_ONE.until(ISO_TWO, { largestUnit: "weeks" });
+const isoDays = ISO_ONE.until(ISO_TWO, { largestUnit: "days" });
+shouldBe(isoWeeks.weeks * 7 + isoWeeks.days, 99981482);
+shouldBe(isoDays.days, 99981482);
+
+// All calendars must match ISO for days/weeks (calendar doesn't affect raw day count)
+for (const cal of ["chinese", "dangi", "hebrew", "islamic-umalqura", "coptic", "persian", "ethiopic", "buddhist", "gregory"]) {
+ const d1 = ISO_ONE.withCalendar(cal);
+ const d2 = ISO_TWO.withCalendar(cal);
+
+ const w = d1.until(d2, { largestUnit: "weeks" });
+ shouldBe(w.weeks * 7 + w.days, 99981482); // must match ISO raw day count
+
+ const d = d1.until(d2, { largestUnit: "days" });
+ shouldBe(d.days, 99981482); // must match ISO raw day count
+}
+
+// Normal date span (well within ICU's accurate range)
+const near1 = Temporal.PlainDate.from("2020-01-01");
+const near2 = Temporal.PlainDate.from("2023-06-15");
+for (const cal of ["chinese", "dangi", "hebrew", "islamic-umalqura"]) {
+ const d1 = near1.withCalendar(cal);
+ const d2 = near2.withCalendar(cal);
+ const isoRef = near1.until(near2, { largestUnit: "days" });
+ const calDiff = d1.until(d2, { largestUnit: "days" });
+ shouldBe(calDiff.days, isoRef.days);
+}
+
+// Negative direction (since)
+for (const cal of ["chinese", "dangi"]) {
+ const d1 = ISO_ONE.withCalendar(cal);
+ const d2 = ISO_TWO.withCalendar(cal);
+ const s = d2.since(d1, { largestUnit: "days" });
+ shouldBe(s.days, 99981482);
+}
diff --git a/JSTests/stress/temporal-calendar-month-boundary.js b/JSTests/stress/temporal-calendar-month-boundary.js
new file mode 100644
index 000000000000..6926358646d5
--- /dev/null
+++ b/JSTests/stress/temporal-calendar-month-boundary.js
@@ -0,0 +1,126 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`expected ${JSON.stringify(expected)} but got ${JSON.stringify(actual)}`);
+}
+
+// Helper: assert diff equals ISO diff (non-ISO calendars use pure ISO arithmetic)
+function assertMatchesISO(isoOneStr, isoTwoStr, largestUnit, calendars) {
+ const isoOne = Temporal.PlainDate.from(isoOneStr);
+ const isoTwo = Temporal.PlainDate.from(isoTwoStr);
+ const isoRef = isoOne.until(isoTwo, { largestUnit });
+ for (const cal of calendars) {
+ const one = isoOne.withCalendar(cal);
+ const two = isoTwo.withCalendar(cal);
+ const diff = one.until(two, { largestUnit });
+ const msg = `${cal} ${isoOneStr} until ${isoTwoStr} ${largestUnit}`;
+ if (diff.years !== isoRef.years || diff.months !== isoRef.months || diff.days !== isoRef.days)
+ throw new Error(`${msg}: expected ${isoRef} got ${diff}`);
+ // Round-trip: one.add(diff) == two
+ const roundTrip = one.add(diff);
+ if (!roundTrip.equals(two))
+ throw new Error(`${msg} round-trip: ${one}.add(${diff}) = ${roundTrip} ≠ ${two}`);
+ }
+}
+
+const CALS = ["gregory", "buddhist", "japanese", "roc"];
+
+// ── Bug 1: Incremental month clamping ──────────────────────────────────────
+// Mar 31 -1mo → Feb 29 (clamp) -1mo → Jan 29 — WRONG
+// Fix: single ucal_add, Mar 31 -2mo → Jan 31 — CORRECT
+
+assertMatchesISO("2020-03-31", "2020-01-01", "months", CALS);
+assertMatchesISO("2020-01-01", "2020-03-31", "months", CALS);
+
+// Month-end clamp: last day of long month → last day of short month
+assertMatchesISO("2020-01-31", "2020-02-29", "months", CALS); // Jan 31 → Feb 29 (leap)
+assertMatchesISO("2020-01-31", "2020-04-30", "months", CALS); // Jan 31 → Apr 30
+assertMatchesISO("2020-03-31", "2020-06-30", "months", CALS); // Mar 31 → Jun 30
+
+// Negative direction
+assertMatchesISO("2020-03-31", "2020-01-31", "months", CALS); // -2M0D
+assertMatchesISO("2020-04-30", "2020-01-31", "months", CALS); // -3M0D
+assertMatchesISO("2020-06-30", "2020-03-31", "months", CALS); // -3M0D
+
+// ── Bug 2: Undo asymmetry (ucal_add +1mo then -1mo ≠ identity at month-end) ─
+// Jan 29 + 1mo = Feb 28 (2100 not leap), Feb 28 - 1mo = Jan 28 — WRONG
+// Fix: single add, same result
+
+assertMatchesISO("2020-01-29", "2100-01-29", "years", CALS); // P80Y exactly
+assertMatchesISO("2020-01-31", "2100-01-31", "years", CALS); // P80Y exactly
+assertMatchesISO("2020-02-29", "2100-01-01", "years", CALS); // P79Y10M3D (source day=29)
+
+// ── Bug 3: Source day preservation (use original day, not clamped year-advance day) ─
+// Feb 29 + 79 years = Feb 28, 2099. Source day=29, target-month Dec has 31 days → Dec 29
+assertMatchesISO("2020-02-29", "2099-12-01", "years", CALS);
+assertMatchesISO("2020-02-29", "2100-03-01", "years", CALS);
+
+// ── Bug 4: Julian/Gregorian reform (ICU 'gregory' uses Julian before 1582-10-15) ──
+// Pure ISO arithmetic avoids the 10-day calendar reform gap
+assertMatchesISO("1581-01-01", "2020-01-01", "years", CALS); // P439Y exactly
+assertMatchesISO("1581-01-01", "2020-01-01", "months", CALS); // P5268M exactly
+assertMatchesISO("1582-10-01", "2020-01-01", "years", CALS); // spans reform date
+assertMatchesISO("1582-10-15", "2020-01-01", "years", CALS); // reform date itself
+assertMatchesISO("1581-12-31", "1582-01-01", "months", CALS); // 1-day difference across reform year
+
+// ── Extreme boundary years ─────────────────────────────────────────────────
+// Large year spans exercise the min_years fast-forward optimization
+assertMatchesISO("-271821-04-19", "+275760-09-13", "years", ["gregory"]); // full Temporal range
+assertMatchesISO("1970-01-01", "+275760-09-13", "years", CALS);
+assertMatchesISO("-271821-04-19", "1970-01-01", "years", CALS);
+assertMatchesISO("1900-01-01", "2100-12-31", "months", CALS); // 2400 months
+
+// ── Epoch and negative years ───────────────────────────────────────────────
+assertMatchesISO("1970-01-01", "2020-01-01", "years", CALS); // P50Y
+assertMatchesISO("1969-12-31", "2020-01-01", "years", CALS); // P50Y1D
+assertMatchesISO("0000-01-01", "2020-01-01", "years", CALS); // year 0
+assertMatchesISO("-000001-01-01", "2020-01-01", "years", CALS); // negative year
+
+// ── Month-day combinations from benchmark interestingMonthDays ────────────
+// (subset of the cross-product that exercises boundary conditions)
+const edgePairs = [
+ ["2016-01-29", "2020-02-29"], // leap day in target
+ ["2016-02-29", "2020-02-29"], // leap day in both
+ ["2020-02-29", "2020-03-01"], // day after leap day
+ ["2020-01-31", "2020-03-01"], // Jan 31 + 1mo = Mar 1 (NOT Feb 31)
+ ["2019-02-28", "2020-02-29"], // non-leap to leap Feb
+ ["2020-02-29", "2021-02-28"], // leap to non-leap Feb
+ ["2020-03-29", "2020-04-19"], // within quarter
+ ["2020-06-30", "2020-07-31"], // short to long month
+ ["2020-07-31", "2020-06-30"], // long to short (negative)
+ ["2020-09-13", "+275760-09-13"], // last supported monthday
+ ["2020-04-19", "2020-04-19"], // same date → zero duration
+];
+for (const [a, b] of edgePairs) {
+ assertMatchesISO(a, b, "months", CALS);
+ assertMatchesISO(a, b, "years", CALS);
+}
+
+// ── since() symmetry ───────────────────────────────────────────────────────
+// one.since(two) must equal two.until(one) for all calendars
+for (const cal of CALS) {
+ const pairs = [
+ ["2020-03-31", "2020-01-01"],
+ ["2020-01-29", "2100-01-29"],
+ ["2016-02-29", "2020-02-29"],
+ ];
+ for (const [a, b] of pairs) {
+ const one = Temporal.PlainDate.from(a).withCalendar(cal);
+ const two = Temporal.PlainDate.from(b).withCalendar(cal);
+ const until = one.until(two, { largestUnit: "months" });
+ const since = two.since(one, { largestUnit: "months" });
+ shouldBe(until.toString(), since.toString());
+ }
+}
+
+// ── largestUnit: "years" vs "months" consistency ───────────────────────────
+// years result and months result must be consistent: years*12 + months_remainder = total_months
+for (const cal of CALS) {
+ const one = Temporal.PlainDate.from("2020-03-31").withCalendar(cal);
+ const two = Temporal.PlainDate.from("2023-01-15").withCalendar(cal);
+ const byYears = one.until(two, { largestUnit: "years" });
+ const byMonths = one.until(two, { largestUnit: "months" });
+ shouldBe(byYears.years * 12 + byYears.months, byMonths.months);
+ shouldBe(byYears.days, byMonths.days);
+}
diff --git a/JSTests/stress/temporal-calendar.js b/JSTests/stress/temporal-calendar.js
index c0c12ba50d3a..38a3b1decec1 100644
--- a/JSTests/stress/temporal-calendar.js
+++ b/JSTests/stress/temporal-calendar.js
@@ -1,6 +1,4 @@
//@ requireOptions("--useTemporal=1")
-// FIXME: toLocaleString requires IntlDateTimeFormat Temporal support, implemented in the next patch.
-//@ skip
function shouldBe(actual, expected) {
if (actual !== expected)
diff --git a/JSTests/stress/temporal-cross-type.js b/JSTests/stress/temporal-cross-type.js
new file mode 100644
index 000000000000..c284ad313b80
--- /dev/null
+++ b/JSTests/stress/temporal-cross-type.js
@@ -0,0 +1,98 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(a, b, msg) {
+ if (a !== b) throw new Error(`${msg}: expected ${b}, got ${a}`);
+}
+
+{
+ const original = Temporal.ZonedDateTime.from("2024-06-15T14:30[America/New_York]");
+ const pd = original.toPlainDate();
+ shouldBe(pd.year, 2024, "ZDT→PD year");
+ shouldBe(pd.month, 6, "ZDT→PD month");
+ shouldBe(pd.day, 15, "ZDT→PD day");
+ const back = pd.toZonedDateTime("America/New_York");
+ shouldBe(back.year, 2024, "PD→ZDT year");
+ shouldBe(back.timeZoneId, "America/New_York", "PD→ZDT tz");
+}
+
+{
+ const original = Temporal.ZonedDateTime.from("2024-06-15T14:30:45.123456789[America/New_York]");
+ const pdt = original.toPlainDateTime();
+ shouldBe(pdt.hour, 14, "ZDT→PDT hour");
+ shouldBe(pdt.nanosecond, 789, "ZDT→PDT nanosecond");
+ const back = pdt.toZonedDateTime("America/New_York");
+ shouldBe(back.epochNanoseconds, original.epochNanoseconds, "PDT→ZDT round-trip ns");
+}
+
+{
+ const original = Temporal.ZonedDateTime.from("2024-06-15T14:30[America/New_York]");
+ const inst = original.toInstant();
+ shouldBe(inst.epochNanoseconds, original.epochNanoseconds, "ZDT→Instant epochNs");
+ const back = inst.toZonedDateTimeISO("America/New_York");
+ shouldBe(back.epochNanoseconds, original.epochNanoseconds, "Instant→ZDT round-trip");
+}
+
+{
+ const date = Temporal.PlainDate.from("2024-03-15");
+ const time = Temporal.PlainTime.from("10:30:00");
+ const dt = date.toPlainDateTime(time);
+ shouldBe(dt.year, 2024, "combined year");
+ shouldBe(dt.hour, 10, "combined hour");
+ shouldBe(dt.minute, 30, "combined minute");
+
+ const backDate = dt.toPlainDate();
+ const backTime = dt.toPlainTime();
+ shouldBe(backDate.equals(date), true, "decomposed date matches");
+ shouldBe(Temporal.PlainTime.compare(backTime, time), 0, "decomposed time matches");
+}
+
+{
+ const date = Temporal.PlainDate.from("2024-03-15");
+ const ym = date.toPlainYearMonth();
+ shouldBe(ym.year, 2024, "ym year");
+ shouldBe(ym.month, 3, "ym month");
+
+ const backDate = ym.toPlainDate({ day: 15 });
+ shouldBe(backDate.equals(date), true, "YM round-trip with day");
+}
+
+{
+ const date = Temporal.PlainDate.from("2024-03-15");
+ const md = date.toPlainMonthDay();
+ shouldBe(md.monthCode, "M03", "md monthCode");
+ shouldBe(md.day, 15, "md day");
+
+ const backDate = md.toPlainDate({ year: 2024 });
+ shouldBe(backDate.equals(date), true, "MD round-trip with year");
+}
+
+{
+ const before = Temporal.Now.instant();
+ const zdtISO = Temporal.Now.zonedDateTimeISO();
+ const pdtISO = zdtISO.toPlainDateTime();
+ const pdISO = zdtISO.toPlainDate();
+ const ptISO = zdtISO.toPlainTime();
+ const after = Temporal.Now.instant();
+
+ shouldBe(Temporal.Instant.compare(before, after) <= 0, true, "before <= after");
+ shouldBe(pdISO.year, pdtISO.year, "Now: PD and PDT year match");
+ shouldBe(pdISO.month, pdtISO.month, "Now: PD and PDT month match");
+ shouldBe(zdtISO.timeZoneId, Temporal.Now.timeZoneId(), "Now: ZDT and timeZoneId match");
+}
+
+{
+ const zdt = Temporal.ZonedDateTime.from("2024-06-15T14:30:00+09:00[Asia/Tokyo]");
+ const zdtParsed = Temporal.ZonedDateTime.from(zdt.toString());
+ shouldBe(zdtParsed.epochNanoseconds, zdt.epochNanoseconds, "ZDT string round-trip");
+
+ const inst = Temporal.Instant.from("2024-06-15T05:30:00Z");
+ const instStr = inst.toString();
+ const instParsed = Temporal.Instant.from(instStr);
+ shouldBe(instParsed.epochNanoseconds, inst.epochNanoseconds, "Instant string round-trip");
+
+ const dur = Temporal.Duration.from("P1Y2M3DT4H5M6S");
+ const durStr = dur.toString();
+ const durParsed = Temporal.Duration.from(durStr);
+ shouldBe(durParsed.years, 1, "Duration string round-trip years");
+ shouldBe(durParsed.seconds, 6, "Duration string round-trip seconds");
+}
diff --git a/JSTests/stress/temporal-duration-relative-to-era-calendar.js b/JSTests/stress/temporal-duration-relative-to-era-calendar.js
new file mode 100644
index 000000000000..415f52ab8730
--- /dev/null
+++ b/JSTests/stress/temporal-duration-relative-to-era-calendar.js
@@ -0,0 +1,61 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`expected ${JSON.stringify(expected)} but got ${JSON.stringify(actual)}`);
+}
+
+function shouldThrow(func, errorType) {
+ let threw = false;
+ try { func(); } catch (e) {
+ threw = true;
+ if (!(e instanceof errorType))
+ throw new Error(`Expected ${errorType.name} but got ${e.constructor.name}: ${e.message}`);
+ }
+ if (!threw)
+ throw new Error(`Expected ${errorType.name} but no exception was thrown`);
+}
+
+// relativeTo PlainDate with era — era+eraYear resolve to correct ISO year
+{
+ const d = Temporal.Duration.from({ days: 10 });
+ const r = d.total({ unit: "days", relativeTo: { era: "showa", eraYear: 50, month: 1, day: 1, calendar: "japanese" } });
+ shouldBe(r, 10);
+}
+
+// relativeTo PlainDate era+eraYear: the resulting plain date has the right year
+{
+ const d = Temporal.Duration.from({ months: 1 });
+ // Showa 50 = 1975 — adding 1 month to 1975-01-01 should give 1975-02-01
+ const r = d.total({ unit: "months", relativeTo: { era: "showa", eraYear: 50, month: 1, day: 1, calendar: "japanese" } });
+ shouldBe(r, 1);
+}
+
+// relativeTo ZDT with era
+{
+ const d = Temporal.Duration.from({ days: 10 });
+ const r = d.total({ unit: "days", relativeTo: { era: "showa", eraYear: 50, month: 1, day: 1, calendar: "japanese", timeZone: "UTC" } });
+ shouldBe(r, 10);
+}
+
+// relativeTo era+eraYear+year consistent → OK
+{
+ const d = Temporal.Duration.from({ days: 1 });
+ const r = d.total({ unit: "days", relativeTo: { era: "showa", eraYear: 50, year: 1975, month: 1, day: 1, calendar: "japanese" } });
+ shouldBe(r, 1);
+}
+
+// relativeTo era+eraYear+year inconsistent → RangeError
+{
+ shouldThrow(() => {
+ const d = Temporal.Duration.from({ days: 1 });
+ d.total({ unit: "days", relativeTo: { era: "showa", eraYear: 50, year: 2000, month: 1, day: 1, calendar: "japanese" } });
+ }, RangeError);
+}
+
+// iso8601 relativeTo unchanged
+{
+ const d = Temporal.Duration.from({ days: 5 });
+ const r = d.total({ unit: "days", relativeTo: { year: 2024, month: 1, day: 1 } });
+ shouldBe(r, 5);
+}
diff --git a/JSTests/stress/temporal-duration-relativeto.js b/JSTests/stress/temporal-duration-relativeto.js
new file mode 100644
index 000000000000..af36c5b1948e
--- /dev/null
+++ b/JSTests/stress/temporal-duration-relativeto.js
@@ -0,0 +1,82 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(a, b, msg) {
+ if (a !== b) throw new Error(`${msg}: expected ${b}, got ${a}`);
+}
+
+function approxEqual(a, b, tolerance, msg) {
+ if (Math.abs(a - b) > tolerance) throw new Error(`${msg}: expected ~${b}, got ${a}`);
+}
+
+{
+ const d = Temporal.Duration.from({ months: 1, days: 15 });
+ const result = d.round({
+ largestUnit: "months",
+ smallestUnit: "months",
+ roundingMode: "halfExpand",
+ relativeTo: Temporal.PlainDate.from("2024-01-01"),
+ });
+ // Jan 2024 → Feb 2024 has 29 days. 1 month + 15/29 ≈ 1.52 months → rounds to 2 (halfExpand rounds 0.48 to 1, so 1+1=2)
+ shouldBe(result.months, 2, "round months halfExpand");
+}
+
+{
+ const d = Temporal.Duration.from({ months: 1, days: 20 });
+ const result = d.round({
+ largestUnit: "months",
+ smallestUnit: "months",
+ roundingMode: "halfExpand",
+ relativeTo: Temporal.PlainDate.from("2024-01-01"),
+ });
+ // 1 month + 20 days out of 29 remaining in Feb = 1.69 months → rounds to 2
+ shouldBe(result.months, 2, "round months halfExpand up");
+}
+
+{
+ const d = Temporal.Duration.from({ months: 1 });
+ const total = d.total({
+ unit: "days",
+ relativeTo: Temporal.PlainDate.from("2024-01-01"),
+ });
+ // January has 31 days
+ shouldBe(total, 31, "1 month from Jan 1 = 31 days");
+}
+
+{
+ const d = Temporal.Duration.from({ months: 1 });
+ const total = d.total({
+ unit: "days",
+ relativeTo: Temporal.PlainDate.from("2024-02-01"),
+ });
+ // February 2024 has 29 days (leap year)
+ shouldBe(total, 29, "1 month from Feb 1 2024 = 29 days");
+}
+
+{
+ const d = Temporal.Duration.from({ hours: 24 });
+ const plainResult = d.round({ largestUnit: "days", relativeTo: Temporal.PlainDate.from("2024-03-10") });
+ shouldBe(plainResult.days, 1, "24h = 1 day with PlainDate relativeTo");
+ const zdtResult = d.round({ largestUnit: "days",
+ relativeTo: Temporal.ZonedDateTime.from("2024-03-10T00:00[America/New_York]") });
+ shouldBe(zdtResult.days, 1, "24h with ZDT on spring-forward = 1 day + 1h");
+ shouldBe(zdtResult.hours, 1, "1 extra hour because day is only 23h");
+}
+
+{
+ const d = Temporal.Duration.from({ days: 1 });
+ const plainTotal = d.total({ unit: "hours", relativeTo: Temporal.PlainDate.from("2024-03-10") });
+ shouldBe(plainTotal, 24, "1 day = 24h with PlainDate");
+ const zdtTotal = d.total({ unit: "hours",
+ relativeTo: Temporal.ZonedDateTime.from("2024-03-10T00:00[America/New_York]") });
+ shouldBe(zdtTotal, 23, "1 day = 23h with ZDT on spring-forward");
+}
+
+{
+ const d1 = Temporal.Duration.from({ days: 1 });
+ const d2 = Temporal.Duration.from({ hours: 24 });
+ const plainCmp = Temporal.Duration.compare(d1, d2, { relativeTo: Temporal.PlainDate.from("2024-03-10") });
+ shouldBe(plainCmp, 0, "1 day == 24h with PlainDate");
+ const zdtCmp = Temporal.Duration.compare(d1, d2, {
+ relativeTo: Temporal.ZonedDateTime.from("2024-03-10T00:00[America/New_York]") });
+ shouldBe(zdtCmp, -1, "1 day (23h) < 24h with ZDT on spring-forward");
+}
diff --git a/JSTests/stress/temporal-duration.js b/JSTests/stress/temporal-duration.js
index d8e8a98a271a..cff0b76d2661 100644
--- a/JSTests/stress/temporal-duration.js
+++ b/JSTests/stress/temporal-duration.js
@@ -1,6 +1,4 @@
//@ requireOptions("--useTemporal=1")
-// FIXME: toLocaleString requires IntlDateTimeFormat Temporal support, implemented in the next patch.
-//@ skip
function shouldBe(actual, expected) {
if (actual !== expected)
diff --git a/JSTests/stress/temporal-gc-jit-stress.js b/JSTests/stress/temporal-gc-jit-stress.js
new file mode 100644
index 000000000000..25d1249ab70e
--- /dev/null
+++ b/JSTests/stress/temporal-gc-jit-stress.js
@@ -0,0 +1,84 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(a, b, msg) {
+ if (a !== b) throw new Error(`${msg}: expected ${b}, got ${a}`);
+}
+
+{
+ const dates = [];
+ for (let i = 0; i < 10000; i++) {
+ dates.push(Temporal.PlainDate.from({ year: 2020 + (i % 50), month: (i % 12) + 1, day: (i % 28) + 1 }));
+ }
+ shouldBe(dates.length, 10000, "created 10k PlainDates");
+ shouldBe(dates[9999].year, 2020 + (9999 % 50), "last date year correct");
+}
+
+{
+ const durations = [];
+ for (let i = 0; i < 10000; i++) {
+ durations.push(new Temporal.Duration(0, 0, 0, i, i % 24, i % 60));
+ }
+ shouldBe(durations.length, 10000, "created 10k Durations");
+ shouldBe(durations[5000].days, 5000, "mid duration days");
+}
+
+{
+ const zdts = [];
+ const baseNs = 1700000000000000000n;
+ for (let i = 0; i < 1000; i++)
+ zdts.push(new Temporal.ZonedDateTime(baseNs + BigInt(i) * 86400000000000n, "UTC"));
+ shouldBe(zdts.length, 1000, "created 1k ZDTs");
+}
+
+{
+ let date = Temporal.PlainDate.from("2020-01-01");
+ for (let i = 0; i < 10000; i++) {
+ const next = date.add({ days: 1 });
+ if (i === 9999) {
+ // 2020-01-01 + 10000 days = 2047-05-19
+ shouldBe(next.year, 2047, "hot loop: final year");
+ }
+ date = next;
+ }
+}
+
+{
+ let total = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ for (let i = 0; i < 5000; i++) {
+ total = Temporal.Duration.from({ hours: total.hours + 1, minutes: total.minutes + 30 });
+ }
+ shouldBe(total.hours, 5000, "accumulated hours");
+ shouldBe(total.minutes, 150000, "accumulated minutes");
+}
+
+{
+ let time = Temporal.PlainTime.from("00:00:00");
+ for (let i = 0; i < 5000; i++) {
+ time = time.add({ seconds: 17 });
+ }
+ // 5000 * 17 = 85000 seconds = 23h 36m 40s (mod 24h)
+ shouldBe(time.hour, 23, "hot loop time hour");
+ shouldBe(time.minute, 36, "hot loop time minute");
+ shouldBe(time.second, 40, "hot loop time second");
+}
+
+{
+ const instants = [];
+ for (let i = 0; i < 1000; i++) {
+ instants.push(Temporal.Instant.fromEpochNanoseconds(BigInt(i) * 1000000000n));
+ }
+ for (let i = 1; i < instants.length; i++) {
+ const cmp = Temporal.Instant.compare(instants[i - 1], instants[i]);
+ shouldBe(cmp, -1, `instant ${i} should be after ${i - 1}`);
+ }
+}
+
+{
+ let prev = Temporal.Now.instant();
+ for (let i = 0; i < 100; i++) {
+ const curr = Temporal.Now.instant();
+ const cmp = Temporal.Instant.compare(prev, curr);
+ if (cmp > 0) throw new Error(`Now.instant() not monotonic at iteration ${i}`);
+ prev = curr;
+ }
+}
diff --git a/JSTests/stress/temporal-int32-overflow-duration.js b/JSTests/stress/temporal-int32-overflow-duration.js
new file mode 100644
index 000000000000..0bcf212f5ed8
--- /dev/null
+++ b/JSTests/stress/temporal-int32-overflow-duration.js
@@ -0,0 +1,52 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldThrow(fn, type) {
+ let err;
+ try { fn(); } catch (e) { err = e; }
+ if (!(err instanceof type))
+ throw new Error(`Expected ${type.name} but got ${err}`);
+}
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`expected ${JSON.stringify(expected)} but got ${JSON.stringify(actual)}`);
+}
+
+// Bug 1: calendarDateAdd (ICU bridge) cast int64_t duration fields to int32_t before
+// passing to ucal_add. 4294967295 = UINT32_MAX truncates to int32_t(-1), so
+// 2020 + (-1) = 2019 instead of RangeError.
+
+// PlainYearMonth.add — large positive years
+shouldThrow(() => Temporal.PlainYearMonth.from("2020-01").add({ years: 4294967295 }), RangeError);
+shouldThrow(() => Temporal.PlainYearMonth.from("2020-01").add({ years: 2147483648 }), RangeError); // INT32_MAX + 1
+shouldThrow(() => Temporal.PlainYearMonth.from("2020-01").add({ years: 1e15 }), RangeError);
+
+// PlainYearMonth.add — large negative years
+shouldThrow(() => Temporal.PlainYearMonth.from("2020-01").add({ years: -4294967295 }), RangeError);
+shouldThrow(() => Temporal.PlainYearMonth.from("2020-01").add({ years: -2147483649 }), RangeError); // INT32_MIN - 1
+
+// PlainYearMonth.add — large months overflow
+shouldThrow(() => Temporal.PlainYearMonth.from("2020-01").add({ months: 4294967295 }), RangeError);
+
+// PlainDate.add — same overflow path through calendarDateAdd
+shouldThrow(() => Temporal.PlainDate.from("2020-01-01").add({ years: 4294967295 }), RangeError);
+shouldThrow(() => Temporal.PlainDate.from("2020-01-01").add({ months: 4294967295 }), RangeError);
+
+// Bug 2: plainYearMonthAdd ISO path incorrectly routed through ICU bridge, which
+// clamps extreme boundary dates silently. -271821 is Temporal's minimum year;
+// ICU's calendar doesn't support it and produces wrong results instead of RangeError.
+
+// Near-minimum boundary: results that push internal ISO date out of range → RangeError
+shouldThrow(() => Temporal.PlainYearMonth.from("-271821-04").add({ months: 1 }), RangeError);
+
+// Near-maximum boundary
+shouldThrow(() => Temporal.PlainYearMonth.from("+275760-09").add({ months: 1 }), RangeError);
+
+// Valid boundary operations still work
+shouldBe(Temporal.PlainYearMonth.from("-271821-04").toString(), "-271821-04");
+shouldBe(Temporal.PlainYearMonth.from("+275760-09").toString(), "+275760-09");
+
+// Sanity: normal values still work
+shouldBe(Temporal.PlainYearMonth.from("2020-01").add({ years: 1 }).toString(), "2021-01");
+shouldBe(Temporal.PlainYearMonth.from("2020-01").add({ months: 13 }).toString(), "2021-02");
+shouldBe(Temporal.PlainYearMonth.from("-271821-04").toString(), "-271821-04");
diff --git a/JSTests/stress/temporal-iso8601-parse-bounds.js b/JSTests/stress/temporal-iso8601-parse-bounds.js
new file mode 100644
index 000000000000..64340cd63046
--- /dev/null
+++ b/JSTests/stress/temporal-iso8601-parse-bounds.js
@@ -0,0 +1,88 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(actual, expected, msg) {
+ if (actual !== expected)
+ throw new Error(`${msg}: expected ${expected} but got ${actual}`);
+}
+
+function shouldThrowRangeError(fn, msg) {
+ let thrown = null;
+ try { fn(); } catch (e) { thrown = e; }
+ if (!thrown)
+ throw new Error(`${msg}: expected RangeError but nothing was thrown`);
+ if (!(thrown instanceof RangeError))
+ throw new Error(`${msg}: expected RangeError but got ${thrown}`);
+}
+
+// ISO8601::parseDate() used to read past the end of the buffer for short
+// truncated strings: month/day length guards only applied to the Date format.
+// All of these must throw RangeError instead of crashing.
+{
+ const crashers = [
+ ["PlainDate", "0"],
+ ["PlainDate", "1"],
+ ["PlainDate", "12"],
+ ["PlainDate", "123"],
+ ["PlainDate", "12-"],
+ ["Instant", "1"],
+ ["PlainTime", "1"],
+ ["PlainDateTime", "12"],
+ ["PlainMonthDay", "1"],
+ ["PlainMonthDay", "12"],
+ ["PlainMonthDay", "12-"],
+ ["PlainMonthDay", "121"],
+ ["PlainMonthDay", "--1"],
+ ["PlainMonthDay", "--12"],
+ ["PlainMonthDay", "--12-"],
+ ["PlainMonthDay", "--121"],
+ ["PlainMonthDay", "02-2"],
+ ["PlainMonthDay", "2024-01"],
+ ["PlainMonthDay", "202401"],
+ ["PlainMonthDay", "2021-12"],
+ ["PlainMonthDay", "2024-05"],
+ ["PlainYearMonth", "2024-0"],
+ ["PlainYearMonth", "2024-01-"],
+ ["PlainYearMonth", "2024-01-1"],
+ ["PlainYearMonth", "202401-"],
+ ["PlainYearMonth", "2024011"],
+ ["PlainYearMonth", "12-1"],
+ ["ZonedDateTime", "1"],
+ ["ZonedDateTime", "12"],
+ ["PlainDate", "2024-0"],
+ ["PlainDate", "2024-01-"],
+ ["PlainDate", "2024-01-1"],
+ ["PlainDate", "1976111"],
+ ["PlainDate", "19761"],
+ ];
+ for (const [type, string] of crashers)
+ shouldThrowRangeError(() => Temporal[type].from(string), `${type}.from(${JSON.stringify(string)})`);
+}
+
+// Valid strings must keep working.
+{
+ shouldBe(Temporal.PlainDate.from("2024-01-15").toString(), "2024-01-15", "PlainDate hyphenated");
+ shouldBe(Temporal.PlainDate.from("20240115").toString(), "2024-01-15", "PlainDate compact");
+ shouldBe(Temporal.PlainDate.from("+002024-01-15").toString(), "2024-01-15", "PlainDate extended year");
+ shouldBe(Temporal.PlainDate.from("2024-01-15T12:30").toString(), "2024-01-15", "PlainDate with time");
+ shouldBe(Temporal.PlainDate.from("2024-01-01[u-ca=iso8601]").toString(), "2024-01-01", "calendar annotation");
+
+ shouldBe(Temporal.PlainYearMonth.from("2024-01").toString(), "2024-01", "YearMonth hyphenated");
+ shouldBe(Temporal.PlainYearMonth.from("202401").toString(), "2024-01", "YearMonth compact");
+ shouldBe(Temporal.PlainYearMonth.from("2024-01[u-ca=iso8601]").toString(), "2024-01", "YearMonth annotated");
+ shouldBe(Temporal.PlainYearMonth.from("19761118").toString(), "1976-11", "YearMonth compact full date");
+ shouldBe(Temporal.PlainYearMonth.from("1976-11-18").toString(), "1976-11", "YearMonth full date");
+ shouldBe(Temporal.PlainYearMonth.from("1976-11-18T15:23:30").toString(), "1976-11", "YearMonth full datetime");
+ shouldBe(Temporal.PlainYearMonth.from("19761118T15:23").toString(), "1976-11", "YearMonth compact datetime");
+
+ shouldBe(Temporal.PlainMonthDay.from("12-14").toString(), "12-14", "MonthDay hyphenated");
+ shouldBe(Temporal.PlainMonthDay.from("1214").toString(), "12-14", "MonthDay compact");
+ shouldBe(Temporal.PlainMonthDay.from("--12-14").toString(), "12-14", "MonthDay double hyphen");
+ shouldBe(Temporal.PlainMonthDay.from("--1214").toString(), "12-14", "MonthDay double hyphen compact");
+ shouldBe(Temporal.PlainMonthDay.from("1130[u-ca=iso8601]").toString(), "11-30", "MonthDay annotated");
+ shouldBe(Temporal.PlainMonthDay.from("1118[+01:00]").toString(), "11-18", "MonthDay timezone annotation");
+ shouldBe(Temporal.PlainMonthDay.from("1976-11-18").toString(), "11-18", "MonthDay full date");
+ shouldBe(Temporal.PlainMonthDay.from("1976-11-18T15:23:30").toString(), "11-18", "MonthDay full datetime");
+
+ shouldBe(Temporal.PlainDateTime.from("2024-01-15T12:30").toString(), "2024-01-15T12:30:00", "PlainDateTime");
+ shouldBe(Temporal.ZonedDateTime.from("2024-01-15T12:30[UTC]").toString(), "2024-01-15T12:30:00+00:00[UTC]", "ZonedDateTime");
+}
diff --git a/JSTests/stress/temporal-japanese-ce-bce-era-validation.js b/JSTests/stress/temporal-japanese-ce-bce-era-validation.js
new file mode 100644
index 000000000000..e6dcdfbcebf6
--- /dev/null
+++ b/JSTests/stress/temporal-japanese-ce-bce-era-validation.js
@@ -0,0 +1,123 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`expected ${JSON.stringify(expected)} but got ${JSON.stringify(actual)}`);
+}
+
+function shouldThrow(func, errorType) {
+ let threw = false;
+ try { func(); } catch (e) {
+ threw = true;
+ if (!(e instanceof errorType))
+ throw new Error(`Expected ${errorType.name} but got ${e.constructor.name}: ${e.message}`);
+ }
+ if (!threw)
+ throw new Error(`Expected ${errorType.name} but no exception was thrown`);
+}
+
+// Japanese "ce"/"bce" era fast path: out-of-range month must be constrained
+// (not read out of bounds and produce dates like "2000-255-00").
+{
+ const r = Temporal.PlainDate.from({ calendar: "japanese", era: "ce", eraYear: 2000, month: 255, day: 1 });
+ shouldBe(r.toString(), "2000-12-01[u-ca=japanese]");
+ shouldBe(r.month, 12);
+ shouldBe(r.day, 1);
+}
+{
+ const r = Temporal.PlainDate.from({ calendar: "japanese", era: "ce", eraYear: 2000, month: 13, day: 1 });
+ shouldBe(r.toString(), "2000-12-01[u-ca=japanese]");
+}
+{
+ // ISO year 0 (bce 1); assert via toString since the year getter goes
+ // through ICU and is subject to Julian/Gregorian switch quirks near year 0.
+ const r = Temporal.PlainDate.from({ calendar: "japanese", era: "bce", eraYear: 1, month: 255, day: 40 });
+ shouldBe(r.toString(), "0000-12-31[u-ca=japanese]");
+ shouldBe(r.month, 12);
+ shouldBe(r.day, 31);
+}
+shouldThrow(() => {
+ Temporal.PlainDate.from({ calendar: "japanese", era: "ce", eraYear: 2000, month: 255, day: 1 }, { overflow: "reject" });
+}, RangeError);
+shouldThrow(() => {
+ Temporal.PlainDate.from({ calendar: "japanese", era: "ce", eraYear: 2000, month: 13, day: 1 }, { overflow: "reject" });
+}, RangeError);
+
+// PlainYearMonth.from through the same fast path.
+{
+ const r = Temporal.PlainYearMonth.from({ calendar: "japanese", era: "ce", eraYear: 2000, month: 255 });
+ shouldBe(r.year, 2000);
+ shouldBe(r.month, 12);
+}
+shouldThrow(() => {
+ Temporal.PlainYearMonth.from({ calendar: "japanese", era: "ce", eraYear: 2000, month: 255 }, { overflow: "reject" });
+}, RangeError);
+
+// ZonedDateTime.with / PlainDateTime.with reach the fast path without the
+// fields-layer rough year range check, so the fast path itself must validate.
+{
+ const z = Temporal.ZonedDateTime.from("2000-01-01T00:00[UTC][u-ca=japanese]");
+ shouldBe(z.with({ era: "ce", eraYear: 2000, month: 255 }).toString(), "2000-12-01T00:00:00+00:00[UTC][u-ca=japanese]");
+ // eraYear INT32_MAX silently truncated the 21-bit ISO year field (year became -1).
+ shouldThrow(() => { z.with({ era: "ce", eraYear: 2147483647 }); }, RangeError);
+ // 1 - INT32_MIN overflows int32.
+ shouldThrow(() => { z.with({ era: "bce", eraYear: -2147483648 }); }, RangeError);
+ shouldThrow(() => { z.with({ era: "ce", eraYear: 300000 }); }, RangeError);
+ shouldThrow(() => { z.with({ era: "bce", eraYear: 300000 }); }, RangeError);
+}
+{
+ const pdt = Temporal.PlainDateTime.from("2000-01-01T00:00[u-ca=japanese]");
+ shouldBe(pdt.with({ era: "ce", eraYear: 2000, month: 255 }).toString(), "2000-12-01T00:00:00[u-ca=japanese]");
+ shouldThrow(() => { pdt.with({ era: "ce", eraYear: 2147483647 }); }, RangeError);
+ shouldThrow(() => { pdt.with({ era: "bce", eraYear: -2147483648 }); }, RangeError);
+}
+
+// eraYear out of ISO year limits must throw RangeError (Debug builds asserted
+// in the ISO8601::PlainDate constructor before the fix).
+shouldThrow(() => {
+ Temporal.PlainDate.from({ calendar: "japanese", era: "ce", eraYear: 300000, month: 1, day: 1 });
+}, RangeError);
+shouldThrow(() => {
+ Temporal.PlainDate.from({ calendar: "japanese", era: "bce", eraYear: 300000, month: 1, day: 1 });
+}, RangeError);
+shouldThrow(() => {
+ Temporal.PlainDate.from({ calendar: "japanese", era: "ce", eraYear: 275761, month: 1, day: 1 });
+}, RangeError);
+shouldThrow(() => {
+ Temporal.PlainDate.from({ calendar: "japanese", era: "bce", eraYear: 271823, month: 1, day: 1 });
+}, RangeError);
+
+// Boundary years remain accepted.
+{
+ const r = Temporal.PlainDate.from({ calendar: "japanese", era: "ce", eraYear: 275760, month: 9, day: 13 });
+ shouldBe(r.year, 275760);
+ shouldBe(r.month, 9);
+ shouldBe(r.day, 13);
+}
+{
+ const r = Temporal.PlainDate.from({ calendar: "japanese", era: "bce", eraYear: 271822, month: 4, day: 19 });
+ shouldBe(r.toString(), "-271821-04-19[u-ca=japanese]");
+ shouldBe(r.month, 4);
+ shouldBe(r.day, 19);
+}
+
+// Normal ce/bce dates still resolve correctly.
+{
+ const r = Temporal.PlainDate.from({ calendar: "japanese", era: "ce", eraYear: 2000, month: 2, day: 29 });
+ shouldBe(r.toString(), "2000-02-29[u-ca=japanese]");
+ // The era getters report the canonical era for the date, not the input.
+ shouldBe(r.era, "heisei");
+ shouldBe(r.eraYear, 12);
+}
+{
+ // bce 1 = ISO year 0, which is a leap year.
+ const r = Temporal.PlainDate.from({ calendar: "japanese", era: "bce", eraYear: 1, month: 2, day: 29 });
+ shouldBe(r.year, 0);
+ shouldBe(r.month, 2);
+ shouldBe(r.day, 29);
+}
+
+// year-consistency check with era+eraYear still applies.
+shouldThrow(() => {
+ Temporal.PlainDate.from({ calendar: "japanese", era: "ce", eraYear: 2000, year: 1999, month: 1, day: 1 });
+}, RangeError);
diff --git a/JSTests/stress/temporal-month-boundary-arithmetic.js b/JSTests/stress/temporal-month-boundary-arithmetic.js
new file mode 100644
index 000000000000..64c457deb12a
--- /dev/null
+++ b/JSTests/stress/temporal-month-boundary-arithmetic.js
@@ -0,0 +1,89 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(a, b, msg) {
+ if (a !== b) throw new Error(`${msg}: expected ${b}, got ${a}`);
+}
+
+{
+ const jan31 = Temporal.PlainDate.from("2024-01-31");
+ const result = jan31.add({ months: 1 });
+ shouldBe(result.month, 2, "Jan 31 + 1M = Feb");
+ shouldBe(result.day, 29, "Jan 31 + 1M = Feb 29 (2024 leap)");
+}
+
+{
+ const jan31 = Temporal.PlainDate.from("2023-01-31");
+ const result = jan31.add({ months: 1 });
+ shouldBe(result.month, 2, "Jan 31 + 1M = Feb (non-leap)");
+ shouldBe(result.day, 28, "Jan 31 + 1M = Feb 28 (2023)");
+}
+
+{
+ const feb29 = Temporal.PlainDate.from("2024-02-29");
+ const result = feb29.add({ years: 1 });
+ shouldBe(result.year, 2025, "Feb 29 + 1Y = 2025");
+ shouldBe(result.day, 28, "Feb 29 + 1Y = Feb 28 (constrain)");
+}
+
+{
+ const jan31 = Temporal.PlainDate.from("2024-01-31");
+ const feb = jan31.add({ months: 1 }); // Feb 29
+ const mar = feb.add({ months: 1 }); // Mar 29 (not 31!)
+ shouldBe(mar.month, 3, "chain month");
+ shouldBe(mar.day, 29, "chain day preserves constrained day");
+}
+
+{
+ const jan1 = Temporal.PlainDate.from("2024-01-01");
+ const result = jan1.subtract({ days: 1 });
+ shouldBe(result.year, 2023, "Jan 1 - 1d = Dec 31 prev year");
+ shouldBe(result.month, 12, "Dec");
+ shouldBe(result.day, 31, "31st");
+}
+
+{
+ const nov15 = Temporal.PlainDate.from("2024-11-15");
+ const result = nov15.add({ months: 3 });
+ shouldBe(result.year, 2025, "Nov + 3M = Feb next year");
+ shouldBe(result.month, 2, "February");
+ shouldBe(result.day, 15, "day preserved");
+}
+
+{
+ const jan31 = Temporal.PlainDate.from("2024-01-31");
+ const mar31 = Temporal.PlainDate.from("2024-03-31");
+ const dur = jan31.until(mar31, { largestUnit: "months" });
+ shouldBe(dur.months, 2, "Jan 31 to Mar 31 = 2 months");
+ shouldBe(dur.days, 0, "exact month boundary");
+}
+
+{
+ const jan31 = Temporal.PlainDate.from("2024-01-31");
+ const feb29 = Temporal.PlainDate.from("2024-02-29");
+ const dur = jan31.until(feb29, { largestUnit: "months" });
+ shouldBe(dur.months, 0, "Jan 31 to Feb 29 = 0 months + days");
+ shouldBe(dur.days, 29, "29 days");
+}
+
+{
+ const pdt = Temporal.PlainDateTime.from("2024-01-31T23:30:00");
+ const result = pdt.add({ months: 1, hours: 1 });
+ shouldBe(result.month, 3, "Jan 31 23:30 + 1M1H = Mar 1 00:30");
+ shouldBe(result.day, 1, "day rolled to 1st");
+ shouldBe(result.hour, 0, "hour rolled from 23+1=24→0");
+}
+
+{
+ const mon = Temporal.PlainDate.from("2024-01-01"); // Monday
+ const result = mon.add({ weeks: 1 });
+ shouldBe(result.day, 8, "+1 week = +7 days");
+ shouldBe(result.dayOfWeek, 1, "same day of week (Monday)");
+}
+
+{
+ const mar15 = Temporal.PlainDate.from("2024-03-15");
+ const result = mar15.subtract({ days: 45 });
+ shouldBe(result.year, 2024, "year");
+ shouldBe(result.month, 1, "Jan (skipped Feb)");
+ shouldBe(result.day, 30, "Jan 30");
+}
diff --git a/JSTests/stress/temporal-month-overflow-truncation.js b/JSTests/stress/temporal-month-overflow-truncation.js
new file mode 100644
index 000000000000..70a659da3041
--- /dev/null
+++ b/JSTests/stress/temporal-month-overflow-truncation.js
@@ -0,0 +1,55 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldThrow(fn, errorType, desc) {
+ let threw = false;
+ try { fn(); } catch(e) {
+ threw = true;
+ if (!(e instanceof errorType))
+ throw new Error(desc + ": expected " + errorType.name + " got " + e.constructor.name + ": " + e.message);
+ }
+ if (!threw) throw new Error(desc + ": expected " + errorType.name + " but did not throw");
+}
+
+function shouldBe(a, e) { if (a !== e) throw new Error("expected " + JSON.stringify(e) + " got " + JSON.stringify(a)); }
+
+// month=257 should NOT silently wrap to month=1 via uint8_t truncation.
+
+// ZonedDateTime.with() era branch: month=257 with overflow:reject must throw.
+{
+ const zdt = Temporal.ZonedDateTime.from("2023-05-01T00:00+09:00[Asia/Tokyo][u-ca=japanese]");
+ shouldThrow(
+ () => zdt.with({ era: "reiwa", eraYear: 5, month: 257 }, { overflow: "reject" }),
+ RangeError, "ZDT.with month=257 reject"
+ );
+ // With constrain: should clamp to valid month range, not wrap to 1.
+ const r = zdt.with({ era: "reiwa", eraYear: 5, month: 257 }, { overflow: "constrain" });
+ if (r.month === 1) throw new Error("month=257 constrain wrapped to 1 — truncation bug");
+}
+
+// PlainDate.with() non-ISO: month=257 with overflow:reject must throw.
+{
+ const pd = Temporal.PlainDate.from({ era: "reiwa", eraYear: 5, month: 1, day: 1, calendar: "japanese" });
+ shouldThrow(
+ () => pd.with({ month: 257 }, { overflow: "reject" }),
+ RangeError, "PlainDate.with month=257 reject"
+ );
+}
+
+// PlainDate.from() non-ISO: month=257 with overflow:reject must throw.
+{
+ shouldThrow(
+ () => Temporal.PlainDate.from({ era: "reiwa", eraYear: 5, month: 257, day: 1, calendar: "japanese" }, { overflow: "reject" }),
+ RangeError, "PlainDate.from month=257 reject"
+ );
+}
+
+// ISO PlainDate: month=257 reject throws.
+{
+ shouldThrow(
+ () => Temporal.PlainDate.from({ year: 2023, month: 257, day: 1 }, { overflow: "reject" }),
+ RangeError, "PlainDate.from ISO month=257 reject"
+ );
+ // ISO constrain: clamps to 12.
+ const r = Temporal.PlainDate.from({ year: 2023, month: 257, day: 1 }, { overflow: "constrain" });
+ shouldBe(r.month, 12);
+}
diff --git a/JSTests/stress/temporal-monthday-parsing-timezone.js b/JSTests/stress/temporal-monthday-parsing-timezone.js
new file mode 100644
index 000000000000..d5d2898d9027
--- /dev/null
+++ b/JSTests/stress/temporal-monthday-parsing-timezone.js
@@ -0,0 +1,45 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`expected ${JSON.stringify(expected)} but got ${JSON.stringify(actual)}`);
+}
+
+function shouldThrow(fn, type) {
+ let err;
+ try { fn(); } catch (e) { err = e; }
+ if (!(err instanceof type))
+ throw new Error(`Expected ${type.name} but got ${err}`);
+}
+
+// Bug: canBeYear() returned true for "1118[+01:00]" because it only checked
+// 4 leading digits and length >= 6, without verifying that the character
+// after the digits is '-' or a digit (not '['). This caused the parser to
+// try to interpret "1118" as year 1118, fail to find a month/day, and reject
+// the whole string instead of treating it as MMDD with a timezone annotation.
+
+// Compact MMDD format with timezone annotation — must be accepted
+shouldBe(Temporal.PlainMonthDay.from("1118[+01:00]").toString(), "11-18");
+shouldBe(Temporal.PlainMonthDay.from("1118[-05:00]").toString(), "11-18");
+shouldBe(Temporal.PlainMonthDay.from("0101[UTC]").toString(), "01-01");
+shouldBe(Temporal.PlainMonthDay.from("1231[America/New_York]").toString(), "12-31");
+
+// Already-working formats remain correct
+shouldBe(Temporal.PlainMonthDay.from("1118").toString(), "11-18");
+shouldBe(Temporal.PlainMonthDay.from("11-18").toString(), "11-18");
+shouldBe(Temporal.PlainMonthDay.from("--11-18").toString(), "11-18");
+shouldBe(Temporal.PlainMonthDay.from("--1118").toString(), "11-18");
+shouldBe(Temporal.PlainMonthDay.from("11-18[+01:00]").toString(), "11-18");
+shouldBe(Temporal.PlainMonthDay.from("--11-18[+01:00]").toString(), "11-18");
+shouldBe(Temporal.PlainMonthDay.from("1972-11-18[+01:00]").toString(), "11-18");
+
+// Full year with timezone annotation still parses correctly as full date
+shouldBe(Temporal.PlainMonthDay.from("1972-11-18[+01:00]").monthCode, "M11");
+shouldBe(Temporal.PlainMonthDay.from("2020-06-15[America/Los_Angeles]").toString(), "06-15");
+
+// canBeYear boundary: 4-digit string followed by digit/hyphen is still a year
+shouldBe(Temporal.PlainMonthDay.from("1972-11-18").toString(), "11-18");
+
+// Invalid strings still throw
+shouldThrow(() => Temporal.PlainMonthDay.from("9999[+01:00]"), RangeError); // month 99 invalid
+shouldThrow(() => Temporal.PlainMonthDay.from("0000[+01:00]"), RangeError); // month 00 invalid
diff --git a/JSTests/stress/temporal-monthday-refyear.js b/JSTests/stress/temporal-monthday-refyear.js
new file mode 100644
index 000000000000..a77c210e6665
--- /dev/null
+++ b/JSTests/stress/temporal-monthday-refyear.js
@@ -0,0 +1,93 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`expected ${JSON.stringify(expected)} but got ${JSON.stringify(actual)}`);
+}
+
+function shouldThrow(fn, type) {
+ let err;
+ try { fn(); } catch (e) { err = e; }
+ if (!(err instanceof type))
+ throw new Error(`Expected ${type.name} but got ${err}`);
+}
+
+// Bug: Multiple issues in ecmaReferenceYear and calendarDateFromFields for non-ISO calendars:
+// 1. Non-lunisolar calendars (gregory, buddhist, japanese, roc, coptic, ethiopic, etc.)
+// accepted leap month codes (M01L etc.) instead of throwing RangeError.
+// 2. ecmaReferenceYear tables had errors:
+// - Chinese M01L/M12L: wrong year (1898) → should be UseRegularIfConstrain
+// - Chinese month 11 bigDay: wrong condition order (day>26 checked before bigDay)
+// - Ethiopic: used Coptic AM years instead of Ethiopic years (+276 offset)
+// - Hebrew: only M05L (Adar I) valid; others should throw MonthNotInCalendar
+// - Islamic-civil/tbla: used UmmAlQura table instead of Tabular table
+// 3. calendarDateFromFields Reject validation: compared UCAL_MONTH+1 and IS_LEAP_MONTH
+// instead of using computeMonthCode for lunisolar calendars (Hebrew M05L gave wrong slot).
+
+// ── Non-lunisolar calendars reject ALL leap month codes ──────────────────
+for (const cal of ["gregory", "buddhist", "japanese", "roc", "coptic", "ethiopic",
+ "ethioaa", "persian", "indian"]) {
+ shouldThrow(() => Temporal.PlainMonthDay.from({monthCode:"M01L", day:1, calendar:cal}), RangeError);
+ shouldThrow(() => Temporal.PlainMonthDay.from({monthCode:"M06L", day:1, calendar:cal}), RangeError);
+}
+
+// ── Chinese/Dangi: M01L and M12L use constrain fallback ──────────────────
+// M01L constrain → use M01 reference year. M01L reject → throw.
+shouldBe(Temporal.PlainMonthDay.from({monthCode:"M01L", day:1, calendar:"chinese"}).toString(), "1972-02-15[u-ca=chinese]");
+shouldBe(Temporal.PlainMonthDay.from({monthCode:"M01L", day:30, calendar:"chinese"}).toString(), "1970-03-07[u-ca=chinese]");
+shouldThrow(() => Temporal.PlainMonthDay.from({monthCode:"M01L", day:1, calendar:"chinese"}, {overflow:"reject"}), RangeError);
+shouldBe(Temporal.PlainMonthDay.from({monthCode:"M12L", day:1, calendar:"chinese"}).toString(), "1972-01-16[u-ca=chinese]");
+shouldThrow(() => Temporal.PlainMonthDay.from({monthCode:"M12L", day:1, calendar:"chinese"}, {overflow:"reject"}), RangeError);
+
+// Valid Chinese leap months work in both modes
+shouldBe(Temporal.PlainMonthDay.from({monthCode:"M04L", day:1, calendar:"chinese"}).toString(), "1963-05-23[u-ca=chinese]");
+shouldBe(Temporal.PlainMonthDay.from({monthCode:"M04L", day:1, calendar:"chinese"}, {overflow:"reject"}).toString(), "1963-05-23[u-ca=chinese]");
+
+// ── Chinese month 11 bigDay: year 1969, not 1971 ──────────────────────────
+shouldBe(Temporal.PlainMonthDay.from({monthCode:"M11", day:30, calendar:"chinese"}).toString(), "1970-01-07[u-ca=chinese]");
+shouldBe(Temporal.PlainMonthDay.from({monthCode:"M11", day:27, calendar:"chinese"}).toString(), "1972-01-13[u-ca=chinese]");
+shouldBe(Temporal.PlainMonthDay.from({monthCode:"M11", day:1, calendar:"chinese"}).toString(), "1972-12-06[u-ca=chinese]");
+
+// ── Ethiopic: uses year offset +276 from Coptic ───────────────────────────
+shouldBe(Temporal.PlainMonthDay.from({monthCode:"M01", day:1, calendar:"ethiopic"}).toString(), "1972-09-11[u-ca=ethiopic]");
+shouldBe(Temporal.PlainMonthDay.from({monthCode:"M01", day:1, calendar:"coptic"}).toString(), "1972-09-11[u-ca=coptic]");
+
+// ── Hebrew: M05L (Adar I) only valid leap month ────────────────────────────
+shouldBe(Temporal.PlainMonthDay.from({monthCode:"M05L", day:1, calendar:"hebrew"}).toString(), "1970-02-07[u-ca=hebrew]");
+shouldBe(Temporal.PlainMonthDay.from({monthCode:"M05L", day:1, calendar:"hebrew"}, {overflow:"reject"}).toString(), "1970-02-07[u-ca=hebrew]");
+// Hebrew M01L, M02L, etc. don't exist in Hebrew calendar
+shouldThrow(() => Temporal.PlainMonthDay.from({monthCode:"M01L", day:1, calendar:"hebrew"}), RangeError);
+shouldThrow(() => Temporal.PlainMonthDay.from({monthCode:"M07L", day:1, calendar:"hebrew"}), RangeError);
+
+// ── Islamic-civil/tbla: use tabular table, not UmmAlQura ─────────────────
+// Tabular: month 2 bigDay (day=30) uses year 1392; UmmAlQura uses 1390
+shouldBe(Temporal.PlainMonthDay.from({monthCode:"M02", day:30, calendar:"islamic-civil"}).toString(), "1972-04-14[u-ca=islamic-civil]");
+shouldBe(Temporal.PlainMonthDay.from({monthCode:"M02", day:30, calendar:"islamic-tbla"}).toString(), "1972-04-13[u-ca=islamic-tbla]");
+// UmmAlQura M02 day=30 → year 1390
+shouldBe(Temporal.PlainMonthDay.from({monthCode:"M02", day:29, calendar:"islamic-civil"}).toString(), "1972-04-14[u-ca=islamic-civil]");
+
+// ── Dangi (Chinese alias): same lunisolar behaviour as Chinese ─────────────
+shouldBe(Temporal.PlainMonthDay.from({monthCode:"M01L", day:1, calendar:"dangi"}).toString(), "1972-02-15[u-ca=dangi]");
+shouldThrow(() => Temporal.PlainMonthDay.from({monthCode:"M01L", day:1, calendar:"dangi"}, {overflow:"reject"}), RangeError);
+shouldBe(Temporal.PlainMonthDay.from({monthCode:"M05L", day:1, calendar:"dangi"}).toString(), "1971-06-23[u-ca=dangi]");
+shouldBe(Temporal.PlainMonthDay.from({monthCode:"M01", day:1, calendar:"dangi"}).toString(), "1972-02-15[u-ca=dangi]");
+
+// ── Persian (non-lunisolar): reject all leap month codes ──────────────────
+shouldThrow(() => Temporal.PlainMonthDay.from({monthCode:"M01L", day:1, calendar:"persian"}), RangeError);
+shouldThrow(() => Temporal.PlainMonthDay.from({monthCode:"M07L", day:1, calendar:"persian"}), RangeError);
+shouldBe(Temporal.PlainMonthDay.from({monthCode:"M01", day:1, calendar:"persian"}).toString(), "1972-03-21[u-ca=persian]");
+shouldBe(Temporal.PlainMonthDay.from({monthCode:"M07", day:30, calendar:"persian"}).toString(), "1972-10-22[u-ca=persian]");
+
+// ── Islamic-umalqura (non-lunisolar for month codes): reject leap month codes ─
+shouldThrow(() => Temporal.PlainMonthDay.from({monthCode:"M01L", day:1, calendar:"islamic-umalqura"}), RangeError);
+shouldBe(Temporal.PlainMonthDay.from({monthCode:"M01", day:1, calendar:"islamic-umalqura"}).toString(), "1972-02-16[u-ca=islamic-umalqura]");
+
+// ── roc, japanese (non-lunisolar): reject leap month codes ───────────────
+shouldThrow(() => Temporal.PlainMonthDay.from({monthCode:"M01L", day:1, calendar:"roc"}), RangeError);
+shouldBe(Temporal.PlainMonthDay.from({monthCode:"M01", day:1, calendar:"roc"}).toString(), "1972-01-01[u-ca=roc]");
+shouldThrow(() => Temporal.PlainMonthDay.from({monthCode:"M01L", day:1, calendar:"japanese"}), RangeError);
+shouldBe(Temporal.PlainMonthDay.from({monthCode:"M01", day:1, calendar:"japanese"}).toString(), "1972-01-01[u-ca=japanese]");
+
+// ── Indian (non-lunisolar): reject leap month codes ───────────────────────
+shouldThrow(() => Temporal.PlainMonthDay.from({monthCode:"M01L", day:1, calendar:"indian"}), RangeError);
+shouldBe(Temporal.PlainMonthDay.from({monthCode:"M01", day:1, calendar:"indian"}).toString(), "1972-03-21[u-ca=indian]");
\ No newline at end of file
diff --git a/JSTests/stress/temporal-nan-fields.js b/JSTests/stress/temporal-nan-fields.js
new file mode 100644
index 000000000000..f1860be3b363
--- /dev/null
+++ b/JSTests/stress/temporal-nan-fields.js
@@ -0,0 +1,133 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldThrow(fn, type) {
+ let err;
+ try { fn(); } catch (e) { err = e; }
+ if (!(err instanceof type))
+ throw new Error(`Expected ${type.name} but got ${err}`);
+}
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`expected ${JSON.stringify(expected)} but got ${JSON.stringify(actual)}`);
+}
+
+// Spec uses ToIntegerWithTruncation (throws on NaN/+/-Infinity) for year, eraYear, and time
+// fields; ToPositiveIntegerWithTruncation (also rejects ≤0) for month and day. Previously
+// JSC used toIntegerOrInfinity which silently maps NaN -> 0, letting NaN inputs slip through
+// across PlainDate/PlainTime/PlainMonthDay/PlainYearMonth/PlainDateTime/ZonedDateTime/Duration.
+
+// ---- PlainDate.from ----
+shouldThrow(() => Temporal.PlainDate.from({ year: NaN, month: 1, day: 1 }), RangeError);
+shouldThrow(() => Temporal.PlainDate.from({ year: 2024, month: NaN, day: 1 }), RangeError);
+shouldThrow(() => Temporal.PlainDate.from({ year: 2024, month: 1, day: NaN }), RangeError);
+shouldThrow(() => Temporal.PlainDate.from({ era: "ce", eraYear: NaN, month: 1, day: 1, calendar: "gregory" }), RangeError);
+
+// ---- PlainDate.prototype.with ----
+{
+ const pd = Temporal.PlainDate.from("2024-01-15");
+ shouldThrow(() => pd.with({ year: NaN }), RangeError);
+ shouldThrow(() => pd.with({ month: NaN }), RangeError);
+ shouldThrow(() => pd.with({ day: NaN }), RangeError);
+}
+
+// ---- PlainDateTime.prototype.with ----
+{
+ const pdt = Temporal.PlainDateTime.from("2024-01-15T12:30:45");
+ shouldThrow(() => pdt.with({ year: NaN }), RangeError);
+ shouldThrow(() => pdt.with({ month: NaN }), RangeError);
+ shouldThrow(() => pdt.with({ day: NaN }), RangeError);
+ shouldThrow(() => pdt.with({ hour: NaN }), RangeError);
+ shouldThrow(() => pdt.with({ minute: NaN }), RangeError);
+ shouldThrow(() => pdt.with({ second: NaN }), RangeError);
+ shouldThrow(() => pdt.with({ millisecond: NaN }), RangeError);
+ shouldThrow(() => pdt.with({ microsecond: NaN }), RangeError);
+ shouldThrow(() => pdt.with({ nanosecond: NaN }), RangeError);
+ const jpdt = pdt.withCalendar("japanese");
+ shouldThrow(() => jpdt.with({ eraYear: NaN }), RangeError);
+}
+
+// ---- PlainTime.from / .with (all six time fields) ----
+shouldThrow(() => Temporal.PlainTime.from({ hour: NaN }), RangeError);
+shouldThrow(() => Temporal.PlainTime.from({ minute: NaN }), RangeError);
+shouldThrow(() => Temporal.PlainTime.from({ second: NaN }), RangeError);
+shouldThrow(() => Temporal.PlainTime.from({ millisecond: NaN }), RangeError);
+shouldThrow(() => Temporal.PlainTime.from({ microsecond: NaN }), RangeError);
+shouldThrow(() => Temporal.PlainTime.from({ nanosecond: NaN }), RangeError);
+{
+ const pt = Temporal.PlainTime.from({ hour: 12 });
+ shouldThrow(() => pt.with({ hour: NaN }), RangeError);
+ shouldThrow(() => pt.with({ minute: NaN }), RangeError);
+ shouldThrow(() => pt.with({ second: NaN }), RangeError);
+ shouldThrow(() => pt.with({ millisecond: NaN }), RangeError);
+ shouldThrow(() => pt.with({ microsecond: NaN }), RangeError);
+ shouldThrow(() => pt.with({ nanosecond: NaN }), RangeError);
+}
+
+// ---- PlainMonthDay.from / .with ----
+shouldThrow(() => Temporal.PlainMonthDay.from({ month: NaN, day: 1 }), RangeError);
+shouldThrow(() => Temporal.PlainMonthDay.from({ month: 1, day: NaN }), RangeError);
+shouldThrow(() => Temporal.PlainMonthDay.from({ era: "ce", eraYear: NaN, month: 1, day: 1, calendar: "gregory" }), RangeError);
+{
+ const pmd = Temporal.PlainMonthDay.from({ month: 5, day: 15 });
+ shouldThrow(() => pmd.with({ day: NaN }), RangeError);
+ shouldThrow(() => pmd.with({ month: NaN, day: 15 }), RangeError);
+ const jpmd = Temporal.PlainMonthDay.from({ monthCode: "M05", day: 15, calendar: "japanese" });
+ shouldThrow(() => jpmd.with({ eraYear: NaN }), RangeError);
+}
+
+// ---- PlainYearMonth.from / .with ----
+shouldThrow(() => Temporal.PlainYearMonth.from({ year: NaN, month: 1 }), RangeError);
+shouldThrow(() => Temporal.PlainYearMonth.from({ year: 2024, month: NaN }), RangeError);
+shouldThrow(() => Temporal.PlainYearMonth.from({ era: "ce", eraYear: NaN, month: 1, calendar: "gregory" }), RangeError);
+{
+ const pym = Temporal.PlainYearMonth.from("2024-01");
+ shouldThrow(() => pym.with({ year: NaN }), RangeError);
+ shouldThrow(() => pym.with({ month: NaN }), RangeError);
+}
+
+// ---- ZonedDateTime.from / .with (calendar fields, time fields, era fields) ----
+shouldThrow(() => Temporal.ZonedDateTime.from({ year: NaN, month: 1, day: 1, timeZone: "UTC" }), RangeError);
+shouldThrow(() => Temporal.ZonedDateTime.from({ year: 2024, month: NaN, day: 1, timeZone: "UTC" }), RangeError);
+shouldThrow(() => Temporal.ZonedDateTime.from({ year: 2024, month: 1, day: NaN, timeZone: "UTC" }), RangeError);
+shouldThrow(() => Temporal.ZonedDateTime.from({ year: 2024, month: 1, day: 1, hour: NaN, timeZone: "UTC" }), RangeError);
+shouldThrow(() => Temporal.ZonedDateTime.from({ year: 2024, month: 1, day: 1, minute: NaN, timeZone: "UTC" }), RangeError);
+shouldThrow(() => Temporal.ZonedDateTime.from({ year: 2024, month: 1, day: 1, second: NaN, timeZone: "UTC" }), RangeError);
+shouldThrow(() => Temporal.ZonedDateTime.from({ year: 2024, month: 1, day: 1, millisecond: NaN, timeZone: "UTC" }), RangeError);
+shouldThrow(() => Temporal.ZonedDateTime.from({ year: 2024, month: 1, day: 1, microsecond: NaN, timeZone: "UTC" }), RangeError);
+shouldThrow(() => Temporal.ZonedDateTime.from({ year: 2024, month: 1, day: 1, nanosecond: NaN, timeZone: "UTC" }), RangeError);
+shouldThrow(() => Temporal.ZonedDateTime.from({ era: "ce", eraYear: NaN, month: 1, day: 1, calendar: "gregory", timeZone: "UTC" }), RangeError);
+{
+ const zdt = Temporal.ZonedDateTime.from({ year: 2024, month: 1, day: 1, timeZone: "UTC" });
+ shouldThrow(() => zdt.with({ year: NaN }), RangeError);
+ shouldThrow(() => zdt.with({ hour: NaN }), RangeError);
+ shouldThrow(() => zdt.with({ nanosecond: NaN }), RangeError);
+}
+
+// ---- Duration relativeTo: nested date object property reads via TemporalDuration.cpp ----
+{
+ const dur = Temporal.Duration.from({ days: 5 });
+ shouldThrow(() => dur.round({ largestUnit: "weeks", relativeTo: { year: NaN, month: 1, day: 1 } }), RangeError);
+ shouldThrow(() => dur.round({ largestUnit: "weeks", relativeTo: { year: 2024, month: NaN, day: 1 } }), RangeError);
+ shouldThrow(() => dur.round({ largestUnit: "weeks", relativeTo: { year: 2024, month: 1, day: NaN } }), RangeError);
+ shouldThrow(() => dur.round({ largestUnit: "weeks", relativeTo: { year: 2024, month: 1, day: 1, hour: NaN, timeZone: "UTC" } }), RangeError);
+ shouldThrow(() => dur.round({ largestUnit: "weeks", relativeTo: { year: 2024, month: 1, day: 1, nanosecond: NaN, timeZone: "UTC" } }), RangeError);
+ shouldThrow(() => dur.round({ largestUnit: "weeks", relativeTo: { era: "ce", eraYear: NaN, month: 1, day: 1, calendar: "gregory" } }), RangeError);
+}
+
+// ---- +/-Infinity continues to throw at the same sites ----
+shouldThrow(() => Temporal.PlainDate.from({ year: Infinity, month: 1, day: 1 }), RangeError);
+shouldThrow(() => Temporal.PlainTime.from({ hour: -Infinity }), RangeError);
+shouldThrow(() => Temporal.PlainYearMonth.from({ year: Infinity, month: 1 }), RangeError);
+shouldThrow(() => Temporal.ZonedDateTime.from({ year: 2024, month: 1, day: 1, nanosecond: Infinity, timeZone: "UTC" }), RangeError);
+
+// ---- Sanity: valid integer-like values still succeed (regression guard) ----
+shouldBe(Temporal.PlainDate.from({ year: 2024, month: 1, day: 1 }).toString(), "2024-01-01");
+shouldBe(Temporal.PlainTime.from({ hour: 1.7, minute: 2.9 }).toString(), "01:02:00");
+shouldBe(Temporal.PlainYearMonth.from({ year: 2024, month: 5 }).toString(), "2024-05");
+shouldBe(Temporal.PlainMonthDay.from({ month: 5, day: 15 }).toString(), "05-15");
+shouldBe(Temporal.PlainDateTime.from({ year: 2024, month: 1, day: 1 }).toString(), "2024-01-01T00:00:00");
+shouldBe(Temporal.ZonedDateTime.from({ year: 2024, month: 1, day: 1, timeZone: "UTC" }).toString(), "2024-01-01T00:00:00+00:00[UTC]");
+// Year 0 and negative year are valid (ToIntegerWithTruncation, not ToPositive).
+shouldBe(Temporal.PlainDate.from({ year: 0, month: 1, day: 1 }).year, 0);
+shouldBe(Temporal.PlainDate.from({ year: -1, month: 1, day: 1 }).year, -1);
diff --git a/JSTests/stress/temporal-non-iso-calendar.js b/JSTests/stress/temporal-non-iso-calendar.js
new file mode 100644
index 000000000000..365edf9dadfe
--- /dev/null
+++ b/JSTests/stress/temporal-non-iso-calendar.js
@@ -0,0 +1,106 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(a, b, msg) {
+ if (a !== b) throw new Error(`${msg}: expected ${b}, got ${a}`);
+}
+
+function shouldBeUndefined(a, msg) {
+ if (a !== undefined) throw new Error(`${msg}: expected undefined, got ${a}`);
+}
+
+{
+ const pd = new Temporal.PlainDate(2024, 3, 15, "gregory");
+ shouldBe(pd.calendarId, "gregory", "gregory calendarId");
+ shouldBe(pd.era, "ce", "gregory era CE");
+ shouldBe(pd.eraYear, 2024, "gregory eraYear");
+ shouldBe(pd.year, 2024, "gregory year");
+}
+
+{
+ const pd = new Temporal.PlainDate(-43, 3, 15, "gregory");
+ shouldBe(pd.era, "bce", "gregory era BCE");
+ shouldBe(pd.eraYear, 44, "gregory BCE eraYear (44 BC)");
+}
+
+{
+ // Reiwa era started 2019-05-01
+ const reiwa = new Temporal.PlainDate(2024, 1, 1, "japanese");
+ shouldBe(reiwa.era, "reiwa", "japanese era reiwa");
+ shouldBe(reiwa.eraYear, 6, "reiwa year 6 (2024)");
+}
+
+{
+ // Heisei era: 1989-01-08 to 2019-04-30
+ const heisei = new Temporal.PlainDate(2018, 6, 1, "japanese");
+ shouldBe(heisei.era, "heisei", "japanese era heisei");
+ shouldBe(heisei.eraYear, 30, "heisei year 30 (2018)");
+}
+
+{
+ const pd = new Temporal.PlainDate(2024, 1, 1, "buddhist");
+ shouldBe(pd.era, "be", "buddhist era");
+ shouldBe(pd.eraYear, 2567, "buddhist year 2567 (2024 CE)");
+}
+
+{
+ const pd = Temporal.PlainDate.from("2024-03-15");
+ shouldBeUndefined(pd.era, "iso8601 era is undefined");
+ shouldBeUndefined(pd.eraYear, "iso8601 eraYear is undefined");
+}
+
+{
+ const pd = new Temporal.PlainDate(2024, 2, 10, "chinese");
+ shouldBeUndefined(pd.era, "chinese era is undefined");
+ shouldBeUndefined(pd.eraYear, "chinese eraYear is undefined");
+ shouldBe(pd.calendarId, "chinese", "chinese calendarId");
+}
+
+{
+ const pd = new Temporal.PlainDate(2024, 1, 1, "hebrew");
+ shouldBe(pd.era, "am", "hebrew era");
+ shouldBe(pd.calendarId, "hebrew", "hebrew calendarId");
+}
+
+{
+ const pd = new Temporal.PlainDate(2024, 1, 1, "roc");
+ shouldBe(pd.era, "roc", "roc era");
+}
+
+{
+ const pd = new Temporal.PlainDate(2024, 1, 1, "persian");
+ shouldBe(pd.era, "ap", "persian era");
+}
+
+{
+ const pd = new Temporal.PlainDate(2024, 1, 1, "islamic-umalqura");
+ shouldBe(pd.era, "ah", "islamic era");
+}
+
+{
+ // February 2024 in gregory: 29 days (leap year)
+ const feb = new Temporal.PlainDate(2024, 2, 1, "gregory");
+ shouldBe(feb.daysInMonth, 29, "gregory Feb 2024 has 29 days");
+
+ // February 2023 in gregory: 28 days
+ const feb23 = new Temporal.PlainDate(2023, 2, 1, "gregory");
+ shouldBe(feb23.daysInMonth, 28, "gregory Feb 2023 has 28 days");
+}
+
+{
+ const leap = new Temporal.PlainDate(2024, 1, 1, "gregory");
+ shouldBe(leap.inLeapYear, true, "2024 is leap year in gregory");
+
+ const nonLeap = new Temporal.PlainDate(2023, 1, 1, "gregory");
+ shouldBe(nonLeap.inLeapYear, false, "2023 is not leap year");
+}
+
+{
+ const pd = new Temporal.PlainDate(2024, 3, 15, "gregory");
+ shouldBe(pd.calendarId, "gregory", "constructor calendar argument");
+ shouldBe(pd.era, "ce", "constructor gregory era");
+}
+
+{
+ const pd = new Temporal.PlainDate(2024, 3, 15, "Gregory");
+ shouldBe(pd.calendarId, "gregory", "calendar is case-insensitive");
+}
diff --git a/JSTests/stress/temporal-plain-date-time-nan-fields.js b/JSTests/stress/temporal-plain-date-time-nan-fields.js
new file mode 100644
index 000000000000..9e26f81b9be6
--- /dev/null
+++ b/JSTests/stress/temporal-plain-date-time-nan-fields.js
@@ -0,0 +1,51 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldThrow(fn, type) {
+ let err;
+ try { fn(); } catch (e) { err = e; }
+ if (!(err instanceof type))
+ throw new Error(`Expected ${type.name} but got ${err}`);
+}
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`expected ${JSON.stringify(expected)} but got ${JSON.stringify(actual)}`);
+}
+
+// Spec uses ToIntegerWithTruncation for year, eraYear, and time fields. ToIntegerWithTruncation
+// throws RangeError on NaN/+Infinity/-Infinity. Previously, JSC used toIntegerOrInfinity which
+// silently maps NaN -> 0, letting NaN inputs slip through.
+shouldThrow(() => Temporal.PlainDateTime.from({ year: NaN, month: 1, day: 1 }), RangeError);
+shouldThrow(() => Temporal.PlainDateTime.from({ year: 2024, month: 1, day: 1, hour: NaN }), RangeError);
+shouldThrow(() => Temporal.PlainDateTime.from({ year: 2024, month: 1, day: 1, minute: NaN }), RangeError);
+shouldThrow(() => Temporal.PlainDateTime.from({ year: 2024, month: 1, day: 1, second: NaN }), RangeError);
+shouldThrow(() => Temporal.PlainDateTime.from({ year: 2024, month: 1, day: 1, millisecond: NaN }), RangeError);
+shouldThrow(() => Temporal.PlainDateTime.from({ year: 2024, month: 1, day: 1, microsecond: NaN }), RangeError);
+shouldThrow(() => Temporal.PlainDateTime.from({ year: 2024, month: 1, day: 1, nanosecond: NaN }), RangeError);
+
+// +/-Infinity continues to throw (was already covered by isFinite check).
+shouldThrow(() => Temporal.PlainDateTime.from({ year: Infinity, month: 1, day: 1 }), RangeError);
+shouldThrow(() => Temporal.PlainDateTime.from({ year: -Infinity, month: 1, day: 1 }), RangeError);
+shouldThrow(() => Temporal.PlainDateTime.from({ year: 2024, month: 1, day: 1, hour: Infinity }), RangeError);
+shouldThrow(() => Temporal.PlainDateTime.from({ year: 2024, month: 1, day: 1, nanosecond: -Infinity }), RangeError);
+
+// eraYear with NaN throws (japanese calendar uses era/eraYear).
+shouldThrow(() => Temporal.PlainDateTime.from({ era: "reiwa", eraYear: NaN, month: 1, day: 1, calendar: "japanese" }), RangeError);
+
+// Day and month use ToPositiveIntegerWithTruncation: NaN/Infinity AND <=0 all throw,
+// regardless of overflow option. (Existing spec behavior, kept by this change.)
+shouldThrow(() => Temporal.PlainDateTime.from({ year: 2024, month: 1, day: NaN }), RangeError);
+shouldThrow(() => Temporal.PlainDateTime.from({ year: 2024, month: NaN, day: 1 }), RangeError);
+shouldThrow(() => Temporal.PlainDateTime.from({ year: 2024, month: 1, day: 0 }, { overflow: "constrain" }), RangeError);
+shouldThrow(() => Temporal.PlainDateTime.from({ year: 2024, month: 0, day: 1 }, { overflow: "constrain" }), RangeError);
+shouldThrow(() => Temporal.PlainDateTime.from({ year: 2024, month: 1, day: -5 }, { overflow: "constrain" }), RangeError);
+shouldThrow(() => Temporal.PlainDateTime.from({ year: 2024, month: -1, day: 1 }, { overflow: "constrain" }), RangeError);
+
+// Sanity: valid integer-like values still succeed.
+shouldBe(Temporal.PlainDateTime.from({ year: 2024, month: 1, day: 1 }).toString(), "2024-01-01T00:00:00");
+shouldBe(Temporal.PlainDateTime.from({ year: 2024, month: 1, day: 1, hour: 1.7, minute: 2.9 }).toString(), "2024-01-01T01:02:00");
+
+// Year=0 is valid (ToIntegerWithTruncation, not ToPositive).
+shouldBe(Temporal.PlainDateTime.from({ year: 0, month: 1, day: 1 }).year, 0);
+// Negative year is valid.
+shouldBe(Temporal.PlainDateTime.from({ year: -1, month: 1, day: 1 }).year, -1);
diff --git a/JSTests/stress/temporal-plain-date-time-with-era-calendar.js b/JSTests/stress/temporal-plain-date-time-with-era-calendar.js
new file mode 100644
index 000000000000..f7e5948b7e25
--- /dev/null
+++ b/JSTests/stress/temporal-plain-date-time-with-era-calendar.js
@@ -0,0 +1,83 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`expected ${JSON.stringify(expected)} but got ${JSON.stringify(actual)}`);
+}
+
+function shouldThrow(func, errorType) {
+ let threw = false;
+ try { func(); } catch (e) {
+ threw = true;
+ if (!(e instanceof errorType))
+ throw new Error(`Expected ${errorType.name} but got ${e.constructor.name}: ${e.message}`);
+ }
+ if (!threw)
+ throw new Error(`Expected ${errorType.name} but no exception was thrown`);
+}
+
+// era+eraYear changes year (Showa 45 → Showa 50 = 1975), time preserved
+{
+ const pdt = Temporal.PlainDateTime.from({ era: "showa", eraYear: 45, month: 1, day: 1, hour: 12, calendar: "japanese" });
+ const r = pdt.with({ era: "showa", eraYear: 50 });
+ shouldBe(r.year, 1975);
+ shouldBe(r.era, "showa");
+ shouldBe(r.eraYear, 50);
+ shouldBe(r.hour, 12);
+}
+
+// year-only: era re-derived from resolved date
+{
+ const pdt = Temporal.PlainDateTime.from({ era: "showa", eraYear: 45, month: 6, day: 15, hour: 10, minute: 30, calendar: "japanese" });
+ const r = pdt.with({ year: 1975 });
+ shouldBe(r.year, 1975);
+ shouldBe(r.era, "showa");
+ shouldBe(r.eraYear, 50);
+ shouldBe(r.hour, 10);
+ shouldBe(r.minute, 30);
+}
+
+// Japanese month change → era re-derived (Showa 64 Jan → month=6 → Heisei 1 Jun)
+{
+ const pdt = Temporal.PlainDateTime.from({ era: "showa", eraYear: 64, month: 1, day: 7, hour: 8, calendar: "japanese" });
+ const r = pdt.with({ month: 6 });
+ shouldBe(r.year, 1989);
+ shouldBe(r.era, "heisei");
+ shouldBe(r.eraYear, 1);
+ shouldBe(r.month, 6);
+ shouldBe(r.hour, 8);
+}
+
+// time-only change — date preserved including era
+{
+ const pdt = Temporal.PlainDateTime.from({ era: "showa", eraYear: 50, month: 6, day: 15, hour: 9, calendar: "japanese" });
+ const r = pdt.with({ hour: 17, minute: 30 });
+ shouldBe(r.year, 1975);
+ shouldBe(r.era, "showa");
+ shouldBe(r.eraYear, 50);
+ shouldBe(r.hour, 17);
+ shouldBe(r.minute, 30);
+}
+
+// era only (no eraYear) → TypeError
+{
+ const pdt = Temporal.PlainDateTime.from({ era: "showa", eraYear: 45, month: 1, day: 1, calendar: "japanese" });
+ shouldThrow(() => pdt.with({ era: "showa" }), TypeError);
+}
+
+// era+eraYear+year inconsistent → RangeError
+{
+ const pdt = Temporal.PlainDateTime.from({ era: "showa", eraYear: 45, month: 1, day: 1, calendar: "japanese" });
+ shouldThrow(() => pdt.with({ era: "showa", eraYear: 50, year: 2000 }), RangeError);
+}
+
+// iso8601 path unchanged
+{
+ const pdt = new Temporal.PlainDateTime(2024, 3, 14, 9, 30, 0);
+ const r = pdt.with({ year: 2025, hour: 12 });
+ shouldBe(r.year, 2025);
+ shouldBe(r.month, 3);
+ shouldBe(r.day, 14);
+ shouldBe(r.hour, 12);
+ shouldBe(r.minute, 30);
+}
diff --git a/JSTests/stress/temporal-plain-date-with-era-calendar.js b/JSTests/stress/temporal-plain-date-with-era-calendar.js
new file mode 100644
index 000000000000..4bdff3e58115
--- /dev/null
+++ b/JSTests/stress/temporal-plain-date-with-era-calendar.js
@@ -0,0 +1,100 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`expected ${JSON.stringify(expected)} but got ${JSON.stringify(actual)}`);
+}
+
+function shouldThrow(func, errorType) {
+ let threw = false;
+ try { func(); } catch (e) {
+ threw = true;
+ if (!(e instanceof errorType))
+ throw new Error(`Expected ${errorType.name} but got ${e.constructor.name}: ${e.message}`);
+ }
+ if (!threw)
+ throw new Error(`Expected ${errorType.name} but no exception was thrown`);
+}
+
+// PlainDate.with() era+eraYear — Showa 50 = 1975
+{
+ const pd = Temporal.PlainDate.from({ era: "showa", eraYear: 45, month: 1, day: 1, calendar: "japanese" });
+ shouldBe(pd.era, "showa");
+ shouldBe(pd.eraYear, 45);
+ const r = pd.with({ era: "showa", eraYear: 50 });
+ shouldBe(r.year, 1975);
+ shouldBe(r.era, "showa");
+ shouldBe(r.eraYear, 50);
+ shouldBe(r.month, 1);
+ shouldBe(r.day, 1);
+}
+
+// Switch era: Heisei 5 = 1993
+{
+ const pd = Temporal.PlainDate.from({ era: "showa", eraYear: 45, month: 6, day: 15, calendar: "japanese" });
+ const r = pd.with({ era: "heisei", eraYear: 5 });
+ shouldBe(r.year, 1993);
+ shouldBe(r.era, "heisei");
+ shouldBe(r.eraYear, 5);
+ shouldBe(r.month, 6);
+ shouldBe(r.day, 15);
+}
+
+// era+eraYear+year consistent → OK
+{
+ const pd = Temporal.PlainDate.from({ era: "showa", eraYear: 45, month: 1, day: 1, calendar: "japanese" });
+ const r = pd.with({ era: "showa", eraYear: 50, year: 1975 });
+ shouldBe(r.year, 1975);
+ shouldBe(r.eraYear, 50);
+}
+
+// era+eraYear+year inconsistent → RangeError
+{
+ const pd = Temporal.PlainDate.from({ era: "showa", eraYear: 45, month: 1, day: 1, calendar: "japanese" });
+ shouldThrow(() => pd.with({ era: "showa", eraYear: 50, year: 2000 }), RangeError);
+}
+
+// era only (no eraYear) → TypeError
+{
+ const pd = Temporal.PlainDate.from({ era: "showa", eraYear: 45, month: 1, day: 1, calendar: "japanese" });
+ shouldThrow(() => pd.with({ era: "showa" }), TypeError);
+}
+
+// eraYear only (no era) → TypeError
+{
+ const pd = Temporal.PlainDate.from({ era: "showa", eraYear: 45, month: 1, day: 1, calendar: "japanese" });
+ shouldThrow(() => pd.with({ eraYear: 50 }), TypeError);
+}
+
+// Changing only day (no era) — era inherited from base
+{
+ const pd = Temporal.PlainDate.from({ era: "showa", eraYear: 50, month: 6, day: 15, calendar: "japanese" });
+ const r = pd.with({ day: 20 });
+ shouldBe(r.year, 1975);
+ shouldBe(r.era, "showa");
+ shouldBe(r.eraYear, 50);
+ shouldBe(r.day, 20);
+}
+
+// iso8601 path unchanged
+{
+ const pd = new Temporal.PlainDate(2020, 3, 14);
+ const r = pd.with({ year: 2024 });
+ shouldBe(r.year, 2024);
+ shouldBe(r.month, 3);
+ shouldBe(r.day, 14);
+}
+
+// Japanese: changing month/day re-derives era (eras don't align with year boundaries).
+// Showa 64 Jan 7 = 1989-01-07. Heisei started Jan 8 1989.
+// Changing month to June: era re-derives to heisei (not inherited showa).
+{
+ const pd = Temporal.PlainDate.from({ era: "showa", eraYear: 64, month: 1, day: 7, calendar: "japanese" });
+ shouldBe(pd.era, "showa");
+ shouldBe(pd.eraYear, 64);
+ const r = pd.with({ month: 6 });
+ shouldBe(r.year, 1989);
+ shouldBe(r.era, "heisei");
+ shouldBe(r.eraYear, 1);
+ shouldBe(r.month, 6);
+}
diff --git a/JSTests/stress/temporal-plain-month-day-with-era-calendar.js b/JSTests/stress/temporal-plain-month-day-with-era-calendar.js
new file mode 100644
index 000000000000..2329780b37b7
--- /dev/null
+++ b/JSTests/stress/temporal-plain-month-day-with-era-calendar.js
@@ -0,0 +1,54 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`expected ${JSON.stringify(expected)} but got ${JSON.stringify(actual)}`);
+}
+
+function shouldThrow(func, errorType) {
+ let threw = false;
+ try { func(); } catch (e) {
+ threw = true;
+ if (!(e instanceof errorType))
+ throw new Error(`Expected ${errorType.name} but got ${e.constructor.name}: ${e.message}`);
+ }
+ if (!threw)
+ throw new Error(`Expected ${errorType.name} but no exception was thrown`);
+}
+
+// MonthDay with japanese calendar — era-aware with()
+{
+ const md = Temporal.PlainMonthDay.from({ monthCode: "M01", day: 1, calendar: "japanese" });
+ const result = md.with({ day: 15 });
+ shouldBe(result.day, 15);
+ shouldBe(result.monthCode, "M01");
+}
+
+// era+eraYear + monthCode, no year: internally monthDayFromFields computes a non-zero
+// ecmaReferenceYear but must NOT trigger the year-consistency check — that only applies
+// to user-provided year. Regresses if CalendarFields.cpp passes ecmaReferenceYear instead of 0.
+{
+ const md = Temporal.PlainMonthDay.from({ era: "showa", eraYear: 50, monthCode: "M06", day: 15, calendar: "japanese" });
+ shouldBe(md.monthCode, "M06");
+ shouldBe(md.day, 15);
+}
+
+// era+eraYear + monthCode + consistent year → OK
+{
+ const md = Temporal.PlainMonthDay.from({ era: "showa", eraYear: 50, monthCode: "M06", day: 15, year: 1975, calendar: "japanese" });
+ shouldBe(md.monthCode, "M06");
+ shouldBe(md.day, 15);
+}
+
+// era+eraYear + monthCode + inconsistent year → RangeError
+{
+ shouldThrow(() => Temporal.PlainMonthDay.from({ era: "showa", eraYear: 50, monthCode: "M06", day: 15, year: 2000, calendar: "japanese" }), RangeError);
+}
+
+// iso8601 (no eras) still works
+{
+ const md = new Temporal.PlainMonthDay(3, 14);
+ const result = md.with({ day: 20 });
+ shouldBe(result.monthCode, "M03");
+ shouldBe(result.day, 20);
+}
diff --git a/JSTests/stress/temporal-plain-year-month-with-era-calendar.js b/JSTests/stress/temporal-plain-year-month-with-era-calendar.js
new file mode 100644
index 000000000000..126dfabc97f2
--- /dev/null
+++ b/JSTests/stress/temporal-plain-year-month-with-era-calendar.js
@@ -0,0 +1,41 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`expected ${JSON.stringify(expected)} but got ${JSON.stringify(actual)}`);
+}
+
+// era+eraYear must be provided together; Showa 10 (1935) is unambiguously mid-Showa.
+{
+ const ym = Temporal.PlainYearMonth.from({ era: "showa", eraYear: 10, month: 6, calendar: "japanese" });
+ shouldBe(ym.year, 1935);
+ shouldBe(ym.era, "showa");
+ shouldBe(ym.eraYear, 10);
+
+ // Change era+eraYear together — Showa 50 = 1975.
+ const r1 = ym.with({ era: "showa", eraYear: 50 });
+ shouldBe(r1.year, 1975);
+ shouldBe(r1.eraYear, 50);
+ shouldBe(r1.month, 6);
+
+ // Switch to a different era — Heisei 5 = 1993.
+ const r2 = ym.with({ era: "heisei", eraYear: 5 });
+ shouldBe(r2.year, 1993);
+ shouldBe(r2.eraYear, 5);
+ shouldBe(r2.month, 6);
+
+ // Change month only — no era fields — base era/eraYear preserved through merge.
+ const r3 = ym.with({ month: 9 });
+ shouldBe(r3.year, 1935);
+ shouldBe(r3.era, "showa");
+ shouldBe(r3.eraYear, 10);
+ shouldBe(r3.month, 9);
+}
+
+// iso8601 (no eras) still works — era/eraYear must not be read for this calendar.
+{
+ const ym = new Temporal.PlainYearMonth(2020, 3);
+ const result = ym.with({ year: 2024 });
+ shouldBe(result.year, 2024);
+ shouldBe(result.month, 3);
+}
diff --git a/JSTests/stress/temporal-plaindate-add-huge-days-out-of-range.js b/JSTests/stress/temporal-plaindate-add-huge-days-out-of-range.js
new file mode 100644
index 000000000000..b12dd8c9b5b2
--- /dev/null
+++ b/JSTests/stress/temporal-plaindate-add-huge-days-out-of-range.js
@@ -0,0 +1,24 @@
+//@ requireOptions("--useTemporal=1")
+
+// Adding/subtracting a huge number of days must throw RangeError instead of
+// crashing (Debug ASSERT: day >= 1 && day <= 31 in ExactTime::fromISOPartsAndOffset).
+
+function shouldThrow(func, errorType) {
+ let threw = false;
+ try { func(); } catch (e) {
+ threw = true;
+ if (!(e instanceof errorType))
+ throw new Error(`Expected ${errorType.name} but got ${e.constructor.name}: ${e.message}`);
+ }
+ if (!threw)
+ throw new Error(`Expected ${errorType.name} but no exception was thrown`);
+}
+
+shouldThrow(() => new Temporal.PlainDate(2024, 1, 1).subtract({ days: 149645246 }), RangeError);
+shouldThrow(() => new Temporal.PlainDate(2024, 1, 1).subtract({ days: 2000000000 }), RangeError);
+shouldThrow(() => new Temporal.PlainDate(2024, 1, 1).add({ days: 149645246 }), RangeError);
+shouldThrow(() => new Temporal.PlainDate(2024, 1, 1).add({ days: 2000000000 }), RangeError);
+
+shouldThrow(() => {
+ Temporal.Duration.from({ hours: -4294967295 }).round({ smallestUnit: "years", roundingMode: "halfEven", relativeTo: "2020-02-29" });
+}, RangeError);
diff --git a/JSTests/stress/temporal-plaindate-to-yearmonth-monthday-calendar.js b/JSTests/stress/temporal-plaindate-to-yearmonth-monthday-calendar.js
new file mode 100644
index 000000000000..2b5d92c9aed9
--- /dev/null
+++ b/JSTests/stress/temporal-plaindate-to-yearmonth-monthday-calendar.js
@@ -0,0 +1,35 @@
+//@ requireOptions("--useTemporal=1")
+
+// PlainDate.toPlainYearMonth()/toPlainMonthDay() calendar propagation.
+// Calendar propagation for non-ISO types must be preserved across conversions.
+
+function assert(cond, msg) {
+ if (!cond) throw new Error("FAIL: " + msg);
+}
+
+// Hebrew PlainDate: 1 Tishri 5784 = 2023-09-16 ISO
+const pd = Temporal.PlainDate.from({ year: 5784, month: 1, day: 1, calendar: "hebrew" });
+assert(pd.calendarId === "hebrew", "calendarId should be hebrew");
+
+// toPlainYearMonth: CalendarYearMonthFromFields should canonicalize the ISO reference
+// date to the first day of that Hebrew month (1 Tishri 5784 = 2023-09-16).
+const pym = pd.toPlainYearMonth();
+assert(pym.calendarId === "hebrew",
+ "toPlainYearMonth calendarId should be hebrew, got: " + pym.calendarId);
+// The PYM's reference ISO date should be 2023-09-16 (start of Tishri 5784), not
+// (5784, 1, 1) in raw ISO which would be a wildly wrong date.
+const pymBack = pym.toPlainDate({ day: 1 });
+assert(pymBack.calendarId === "hebrew", "round-trip calendarId should be hebrew");
+assert(pymBack.year === 5784, "round-trip year should be 5784, got: " + pymBack.year);
+assert(pymBack.month === 1, "round-trip month should be 1, got: " + pymBack.month);
+
+// toPlainMonthDay: CalendarMonthDayFromFields should pick the correct reference ISO year.
+const pmd = pd.toPlainMonthDay();
+assert(pmd.calendarId === "hebrew",
+ "toPlainMonthDay calendarId should be hebrew, got: " + pmd.calendarId);
+// Round-trip: PMD.toPlainDate with year 5784 should give back 1 Tishri 5784.
+const pmdBack = pmd.toPlainDate({ year: 5784 });
+assert(pmdBack.calendarId === "hebrew", "round-trip calendarId should be hebrew");
+assert(pmdBack.year === 5784, "round-trip year should be 5784, got: " + pmdBack.year);
+assert(pmdBack.month === 1, "round-trip month should be 1, got: " + pmdBack.month);
+assert(pmdBack.day === 1, "round-trip day should be 1, got: " + pmdBack.day);
diff --git a/JSTests/stress/temporal-plaindatetime-calendar-propagation.js b/JSTests/stress/temporal-plaindatetime-calendar-propagation.js
new file mode 100644
index 000000000000..3cbede03f75f
--- /dev/null
+++ b/JSTests/stress/temporal-plaindatetime-calendar-propagation.js
@@ -0,0 +1,27 @@
+//@ requireOptions("--useTemporal=1")
+
+// PlainDateTime calendar propagation for non-ISO types across conversions.
+// Calendar propagation for non-ISO types must be preserved across conversions.
+
+function assert(cond, msg) {
+ if (!cond) throw new Error("FAIL: " + msg);
+}
+
+// 1. PlainDate with non-ISO calendar -> toPlainDateTime must keep the calendar.
+const pd = Temporal.PlainDate.from({ year: 5784, month: 7, day: 1, calendar: "hebrew" });
+assert(pd.calendarId === "hebrew", "PlainDate calendarId should be hebrew");
+
+const pdt = pd.toPlainDateTime();
+assert(pdt.calendarId === "hebrew",
+ "PlainDateTime from PlainDate must preserve calendarId, got: " + pdt.calendarId);
+
+// 2. Temporal.PlainDateTime.from(hebrewPlainDate) must preserve calendar.
+const pdt2 = Temporal.PlainDateTime.from(pd);
+assert(pdt2.calendarId === "hebrew",
+ "PlainDateTime.from(PlainDate) must preserve calendarId, got: " + pdt2.calendarId);
+
+// 3. ZonedDateTime with non-ISO calendar -> toPlainDateTime must keep the calendar.
+const zdt = Temporal.ZonedDateTime.from("2024-03-15T12:00:00[UTC][u-ca=hebrew]");
+assert(zdt.calendarId === "hebrew", "ZonedDateTime calendarId should be hebrew");
+const pdtFromZdt = zdt.toPlainDateTime();
+assert(pdtFromZdt.calendarId === "hebrew", "PlainDateTime from ZDT must preserve calendarId");
diff --git a/JSTests/stress/temporal-plainmonthday-from-large-string.js b/JSTests/stress/temporal-plainmonthday-from-large-string.js
new file mode 100644
index 000000000000..188cb0098324
--- /dev/null
+++ b/JSTests/stress/temporal-plainmonthday-from-large-string.js
@@ -0,0 +1,18 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldThrow(func, errorType) {
+ let error;
+ try {
+ func();
+ } catch (e) {
+ error = e;
+ }
+ if (!(error instanceof errorType))
+ throw new Error(`Expected ${errorType.name} but got ${error}`);
+}
+
+shouldThrow(() => Temporal.PlainMonthDay.from("a".repeat(1 << 20)), RangeError);
+shouldThrow(() => Temporal.PlainMonthDay.from("x".repeat(1 << 16), { overflow: "constrain" }), RangeError);
+
+shouldThrow(() => Temporal.PlainMonthDay.from("not-a-month-day"), RangeError);
+shouldThrow(() => Temporal.PlainMonthDay.from(""), RangeError);
diff --git a/JSTests/stress/temporal-plainmonthday-to-plaindate-calendar.js b/JSTests/stress/temporal-plainmonthday-to-plaindate-calendar.js
new file mode 100644
index 000000000000..a2b93a2b4089
--- /dev/null
+++ b/JSTests/stress/temporal-plainmonthday-to-plaindate-calendar.js
@@ -0,0 +1,33 @@
+//@ requireOptions("--useTemporal=1")
+
+// PlainMonthDay.toPlainDate() calendar propagation for non-ISO types.
+// Calendar propagation for non-ISO types must be preserved across conversions.
+
+function assert(cond, msg) {
+ if (!cond) throw new Error("FAIL: " + msg);
+}
+
+// Create a Hebrew PlainMonthDay: month 1 (Tishri), day 1.
+// The PMD's reference ISO date should be 2023-09-16 (1 Tishri 5784 in a non-leap year).
+const pmd = Temporal.PlainMonthDay.from({ monthCode: "M01", day: 1, calendar: "hebrew" });
+assert(pmd.calendarId === "hebrew", "calendarId should be hebrew");
+
+// toPlainDate with year 5784: should give 1 Tishri 5784 = 2023-09-16.
+const pd = pmd.toPlainDate({ year: 5784 });
+assert(pd.calendarId === "hebrew",
+ "toPlainDate calendarId should be hebrew, got: " + pd.calendarId);
+assert(pd.year === 5784, "year should be 5784, got: " + pd.year);
+assert(pd.month === 1, "month should be 1 (Tishri), got: " + pd.month);
+assert(pd.day === 1, "day should be 1, got: " + pd.day);
+
+// Verify the ISO date is correct: 1 Tishri 5784 = 2023-09-16.
+// If JSC uses raw regulateISODate it would produce a wrong ISO date.
+assert(pd.toPlainDateTime().toString().startsWith("2023-09-16"),
+ "ISO date of 1 Tishri 5784 should be 2023-09-16, got: " + pd.toPlainDateTime().toString());
+
+// toPlainDate with year 5785 (next year): should give 1 Tishri 5785 = 2024-10-03.
+const pd2 = pmd.toPlainDate({ year: 5785 });
+assert(pd2.year === 5785, "year should be 5785, got: " + pd2.year);
+assert(pd2.month === 1, "month should be 1, got: " + pd2.month);
+assert(pd2.toPlainDateTime().toString().startsWith("2024-10-03"),
+ "ISO date of 1 Tishri 5785 should be 2024-10-03, got: " + pd2.toPlainDateTime().toString());
diff --git a/JSTests/stress/temporal-plainyearmonth-constructor-exception-check.js b/JSTests/stress/temporal-plainyearmonth-constructor-exception-check.js
new file mode 100644
index 000000000000..1fb9ff7935c7
--- /dev/null
+++ b/JSTests/stress/temporal-plainyearmonth-constructor-exception-check.js
@@ -0,0 +1,11 @@
+//@ requireOptions("--useTemporal=1", "--validateExceptionChecks=1")
+
+function opt(a1) {
+ var a2 = new Temporal.PlainYearMonth(((NaN * a1 <= 0).__proto__ = "\\u{1F4A9}ba"), 1, 'chinese', 1);
+ a3 = a1;
+ let a4 = a4.withPlainTime(a3);
+ throw a1;
+}
+try { opt((function* () { }.constructor)) } catch (y) { }
+try { opt(9007199254740991) } catch (y) { }
+try { opt("\\u{10428}\\u{10000}x") } catch (y) { }
diff --git a/JSTests/stress/temporal-pre-lmt-timezone.js b/JSTests/stress/temporal-pre-lmt-timezone.js
new file mode 100644
index 000000000000..2a07aa1973bd
--- /dev/null
+++ b/JSTests/stress/temporal-pre-lmt-timezone.js
@@ -0,0 +1,86 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`expected ${JSON.stringify(expected)} but got ${JSON.stringify(actual)}`);
+}
+// Bug: getPossibleEpochNanosecondsFor returned 0 candidates (false gap) for pre-LMT dates
+// in named timezones. ICU4C's ucal_setDateTime snaps year 1385 → 1884 (first recorded year),
+// making the consistency check fail (1884-offset ≠ 1385-midnight) and returning empty.
+//
+// Fix: detect when mismatch > 1 day (not a real DST gap of ±hours), retry using the
+// forward direction (getOffsetMsAtEpoch) which ICU4C handles correctly for pre-LMT epochs.
+// This matches icu4x zoneinfo64/src/lib.rs:394-402: use type_offsets[0] for pre-first-transition.
+// PlainDate.toZonedDateTime (uses getStartOfDay → getPossibleEpochNanosecondsFor)
+{
+ const ep = -(2n**64n); // year 1385 in America/Vancouver
+ const zdt = new Temporal.ZonedDateTime(ep, "America/Vancouver");
+ shouldBe(zdt.year, 1385);
+ // This was returning 1884-01-01 instead of 1385-06-11T00:00:00
+ const startOfDay = zdt.toPlainDate().toZonedDateTime({timeZone: "America/Vancouver"});
+ shouldBe(startOfDay.year, 1385);
+ shouldBe(startOfDay.month, 6);
+ shouldBe(startOfDay.day, 11);
+ shouldBe(startOfDay.hour, 0);
+ shouldBe(startOfDay.minute, 0);
+ shouldBe(startOfDay.second, 0);
+}
+// ZonedDateTime rounding to 1 day (uses getStartOfDay internally)
+{
+ const ep = -(2n**64n);
+ const zdt = new Temporal.ZonedDateTime(ep, "America/Vancouver");
+ const rounded = zdt.round({smallestUnit: "days", roundingMode: "ceil"});
+ shouldBe(rounded.year, 1385);
+ shouldBe(rounded.month, 6);
+ shouldBe(rounded.day, 12);
+}
+// Europe/Amsterdam pre-LMT dates also affected
+{
+ const ep = -(2n**64n);
+ const zdt = new Temporal.ZonedDateTime(ep, "Europe/Amsterdam");
+ const startOfDay = zdt.toPlainDate().toZonedDateTime({timeZone: "Europe/Amsterdam"});
+ shouldBe(startOfDay.year, zdt.year);
+ shouldBe(startOfDay.hour, 0);
+ shouldBe(startOfDay.minute, 0);
+}
+// Real DST gaps still work correctly (not falsely detected as pre-LMT)
+{
+ // 2025-03-09 02:30 America/Vancouver is in a DST gap
+ const earlier = Temporal.ZonedDateTime.from(
+ {year:2025, month:3, day:9, hour:2, minute:30, second:0, timeZone:"America/Vancouver"},
+ {disambiguation:"earlier"});
+ const later = Temporal.ZonedDateTime.from(
+ {year:2025, month:3, day:9, hour:2, minute:30, second:0, timeZone:"America/Vancouver"},
+ {disambiguation:"later"});
+ // earlier and later should give different results for a real gap
+ const isAmbiguous = !earlier.equals(later);
+ shouldBe(isAmbiguous, true); // 2:30 AM on DST spring-forward is non-existent → different results
+}
+// Additional timezones affected by pre-LMT fix (diversity check)
+// Asia/Tokyo and Pacific/Auckland also have LMT→standard transitions in the pre-1900 era.
+{
+ const ep = -(2n**64n);
+ // Asia/Tokyo: same epoch as the Vancouver test
+ const zdtTokyo = new Temporal.ZonedDateTime(ep, 'Asia/Tokyo');
+ shouldBe(zdtTokyo.year, 1385);
+ const startTokyo = zdtTokyo.toPlainDate().toZonedDateTime({ timeZone: 'Asia/Tokyo' });
+ shouldBe(startTokyo.year, 1385);
+ shouldBe(startTokyo.hour, 0);
+ shouldBe(startTokyo.minute, 0);
+ // Pacific/Auckland: same fix path
+ const zdtAuckland = new Temporal.ZonedDateTime(ep, 'Pacific/Auckland');
+ shouldBe(zdtAuckland.year, 1385);
+ const startAuckland = zdtAuckland.toPlainDate().toZonedDateTime({ timeZone: 'Pacific/Auckland' });
+ shouldBe(startAuckland.year, 1385);
+ shouldBe(startAuckland.hour, 0);
+}
+// ZonedDateTime.from(zdt.toString()) roundtrip must preserve epoch for pre-LMT dates (zonedroundtrip)
+// The false-gap fix ensures that from() can resolve the local time back to the original epoch.
+{
+ const ep = -(2n**64n);
+ for (const tz of ['America/Vancouver', 'Europe/Amsterdam', 'Asia/Tokyo']) {
+ const zdt = new Temporal.ZonedDateTime(ep, tz);
+ const rt = Temporal.ZonedDateTime.from(zdt.toString());
+ shouldBe(rt.epochNanoseconds, ep);
+ }
+}
diff --git a/JSTests/stress/temporal-rounding-modes.js b/JSTests/stress/temporal-rounding-modes.js
new file mode 100644
index 000000000000..e126d911a0e4
--- /dev/null
+++ b/JSTests/stress/temporal-rounding-modes.js
@@ -0,0 +1,104 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(a, b, msg) {
+ if (a !== b) throw new Error(`${msg}: expected ${b}, got ${a}`);
+}
+
+const modes = ["ceil", "floor", "expand", "trunc", "halfCeil", "halfFloor", "halfExpand", "halfTrunc", "halfEven"];
+
+// --- PlainTime.round with each mode ---
+{
+ const time = Temporal.PlainTime.from("12:34:56");
+ const expected = {
+ ceil: 13, floor: 12, expand: 13, trunc: 12,
+ halfCeil: 13, halfFloor: 13, halfExpand: 13, halfTrunc: 13, halfEven: 13,
+ };
+ for (const mode of modes) {
+ const result = time.round({ smallestUnit: "hour", roundingMode: mode });
+ shouldBe(result.hour, expected[mode], `12:34:56 round hour ${mode}`);
+ }
+}
+
+{
+ const time = Temporal.PlainTime.from("12:30:00");
+ const expected = {
+ ceil: 13, floor: 12, expand: 13, trunc: 12,
+ halfCeil: 13, halfFloor: 12, halfExpand: 13, halfTrunc: 12, halfEven: 12,
+ };
+ for (const mode of modes) {
+ const result = time.round({ smallestUnit: "hour", roundingMode: mode });
+ shouldBe(result.hour, expected[mode], `12:30 exact half round hour ${mode}`);
+ }
+}
+
+{
+ const time = Temporal.PlainTime.from("12:29:59");
+ const expected = {
+ ceil: 13, floor: 12, expand: 13, trunc: 12,
+ halfCeil: 12, halfFloor: 12, halfExpand: 12, halfTrunc: 12, halfEven: 12,
+ };
+ for (const mode of modes) {
+ const result = time.round({ smallestUnit: "hour", roundingMode: mode });
+ shouldBe(result.hour, expected[mode], `12:29:59 below half round hour ${mode}`);
+ }
+}
+
+{
+ const dur = Temporal.Duration.from({ hours: 5, minutes: 30 });
+ const expected = {
+ ceil: 6, floor: 5, expand: 6, trunc: 5,
+ halfCeil: 6, halfFloor: 5, halfExpand: 6, halfTrunc: 5, halfEven: 6,
+ };
+ for (const mode of modes) {
+ const result = dur.round({
+ largestUnit: "hours",
+ smallestUnit: "hours",
+ roundingMode: mode,
+ });
+ shouldBe(result.hours, expected[mode], `5h30m round hour ${mode}`);
+ }
+}
+
+// Negative duration — sign-aware rounding
+{
+ const dur = Temporal.Duration.from({ hours: -5, minutes: -30 });
+ const expected = {
+ ceil: -5, floor: -6, expand: -6, trunc: -5,
+ halfCeil: -5, halfFloor: -6, halfExpand: -6, halfTrunc: -5, halfEven: -6,
+ };
+ for (const mode of modes) {
+ const result = dur.round({
+ largestUnit: "hours",
+ smallestUnit: "hours",
+ roundingMode: mode,
+ });
+ shouldBe(result.hours, expected[mode], `-5h30m round hour ${mode}`);
+ }
+}
+
+// --- Instant.round with each mode ---
+{
+ // 2024-01-01T12:30:00Z → round to hour
+ const inst = Temporal.Instant.from("2024-01-01T12:30:00Z");
+ for (const mode of modes) {
+ const result = inst.round({ smallestUnit: "hour", roundingMode: mode });
+ // Result must be on a whole-hour boundary (epochNs divisible by 3_600_000_000_000).
+ shouldBe(result.epochNanoseconds % 3600000000000n === 0n, true, `Instant round hour ${mode} on boundary`);
+ }
+}
+
+{
+ const pdt = Temporal.PlainDateTime.from("2024-01-15T12:30:00");
+ for (const mode of modes) {
+ const result = pdt.round({ smallestUnit: "hour", roundingMode: mode });
+ shouldBe(result.hour === 12 || result.hour === 13, true, `PDT round hour ${mode} valid`);
+ }
+}
+
+{
+ const dur = Temporal.Duration.from({ seconds: 1, milliseconds: 500 });
+ shouldBe(dur.toString({ fractionalSecondDigits: 0 }), "PT1S", "fsd 0 truncates sub-second");
+ shouldBe(dur.toString({ fractionalSecondDigits: 1 }), "PT1.5S", "fsd 1");
+ shouldBe(dur.toString({ fractionalSecondDigits: 3 }), "PT1.500S", "fsd 3");
+ shouldBe(dur.toString({ fractionalSecondDigits: 9 }), "PT1.500000000S", "fsd 9");
+}
diff --git a/JSTests/stress/temporal-string-parsing-edge.js b/JSTests/stress/temporal-string-parsing-edge.js
new file mode 100644
index 000000000000..61f5eb44cf1b
--- /dev/null
+++ b/JSTests/stress/temporal-string-parsing-edge.js
@@ -0,0 +1,121 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(a, b, msg) {
+ if (a !== b) throw new Error(`${msg}: expected ${b}, got ${a}`);
+}
+
+function shouldThrow(fn, msg) {
+ try { fn(); throw new Error(`NOTHROW: ${msg}`); }
+ catch (e) { if (e.message.startsWith("NOTHROW:")) throw e; }
+}
+
+{
+ const inst = Temporal.Instant.from("2024-01-01T00:00:00+05:30");
+ shouldBe(inst.epochNanoseconds, 1704047400000000000n, "+05:30 offset");
+
+ const inst2 = Temporal.Instant.from("2024-01-01T00:00:00-12:00");
+ const inst2Ns = inst2.epochNanoseconds;
+ shouldBe(inst2Ns > 1704067200000000000n, true, "-12:00 is after UTC midnight");
+
+ const inst3 = Temporal.Instant.from("2024-01-01T00:00:00Z");
+ shouldBe(inst3.epochNanoseconds, 1704067200000000000n, "Z offset");
+}
+
+{
+ const inst = Temporal.Instant.from("2024-01-01T00:00:00.123456789Z");
+ shouldBe(inst.epochNanoseconds, 1704067200123456789n, "9-digit nanosecond precision");
+
+ const zdt = Temporal.ZonedDateTime.from("2024-01-01T00:00:00.000000001[UTC]");
+ shouldBe(zdt.nanosecond, 1, "1 nanosecond precision");
+ const zdt2 = Temporal.ZonedDateTime.from("2024-01-01T00:00:00.999999999[UTC]");
+ shouldBe(zdt2.nanosecond, 999, "999ns");
+}
+
+{
+ const pd = Temporal.PlainDate.from("2024-03-15[u-ca=iso8601]");
+ shouldBe(pd.calendarId, "iso8601", "calendar annotation iso8601");
+ shouldBe(pd.year, 2024, "year with calendar annotation");
+}
+
+{
+ shouldBe(Temporal.Duration.from("P1Y").years, 1, "P1Y");
+ shouldBe(Temporal.Duration.from("P1M").months, 1, "P1M");
+ shouldBe(Temporal.Duration.from("P1W").weeks, 1, "P1W");
+ shouldBe(Temporal.Duration.from("P1D").days, 1, "P1D");
+ shouldBe(Temporal.Duration.from("PT1H").hours, 1, "PT1H");
+ shouldBe(Temporal.Duration.from("PT1M").minutes, 1, "PT1M");
+ shouldBe(Temporal.Duration.from("PT1S").seconds, 1, "PT1S");
+ shouldBe(Temporal.Duration.from("PT0.5S").milliseconds, 500, "PT0.5S");
+ shouldBe(Temporal.Duration.from("PT0.000000001S").nanoseconds, 1, "PT0.000000001S");
+ shouldBe(Temporal.Duration.from("-P1D").days, -1, "negative duration");
+ shouldBe(Temporal.Duration.from("P1Y2M3W4DT5H6M7.89S").years, 1, "full duration");
+}
+
+{
+ shouldThrow(() => Temporal.PlainDate.from(""), "empty string");
+ shouldThrow(() => Temporal.PlainDate.from("not-a-date"), "invalid date");
+ shouldThrow(() => Temporal.PlainDate.from("2024-13-01"), "month 13");
+ shouldThrow(() => Temporal.PlainDate.from("2024-02-30"), "Feb 30");
+ shouldThrow(() => Temporal.Instant.from("2024-01-01"), "Instant needs offset");
+ shouldThrow(() => Temporal.Duration.from("P"), "empty duration");
+ shouldThrow(() => Temporal.Duration.from("1Y"), "missing P prefix");
+}
+
+{
+ const pd = new Temporal.PlainDate(275760, 9, 13);
+ shouldBe(pd.year, 275760, "max year 275760");
+
+ const pd2 = new Temporal.PlainDate(-271821, 4, 20);
+ shouldBe(pd2.year, -271821, "min year -271821");
+}
+
+{
+ const t1 = Temporal.PlainTime.from("12:34:56.789");
+ shouldBe(t1.hour, 12, "time hour");
+ shouldBe(t1.millisecond, 789, "time ms");
+
+ const t2 = Temporal.PlainTime.from("00:00");
+ shouldBe(t2.hour, 0, "midnight");
+ shouldBe(t2.minute, 0, "midnight min");
+
+ const t3 = Temporal.PlainTime.from("23:59:59.999999999");
+ shouldBe(t3.hour, 23, "max time hour");
+ shouldBe(t3.nanosecond, 999, "max time ns");
+}
+
+// Regression: short DateMonth/DateDay-prefixed inputs used to dereference
+// past the end of the parsing buffer in ISO8601::parseDate, producing SIGTRAP
+// on hardened builds and OOB reads on unhardened builds. They must throw cleanly.
+{
+ const oobInputs = [
+ "0", "1", // 1-char crashers
+ "01", "02", "03", "04", "05", "06", "07", // 2-char crashers
+ "08", "09", "10", "11", "12",
+ "010", "011", "012", "013", // 3-char crashers
+ "120", "121", "122", "123",
+ ];
+ const parsers = [
+ ["PlainDate", s => Temporal.PlainDate.from(s)],
+ ["PlainDateTime", s => Temporal.PlainDateTime.from(s)],
+ ["PlainMonthDay", s => Temporal.PlainMonthDay.from(s)],
+ ["PlainYearMonth", s => Temporal.PlainYearMonth.from(s)],
+ ["Instant", s => Temporal.Instant.from(s)],
+ ];
+ for (const [name, fn] of parsers) {
+ for (const s of oobInputs)
+ shouldThrow(() => fn(s), `${name}.from("${s}") OOB regression`);
+ }
+}
+
+// Per Temporal grammar, Date and DateSpecYearMonth both require a DateYear.
+// Only DateSpecMonthDay permits the no-year (MMDD / MM-DD) form. These inputs
+// previously parsed as year=0000.
+{
+ shouldThrow(() => Temporal.PlainYearMonth.from("0115"), "YM 0115 needs year");
+ shouldThrow(() => Temporal.PlainDate.from("01-15"), "Date 01-15 needs year");
+ // MonthDay accepts the no-year form.
+ const md = Temporal.PlainMonthDay.from("0115");
+ shouldBe(md.toString(), "01-15", "MonthDay 0115");
+ const md2 = Temporal.PlainMonthDay.from("01-15");
+ shouldBe(md2.toString(), "01-15", "MonthDay 01-15");
+}
diff --git a/JSTests/stress/temporal-timezone-diversity.js b/JSTests/stress/temporal-timezone-diversity.js
new file mode 100644
index 000000000000..f955a24a2130
--- /dev/null
+++ b/JSTests/stress/temporal-timezone-diversity.js
@@ -0,0 +1,82 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(a, b, msg) {
+ if (a !== b) throw new Error(`${msg}: expected ${b}, got ${a}`);
+}
+{
+ const plus530 = new Temporal.ZonedDateTime(0n, "+05:30");
+ shouldBe(plus530.offset, "+05:30", "+05:30 offset");
+ shouldBe(plus530.hour, 5, "+05:30 hour at epoch 0");
+ shouldBe(plus530.minute, 30, "+05:30 minute at epoch 0");
+ shouldBe(plus530.hoursInDay, 24, "+05:30 always 24h days");
+}
+{
+ const minus12 = new Temporal.ZonedDateTime(0n, "-12:00");
+ shouldBe(minus12.offset, "-12:00", "-12:00 offset");
+ shouldBe(minus12.day, 31, "-12:00 is still Dec 31 1969");
+}
+{
+ // Asia/Kolkata: always +05:30
+ const kolkata = Temporal.ZonedDateTime.from("2024-06-15T12:00[Asia/Kolkata]");
+ shouldBe(kolkata.offset, "+05:30", "Kolkata offset summer");
+ const kolkataWinter = Temporal.ZonedDateTime.from("2024-01-15T12:00[Asia/Kolkata]");
+ shouldBe(kolkataWinter.offset, "+05:30", "Kolkata offset winter (same)");
+ shouldBe(kolkata.hoursInDay, 24, "Kolkata always 24h");
+}
+{
+ // Asia/Tokyo: always +09:00
+ const tokyo = Temporal.ZonedDateTime.from("2024-06-15T12:00[Asia/Tokyo]");
+ shouldBe(tokyo.offset, "+09:00", "Tokyo offset");
+ shouldBe(tokyo.getTimeZoneTransition("next"), null, "Tokyo no future DST");
+}
+{
+ // Asia/Kathmandu: +05:45 (not on hour or half-hour boundary)
+ const ktm = Temporal.ZonedDateTime.from("2024-01-01T12:00[Asia/Kathmandu]");
+ shouldBe(ktm.offset, "+05:45", "Kathmandu +05:45");
+}
+{
+ // Pacific/Chatham: +12:45 / +13:45 (New Zealand's Chatham Islands)
+ const chatham = Temporal.ZonedDateTime.from("2024-01-15T12:00[Pacific/Chatham]");
+ // Summer in southern hemisphere — DST active
+ shouldBe(chatham.offset, "+13:45", "Chatham summer +13:45");
+}
+{
+ const epoch = 1718456400000000000n; // 2024-06-15T13:00:00Z
+ const utc = new Temporal.ZonedDateTime(epoch, "UTC");
+ const nyc = new Temporal.ZonedDateTime(epoch, "America/New_York");
+ const tokyo = new Temporal.ZonedDateTime(epoch, "Asia/Tokyo");
+ shouldBe(utc.hour, 13, "UTC 13:00");
+ shouldBe(nyc.hour, 9, "NYC 09:00 (EDT)");
+ shouldBe(tokyo.hour, 22, "Tokyo 22:00 (JST)");
+ shouldBe(utc.epochNanoseconds, nyc.epochNanoseconds, "same instant UTC=NYC");
+ shouldBe(utc.epochNanoseconds, tokyo.epochNanoseconds, "same instant UTC=Tokyo");
+}
+{
+ // Apia is UTC+13 (ahead of date line)
+ const apia = Temporal.ZonedDateTime.from("2024-01-01T00:00[Pacific/Apia]");
+ shouldBe(apia.offset, "+13:00", "Apia +13:00");
+}
+{
+ // Australia/Sydney: DST in southern summer (Oct-Apr)
+ const sydSummer = Temporal.ZonedDateTime.from("2024-01-15T12:00[Australia/Sydney]");
+ shouldBe(sydSummer.offset, "+11:00", "Sydney summer AEDT");
+ const sydWinter = Temporal.ZonedDateTime.from("2024-07-15T12:00[Australia/Sydney]");
+ shouldBe(sydWinter.offset, "+10:00", "Sydney winter AEST");
+}
+{
+ const nyc = Temporal.ZonedDateTime.from("2024-06-15T12:00[America/New_York]");
+ const nextTrans = nyc.getTimeZoneTransition("next");
+ shouldBe(nextTrans !== null, true, "NYC has next transition");
+ shouldBe(nextTrans instanceof Temporal.ZonedDateTime, true, "getTimeZoneTransition returns ZDT");
+ shouldBe(nextTrans.month, 11, "Next NYC transition in November");
+ const prevTrans = nyc.getTimeZoneTransition("previous");
+ shouldBe(prevTrans !== null, true, "NYC has prev transition");
+ shouldBe(prevTrans.month, 3, "Prev NYC transition in March");
+}
+{
+ const utc = Temporal.ZonedDateTime.from("2024-06-15T12:00[UTC]");
+ shouldBe(utc.offset, "+00:00", "UTC offset string");
+ shouldBe(utc.hoursInDay, 24, "UTC always 24h");
+ shouldBe(utc.getTimeZoneTransition("next"), null, "UTC no transitions");
+ shouldBe(utc.getTimeZoneTransition("previous"), null, "UTC no transitions prev");
+}
diff --git a/JSTests/stress/temporal-timezone-offset-identity.js b/JSTests/stress/temporal-timezone-offset-identity.js
new file mode 100644
index 000000000000..9adf5e02dc15
--- /dev/null
+++ b/JSTests/stress/temporal-timezone-offset-identity.js
@@ -0,0 +1,27 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`expected ${JSON.stringify(expected)} but got ${JSON.stringify(actual)}`);
+}
+// Bug: PlainDateTime.toZonedDateTime("+00:00") wrongly returned timeZoneId "UTC"
+// because intlAvailableNamedTimeZone("+00:00") resolved the offset to the UTC IANA entry.
+// Offset timezone identifiers must be preserved as-is, not canonicalized to a named zone.
+// PlainDateTime.toZonedDateTime preserves offset timezone identifier
+shouldBe(new Temporal.PlainDateTime(2020, 1, 1).toZonedDateTime("+00:00").timeZoneId, "+00:00");
+shouldBe(new Temporal.PlainDateTime(2020, 1, 1).toZonedDateTime("+05:30").timeZoneId, "+05:30");
+shouldBe(new Temporal.PlainDateTime(2020, 1, 1).toZonedDateTime("-08:00").timeZoneId, "-08:00");
+// PlainDate.toZonedDateTime preserves offset timezone identifier
+shouldBe(new Temporal.PlainDate(2020, 1, 1).toZonedDateTime("+00:00").timeZoneId, "+00:00");
+shouldBe(new Temporal.PlainDate(2020, 1, 1).toZonedDateTime({ timeZone: "+00:00" }).timeZoneId, "+00:00");
+// Parsing preserves offset timezone identifier (was already correct)
+shouldBe(Temporal.ZonedDateTime.from("2020-01-01T00:00:00+00:00[+00:00]").timeZoneId, "+00:00");
+// Round-trip: toZonedDateTime then compare to parsed string
+const zdt1 = new Temporal.PlainDateTime(2020, 1, 1).toZonedDateTime("+00:00");
+const zdt2 = Temporal.ZonedDateTime.from("2020-01-01T00:00:00+00:00[+00:00]");
+shouldBe(zdt1.timeZoneId, "+00:00");
+shouldBe(zdt2.timeZoneId, "+00:00");
+shouldBe(zdt1.equals(zdt2), true);
+// UTC and +00:00 are different timezone identifiers
+shouldBe(new Temporal.PlainDateTime(2020, 1, 1).toZonedDateTime("UTC").timeZoneId, "UTC");
+shouldBe(new Temporal.PlainDateTime(2020, 1, 1).toZonedDateTime("+00:00").timeZoneId, "+00:00");
diff --git a/JSTests/stress/temporal-timezone.js b/JSTests/stress/temporal-timezone.js
index 764997565d6e..e2125c1ae8a5 100644
--- a/JSTests/stress/temporal-timezone.js
+++ b/JSTests/stress/temporal-timezone.js
@@ -1,6 +1,4 @@
//@ requireOptions("--useTemporal=1")
-// FIXME: toLocaleString requires IntlDateTimeFormat Temporal support, implemented in the next patch.
-//@ skip
function shouldBe(actual, expected) {
if (actual !== expected)
@@ -23,26 +21,26 @@ function shouldThrow(func, errorType) {
shouldThrow(() => { new Temporal.TimeZone("UTC"); }, TypeError);
shouldThrow(() => { Temporal.TimeZone.from("UTC"); }, TypeError);
-// FIXME: TimeZone is accessed via timeZoneId on ZonedDateTime
-// {
-// let zdt = Temporal.ZonedDateTime.from("2024-06-15T12:00[Asia/Tokyo]");
-// shouldBe(zdt.timeZoneId, "Asia/Tokyo");
-// shouldBe(zdt.offset, "+09:00");
-// }
-
-// FIXME: UTC, fixed offset, ZonedDateTime
-// {
-// let zdt = Temporal.ZonedDateTime.from("2024-06-15T12:00[UTC]");
-// shouldBe(zdt.timeZoneId, "UTC");
-// shouldBe(zdt.offset, "+00:00");
-// }
-
-// FIXME: Fixed UTC offset ZonedDateTime
-// {
-// let zdt = new Temporal.ZonedDateTime(0n, "+05:30");
-// shouldBe(zdt.timeZoneId, "+05:30");
-// shouldBe(zdt.offset, "+05:30");
-// }
+// TimeZone is accessed via timeZoneId on ZonedDateTime
+{
+ let zdt = Temporal.ZonedDateTime.from("2024-06-15T12:00[Asia/Tokyo]");
+ shouldBe(zdt.timeZoneId, "Asia/Tokyo");
+ shouldBe(zdt.offset, "+09:00");
+}
+
+// UTC, fixed offset, ZonedDateTime
+{
+ let zdt = Temporal.ZonedDateTime.from("2024-06-15T12:00[UTC]");
+ shouldBe(zdt.timeZoneId, "UTC");
+ shouldBe(zdt.offset, "+00:00");
+}
+
+// Fixed UTC offset ZonedDateTime
+{
+ let zdt = new Temporal.ZonedDateTime(0n, "+05:30");
+ shouldBe(zdt.timeZoneId, "+05:30");
+ shouldBe(zdt.offset, "+05:30");
+}
// Temporal.Now.timeZoneId returns a string
{
diff --git a/JSTests/stress/temporal-zdt-large-fold-disambiguation.js b/JSTests/stress/temporal-zdt-large-fold-disambiguation.js
new file mode 100644
index 000000000000..51605b1f617e
--- /dev/null
+++ b/JSTests/stress/temporal-zdt-large-fold-disambiguation.js
@@ -0,0 +1,192 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`expected ${JSON.stringify(expected)} but got ${JSON.stringify(actual)}`);
+}
+function shouldThrowRangeError(fn) {
+ try { fn(); throw new Error("expected RangeError but no exception thrown"); }
+ catch (e) { if (!(e instanceof RangeError)) throw new Error(`expected RangeError, got ${e}`); }
+}
+
+// Alaska Purchase: Aleutian Islands crossed the date line, -24h fall-back.
+// Before: +12:13:22 (Russian side), After: -11:46:38 (US side).
+// This was the primary failing case (earlier → wrong offset -11:47).
+{
+ const transition = '1867-10-19T00:44:35'; // mid-fold: ambiguous local time
+ const tz = 'America/Adak';
+ const mid = Temporal.PlainDateTime.from(transition);
+
+ // Earlier (pre-fold, Russian side offset) — this was the broken case
+ const e = mid.toZonedDateTime(tz, { disambiguation: 'earlier' });
+ shouldBe(e.offset, '+12:13:22');
+ shouldBe(e.year, 1867); shouldBe(e.month, 10); shouldBe(e.day, 19);
+ shouldBe(e.hour, 0); shouldBe(e.minute, 44); shouldBe(e.second, 35);
+
+ // Later (post-fold, US side offset)
+ const l = mid.toZonedDateTime(tz, { disambiguation: 'later' });
+ shouldBe(l.offset, '-11:46:38');
+ shouldBe(l.year, 1867); shouldBe(l.month, 10); shouldBe(l.day, 19);
+ shouldBe(l.hour, 0); shouldBe(l.minute, 44); shouldBe(l.second, 35);
+
+ const c = mid.toZonedDateTime(tz, { disambiguation: 'compatible' });
+ shouldBe(c.epochNanoseconds, e.epochNanoseconds);
+
+ shouldThrowRangeError(() => mid.toZonedDateTime(tz, { disambiguation: 'reject' }));
+
+ const ef = Temporal.ZonedDateTime.from({ year: 1867, monthCode: 'M10', day: 19,
+ hour: 0, minute: 44, second: 35, timeZone: tz }, { disambiguation: 'earlier' });
+ const lf = Temporal.ZonedDateTime.from({ year: 1867, monthCode: 'M10', day: 19,
+ hour: 0, minute: 44, second: 35, timeZone: tz }, { disambiguation: 'later' });
+ shouldBe(ef.epochNanoseconds, e.epochNanoseconds);
+ shouldBe(lf.epochNanoseconds, l.epochNanoseconds);
+
+ shouldBe(e.with({ offset: l.offset }).epochNanoseconds, l.epochNanoseconds);
+ shouldBe(l.with({ offset: e.offset }).epochNanoseconds, e.epochNanoseconds);
+
+ const diffNs = l.epochNanoseconds - e.epochNanoseconds;
+ shouldBe(diffNs, 86400000000000n);
+}
+
+// Same Alaska Purchase event, different Alaskan city. -24h fall-back.
+{
+ const tz = 'America/Anchorage';
+ const mid = Temporal.PlainDateTime.from('1867-10-19T02:31:37');
+
+ const e = mid.toZonedDateTime(tz, { disambiguation: 'earlier' });
+ const l = mid.toZonedDateTime(tz, { disambiguation: 'later' });
+
+ shouldBe(e.offset, '+14:00:24');
+ shouldBe(l.offset, '-09:59:36');
+ shouldBe(e.epochNanoseconds < l.epochNanoseconds, true);
+ shouldBe(l.epochNanoseconds - e.epochNanoseconds, 86400000000000n); // 24h apart
+
+ const c = mid.toZonedDateTime(tz, { disambiguation: 'compatible' });
+ shouldBe(c.epochNanoseconds, e.epochNanoseconds);
+
+ shouldThrowRangeError(() => mid.toZonedDateTime(tz, { disambiguation: 'reject' }));
+}
+
+// Samoa crossed the date line from east to west, -24h fall-back.
+{
+ const tz = 'Pacific/Apia';
+ const mid = Temporal.PlainDateTime.from('1892-07-04T12:00:00');
+
+ const e = mid.toZonedDateTime(tz, { disambiguation: 'earlier' });
+ const l = mid.toZonedDateTime(tz, { disambiguation: 'later' });
+
+ shouldBe(e.offset, '+12:33:04');
+ shouldBe(l.offset, '-11:26:56');
+ shouldBe(l.epochNanoseconds - e.epochNanoseconds, 86400000000000n); // 24h apart
+
+ shouldBe(e.with({ offset: l.offset }).epochNanoseconds, l.epochNanoseconds);
+ shouldBe(l.with({ offset: e.offset }).epochNanoseconds, e.epochNanoseconds);
+ shouldThrowRangeError(() => mid.toZonedDateTime(tz, { disambiguation: 'reject' }));
+}
+
+// Kwajalein switched sides, ~23h fall-back (not quite 24 due to non-integer offset).
+{
+ const tz = 'Pacific/Kwajalein';
+ const mid = Temporal.PlainDateTime.from('1969-09-30T12:30:00');
+
+ const e = mid.toZonedDateTime(tz, { disambiguation: 'earlier' });
+ const l = mid.toZonedDateTime(tz, { disambiguation: 'later' });
+
+ shouldBe(e.offset, '+11:00');
+ shouldBe(l.offset, '-12:00');
+ shouldBe(e.epochNanoseconds < l.epochNanoseconds, true);
+
+ shouldThrowRangeError(() => mid.toZonedDateTime(tz, { disambiguation: 'reject' }));
+}
+
+// Expanding the fold window must not break standard 1h DST folds.
+{
+ // 2024-11-03 01:30 America/Los_Angeles — fall back from PDT to PST
+ const mid = Temporal.PlainDateTime.from('2024-11-03T01:30:00');
+ const tz = 'America/Los_Angeles';
+
+ const e = mid.toZonedDateTime(tz, { disambiguation: 'earlier' });
+ const l = mid.toZonedDateTime(tz, { disambiguation: 'later' });
+ const c = mid.toZonedDateTime(tz, { disambiguation: 'compatible' });
+
+ shouldBe(e.offset, '-07:00'); // PDT (summer time, earlier UTC)
+ shouldBe(l.offset, '-08:00'); // PST (winter time, later UTC)
+ shouldBe(c.epochNanoseconds, e.epochNanoseconds); // compatible = earlier for fall-back
+ shouldBe(l.epochNanoseconds - e.epochNanoseconds, 3600000000000n); // 1h apart
+
+ shouldThrowRangeError(() => mid.toZonedDateTime(tz, { disambiguation: 'reject' }));
+
+ // with({offset}) switching
+ shouldBe(e.with({ offset: '-08:00' }).epochNanoseconds, l.epochNanoseconds);
+ shouldBe(l.with({ offset: '-07:00' }).epochNanoseconds, e.epochNanoseconds);
+}
+
+{
+ const td = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 1);
+ const t = Temporal.Instant.from('1850-01-01T00Z')
+ .toZonedDateTimeISO('America/Adak')
+ .getTimeZoneTransition('next');
+
+ // The transition ZDT itself carries the post-transition (new) offset
+ const after = t.add(td);
+ shouldBe(t.offsetNanoseconds, after.offsetNanoseconds);
+
+ // Wall-clock distance between 1ns before and 1ns after = |offsetChange| + 2ns
+ const before = t.subtract(td);
+ const offsetChange = after.offsetNanoseconds - before.offsetNanoseconds; // -24h in ns
+ const wallBefore = before.toPlainDateTime();
+ const wallAfter = after.toPlainDateTime();
+ const wallDiffNs = BigInt(wallBefore.until(wallAfter, { largestUnit: 'nanoseconds' }).nanoseconds);
+ // offsetChange is negative (fall-back), so wall diff = offsetChange + 2ns
+ shouldBe(wallDiffNs, BigInt(offsetChange) + 2n);
+}
+
+// Same date-line crossing pattern as Alaska but for Samoa.
+{
+ const tz = 'Pacific/Apia';
+ const mid = Temporal.PlainDateTime.from('1892-07-04T12:00:00');
+ const e = mid.toZonedDateTime(tz, { disambiguation: 'earlier' });
+ const l = mid.toZonedDateTime(tz, { disambiguation: 'later' });
+ shouldBe(e.offset, '+12:33:04');
+ shouldBe(l.offset, '-11:26:56');
+ shouldBe(l.epochNanoseconds - e.epochNanoseconds, 86400000000000n);
+ shouldThrowRangeError(() => mid.toZonedDateTime(tz, { disambiguation: 'reject' }));
+ shouldBe(mid.toZonedDateTime(tz, { disambiguation: 'compatible' }).epochNanoseconds, e.epochNanoseconds);
+}
+
+// ─── Pacific/Midway 1892 (24h fall-back, Midway Islands date-line crossing) ─
+{
+ const tz = 'Pacific/Midway';
+ const mid = Temporal.PlainDateTime.from('1892-07-04T12:00:00');
+ const e = mid.toZonedDateTime(tz, { disambiguation: 'earlier' });
+ const l = mid.toZonedDateTime(tz, { disambiguation: 'later' });
+ shouldBe(e.offset, '+12:37:12');
+ shouldBe(l.offset, '-11:22:48');
+ shouldBe(l.epochNanoseconds - e.epochNanoseconds, 86400000000000n);
+ shouldThrowRangeError(() => mid.toZonedDateTime(tz, { disambiguation: 'reject' }));
+}
+
+// Tests the expanded fold window also handles large spring-forwards (gaps).
+{
+ const tz = 'Antarctica/Davis';
+ // 1957-01-13T03:30 is in the 7h DST gap (spring forward from UTC+0 to UTC+7)
+ const mid = Temporal.PlainDateTime.from('1957-01-13T03:30:00');
+ const l = mid.toZonedDateTime(tz, { disambiguation: 'later' });
+ const e = mid.toZonedDateTime(tz, { disambiguation: 'earlier' });
+ shouldBe(l.offset, '+07:00');
+ shouldBe(e.offset, '+00:00');
+ // compatible = later for spring-forward (gap)
+ shouldBe(mid.toZonedDateTime(tz, { disambiguation: 'compatible' }).epochNanoseconds, l.epochNanoseconds);
+ shouldThrowRangeError(() => mid.toZonedDateTime(tz, { disambiguation: 'reject' }));
+}
+
+{
+ const tz = 'Antarctica/Casey';
+ const mid = Temporal.PlainDateTime.from('1969-01-01T04:00:00');
+ const l = mid.toZonedDateTime(tz, { disambiguation: 'later' });
+ const e = mid.toZonedDateTime(tz, { disambiguation: 'earlier' });
+ shouldBe(l.offset, '+08:00');
+ shouldBe(e.offset, '+00:00');
+ shouldBe(mid.toZonedDateTime(tz, { disambiguation: 'compatible' }).epochNanoseconds, l.epochNanoseconds);
+ shouldThrowRangeError(() => mid.toZonedDateTime(tz, { disambiguation: 'reject' }));
+}
diff --git a/JSTests/stress/temporal-zdt-round-dst-midnight-crossing.js b/JSTests/stress/temporal-zdt-round-dst-midnight-crossing.js
new file mode 100644
index 000000000000..aa29c7d1a4df
--- /dev/null
+++ b/JSTests/stress/temporal-zdt-round-dst-midnight-crossing.js
@@ -0,0 +1,33 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldNotThrow(fn, desc) {
+ try { fn(); } catch(e) { throw new Error(desc + " threw: " + e.constructor.name + ": " + e.message); }
+}
+function shouldBe(a, e) { if (a !== e) throw new Error("expected " + JSON.stringify(e) + " got " + JSON.stringify(a)); }
+
+// Antarctica/Casey had a backward DST transition on 2010-03-05: clocks went from +11 to +08.
+// This skipped midnight — the transition crossed midnight, causing some instants that have a
+// local date of 2010-03-04 to have epochNs > GetStartOfDay(2010-03-05 in +11).
+// ZonedDateTime.round({ smallestUnit: "day" }) must not throw for any rounding mode.
+// Polyfill fix: https://github.com/tc39/proposal-temporal/issues/3312
+{
+ const zdt = Temporal.ZonedDateTime.from("2010-03-04T23:10:00+08:00[Antarctica/Casey]");
+ for (const roundingMode of ["ceil", "floor", "trunc", "expand", "halfExpand", "halfTrunc", "halfCeil", "halfFloor", "halfEven"]) {
+ shouldNotThrow(() => {
+ const r = zdt.round({ smallestUnit: "day", roundingMode });
+ shouldBe(r.timeZoneId, "Antarctica/Casey");
+ // Result must be at a day boundary (time = 00:00:00 local).
+ shouldBe(r.hour, 0);
+ shouldBe(r.minute, 0);
+ shouldBe(r.second, 0);
+ }, `round(${roundingMode})`);
+ }
+
+ // floor/trunc round down → 2010-03-04 start.
+ const floor = zdt.round({ smallestUnit: "day", roundingMode: "floor" });
+ shouldBe(floor.toPlainDate().toString(), "2010-03-04");
+
+ // ceil/expand round up → 2010-03-05 start.
+ const ceil = zdt.round({ smallestUnit: "day", roundingMode: "ceil" });
+ shouldBe(ceil.toPlainDate().toString(), "2010-03-05");
+}
diff --git a/JSTests/stress/temporal-zdt-rounding-boundary.js b/JSTests/stress/temporal-zdt-rounding-boundary.js
new file mode 100644
index 000000000000..49b5e7f8deaa
--- /dev/null
+++ b/JSTests/stress/temporal-zdt-rounding-boundary.js
@@ -0,0 +1,135 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`expected ${JSON.stringify(expected)} but got ${JSON.stringify(actual)}`);
+}
+function shouldThrowRangeError(fn) {
+ try { fn(); throw new Error("expected RangeError but no exception thrown"); }
+ catch (e) { if (!(e instanceof RangeError)) throw new Error(`expected RangeError, got ${e}`); }
+}
+
+const minEpoch = -(86400n * 100_000_000n * 1_000_000_000n);
+const maxEpoch = 86400n * 100_000_000n * 1_000_000_000n;
+
+// minEpoch in -12:34 → local date -271821-04-19 (day -100000001, outside ±10^8)
+{
+ const zdt = new Temporal.ZonedDateTime(minEpoch, '-12:34');
+ shouldBe(zdt.toString(), '-271821-04-19T11:26:00-12:34[-12:34]');
+
+ // All rounding modes with 1ns increment must return the same value (spec step 13 early return)
+ for (const mode of ['ceil','floor','trunc','expand','halfCeil','halfFloor','halfTrunc','halfExpand','halfEven']) {
+ const r = zdt.round({ smallestUnit: 'nanosecond', roundingIncrement: 1, roundingMode: mode });
+ shouldBe(r.epochNanoseconds, minEpoch);
+ }
+}
+
+// minEpoch+1ns in -12:34 → local ns=1, 1ns still returns same
+{
+ const zdt = new Temporal.ZonedDateTime(minEpoch + 1n, '-12:34');
+ const r = zdt.round({ smallestUnit: 'nanosecond', roundingMode: 'ceil' });
+ shouldBe(r.epochNanoseconds, minEpoch + 1n);
+}
+
+// minEpoch in -12:34: local date -271821-04-19 (day -100000001 > 10^8) must throw for non-1ns
+{
+ const zdt = new Temporal.ZonedDateTime(minEpoch, '-12:34');
+ for (const unit of ['microsecond', 'millisecond', 'second', 'minute', 'hour']) {
+ shouldThrowRangeError(() => zdt.round({ smallestUnit: unit, roundingMode: 'ceil' }));
+ shouldThrowRangeError(() => zdt.round({ smallestUnit: unit, roundingMode: 'floor' }));
+ }
+ // increment > 1 for nanosecond also throws (not covered by spec step 13)
+ shouldThrowRangeError(() => zdt.round({ smallestUnit: 'nanosecond', roundingIncrement: 2, roundingMode: 'ceil' }));
+ shouldThrowRangeError(() => zdt.round({ smallestUnit: 'nanosecond', roundingIncrement: 5, roundingMode: 'floor' }));
+}
+
+// minEpoch in +01:23 → local date -271821-04-20 (day -100000000, at boundary, valid)
+// Rounding up succeeds (result UTC = +00:37, valid); rounding down throws (result UTC < minEpoch)
+{
+ const zdt = new Temporal.ZonedDateTime(minEpoch, '+01:23');
+ shouldBe(zdt.toString(), '-271821-04-20T01:23:00+01:23[+01:23]');
+
+ // ceil: 01:23 → 02:00 local → UTC 00:37 (after minEpoch) → valid
+ const r1 = zdt.round({ smallestUnit: 'hour', roundingMode: 'ceil' });
+ shouldBe(r1.toString(), '-271821-04-20T02:00:00+01:23[+01:23]');
+
+ const r2 = zdt.round({ smallestUnit: 'hour', roundingMode: 'expand' });
+ shouldBe(r2.toString(), '-271821-04-20T02:00:00+01:23[+01:23]');
+
+ // floor/trunc: 01:23 → 01:00 local → UTC -271821-04-19T23:37 (before minEpoch) → RangeError
+ shouldThrowRangeError(() => zdt.round({ smallestUnit: 'hour', roundingMode: 'floor' }));
+ shouldThrowRangeError(() => zdt.round({ smallestUnit: 'hour', roundingMode: 'trunc' }));
+
+ // half* rounding: 01:23 is closer to 01:00 than 02:00 → rounds down → RangeError
+ for (const mode of ['halfCeil','halfFloor','halfTrunc','halfExpand','halfEven']) {
+ shouldThrowRangeError(() => zdt.round({ smallestUnit: 'hour', roundingMode: mode }));
+ }
+
+ shouldThrowRangeError(() => zdt.round({ smallestUnit: 'day', roundingMode: 'ceil' }));
+ shouldThrowRangeError(() => zdt.round({ smallestUnit: 'day', roundingMode: 'floor' }));
+}
+
+// 1ns at +01:23 boundary also works (spec step 13)
+{
+ const zdt = new Temporal.ZonedDateTime(minEpoch, '+01:23');
+ const r = zdt.round({ smallestUnit: 'nanosecond', roundingMode: 'ceil' });
+ shouldBe(r.epochNanoseconds, minEpoch);
+}
+
+{
+ const zdt = Temporal.ZonedDateTime.from('2024-03-15T14:37:22.456789012+05:30[Asia/Kolkata]');
+ // 1 hour ceil: 14:37 → 15:00
+ const r = zdt.round({ smallestUnit: 'hour', roundingMode: 'ceil' });
+ shouldBe(r.hour, 15);
+ shouldBe(r.minute, 0);
+ // 30 min floor: 14:37 → 14:30
+ const r2 = zdt.round({ smallestUnit: 'minute', roundingIncrement: 30, roundingMode: 'floor' });
+ shouldBe(r2.hour, 14);
+ shouldBe(r2.minute, 30);
+}
+
+// maxEpoch UTC = +275760-09-13T00:00:00Z. Any offset ≤ +14:00 gives local date +275760-09-13
+// (day +100000000, at boundary, valid). No out-of-range local date is achievable at max boundary.
+{
+ const zdt = new Temporal.ZonedDateTime(maxEpoch, '+12:34');
+ shouldBe(zdt.toString(), '+275760-09-13T12:34:00+12:34[+12:34]');
+ // 1ns step 13 always returns original
+ const r = zdt.round({ smallestUnit: 'nanosecond', roundingMode: 'floor' });
+ shouldBe(r.epochNanoseconds, maxEpoch);
+ // hour trunc: 12:34 → 12:00 local → UTC -0:34 from day end → valid
+ const r2 = zdt.round({ smallestUnit: 'hour', roundingMode: 'trunc' });
+ shouldBe(r2.toString(), '+275760-09-13T12:00:00+12:34[+12:34]');
+ // hour ceil: 12:34 → 13:00 local → UTC = maxEpoch + 26min → exceeds max epoch → RangeError
+ shouldThrowRangeError(() => zdt.round({ smallestUnit: 'hour', roundingMode: 'ceil' }));
+}
+
+// maxEpoch in UTC: day rounding calls getStartOfDay(date) and getStartOfDay(nextDate=+275760-09-14).
+// +275760-09-14 is one day past the spec limit → getStartOfDay should propagate error → RangeError.
+{
+ const zdt = new Temporal.ZonedDateTime(maxEpoch, '+00:00');
+ shouldBe(zdt.toString(), '+275760-09-13T00:00:00+00:00[+00:00]');
+ // Any day rounding throws because getStartOfDay(nextDate) goes out of range.
+ shouldThrowRangeError(() => zdt.round({ smallestUnit: 'day', roundingMode: 'ceil' }));
+ shouldThrowRangeError(() => zdt.round({ smallestUnit: 'day', roundingMode: 'floor' }));
+ shouldThrowRangeError(() => zdt.round({ smallestUnit: 'day', roundingMode: 'trunc' }));
+}
+
+// toZonedDateTime() calls getStartOfDay. At max epoch UTC, the start of +275760-09-13 is exactly
+// maxEpoch (since UTC midnight = maxEpoch). Valid result.
+{
+ const zdt = new Temporal.ZonedDateTime(maxEpoch, '+00:00');
+ const sd = zdt.toPlainDate().toZonedDateTime({ timeZone: '+00:00' });
+ shouldBe(sd.epochNanoseconds, maxEpoch); // midnight = maxEpoch
+ shouldBe(sd.hour, 0);
+ shouldBe(sd.minute, 0);
+}
+// maxEpoch in -12:34: local date +275760-09-12 (valid), startOfDay = +275760-09-12T00:00:00-12:34
+{
+ const zdt = new Temporal.ZonedDateTime(maxEpoch, '-12:34');
+ const sd = zdt.toPlainDate().toZonedDateTime({ timeZone: '-12:34' });
+ shouldBe(sd.hour, 0);
+ shouldBe(sd.minute, 0);
+ shouldBe(sd.year, 275760);
+ shouldBe(sd.month, 9);
+ shouldBe(sd.day, 12);
+}
diff --git a/JSTests/stress/temporal-zdt-rounding-halfeven.js b/JSTests/stress/temporal-zdt-rounding-halfeven.js
new file mode 100644
index 000000000000..1d94603a2ff0
--- /dev/null
+++ b/JSTests/stress/temporal-zdt-rounding-halfeven.js
@@ -0,0 +1,168 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`expected ${JSON.stringify(expected)} but got ${JSON.stringify(actual)}`);
+}
+
+// Verify ZDT rounding against V8/temporal_rs by checking JSC matches expected values.
+//
+// Root bug: roundZonedDateTimeSubDay used quantity from LOCAL MIDNIGHT for all units,
+// but spec RoundTime uses sub-unit quantities:
+// hour: from midnight (all components)
+// minute: sub-hour (minute×60 + sub-minute ns)
+// second: sub-minute (second + sub-second ns)
+// millisecond: sub-second (ms + sub-ms ns)
+// microsecond: sub-microsecond (us + ns)
+// nanosecond: nanosecond only
+//
+// This affects halfEven/halfFloor/halfCeil when the quotient parity differs
+// between the from-midnight value and the sub-unit value.
+
+// ── Helper: round ZDT and check ─────────────────────────────────────────
+function checkRound(zdtStr, unit, increment, mode, expectedField, expectedValue) {
+ const zdt = Temporal.ZonedDateTime.from(zdtStr);
+ const r = zdt.round({ smallestUnit: unit, roundingIncrement: increment, roundingMode: mode });
+ if (r[expectedField] !== expectedValue)
+ throw new Error(`${zdtStr} round(${unit},${increment},${mode}): expected ${expectedField}=${expectedValue} got ${r[expectedField]}`);
+}
+
+// ── Minute rounding: halfEven ─────────────────────────────────────────────
+// Sub-hour quotient: 26*60e9/240e9 = 6.5 → r1=6 (even) → 11:24
+checkRound("1969-12-31T11:26:00-12:34[-12:34]", "minutes", 4, "halfEven", "minute", 24);
+checkRound("2020-01-01T11:26:00+00:00[+00:00]", "minutes", 4, "halfEven", "minute", 24);
+checkRound("2020-01-01T11:26:00+01:23[+01:23]", "minutes", 4, "halfEven", "minute", 24);
+
+// All 15 half-increment points in an hour (4-min increment, halfEven)
+// q = minute/4 — even q → round to q*4, odd q → round to (q+1)*4
+for (const [min, expected] of [
+ [2,0],[6,8],[10,8],[14,16],[18,16],[22,24],[26,24],
+ [30,32],[34,32],[38,40],[42,40],[46,48],[50,48],[54,56],[58,56]
+]) {
+ checkRound(`2020-01-01T00:${String(min).padStart(2,"0")}:00+00:00[+00:00]`, "minutes", 4, "halfEven", "minute", expected);
+}
+
+// ── Minute rounding: halfCeil and halfFloor ─────────────────────────────
+// halfCeil ties go to ceiling (r2), halfFloor ties go to floor (r1)
+// 11:26 tie: r1=11:24, r2=11:28
+checkRound("2020-01-01T11:26:00+00:00[+00:00]", "minutes", 4, "halfCeil", "minute", 28);
+checkRound("2020-01-01T11:26:00+00:00[+00:00]", "minutes", 4, "halfFloor", "minute", 24);
+checkRound("2020-01-01T11:26:00+00:00[+00:00]", "minutes", 4, "halfTrunc", "minute", 24); // toward zero
+checkRound("2020-01-01T11:26:00+00:00[+00:00]", "minutes", 4, "halfExpand", "minute", 28); // away from zero
+
+// ── Minute: various increments at half points ──────────────────────────
+// 3-minute: 01:30:00 → 1.5 minutes. sub-hour=90e9/180e9=0.5, r1=0(even) → :00
+checkRound("2020-01-01T00:01:30+00:00[+00:00]", "minutes", 3, "halfEven", "minute", 0);
+// 5-minute: 02:30:00 → sub-hour=150e9/300e9=0.5, r1=0(even) → :00
+checkRound("2020-01-01T00:02:30+00:00[+00:00]", "minutes", 5, "halfEven", "minute", 0);
+// 6-minute: 03:00:00 → sub-hour=180e9/360e9=0.5, r1=0(even) → :00
+checkRound("2020-01-01T00:03:00+00:00[+00:00]", "minutes", 6, "halfEven", "minute", 0);
+// 15-minute: 07:30:00 → 450e9/900e9=0.5, r1=0(even) → :00
+checkRound("2020-01-01T00:07:30+00:00[+00:00]", "minutes", 15, "halfEven", "minute", 0);
+// 30-minute: 15:00:00 → sub-hour=900e9/1800e9=0.5, r1=0(even) → :00
+checkRound("2020-01-01T00:15:00+00:00[+00:00]", "minutes", 30, "halfEven", "minute", 0);
+
+// ── Second rounding: halfEven ─────────────────────────────────────────
+// sub-minute quantity = second×1e9 + sub-second
+// 00:00:30.000 with 30s increment → sub-minute=30e9/30e9=1.0 exact → 30s
+checkRound("2020-01-01T00:00:30+00:00[+00:00]", "seconds", 30, "halfEven", "second", 30);
+// 00:00:15 with 30s increment → sub-minute=15e9/30e9=0.5, r1=0(even) → 0s
+checkRound("2020-01-01T00:00:15+00:00[+00:00]", "seconds", 30, "halfEven", "second", 0);
+// 00:00:30 with 1s increment → exact, no rounding
+checkRound("2020-01-01T00:00:30+00:00[+00:00]", "seconds", 1, "halfEven", "second", 30);
+// 00:01:15 with 30s halfEven: quantity=second=15e9, 15/30=0.5, r1=0(0,even) → second=0
+// baseOffset = 1*60e9. roundedLocalNs = 60e9 → minute=1, second=0
+{
+ const r = Temporal.ZonedDateTime.from("2020-01-01T00:01:15+00:00[+00:00]")
+ .round({ smallestUnit: "seconds", roundingIncrement: 30, roundingMode: "halfEven" });
+ shouldBe(r.minute, 1);
+ shouldBe(r.second, 0);
+}
+// 00:00:45 with 30s halfEven: quantity=45e9, 45/30=1.5, r1=30e9(1,odd) r2=60e9(2,even) → overflow
+// baseOffset=0. roundedLocalNs=60e9 → minute=1, second=0
+{
+ const r = Temporal.ZonedDateTime.from("2020-01-01T00:00:45+00:00[+00:00]")
+ .round({ smallestUnit: "seconds", roundingIncrement: 30, roundingMode: "halfEven" });
+ shouldBe(r.minute, 1);
+ shouldBe(r.second, 0);
+}
+
+// ── Millisecond rounding: halfEven ────────────────────────────────────
+// sub-second quantity = ms×1e6 + sub-ms
+// 00:00:00.250 with 500ms increment → 250e6/500e6=0.5, r1=0(0,even) → 0ms
+checkRound("2020-01-01T00:00:00.250+00:00[+00:00]", "milliseconds", 500, "halfEven", "millisecond", 0);
+// 00:00:00.500 with 500ms → 500e6/500e6=1.0 exact → 500ms
+checkRound("2020-01-01T00:00:00.500+00:00[+00:00]", "milliseconds", 500, "halfEven", "millisecond", 500);
+// 00:00:00.100 with 200ms → 100e6/200e6=0.5, r1=0(0,even) → 0ms
+checkRound("2020-01-01T00:00:00.100+00:00[+00:00]", "milliseconds", 200, "halfEven", "millisecond", 0);
+// 00:00:00.750 with 500ms → 750e6/500e6=1.5, r1=500e6(1,odd) r2=1000e6(2,even) → 2*500=1000ms → 1s overflow
+{
+ const r = Temporal.ZonedDateTime.from("2020-01-01T00:00:00.750+00:00[+00:00]")
+ .round({ smallestUnit: "milliseconds", roundingIncrement: 500, roundingMode: "halfEven" });
+ shouldBe(r.second, 1);
+ shouldBe(r.millisecond, 0);
+}
+
+// ── Microsecond rounding: halfEven ────────────────────────────────────
+// sub-ms quantity = us×1000 + ns
+// 00:00:00.000500 (500µs) with 500µs increment → exact → 500µs
+checkRound("2020-01-01T00:00:00.0005+00:00[+00:00]", "microseconds", 500, "halfEven", "microsecond", 500);
+// 00:00:00.000250 (250µs) with 500µs increment → 250000/500000=0.5, r1=0(0,even) → 0µs
+checkRound("2020-01-01T00:00:00.00025+00:00[+00:00]", "microseconds", 500, "halfEven", "microsecond", 0);
+
+// ── Nanosecond rounding: halfEven ─────────────────────────────────────
+// quantity = nanosecond only (independent of higher units)
+// ns=500 with 500ns increment → exact → 500ns; ns=250 with 500ns → 0.5 → r1=0(0,even) → 0ns
+checkRound("2020-01-01T00:00:00.0000005+00:00[+00:00]", "nanoseconds", 500, "halfEven", "nanosecond", 500);
+checkRound("2020-01-01T00:00:00.00000025+00:00[+00:00]", "nanoseconds", 500, "halfEven", "nanosecond", 0);
+
+// ── Hour rounding: halfEven (from-midnight, unchanged behavior) ────────
+// 01:30 → 1.5h, r1=1(odd) r2=2(even) → 2h
+checkRound("2020-01-01T01:30:00+00:00[+00:00]", "hours", 1, "halfEven", "hour", 2);
+// 12:30 → 12.5h, r1=12(even) r2=13(odd) → 12h
+checkRound("2020-01-01T12:30:00+00:00[+00:00]", "hours", 1, "halfEven", "hour", 12);
+// 2h with 2h increment: 1:00 → 1/2=0.5, r1=0(even) → 0h
+checkRound("2020-01-01T01:00:00+00:00[+00:00]", "hours", 2, "halfEven", "hour", 0);
+
+// ── Day overflow on round up ─────────────────────────────────────────
+// 23:58 ceiled to 4-min → 24:00 = midnight next day
+{
+ const r = Temporal.ZonedDateTime.from("2020-01-01T23:58:00+00:00[+00:00]")
+ .round({ smallestUnit: "minutes", roundingIncrement: 4, roundingMode: "ceil" });
+ shouldBe(r.day, 2);
+ shouldBe(r.hour, 0);
+ shouldBe(r.minute, 0);
+}
+
+// ── Large epoch ns values (from interestingEpochNs) ──────────────────
+// These should round correctly regardless of the epoch magnitude.
+// epoch=2^64 with offset -12:34: local time computed from epoch, must round correctly.
+for (const epochNs of [0n, 1n, -1n, 2n**32n, -(2n**32n), 1_000_000_000_000_000_000n]) {
+ const zdt = new Temporal.ZonedDateTime(epochNs, "+00:00");
+ // Should not throw; result must be on a 4-minute boundary
+ const r = zdt.round({ smallestUnit: "minutes", roundingIncrement: 4, roundingMode: "trunc" });
+ shouldBe(r.minute % 4, 0);
+ shouldBe(r.second, 0);
+ shouldBe(r.millisecond, 0);
+ shouldBe(r.microsecond, 0);
+ shouldBe(r.nanosecond, 0);
+}
+
+// ── Consistency: result must always land on increment boundary ────────
+const testZDTs = [
+ "2020-01-01T11:26:37.123456789+00:00[+00:00]",
+ "1969-12-31T11:26:00-12:34[-12:34]",
+ "2020-06-15T23:59:59.999999999+00:00[+00:00]",
+];
+for (const s of testZDTs) {
+ for (const [unit, inc, field] of [
+ ["minutes", 4, "minute"], ["minutes", 15, "minute"], ["minutes", 30, "minute"],
+ ["seconds", 30, "second"], ["milliseconds", 500, "millisecond"],
+ ]) {
+ for (const mode of ["ceil","floor","trunc","expand","halfEven","halfCeil","halfFloor","halfExpand","halfTrunc"]) {
+ const r = Temporal.ZonedDateTime.from(s).round({ smallestUnit: unit, roundingIncrement: inc, roundingMode: mode });
+ if (r[field] % inc !== 0)
+ throw new Error(`${s} ${unit}/${inc}/${mode}: ${field}=${r[field]} not on boundary`);
+ }
+ }
+}
diff --git a/JSTests/stress/temporal-zdt-timezone-canonicalization.js b/JSTests/stress/temporal-zdt-timezone-canonicalization.js
new file mode 100644
index 000000000000..4d76cd05cd9b
--- /dev/null
+++ b/JSTests/stress/temporal-zdt-timezone-canonicalization.js
@@ -0,0 +1,27 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(actual, expected, msg) {
+ if (actual !== expected)
+ throw new Error(msg + ": expected " + JSON.stringify(expected) + ", got " + JSON.stringify(actual));
+}
+
+const zdt1 = new Temporal.ZonedDateTime(0n, "+0530");
+shouldBe(zdt1.timeZoneId, "+05:30", "'+0530' canonicalized to '+05:30'");
+
+const zdt2 = new Temporal.ZonedDateTime(0n, "-0800");
+shouldBe(zdt2.timeZoneId, "-08:00", "'-0800' canonicalized to '-08:00'");
+
+const zdt3 = new Temporal.ZonedDateTime(0n, "+00:00");
+shouldBe(zdt3.timeZoneId, "+00:00", "'+00:00' stays '+00:00'");
+
+const zdt4 = new Temporal.ZonedDateTime(0n, "Africa/CAIRO");
+shouldBe(zdt4.timeZoneId, "Africa/Cairo", "'Africa/CAIRO' case-normalized to 'Africa/Cairo'");
+
+const zdt5 = new Temporal.ZonedDateTime(0n, "Asia/Calcutta");
+shouldBe(zdt5.timeZoneId, "Asia/Calcutta", "'Asia/Calcutta' alias preserved");
+
+const zdt6 = new Temporal.ZonedDateTime(0n, "+05:30");
+shouldBe(zdt6.timeZoneId, "+05:30", "'+05:30' stays '+05:30'");
+
+const zdt7 = new Temporal.ZonedDateTime(0n, "UTC");
+shouldBe(zdt7.timeZoneId, "UTC", "'UTC' stays 'UTC'");
diff --git a/JSTests/stress/temporal-zdt-to-locale-string-inherited-options.js b/JSTests/stress/temporal-zdt-to-locale-string-inherited-options.js
new file mode 100644
index 000000000000..b6f251a1de4c
--- /dev/null
+++ b/JSTests/stress/temporal-zdt-to-locale-string-inherited-options.js
@@ -0,0 +1,56 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`expected ${JSON.stringify(expected)} but got ${JSON.stringify(actual)}`);
+}
+
+function shouldThrow(func, errorType) {
+ let threw = false;
+ try { func(); } catch (e) {
+ threw = true;
+ if (!(e instanceof errorType))
+ throw new Error(`Expected ${errorType.name} but got ${e.constructor.name}: ${e.message}`);
+ }
+ if (!threw)
+ throw new Error(`Expected ${errorType.name} but no exception was thrown`);
+}
+
+const zdt = Temporal.ZonedDateTime.from("1970-01-01T00:00:00+00:00[UTC]");
+
+// Inherited month option.
+{
+ const zdt2 = new Temporal.ZonedDateTime(0n, "UTC");
+ shouldBe(zdt2.toLocaleString("en-US", Object.create({ month: "long" })), "January");
+}
+
+// Inherited option must be honored (spec uses Get(), not own-only access).
+{
+ const opts = Object.create({ month: "long" });
+ shouldBe(zdt.toLocaleString("en-US", opts), "January");
+}
+
+// Inherited dateStyle must be honored.
+{
+ const opts = Object.create({ dateStyle: "full" });
+ shouldBe(zdt.toLocaleString("en-US", opts), "Thursday, January 1, 1970");
+}
+
+// Own property overrides inherited one.
+{
+ const opts = Object.create({ month: "long" });
+ opts.month = "short";
+ shouldBe(zdt.toLocaleString("en-US", opts), "Jan");
+}
+
+// timeZone in inherited options must also throw TypeError.
+{
+ shouldThrow(() => zdt.toLocaleString("en-US", Object.create({ timeZone: "America/New_York" })), TypeError);
+}
+
+// null-prototype object (no inherited props) works like a plain object.
+{
+ const opts = Object.create(null);
+ opts.month = "numeric";
+ shouldBe(zdt.toLocaleString("en-US", opts), "1");
+}
diff --git a/JSTests/stress/temporal-zdt-with-boundary.js b/JSTests/stress/temporal-zdt-with-boundary.js
new file mode 100644
index 000000000000..a7ba5f5637b2
--- /dev/null
+++ b/JSTests/stress/temporal-zdt-with-boundary.js
@@ -0,0 +1,98 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`expected ${JSON.stringify(expected)} but got ${JSON.stringify(actual)}`);
+}
+function shouldThrowRangeError(fn) {
+ try { fn(); throw new Error("expected RangeError but no exception thrown"); }
+ catch (e) { if (!(e instanceof RangeError)) throw new Error(`expected RangeError, got ${e}`); }
+}
+
+const minEpoch = -(86400n * 100_000_000n * 1_000_000_000n);
+const maxEpoch = 86400n * 100_000_000n * 1_000_000_000n;
+
+// minEpoch in -12:34: local date -271821-04-19 (day -100000001, outside ±10^8)
+// Any with() that results in this date must throw.
+{
+ const zdt = new Temporal.ZonedDateTime(minEpoch, '-12:34');
+ shouldBe(zdt.toString(), '-271821-04-19T11:26:00-12:34[-12:34]');
+
+ // Modifying sub-second fields keeps same date → RangeError (from benchmark case)
+ shouldThrowRangeError(() => zdt.with({ year: -271821, microsecond: 1, nanosecond: 0 }));
+ shouldThrowRangeError(() => zdt.with({ year: -271821, microsecond: 1, nanosecond: 0 }, { overflow: 'constrain' }));
+ shouldThrowRangeError(() => zdt.with({ microsecond: 1 }));
+ shouldThrowRangeError(() => zdt.with({ nanosecond: 999 }));
+ shouldThrowRangeError(() => zdt.with({ hour: 12 }));
+ shouldThrowRangeError(() => zdt.with({ minute: 0 }));
+
+ // Default offset option is 'prefer' — also throws
+ shouldThrowRangeError(() => zdt.with({ second: 30 }, { offset: 'prefer' }));
+ shouldThrowRangeError(() => zdt.with({ second: 30 }, { offset: 'reject' }));
+
+ // 'use' branch computes balanced UTC date — also throws because balanced UTC date
+ // -271821-04-20 would give valid epoch, but the local date check still runs
+}
+
+// minEpoch in +01:23: local date -271821-04-20 (day -100000000, at boundary, valid)
+// with() that keeps same local date must succeed.
+{
+ const zdt = new Temporal.ZonedDateTime(minEpoch, '+01:23');
+ shouldBe(zdt.toString(), '-271821-04-20T01:23:00+01:23[+01:23]');
+
+ // Modifying time fields within same day succeeds
+ const r1 = zdt.with({ hour: 2, minute: 0 });
+ shouldBe(r1.toString(), '-271821-04-20T02:00:00+01:23[+01:23]');
+
+ const r2 = zdt.with({ nanosecond: 500 });
+ shouldBe(r2.toString(), '-271821-04-20T01:23:00.0000005+01:23[+01:23]');
+
+ // with({day:19}) pushes local date to -271821-04-19 (day -100000001, out of range) → RangeError
+ // This is distinct from the above tests: the *starting* local date is valid but the *result* is not.
+ shouldThrowRangeError(() => zdt.with({ day: 19 }));
+ shouldThrowRangeError(() => zdt.with({ day: 19 }, { overflow: 'constrain' }));
+}
+
+// ZDT one day ahead of minEpoch in -12:34: local date -271821-04-20 (day -100000000, valid)
+// with() must succeed.
+{
+ const oneDayNs = 86400n * 1_000_000_000n;
+ const zdt = new Temporal.ZonedDateTime(minEpoch + oneDayNs, '-12:34');
+ shouldBe(zdt.toString(), '-271821-04-20T11:26:00-12:34[-12:34]');
+
+ // Changing hour only (minute stays 26)
+ const r = zdt.with({ hour: 15 });
+ shouldBe(r.toString(), '-271821-04-20T15:26:00-12:34[-12:34]');
+ // Changing hour and minute
+ const r2 = zdt.with({ hour: 15, minute: 0 });
+ shouldBe(r2.toString(), '-271821-04-20T15:00:00-12:34[-12:34]');
+}
+
+// maxEpoch boundary: maxEpoch UTC = +275760-09-13T00:00:00Z.
+// Any offset ≤ +14:00 gives local date +275760-09-13 (day +100000000, valid).
+// No out-of-range local date is achievable at max boundary (would need UTC > maxEpoch).
+{
+ const zdt = new Temporal.ZonedDateTime(maxEpoch, '+12:34');
+ shouldBe(zdt.toString(), '+275760-09-13T12:34:00+12:34[+12:34]');
+ // with() succeeds for valid local date
+ const r = zdt.with({ minute: 0 });
+ shouldBe(r.toString(), '+275760-09-13T12:00:00+12:34[+12:34]');
+}
+
+// maxEpoch in -01:23: local date +275760-09-12 (safely within range)
+{
+ const zdt = new Temporal.ZonedDateTime(maxEpoch, '-01:23');
+ shouldBe(zdt.toString(), '+275760-09-12T22:37:00-01:23[-01:23]');
+ // Modifying within same day is fine
+ const r = zdt.with({ hour: 20 });
+ shouldBe(r.toString(), '+275760-09-12T20:37:00-01:23[-01:23]');
+}
+
+{
+ const zdt = Temporal.ZonedDateTime.from('2024-06-15T10:30:00+09:00[Asia/Tokyo]');
+ const r = zdt.with({ hour: 14, minute: 45, second: 30 });
+ shouldBe(r.hour, 14);
+ shouldBe(r.minute, 45);
+ shouldBe(r.second, 30);
+ shouldBe(r.timeZoneId, 'Asia/Tokyo');
+}
diff --git a/JSTests/stress/temporal-zoned-date-time-with-era-calendar.js b/JSTests/stress/temporal-zoned-date-time-with-era-calendar.js
new file mode 100644
index 000000000000..2a976a5e7fcb
--- /dev/null
+++ b/JSTests/stress/temporal-zoned-date-time-with-era-calendar.js
@@ -0,0 +1,123 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`expected ${JSON.stringify(expected)} but got ${JSON.stringify(actual)}`);
+}
+
+function shouldThrow(func, errorType) {
+ let threw = false;
+ try { func(); } catch (e) {
+ threw = true;
+ if (!(e instanceof errorType))
+ throw new Error(`Expected ${errorType.name} but got ${e.constructor.name}: ${e.message}`);
+ }
+ if (!threw)
+ throw new Error(`Expected ${errorType.name} but no exception was thrown`);
+}
+
+// --- Japanese calendar era tests ---
+
+// Showa 50 = 1975: era+eraYear without explicit year
+{
+ const zdt = new Temporal.ZonedDateTime(0n, "UTC", "japanese");
+ const result = zdt.with({ era: "showa", eraYear: 50, month: 1, day: 5 });
+ shouldBe(result.year, 1975);
+ shouldBe(result.month, 1);
+ shouldBe(result.day, 5);
+ shouldBe(result.era, "showa");
+ shouldBe(result.eraYear, 50);
+}
+
+// Month inherited from base (epoch 0 = Jan), not explicit.
+{
+ const zdt = new Temporal.ZonedDateTime(0n, "UTC", "japanese");
+ const result = zdt.with({ era: "showa", eraYear: 50, day: 5 });
+ shouldBe(result.toString(), "1975-01-05T00:00:00+00:00[UTC][u-ca=japanese]");
+ shouldBe(result.year, 1975);
+ shouldBe(result.month, 1);
+ shouldBe(result.day, 5);
+}
+
+// Heisei 1 = 1989: era+eraYear without explicit year
+{
+ const zdt = new Temporal.ZonedDateTime(0n, "UTC", "japanese");
+ const result = zdt.with({ era: "heisei", eraYear: 1, month: 1, day: 8 });
+ shouldBe(result.year, 1989);
+ shouldBe(result.era, "heisei");
+ shouldBe(result.eraYear, 1);
+}
+
+// era+eraYear+year all present: should all agree (Showa 50 = 1975)
+{
+ const zdt = new Temporal.ZonedDateTime(0n, "UTC", "japanese");
+ const result = zdt.with({ era: "showa", eraYear: 50, year: 1975, month: 1, day: 5 });
+ shouldBe(result.year, 1975);
+ shouldBe(result.eraYear, 50);
+}
+
+// Change eraYear (with era) on an existing era-based ZDT: Showa 50->51 = 1975->1976
+{
+ const base = Temporal.ZonedDateTime.from("1975-06-15T12:00:00+00:00[UTC][u-ca=japanese]");
+ shouldBe(base.era, "showa");
+ shouldBe(base.eraYear, 50);
+ const result = base.with({ era: "showa", eraYear: 51 });
+ shouldBe(result.year, 1976);
+ shouldBe(result.era, "showa");
+ shouldBe(result.eraYear, 51);
+ shouldBe(result.month, 6);
+ shouldBe(result.day, 15);
+}
+
+// era only (no eraYear) must throw TypeError ("Insufficient fields")
+{
+ const zdt = new Temporal.ZonedDateTime(0n, "UTC", "japanese");
+ shouldThrow(() => zdt.with({ era: "showa", month: 1, day: 5 }), TypeError);
+}
+
+// eraYear only (no era) must throw TypeError ("Insufficient fields")
+{
+ const zdt = new Temporal.ZonedDateTime(0n, "UTC", "japanese");
+ shouldThrow(() => zdt.with({ eraYear: 50, month: 1, day: 5 }), TypeError);
+}
+
+// --- Gregory calendar era test ---
+{
+ const zdt = new Temporal.ZonedDateTime(0n, "UTC", "gregory");
+ const result = zdt.with({ era: "ce", eraYear: 2024, month: 6, day: 1 });
+ shouldBe(result.year, 2024);
+ shouldBe(result.era, "ce");
+ shouldBe(result.eraYear, 2024);
+}
+
+// era+eraYear+year that DON'T agree must throw RangeError ("Inconsistent year")
+{
+ const zdt = new Temporal.ZonedDateTime(0n, "UTC", "japanese");
+ shouldThrow(() => zdt.with({ era: "showa", eraYear: 50, year: 2000, month: 1, day: 5 }), RangeError);
+}
+
+// year=0 (ISO year 0 = 1 BC) with conflicting era+eraYear must also throw RangeError.
+// Requires optional year in calendarDateFromFields — a 0-sentinel would silently skip.
+{
+ const zdt = new Temporal.ZonedDateTime(0n, "UTC", "gregory");
+ shouldThrow(() => zdt.with({ era: "ce", eraYear: 2024, year: 0, month: 1, day: 5 }), RangeError);
+}
+
+// year=0 consistent with bce eraYear=1 (BCE 1 = ISO 0) must NOT throw.
+{
+ const zdt = new Temporal.ZonedDateTime(0n, "UTC", "gregory");
+ const result = zdt.with({ era: "bce", eraYear: 1, year: 0, month: 6, day: 15 });
+ shouldBe(result.year, 0);
+ shouldBe(result.era, "bce");
+ shouldBe(result.eraYear, 1);
+}
+
+// --- iso8601 (no eras): with() still works correctly ---
+{
+ const zdt = new Temporal.ZonedDateTime(0n, "UTC");
+ shouldBe(zdt.calendarId, "iso8601");
+ const result = zdt.with({ year: 2024, month: 3, day: 15 });
+ shouldBe(result.year, 2024);
+ shouldBe(result.month, 3);
+ shouldBe(result.day, 15);
+}
diff --git a/JSTests/stress/temporal-zoned-datetime-difference-day-correction.js b/JSTests/stress/temporal-zoned-datetime-difference-day-correction.js
new file mode 100644
index 000000000000..632b6dda1417
--- /dev/null
+++ b/JSTests/stress/temporal-zoned-datetime-difference-day-correction.js
@@ -0,0 +1,63 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(a, b, msg) {
+ if (a !== b) throw new Error(`${msg}: expected ${b}, got ${a}`);
+}
+
+// Key test: difference spanning spring-forward where 24h assumption fails
+// March 9 23:00 EST → March 11 01:00 EDT
+// Naive: endTime(01:00) < startTime(23:00) → borrow → 1 day + 26h? No.
+// Correct: use timezone-aware day boundary
+{
+ const start = Temporal.ZonedDateTime.from("2024-03-09T23:00[America/New_York]");
+ const end = Temporal.ZonedDateTime.from("2024-03-11T01:00[America/New_York]");
+ const dur = start.until(end, { largestUnit: "days" });
+ // Real elapsed: March 9 23:00 EST → March 11 01:00 EDT
+ // = 1 day (March 10, 23h due to DST) + 2 hours = 25 hours total
+ // As days+hours: 1 day + 2 hours (where the "day" is the 23h spring-forward day)
+ shouldBe(dur.days, 1, "days component");
+ shouldBe(dur.hours, 2, "hours component");
+}
+
+{
+ const start = Temporal.ZonedDateTime.from("2024-11-02T23:00[America/New_York]");
+ const end = Temporal.ZonedDateTime.from("2024-11-04T01:00[America/New_York]");
+ const dur = start.until(end, { largestUnit: "days" });
+ // Nov 2 23:00 EDT → Nov 4 01:00 EST
+ // Day 1 = Nov 3 (25h fall-back day), then 2 more hours
+ shouldBe(dur.days, 1, "fall-back: days");
+ shouldBe(dur.hours, 2, "fall-back: hours");
+}
+
+{
+ const start = Temporal.ZonedDateTime.from("2024-06-15T10:00[America/New_York]");
+ const end = Temporal.ZonedDateTime.from("2024-06-15T14:30[America/New_York]");
+ const dur = start.until(end, { largestUnit: "hours" });
+ shouldBe(dur.hours, 4, "same-day hours");
+ shouldBe(dur.minutes, 30, "same-day minutes");
+}
+
+{
+ const start = Temporal.ZonedDateTime.from("2024-01-15T12:00[America/New_York]");
+ const end = Temporal.ZonedDateTime.from("2024-04-15T12:00[America/New_York]");
+ const dur = start.until(end, { largestUnit: "months" });
+ shouldBe(dur.months, 3, "3 months across DST");
+ shouldBe(dur.days, 0, "exact month boundary");
+ shouldBe(dur.hours, 0, "same wall-clock time");
+}
+
+{
+ const start = Temporal.ZonedDateTime.from("2024-03-11T01:00[America/New_York]");
+ const end = Temporal.ZonedDateTime.from("2024-03-09T23:00[America/New_York]");
+ const dur = start.until(end, { largestUnit: "days" });
+ shouldBe(dur.days, -1, "negative days");
+ shouldBe(dur.hours, -2, "negative hours");
+}
+
+{
+ const zdt = Temporal.ZonedDateTime.from("2024-03-10T03:00[America/New_York]");
+ const dur = zdt.until(zdt, { largestUnit: "days" });
+ shouldBe(dur.days, 0, "same instant: days");
+ shouldBe(dur.hours, 0, "same instant: hours");
+ shouldBe(dur.sign, 0, "same instant: sign");
+}
diff --git a/JSTests/stress/temporal-zoned-datetime-dst-arithmetic.js b/JSTests/stress/temporal-zoned-datetime-dst-arithmetic.js
new file mode 100644
index 000000000000..7ac34d62188d
--- /dev/null
+++ b/JSTests/stress/temporal-zoned-datetime-dst-arithmetic.js
@@ -0,0 +1,81 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(a, b, msg) {
+ if (a !== b) throw new Error(`${msg}: expected ${b}, got ${a}`);
+}
+
+{
+ const before = Temporal.ZonedDateTime.from("2024-03-09T12:00[America/New_York]");
+ shouldBe(before.offset, "-05:00", "March 9 is EST");
+ const after = before.add({ days: 1 });
+ shouldBe(after.offset, "-04:00", "March 10 is EDT");
+ shouldBe(after.hour, 12, "Hour preserved across spring-forward");
+ shouldBe(after.day, 10, "Day is March 10");
+}
+
+{
+ const before = Temporal.ZonedDateTime.from("2024-11-02T12:00[America/New_York]");
+ shouldBe(before.offset, "-04:00", "Nov 2 is EDT");
+ const after = before.add({ days: 1 });
+ shouldBe(after.offset, "-05:00", "Nov 3 is EST");
+ shouldBe(after.hour, 12, "Hour preserved across fall-back");
+ shouldBe(after.day, 3, "Day is Nov 3");
+}
+
+{
+ const springForward = Temporal.ZonedDateTime.from("2024-03-10T12:00[America/New_York]");
+ shouldBe(springForward.hoursInDay, 23, "Spring-forward day is 23 hours");
+}
+
+{
+ const fallBack = Temporal.ZonedDateTime.from("2024-11-03T12:00[America/New_York]");
+ shouldBe(fallBack.hoursInDay, 25, "Fall-back day is 25 hours");
+}
+
+{
+ const normal = Temporal.ZonedDateTime.from("2024-06-15T12:00[America/New_York]");
+ shouldBe(normal.hoursInDay, 24, "Normal day is 24 hours");
+}
+
+{
+ const utc = Temporal.ZonedDateTime.from("2024-03-10T12:00[UTC]");
+ shouldBe(utc.hoursInDay, 24, "UTC day is always 24 hours");
+}
+
+{
+ const start = Temporal.ZonedDateTime.from("2024-03-09T23:00[America/New_York]");
+ const end = Temporal.ZonedDateTime.from("2024-03-10T03:00[America/New_York]");
+ const dur = start.until(end, { largestUnit: "hours" });
+ // 23:00 EST to 03:00 EDT = 3 hours (not 4, because of spring-forward)
+ shouldBe(dur.hours, 3, "Spring-forward: 23:00→03:00 is 3h not 4h");
+}
+
+{
+ const start = Temporal.ZonedDateTime.from("2024-11-02T23:00[America/New_York]");
+ const end = Temporal.ZonedDateTime.from("2024-11-03T03:00[America/New_York]");
+ const dur = start.until(end, { largestUnit: "hours" });
+ // 23:00 EDT to 03:00 EST = 5 hours (not 4, because of fall-back)
+ shouldBe(dur.hours, 5, "Fall-back: 23:00→03:00 is 5h not 4h");
+}
+
+{
+ const start = Temporal.ZonedDateTime.from("2024-03-09T23:00[America/New_York]");
+ const end = Temporal.ZonedDateTime.from("2024-03-10T03:00[America/New_York]");
+ const dur = end.since(start, { largestUnit: "hours" });
+ shouldBe(dur.hours, 3, "since should match until for spring-forward");
+}
+
+{
+ const zdt = Temporal.ZonedDateTime.from("2024-03-10T18:00[America/New_York]");
+ const rounded = zdt.round({ smallestUnit: "day" });
+ // 18:00 on a 23-hour day → rounds to next day (18 > 23/2 = 11.5)
+ shouldBe(rounded.day, 11, "Rounding 18:00 on 23h day → next day");
+}
+
+{
+ const zdt = Temporal.ZonedDateTime.from("2024-03-10T12:00[America/New_York]");
+ const result = zdt.subtract({ days: 1 });
+ shouldBe(result.day, 9, "Subtract 1 day from March 10");
+ shouldBe(result.hour, 12, "Hour preserved");
+ shouldBe(result.offset, "-05:00", "March 9 is EST");
+}
diff --git a/JSTests/stress/temporal-zoned-datetime-dst-fold.js b/JSTests/stress/temporal-zoned-datetime-dst-fold.js
new file mode 100644
index 000000000000..e68ae66010e3
--- /dev/null
+++ b/JSTests/stress/temporal-zoned-datetime-dst-fold.js
@@ -0,0 +1,64 @@
+//@ requireOptions("--useTemporal=1")
+
+function shouldBe(a, b, msg) {
+ if (a !== b) throw new Error(`${msg}: expected ${b}, got ${a}`);
+}
+
+function shouldThrow(fn, msg) {
+ try { fn(); throw new Error(`${msg}: should have thrown`); }
+ catch (e) { if (e.message.startsWith(msg)) throw e; }
+}
+
+{
+ const zdt = Temporal.ZonedDateTime.from("2024-11-03T01:30[America/New_York]");
+ shouldBe(zdt.offset, "-04:00", "Compatible fold should pick EDT (earlier)");
+ shouldBe(zdt.hour, 1, "hour");
+ shouldBe(zdt.minute, 30, "minute");
+}
+
+{
+ const zdt = Temporal.ZonedDateTime.from("2024-11-03T01:30[America/New_York]", { disambiguation: "compatible" });
+ shouldBe(zdt.offset, "-04:00", "Explicit compatible fold should pick EDT");
+}
+
+{
+ const zdt = Temporal.ZonedDateTime.from("2024-11-03T01:30[America/New_York]", { disambiguation: "earlier" });
+ shouldBe(zdt.offset, "-04:00", "Earlier fold should pick EDT");
+}
+
+{
+ const zdt = Temporal.ZonedDateTime.from("2024-11-03T01:30[America/New_York]", { disambiguation: "later" });
+ shouldBe(zdt.offset, "-05:00", "Later fold should pick EST");
+}
+
+{
+ shouldThrow(
+ () => Temporal.ZonedDateTime.from("2024-11-03T01:30[America/New_York]", { disambiguation: "reject" }),
+ "Reject fold should throw"
+ );
+}
+
+{
+ const earlier = Temporal.ZonedDateTime.from("2024-11-03T01:30[America/New_York]", { disambiguation: "earlier" });
+ const later = Temporal.ZonedDateTime.from("2024-11-03T01:30[America/New_York]", { disambiguation: "later" });
+ const diffNs = later.epochNanoseconds - earlier.epochNanoseconds;
+ shouldBe(diffNs, 3600000000000n, "Earlier and later should be 1h apart");
+}
+
+// Australia/Lord_Howe: 30-minute DST shift (non-standard)
+// Fall-back: +11:00 → +10:30 (clocks go back 30 min)
+// 2024-04-07 at 02:00 LHDT → 01:30 LHST
+{
+ const earlier = Temporal.ZonedDateTime.from("2024-04-07T01:45[Australia/Lord_Howe]", { disambiguation: "earlier" });
+ const later = Temporal.ZonedDateTime.from("2024-04-07T01:45[Australia/Lord_Howe]", { disambiguation: "later" });
+ const diffNs = later.epochNanoseconds - earlier.epochNanoseconds;
+ shouldBe(diffNs, 1800000000000n, "Lord Howe fold should be 30min apart");
+}
+
+{
+ const c = Temporal.ZonedDateTime.from("2024-06-15T12:00[America/New_York]", { disambiguation: "compatible" });
+ const e = Temporal.ZonedDateTime.from("2024-06-15T12:00[America/New_York]", { disambiguation: "earlier" });
+ const l = Temporal.ZonedDateTime.from("2024-06-15T12:00[America/New_York]", { disambiguation: "later" });
+ shouldBe(c.epochNanoseconds, e.epochNanoseconds, "Non-fold: compatible == earlier");
+ shouldBe(c.epochNanoseconds, l.epochNanoseconds, "Non-fold: compatible == later");
+}
diff --git a/JSTests/stress/temporal-zoned-datetime-dst-gap.js b/JSTests/stress/temporal-zoned-datetime-dst-gap.js
new file mode 100644
index 000000000000..39419124defc
--- /dev/null
+++ b/JSTests/stress/temporal-zoned-datetime-dst-gap.js
@@ -0,0 +1,62 @@
+//@ requireOptions("--useTemporal=1")
+
+// temporal-zdt-dst-gap.js — DST spring-forward gap disambiguation stress test
+
+function shouldBe(a, b, msg) {
+ if (a !== b) throw new Error(`${msg}: expected ${b}, got ${a}`);
+}
+
+function shouldThrow(fn, msg) {
+ try { fn(); throw new Error(`${msg}: should have thrown`); }
+ catch (e) { if (e.message.startsWith(msg)) throw e; }
+}
+
+// America/New_York spring-forward 2024-03-10: 02:00-02:59 doesn't exist
+// Clocks jump from 01:59 EST → 03:00 EDT
+
+// Compatible for gap: per spec, "compatible" acts like "later" for gaps
+// (jump forward past the gap → post-transition time)
+{
+ const zdt = Temporal.ZonedDateTime.from("2024-03-10T02:30[America/New_York]");
+ shouldBe(zdt.hour, 3, "Compatible gap jumps forward past gap");
+ shouldBe(zdt.offset, "-04:00", "Compatible gap uses EDT (post-transition)");
+}
+
+// "earlier" for gap: use post-transition offset → maps to pre-gap time
+// Spec: naiveNs - offsetAfter → 02:30 - (-4h) = 06:30Z → 01:30 EST
+{
+ const zdt = Temporal.ZonedDateTime.from("2024-03-10T02:30[America/New_York]", { disambiguation: "earlier" });
+ shouldBe(zdt.hour, 1, "Earlier gap result hour");
+ shouldBe(zdt.offset, "-05:00", "Earlier gap offset");
+}
+
+// "later" for gap: use pre-transition offset → maps to post-gap time
+// Spec: naiveNs - offsetBefore → 02:30 - (-5h) = 07:30Z → 03:30 EDT
+{
+ const zdt = Temporal.ZonedDateTime.from("2024-03-10T02:30[America/New_York]", { disambiguation: "later" });
+ shouldBe(zdt.hour, 3, "Later gap result hour");
+ shouldBe(zdt.offset, "-04:00", "Later gap offset");
+}
+
+{
+ shouldThrow(
+ () => Temporal.ZonedDateTime.from("2024-03-10T02:30[America/New_York]", { disambiguation: "reject" }),
+ "Reject gap should throw"
+ );
+}
+
+{
+ const before = Temporal.ZonedDateTime.from("2024-03-10T01:59[America/New_York]");
+ shouldBe(before.offset, "-05:00", "1:59 AM is still EST");
+ shouldBe(before.hour, 1, "hour before gap");
+
+ const after = Temporal.ZonedDateTime.from("2024-03-10T03:00[America/New_York]");
+ shouldBe(after.offset, "-04:00", "3:00 AM is EDT");
+ shouldBe(after.hour, 3, "hour after gap");
+}
+
+{
+ const zdt = Temporal.ZonedDateTime.from("2024-03-10T02:30[UTC]");
+ shouldBe(zdt.hour, 2, "UTC has no gap");
+ shouldBe(zdt.offset, "+00:00", "UTC offset");
+}
diff --git a/JSTests/stress/temporal-zoned-datetime-timezone-identifier.js b/JSTests/stress/temporal-zoned-datetime-timezone-identifier.js
new file mode 100644
index 000000000000..e0810dfc1e79
--- /dev/null
+++ b/JSTests/stress/temporal-zoned-datetime-timezone-identifier.js
@@ -0,0 +1,169 @@
+//@ requireOptions("--useTemporal=1")
+
+// Coverage for alias-preserving [[TimeZone]] identifiers in Temporal.ZonedDateTime
+// and the spec-aligned TimeZoneEquals algorithm
+// (https://tc39.es/proposal-canonical-tz/#sec-temporal-timezoneequals).
+
+function shouldBe(a, b, msg) {
+ if (a !== b) throw new Error(`${msg}: expected ${JSON.stringify(b)}, got ${JSON.stringify(a)}`);
+}
+
+function shouldBeTrue(v, msg) { if (v !== true) throw new Error(`${msg}: expected true`); }
+function shouldBeFalse(v, msg) { if (v !== false) throw new Error(`${msg}: expected false`); }
+
+// Bracket annotation preserves "+00:00" as distinct from "UTC". The offset designator
+// (Z or +HH:MM) only contributes to the instant; only the bracket sets [[TimeZone]].
+{
+ const cases = [
+ ["1970-01-01T00:00[UTC]", "UTC"],
+ ["1970-01-01T00:00[!UTC]", "UTC"],
+ ["1970-01-01T00:00[+00:00]", "+00:00"],
+ ["1970-01-01T00:00[!+00:00]", "+00:00"],
+ ["1970-01-01T00:00Z[UTC]", "UTC"],
+ ["1970-01-01T00:00Z[+00:00]", "+00:00"],
+ ["1970-01-01T00:00+00:00[UTC]", "UTC"],
+ ["1970-01-01T00:00+00:00[+00:00]", "+00:00"],
+ ];
+ for (const [arg, expected] of cases)
+ shouldBe(Temporal.ZonedDateTime.from(arg).timeZoneId, expected, `from(${arg}).timeZoneId`);
+}
+
+// Backward-link aliases survive verbatim: timeZoneId returns the alias text, not
+// the canonical IANA primary.
+{
+ const aliases = [
+ "Asia/Calcutta",
+ "Asia/Kolkata",
+ "America/Buenos_Aires",
+ "Europe/Kiev",
+ "Etc/UTC",
+ "Etc/GMT",
+ "GMT",
+ "Greenwich",
+ "Universal",
+ "Zulu",
+ ];
+ for (const id of aliases)
+ shouldBe(new Temporal.ZonedDateTime(0n, id).timeZoneId, id, `${id} preserved`);
+}
+
+// Case normalization: input case folded to ICU's canonical case for the identifier;
+// supports both primaries (UTC) and aliases (asia/calcutta).
+shouldBe(new Temporal.ZonedDateTime(0n, "utc").timeZoneId, "UTC", "lowercase utc → UTC");
+shouldBe(new Temporal.ZonedDateTime(0n, "asia/calcutta").timeZoneId, "Asia/Calcutta", "lowercase alias case-normalized");
+shouldBe(new Temporal.ZonedDateTime(0n, "ASIA/KOLKATA").timeZoneId, "Asia/Kolkata", "uppercase primary case-normalized");
+
+// Offset canonicalization: any input form folds to +HH:MM[:SS[.fff]] per
+// FormatOffsetTimeZoneIdentifier. -00:00 collapses to +00:00 (offsetMinutes = 0).
+shouldBe(new Temporal.ZonedDateTime(0n, "+0530").timeZoneId, "+05:30", "+0530 → +05:30");
+shouldBe(new Temporal.ZonedDateTime(0n, "+05").timeZoneId, "+05:00", "+05 → +05:00");
+shouldBe(new Temporal.ZonedDateTime(0n, "-00:00").timeZoneId, "+00:00", "-00:00 → +00:00 (negative zero)");
+shouldBe(new Temporal.ZonedDateTime(0n, "+00:00").timeZoneId, "+00:00", "+00:00 stays +00:00");
+
+// Z designator (no bracket) parsed as full datetime → [[TimeZone]] = "UTC" (named),
+// per ParseTemporalTimeZoneString step 6: "If timeZoneResult.[[Z]] is true, return
+// ! ParseTimeZoneIdentifier('UTC')."
+{
+ const z = new Temporal.ZonedDateTime(0n, "UTC").withTimeZone("1994-11-05T13:15:30Z");
+ shouldBe(z.timeZoneId, "UTC", "Z designator yields named UTC, not +00:00");
+}
+
+// equals(): both named → primary comparison. Aliases of the same primary are equal;
+// aliases of different primaries (or named vs offset) are not.
+{
+ const calcutta = new Temporal.ZonedDateTime(0n, "Asia/Calcutta");
+ const kolkata = new Temporal.ZonedDateTime(0n, "Asia/Kolkata");
+ shouldBeTrue(calcutta.equals(kolkata), "Asia/Calcutta equals Asia/Kolkata (same primary)");
+ shouldBeTrue(kolkata.equals(calcutta), "Asia/Kolkata equals Asia/Calcutta (commutative)");
+
+ const utc = new Temporal.ZonedDateTime(0n, "UTC");
+ const etcUtc = new Temporal.ZonedDateTime(0n, "Etc/UTC");
+ const universal = new Temporal.ZonedDateTime(0n, "Universal");
+ shouldBeTrue(utc.equals(etcUtc), "UTC equals Etc/UTC");
+ shouldBeTrue(utc.equals(universal), "UTC equals Universal");
+
+ const ny = new Temporal.ZonedDateTime(0n, "America/New_York");
+ shouldBeFalse(utc.equals(ny), "UTC not equals America/New_York");
+}
+
+// equals(): both offset → numeric comparison.
+{
+ const a = new Temporal.ZonedDateTime(0n, "+05:30");
+ const b = new Temporal.ZonedDateTime(0n, "+0530");
+ const c = new Temporal.ZonedDateTime(0n, "+05:00");
+ shouldBeTrue(a.equals(b), "+05:30 equals +0530 (same canonical offset)");
+ shouldBeFalse(a.equals(c), "+05:30 not equals +05:00");
+}
+
+// equals(): named vs offset → false even when offsets coincide. "UTC" is a named TZ
+// that happens to be at offset 0; "+00:00" is an offset TZ. Per spec they are distinct.
+{
+ const named = new Temporal.ZonedDateTime(0n, "UTC");
+ const offset = new Temporal.ZonedDateTime(0n, "+00:00");
+ shouldBeFalse(named.equals(offset), "UTC not equals +00:00 (named vs offset)");
+ shouldBeFalse(offset.equals(named), "+00:00 not equals UTC (commutative)");
+}
+
+// withCalendar preserves the original alias identifier (no canonicalization).
+{
+ const z = new Temporal.ZonedDateTime(0n, "Asia/Calcutta").withCalendar("iso8601");
+ shouldBe(z.timeZoneId, "Asia/Calcutta", "withCalendar preserves alias");
+}
+
+// withTimeZone with an offset-string argument produces an offset TZ.
+{
+ const z = new Temporal.ZonedDateTime(0n, "UTC").withTimeZone("+05:30");
+ shouldBe(z.timeZoneId, "+05:30", "withTimeZone(offset)");
+}
+
+// withTimeZone with a ZonedDateTime argument adopts its [[TimeZone]] verbatim.
+{
+ const src = new Temporal.ZonedDateTime(0n, "Asia/Calcutta");
+ const dst = new Temporal.ZonedDateTime(0n, "UTC").withTimeZone(src);
+ shouldBe(dst.timeZoneId, "Asia/Calcutta", "withTimeZone(ZDT) adopts alias");
+}
+
+// toLocaleString GMT fixup applies only to +00:00 (offset 0) — not to named UTC and
+// not to non-zero offsets (ICU formats those as GMT±HH:MM directly).
+{
+ const offsetUtc = new Temporal.ZonedDateTime(0n, "+00:00").toLocaleString("en-US", { timeZoneName: "short" });
+ if (!offsetUtc.includes("GMT") || offsetUtc.includes("UTC"))
+ throw new Error(`+00:00 toLocaleString should contain "GMT" not "UTC", got: ${offsetUtc}`);
+
+ const namedUtc = new Temporal.ZonedDateTime(0n, "UTC").toLocaleString("en-US", { timeZoneName: "short" });
+ if (!namedUtc.includes("UTC"))
+ throw new Error(`UTC toLocaleString should contain "UTC", got: ${namedUtc}`);
+
+ const offset530 = new Temporal.ZonedDateTime(0n, "+05:30").toLocaleString("en-US", { timeZoneName: "short" });
+ if (!offset530.includes("GMT+5:30") && !offset530.includes("GMT+05:30"))
+ throw new Error(`+05:30 toLocaleString should contain "GMT+5:30" or "GMT+05:30", got: ${offset530}`);
+}
+
+// since/until with day-or-larger units rejects ZDTs whose time zones differ; aliases
+// of the same primary are accepted (same-primary check goes through TimeZoneEquals).
+{
+ const a = new Temporal.ZonedDateTime(0n, "Asia/Calcutta");
+ const b = new Temporal.ZonedDateTime(86400000000000n, "Asia/Kolkata");
+ // Should not throw — same primary.
+ a.since(b, { largestUnit: "day" });
+ a.until(b, { largestUnit: "day" });
+
+ const c = new Temporal.ZonedDateTime(86400000000000n, "America/New_York");
+ let threw = false;
+ try { a.since(c, { largestUnit: "day" }); } catch (e) { threw = true; }
+ if (!threw)
+ throw new Error("since() across different primaries should throw with day-largest unit");
+}
+
+// Intl.supportedValuesOf("timeZone") returns primaries only — aliases excluded.
+{
+ const tzs = Intl.supportedValuesOf("timeZone");
+ if (!tzs.includes("UTC"))
+ throw new Error(`supportedValuesOf("timeZone") should include "UTC"`);
+ if (!tzs.includes("Asia/Kolkata"))
+ throw new Error(`supportedValuesOf("timeZone") should include "Asia/Kolkata"`);
+ if (tzs.includes("Asia/Calcutta"))
+ throw new Error(`supportedValuesOf("timeZone") must exclude alias "Asia/Calcutta"`);
+ if (tzs.includes("Etc/UTC"))
+ throw new Error(`supportedValuesOf("timeZone") must exclude alias "Etc/UTC"`);
+}
diff --git a/JSTests/stress/wasm-table-constructor-wrapper-function-default.js b/JSTests/stress/wasm-table-constructor-wrapper-function-default.js
new file mode 100644
index 000000000000..8ff3dffcbea4
--- /dev/null
+++ b/JSTests/stress/wasm-table-constructor-wrapper-function-default.js
@@ -0,0 +1,100 @@
+// Regression test: new WebAssembly.Table({element: "funcref", initial: N}, value)
+// with a WebAssemblyWrapperFunction default value (a JS function imported into
+// wasm and re-exported) passed the constructor's type check but the fill loop
+// only stored WebAssemblyFunction defaults, leaving every funcref entry null.
+// Table.prototype.set and Table.prototype.grow stored the same value correctly.
+
+if (!this.WebAssembly)
+ quit(0);
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error("bad value: " + actual + " (expected " + expected + ")");
+}
+
+// (module (import "m" "f" (func (result i32))) (export "f" (func 0)))
+const reexportingModule = new WebAssembly.Module(new Uint8Array([
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x05, 0x01, 0x60, 0x00, 0x01, 0x7f,
+ 0x02, 0x07, 0x01, 0x01, 0x6d, 0x01, 0x66, 0x00, 0x00,
+ 0x07, 0x05, 0x01, 0x01, 0x66, 0x00, 0x00,
+]));
+
+// (module (func (export "f") (result i32) (i32.const 7)))
+const exportingModule = new WebAssembly.Module(new Uint8Array([
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x05, 0x01, 0x60, 0x00, 0x01, 0x7f,
+ 0x03, 0x02, 0x01, 0x00,
+ 0x07, 0x05, 0x01, 0x01, 0x66, 0x00, 0x00,
+ 0x0a, 0x06, 0x01, 0x04, 0x00, 0x41, 0x07, 0x0b,
+]));
+
+// (module (import "m" "t" (table 3 funcref))
+// (func (export "call") (param i32) (result i32)
+// (call_indirect (result i32) (local.get 0))))
+const callingModule = new WebAssembly.Module(new Uint8Array([
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x0a, 0x02, 0x60, 0x00, 0x01, 0x7f, 0x60, 0x01, 0x7f, 0x01, 0x7f,
+ 0x02, 0x09, 0x01, 0x01, 0x6d, 0x01, 0x74, 0x01, 0x70, 0x00, 0x03,
+ 0x03, 0x02, 0x01, 0x01,
+ 0x07, 0x08, 0x01, 0x04, 0x63, 0x61, 0x6c, 0x6c, 0x00, 0x00,
+ 0x0a, 0x09, 0x01, 0x07, 0x00, 0x20, 0x00, 0x11, 0x00, 0x00, 0x0b,
+]));
+
+const wrapper = new WebAssembly.Instance(reexportingModule, { m: { f: () => 42 } }).exports.f;
+
+for (const element of ["funcref", "anyfunc"]) {
+ const table = new WebAssembly.Table({ element, initial: 3 }, wrapper);
+ for (let i = 0; i < 3; ++i) {
+ shouldBe(table.get(i), wrapper);
+ shouldBe(table.get(i)(), 42);
+ }
+
+ // The wasm-callable half of each slot must be populated too: call_indirect
+ // through the constructor-filled slots dispatches to the wrapped JS function.
+ const caller = new WebAssembly.Instance(callingModule, { m: { t: table } }).exports.call;
+ for (let i = 0; i < 3; ++i)
+ shouldBe(caller(i), 42);
+}
+
+// Constructor-filled slots keep their values alive across GC.
+{
+ let table;
+ (function () {
+ const transientWrapper = new WebAssembly.Instance(reexportingModule, { m: { f: () => 13 } }).exports.f;
+ table = new WebAssembly.Table({ element: "funcref", initial: 2 }, transientWrapper);
+ })();
+ if (typeof fullGC === "function")
+ fullGC();
+ for (let i = 0; i < 2; ++i) {
+ shouldBe(typeof table.get(i), "function");
+ shouldBe(table.get(i)(), 13);
+ }
+}
+
+// WebAssemblyFunction default values keep working.
+const wasmFunction = new WebAssembly.Instance(exportingModule).exports.f;
+const table = new WebAssembly.Table({ element: "funcref", initial: 2 }, wasmFunction);
+for (let i = 0; i < 2; ++i) {
+ shouldBe(table.get(i), wasmFunction);
+ shouldBe(table.get(i)(), 7);
+}
+
+// Null and absent default values keep producing null entries.
+shouldBe(new WebAssembly.Table({ element: "funcref", initial: 1 }, null).get(0), null);
+shouldBe(new WebAssembly.Table({ element: "funcref", initial: 1 }).get(0), null);
+
+// Non-function default values keep throwing.
+let threw = false;
+try {
+ new WebAssembly.Table({ element: "funcref", initial: 1 }, () => 1);
+} catch (error) {
+ threw = true;
+ shouldBe(error instanceof TypeError, true);
+}
+shouldBe(threw, true);
+
+// Externref tables keep storing arbitrary default values.
+const externTable = new WebAssembly.Table({ element: "externref", initial: 2 }, "hello");
+shouldBe(externTable.get(0), "hello");
+shouldBe(externTable.get(1), "hello");
diff --git a/JSTests/stress/yarr-auto-possessification.js b/JSTests/stress/yarr-auto-possessification.js
new file mode 100644
index 000000000000..c967f9b86d32
--- /dev/null
+++ b/JSTests/stress/yarr-auto-possessification.js
@@ -0,0 +1,109 @@
+// Regression coverage for YarrJIT auto-possessification: a Greedy single-character term
+// (PatternCharacter / CharacterClass) immediately followed by a mandatory term whose first
+// character is disjoint from the greedy term's set is matched possessively (the JIT skips the
+// futile give-back/retry backtracking). Results must be identical to a fully-backtracking
+// engine, so every case below is also validated against the Yarr interpreter and V8.
+// Throws on mismatch; no output on success.
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`bad value: got ${actual}, expected ${expected}`);
+}
+
+function match(re, str) {
+ let m = re.exec(str);
+ return m === null ? "null" : JSON.stringify(Array.from(m).map(x => x === undefined ? null : x)) + "@" + m.index;
+}
+
+function test(reSource, flags, str, expected) {
+ // Compile once and run hot so the JIT tiers up and the possessive backtrack path runs.
+ let re = new RegExp(reSource, flags);
+ for (let i = 0; i < 200; ++i)
+ shouldBe(match(re, str), expected);
+}
+
+// --- Greedy followed by a disjoint mandatory single character (the possessified case) ---
+// Successful matches must still produce the correct longest match.
+test("[0-9a-f]{1,4}:", "", "abcd:", '["abcd:"]@0');
+test("[0-9a-f]{1,4}:", "", "a:", '["a:"]@0');
+test("^([0-9a-f]{1,4}:)+x$", "", "abcd:1:ef01:x", '["abcd:1:ef01:x","ef01:"]@0');
+test("^[0-9]+\\.[0-9]+$", "", "123.456", '["123.456"]@0');
+test("^[a-z]+-end$", "", "hello-end", '["hello-end"]@0');
+test("a+b", "", "aaab", '["aaab"]@0');
+
+// --- Failing inputs: possessification must not change the (non-)match ---
+test("^([0-9a-f]{1,8}-)+x$", "", "abcdef12-abcdef12-y", "null");
+test("[0-9a-f]{1,4}:", "", "abcd", "null");
+test("^[0-9]+\\.[0-9]+$", "", "123.", "null");
+test("a+b", "", "aaac", "null");
+test("^([0-9a-f]{1,4}:){7}(?:[0-9a-f]{1,4}|:)$", "", "2001:db8:0:1:1:1:1::1", "null");
+
+// --- Overlapping follower: MUST NOT be possessified (give-back is required) ---
+test("a+a$", "", "aaaa", '["aaaa"]@0'); // 'a+' followed by 'a' (same char): must give back one.
+test("a+ab", "", "aaab", '["aaab"]@0'); // 'a+' must yield the 'a' that 'ab' needs.
+test("[0-9]+[0-9a-f]+g$", "", "12ffg", '["12ffg"]@0'); // overlapping classes.
+test("[0-9]+[5-9]z$", "", "1239z", '["1239z"]@0'); // overlapping ranges: give-back needed.
+
+// --- ignoreCase: class folds in case; follower case-insensitive ---
+test("[a-z]+;", "i", "ABCdef;", '["ABCdef;"]@0');
+test("[a-z]+X", "i", "abcx", '["abcx"]@0'); // follower 'X' under /i matches 'x' which IS in [a-z]: must not possessify.
+
+// --- Disjoint follower under /i where follower has no case (digit/punct) ---
+test("[a-z]+9", "i", "ABc9", '["ABc9"]@0');
+
+// --- Greedy CharacterClass followed by disjoint class-shaped literal, with captures intact ---
+test("^(\\d{1,3})\\.(\\d{1,3})$", "", "12.250", '["12.250","12","250"]@0');
+
+// --- Quantified parentheses wrapping a possessive inner term (IPv6-like) ---
+test("^(?:[0-9a-f]{1,4}:){2}[0-9a-f]{1,4}$", "", "ab:cd:ef", '["ab:cd:ef"]@0');
+test("^(?:[0-9a-f]{1,4}:){2}[0-9a-f]{1,4}$", "", "ab:cd:", "null");
+
+// === Unicode (/u) and unicodeSets (/v): surrogate-pair decode and case-fold soundness ===
+// All expected values below were cross-checked against V8 (a fully-backtracking engine).
+
+// --- Non-BMP greedy PatternCharacter + disjoint BMP follower: give-back must be doubled
+// (2 code units per matched code point). ---
+test("\\u{1F600}+x", "u", "\u{1F600}\u{1F600}\u{1F600}x", '["\u{1F600}\u{1F600}\u{1F600}x"]@0');
+test("\\u{1F600}+x", "u", "\u{1F600}\u{1F600}\u{1F600}y", "null"); // possessive give-back is futile.
+
+// --- Non-BMP, fixed-width CharacterClass greedy + disjoint follower (count << 1 give-back). ---
+test("[\\u{1F600}-\\u{1F610}]+!", "u", "\u{1F600}\u{1F601}\u{1F602}!", '["\u{1F600}\u{1F601}\u{1F602}!"]@0');
+test("[\\u{1F600}-\\u{1F610}]+!", "u", "\u{1F600}\u{1F601}\u{1F602}?", "null");
+// Bounded greedy: possessive at index 0 fails, engine still finds the later non-anchored match.
+test("[\\u{1F600}-\\u{1F610}]{1,3}!", "u", "\u{1F600}\u{1F601}\u{1F602}\u{1F603}!", '["\u{1F601}\u{1F602}\u{1F603}!"]@2');
+
+// --- Variable-width class (mixes BMP + non-BMP): NOT fixed width, so the JIT must fall back to
+// the normal per-step backtrack under surrogate decoding. Result must still be correct. ---
+test("[a\\u{1F600}]+!", "u", "a\u{1F600}a!", '["a\u{1F600}a!"]@0');
+test("[a\\u{1F600}]+!", "u", "a\u{1F600}a?", "null");
+
+// --- Inverted class under /u is variable width too (JIT falls back); must stay correct. ---
+test("[^0-9]+5", "u", "abc5", '["abc5"]@0');
+test("[^0-9]+5", "u", "ab\u{1F600}c5", '["ab\u{1F600}c5"]@0');
+test("[^0-9]+5", "u", "abc6", "null");
+
+// --- Case-fold soundness under /iu: a disjoint follower whose Unicode fold reaches a non-ASCII
+// char (Kelvin U+212A folds to 'k', long-s U+017F folds to 's'). The greedy [0-9]/[a-z] class
+// genuinely rejects those, so possessification is valid and the follower still matches them. ---
+test("[0-9]+k", "iu", "123K", '["123K"]@0'); // follower /k/iu matches Kelvin.
+test("[0-9]+s", "iu", "12ſ", '["12ſ"]@0'); // follower /s/iu matches long-s.
+test("[0-9]+k", "iu", "123k", '["123k"]@0');
+
+// --- The dangerous case that must NOT be possessified: under /iu, [K] folds to {k,K,Kelvin},
+// so it DOES overlap the ASCII follower 'k' and give-back is required. (A naive analysis that
+// only saw the literal Kelvin code point would wrongly possessify and return null.) ---
+test("[\\u212A]+k", "iu", "KKKk", '["KKKk"]@0');
+test("[\\u212A]+k", "iu", "KKk", '["KKk"]@0');
+
+// --- /v string-disjunction class: [\q{..}] is decomposed into a group, so the inner single-char
+// class is FixedCount (never the greedy term) and the strings are never possessified away. ---
+test("[\\q{ab}c]+d", "v", "ababccd", '["ababccd"]@0');
+test("[\\q{xy}a]+b", "v", "xyaab", '["xyaab"]@0');
+test("[\\q{ab}]+a", "v", "ababa", '["ababa"]@0');
+
+// --- Dot (non-dotAll) is the newline class inverted: '.' rejects '\n', so '.+\n' is possessifiable.
+// This is the BMP path (one code unit per match). ---
+test(".+\\n", "", "abc\ndef", '["abc\\n"]@0');
+test(".+\\n", "", "abcdef", "null");
+test("[^0-9]+5", "", "abc5", '["abc5"]@0');
+test("[^0-9]+5", "", "abc6", "null");
diff --git a/JSTests/stress/yarr-quantified-empty-parentheses.js b/JSTests/stress/yarr-quantified-empty-parentheses.js
new file mode 100644
index 000000000000..ff3f7466753a
--- /dev/null
+++ b/JSTests/stress/yarr-quantified-empty-parentheses.js
@@ -0,0 +1,75 @@
+// Coverage for quantified parentheses whose body can match an empty string —
+// e.g. /(){3}/, /(?:){5}/, /((?:)){2}/, /(a?){3}/. The Yarr JIT punts these
+// runs to the interpreter via the empty-match-detection branch in
+// ParenthesesSubpattern[FixedCount]End; this test exercises both the JIT
+// (which aborts and bails) and the interpreter to confirm they agree on
+// the captured value, the match index, and overall match success.
+// Expected values were verified against the Yarr interpreter and V8.
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`bad value: got ${actual}, expected ${expected}`);
+}
+
+function match(re, str) {
+ let m = re.exec(str);
+ return m === null ? "null" : JSON.stringify(Array.from(m).map(x => x === undefined ? null : x)) + "@" + m.index;
+}
+
+function test(reSource, flags, str, expected) {
+ let re = new RegExp(reSource, flags);
+ for (let i = 0; i < 200; ++i)
+ shouldBe(match(re, str), expected);
+}
+
+// --- Capturing FixedCount empty body ---
+test("(){3}", "", "", '["",""]@0');
+test("(){3}", "", "abc", '["",""]@0');
+test("^(){3}$", "", "", '["",""]@0');
+test("^(){3}$", "", "a", "null");
+test("a(){3}", "", "a", '["a",""]@0');
+test("a(){3}", "", "abc", '["a",""]@0');
+test("a(){3}b", "", "ab", '["ab",""]@0');
+test("(){1}", "", "x", '["",""]@0');
+test("(){10}", "", "x", '["",""]@0');
+
+// --- Non-capturing FixedCount empty body ---
+test("(?:){3}", "", "abc", '[""]@0');
+test("^(?:){3}$", "", "", '[""]@0');
+test("a(?:){3}b", "", "ab", '["ab"]@0');
+
+// --- Capturing empty alternative inside ---
+test("((?:)){3}", "", "abc", '["",""]@0');
+test("((?:)){2}x", "", "x", '["x",""]@0');
+
+// --- Mixed: outer non-empty, inner can be empty ---
+test("(a()){3}", "", "aaa", '["aaa","a",""]@0');
+test("(a()){3}b", "", "aaab", '["aaab","a",""]@0');
+test("((a)()){2}", "", "aa", '["aa","a","a",""]@0');
+
+// --- Optional content (a? matches empty when a is absent) ---
+test("(a?){3}", "", "", '["",""]@0');
+test("(a?){3}", "", "a", '["a",""]@0');
+test("(a?){3}", "", "aaa", '["aaa","a"]@0');
+test("(a?){3}", "", "aaaa", '["aaa","a"]@0');
+test("^(a?){3}$", "", "", '["",""]@0');
+test("^(a?){3}$", "", "aaa", '["aaa","a"]@0');
+test("^(a?){3}$", "", "aaaa", "null");
+
+// --- Empty alternation ---
+test("(|x){3}", "", "", '["",""]@0');
+test("(|x){3}", "", "xxx", '["",""]@0');
+test("(x|){3}", "", "xx", '["xx",""]@0');
+
+// --- Quantified empty paren followed by capturing content ---
+test("(){2}(a)", "", "a", '["a","","a"]@0');
+test("(){2}(a)b", "", "ab", '["ab","","a"]@0');
+
+// --- Greedy empty (?: ... )* — JIT also bails for the empty case ---
+test("(?:)*", "", "abc", '[""]@0');
+test("()*", "", "abc", '["",null]@0');
+test("()+", "", "abc", '["",""]@0');
+
+// --- Backreference to an empty-body capture group ---
+test("(){3}\\1", "", "abc", '["",""]@0');
+test("()\\1", "", "abc", '["",""]@0');
diff --git a/JSTests/stress/yarr-quantified-parentheses-unified-backtracking.js b/JSTests/stress/yarr-quantified-parentheses-unified-backtracking.js
new file mode 100644
index 000000000000..484e67a26677
--- /dev/null
+++ b/JSTests/stress/yarr-quantified-parentheses-unified-backtracking.js
@@ -0,0 +1,78 @@
+// Regression coverage for the unified save-at-BEGIN model for quantified
+// parenthesised subpatterns in YarrJIT (FixedCount/Greedy/NonGreedy share one
+// ParenContext push/pop flow; FixedCount is min == max). Throws on mismatch.
+// Expected values were verified against the Yarr interpreter and V8.
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`bad value: got ${actual}, expected ${expected}`);
+}
+
+function match(re, str) {
+ let m = re.exec(str);
+ return m === null ? "null" : JSON.stringify(Array.from(m).map(x => x === undefined ? null : x)) + "@" + m.index;
+}
+
+function test(reSource, flags, str, expected) {
+ // Compile once and execute repeatedly so the Yarr JIT tiers up and the
+ // JIT-compiled code path (not just the interpreter) is exercised.
+ let re = new RegExp(reSource, flags);
+ for (let i = 0; i < 200; ++i)
+ shouldBe(match(re, str), expected);
+}
+
+// --- FixedCount with backtrackable single-alternative content ---
+test("(a+){2}b", "", "aaab", '["aaab","a"]@0');
+test("(a+){3}", "", "aaaa", '["aaaa","a"]@0');
+test("(a+){2}$", "", "aaaa", '["aaaa","a"]@0');
+test("(a){3}", "", "aaa", '["aaa","a"]@0');
+test("(a){3}", "", "aa", "null");
+test("(ab){2,2}", "", "abab", '["abab","ab"]@0');
+
+// --- FixedCount with multiple alternatives (inter-iteration alt retry) ---
+test("(a+|b+){2}c", "", "aabbc", '["aabbc","bb"]@0');
+test("(a+|b+){2}c", "", "aabc", '["aabc","b"]@0');
+test("(a|b){3}", "", "abab", '["aba","a"]@0');
+test("(?:(a+)b){2}", "", "abaab", '["abaab","aa"]@0');
+test("(?:(a+)b){2}", "", "aabab", '["aabab","a"]@0');
+
+// --- Nested FixedCount / Greedy ---
+test("((a+)+){2}", "", "aaaa", '["aaaa","a","a"]@0');
+test("(\\w)\\1{2}", "", "aaa", '["aaa","a"]@0');
+
+// --- Greedy / NonGreedy regression guards (must be unaffected) ---
+test("(ab|cd)*", "", "abcdab", '["abcdab","ab"]@0');
+test("(a+?){2,4}", "", "aaaa", '["aaaa","a"]@0');
+test("(a+){2,}", "", "aaaa", '["aaaa","a"]@0');
+test("(a*){3}", "", "aaa", '["aaa",""]@0');
+test("(a+)*?b", "", "aaab", '["aaab","aaa"]@0');
+
+// --- Backtrack-then-FAIL: backtracking exhausts every iteration distribution and
+// bails out (no trailing match exists). This drives BEGIN.bt all the way down
+// to count == 0 (noPreviousIteration) and exercises the failure-propagation /
+// capture-clearing paths that the "backtrack then succeed" cases above do not. ---
+
+// Single-alt backtrackable FixedCount: every split of the a's across N iterations
+// is tried, the trailing 'b' never matches, so the whole match fails.
+test("(a+){2}b", "", "aaa", "null");
+test("(a+){3}b", "", "aaaa", "null");
+test("(a+){4}b", "", "aaaaaa", "null");
+
+// Multi-alternative FixedCount: both alternatives are tried in every iteration
+// before bailing out.
+test("(a+|b+){2}c", "", "aabb", "null");
+test("(a+|b+){2}c", "", "ab", "null");
+test("(a+|b+){3}c", "", "aabb", "null");
+
+// Nested FixedCount: inner and outer iteration counts both backtrack to exhaustion.
+test("((a+)+){2}b", "", "aaaa", "null");
+
+// FixedCount bails out, then an enclosing alternation succeeds: the failed group's
+// captures must be cleared (undefined) before the alternative is taken.
+test("(?:(a+){2}b|a+)", "", "aaa", '["aaa",null]@0');
+test("((a+){2}b)|(a+)", "", "aaa", '["aaa",null,null,"aaa"]@0');
+
+// Same bail-out chain through the shared code path for Greedy-below-min and
+// NonGreedy (both reduce to "re-drive previous content, then fail").
+test("(a+){2,}b", "", "aaa", "null");
+test("(a+){2,4}?b", "", "aaa", "null");
diff --git a/JSTests/test262/config.yaml b/JSTests/test262/config.yaml
index 90225f395846..28d40d104dc0 100644
--- a/JSTests/test262/config.yaml
+++ b/JSTests/test262/config.yaml
@@ -27,30 +27,8 @@ skip:
# Incorrect tests, see https://github.com/tc39/test262/issues/4980
- test/language/import/import-defer/evaluation-triggers/ignore-super-property-set-exported.js
- test/language/import/import-defer/evaluation-triggers/ignore-super-property-set-not-exported.js
- - test/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO
- - test/built-ins/Temporal/Now/plainDateISO
- - test/built-ins/Temporal/Now/plainDateTimeISO
- - test/built-ins/Temporal/Now/plainTimeISO
- - test/built-ins/Temporal/Now/zonedDateTimeISO
- - test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime
- - test/built-ins/Temporal/PlainDate/prototype/withCalendar
- - test/built-ins/Temporal/PlainDateTime/prototype/since
- - test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime
- - test/built-ins/Temporal/PlainDateTime/prototype/until
- - test/built-ins/Temporal/PlainDateTime/prototype/withCalendar
- - test/built-ins/Temporal/ZonedDateTime
- - test/intl402/Temporal/Instant/prototype/toZonedDateTimeISO
- - test/intl402/Temporal/Now
- - test/intl402/Temporal/PlainDate
- - test/intl402/Temporal/PlainDateTime
- - test/intl402/Temporal/PlainMonthDay
- - test/intl402/Temporal/PlainTime
- - test/intl402/Temporal/PlainYearMonth
- - test/intl402/Temporal/ZonedDateTime
- test/staging/Intl402
- test/staging/JSON
- - test/staging/Temporal
- - test/staging/sm/Temporal
files:
# https://github.com/claudepache/es-legacy-function-reflection
- test/built-ins/ThrowTypeError/unique-per-realm-function-proto.js
@@ -74,394 +52,10 @@ skip:
- test/intl402/Intl/getCanonicalLocales/unicode-ext-canonicalize-region.js
- test/intl402/Intl/getCanonicalLocales/unicode-ext-canonicalize-subdivision.js
- test/intl402/Locale/constructor-options-region-valid.js
+ - test/intl402/Locale/extensions-grandfathered.js
- test/intl402/Locale/getters-grandfathered.js
- test/intl402/Locale/likely-subtags-grandfathered.js
# Skipping temporarily due to a bug with handling case-insensitive \p escapes
- test/built-ins/RegExp/regexp-modifiers/add-ignoreCase-affects-slash-upper-p.js
- test/built-ins/RegExp/regexp-modifiers/add-ignoreCase-affects-slash-lower-p.js
-
- # Depends on Temporal.Duration relativeTo option
- - test/built-ins/Temporal/Duration/compare/basic.js
- - test/built-ins/Temporal/Duration/compare/calendar-possibly-required.js
- - test/built-ins/Temporal/Duration/compare/calendar-temporal-object.js
- - test/built-ins/Temporal/Duration/compare/options-wrong-type.js
- - test/built-ins/Temporal/Duration/compare/order-of-operations.js
- - test/built-ins/Temporal/Duration/compare/relativeto-hour.js
- - test/built-ins/Temporal/Duration/compare/relativeto-month.js
- - test/built-ins/Temporal/Duration/compare/relativeto-propertybag-infinity-throws-rangeerror.js
- - test/built-ins/Temporal/Duration/compare/relativeto-propertybag-invalid.js
- - test/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string-datetime.js
- - test/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string-leap-second.js
- - test/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string-year-zero.js
- - test/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-wrong-type.js
- - test/built-ins/Temporal/Duration/compare/relativeto-string-plaindatetime.js
- - test/built-ins/Temporal/Duration/compare/relativeto-string-zoneddatetime.js
- - test/built-ins/Temporal/Duration/compare/relativeto-sub-minute-offset.js
- - test/built-ins/Temporal/Duration/compare/relativeto-year.js
- - test/built-ins/Temporal/Duration/prototype/round/calendar-possibly-required.js
- - test/built-ins/Temporal/Duration/prototype/round/calendar-temporal-object.js
- - test/built-ins/Temporal/Duration/prototype/round/duration-out-of-range-added-to-relativeto.js
- - test/built-ins/Temporal/Duration/prototype/round/february-leap-year.js
- - test/built-ins/Temporal/Duration/prototype/round/largestunit-plurals-accepted.js
- - test/built-ins/Temporal/Duration/prototype/round/largestunit-smallestunit-default.js
- - test/built-ins/Temporal/Duration/prototype/round/order-of-operations.js
- - test/built-ins/Temporal/Duration/prototype/round/relativeto-infinity-throws-rangeerror.js
- - test/built-ins/Temporal/Duration/prototype/round/relativeto-leap-second.js
- - test/built-ins/Temporal/Duration/prototype/round/relativeto-number.js
- - test/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-number.js
- - test/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-wrong-type.js
- - test/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-invalid-offset-string.js
- - test/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-no-time-units.js
- - test/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string-datetime.js
- - test/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string-leap-second.js
- - test/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string.js
- - test/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-wrong-type.js
- - test/built-ins/Temporal/Duration/prototype/round/relativeto-string-datetime.js
- - test/built-ins/Temporal/Duration/prototype/round/relativeto-string-plaindatetime.js
- - test/built-ins/Temporal/Duration/prototype/round/relativeto-string-zoneddatetime-wrong-offset.js
- - test/built-ins/Temporal/Duration/prototype/round/relativeto-string-zoneddatetime.js
- - test/built-ins/Temporal/Duration/prototype/round/relativeto-sub-minute-offset.js
- - test/built-ins/Temporal/Duration/prototype/round/relativeto-wrong-type.js
- - test/built-ins/Temporal/Duration/prototype/round/roundingmode-ceil.js
- - test/built-ins/Temporal/Duration/prototype/round/roundingmode-expand.js
- - test/built-ins/Temporal/Duration/prototype/round/roundingmode-floor.js
- - test/built-ins/Temporal/Duration/prototype/round/roundingmode-halfCeil.js
- - test/built-ins/Temporal/Duration/prototype/round/roundingmode-halfEven.js
- - test/built-ins/Temporal/Duration/prototype/round/roundingmode-halfExpand.js
- - test/built-ins/Temporal/Duration/prototype/round/roundingmode-halfFloor.js
- - test/built-ins/Temporal/Duration/prototype/round/roundingmode-halfTrunc.js
- - test/built-ins/Temporal/Duration/prototype/round/roundingmode-trunc.js
- - test/built-ins/Temporal/Duration/prototype/round/smallestunit-plurals-accepted.js
- - test/built-ins/Temporal/Duration/prototype/round/total-duration-nanoseconds-too-large-with-zoned-datetime.js
- - test/built-ins/Temporal/Duration/prototype/round/year-zero.js
- - test/built-ins/Temporal/Duration/prototype/total/balances-days-up-to-both-years-and-months.js
- - test/built-ins/Temporal/Duration/prototype/total/calendar-possibly-required.js
- - test/built-ins/Temporal/Duration/prototype/total/calendar-temporal-object.js
- - test/built-ins/Temporal/Duration/prototype/total/does-not-accept-non-string-primitives-for-relativeTo.js
- - test/built-ins/Temporal/Duration/prototype/total/order-of-operations.js
- - test/built-ins/Temporal/Duration/prototype/total/relativeto-infinity-throws-rangeerror.js
- - test/built-ins/Temporal/Duration/prototype/total/relativeto-leap-second.js
- - test/built-ins/Temporal/Duration/prototype/total/relativeTo-must-have-required-properties.js
- - test/built-ins/Temporal/Duration/prototype/total/relativeto-number.js
- - test/built-ins/Temporal/Duration/prototype/total/relativeto-plaindatetime.js
- - test/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-number.js
- - test/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-wrong-type.js
- - test/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-no-time-units.js
- - test/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string-datetime.js
- - test/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string-leap-second.js
- - test/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string.js
- - test/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-wrong-type.js
- - test/built-ins/Temporal/Duration/prototype/total/relativeto-string-datetime.js
- - test/built-ins/Temporal/Duration/prototype/total/relativeto-string-plaindatetime-invalid.js
- - test/built-ins/Temporal/Duration/prototype/total/relativeto-string-plaindatetime.js
- - test/built-ins/Temporal/Duration/prototype/total/relativeto-string-zoneddatetime.js
- - test/built-ins/Temporal/Duration/prototype/total/relativeto-sub-minute-offset.js
- - test/built-ins/Temporal/Duration/prototype/total/relativeto-wrong-type.js
- - test/built-ins/Temporal/Duration/prototype/total/unit-plurals-accepted.js
- - test/built-ins/Temporal/PlainDateTime/datetime-math.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/since/round-cross-unit-boundary.js
- - test/intl402/Temporal/Duration/compare/relativeto-hour.js
- - test/intl402/Temporal/Duration/prototype/round/relativeto-infinity-throws-rangeerror.js
- - test/intl402/Temporal/Duration/prototype/round/relativeto-string-datetime.js
- - test/intl402/Temporal/Duration/prototype/total/relativeto-infinity-throws-rangeerror.js
- - test/intl402/Temporal/Duration/prototype/total/relativeto-string-datetime.js
-
- # Depends on Calendar support
- - test/built-ins/Temporal/PlainDate/basic.js
- - test/built-ins/Temporal/PlainDate/calendar-case-insensitive.js
- - test/built-ins/Temporal/PlainDate/calendar-invalid-iso-string.js
- - test/built-ins/Temporal/PlainDate/calendar-number.js
- - test/built-ins/Temporal/PlainDate/calendar-wrong-type.js
- - test/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-leap-second.js
- - test/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-number.js
- - test/built-ins/Temporal/PlainDate/compare/argument-propertybag-calendar-wrong-type.js
- - test/built-ins/Temporal/PlainDate/compare/argument-string-calendar-annotation.js
- - test/built-ins/Temporal/PlainDate/compare/argument-string-invalid.js
- - test/built-ins/Temporal/PlainDate/compare/calendar-temporal-object.js
- - test/built-ins/Temporal/PlainDate/from/argument-plaindate.js
- - test/built-ins/Temporal/PlainDate/from/argument-plaindatetime.js
- - test/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-case-insensitive.js
- - test/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-leap-second.js
- - test/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-number.js
- - test/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar-wrong-type.js
- - test/built-ins/Temporal/PlainDate/from/argument-propertybag-calendar.js
- - test/built-ins/Temporal/PlainDate/from/argument-string-calendar-annotation.js
- - test/built-ins/Temporal/PlainDate/from/argument-string-invalid.js
- - test/built-ins/Temporal/PlainDate/from/argument-string-unknown-annotation.js
- - test/built-ins/Temporal/PlainDate/from/calendar-temporal-object.js
- - test/built-ins/Temporal/PlainDate/prototype/add/balance-smaller-units.js
- - test/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-case-insensitive.js
- - test/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-leap-second.js
- - test/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-number.js
- - test/built-ins/Temporal/PlainDate/prototype/equals/argument-propertybag-calendar-wrong-type.js
- - test/built-ins/Temporal/PlainDate/prototype/equals/argument-string-calendar-annotation.js
- - test/built-ins/Temporal/PlainDate/prototype/equals/argument-string-invalid.js
- - test/built-ins/Temporal/PlainDate/prototype/equals/argument-string.js
- - test/built-ins/Temporal/PlainDate/prototype/equals/calendar-temporal-object.js
- - test/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-case-insensitive.js
- - test/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-leap-second.js
- - test/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-number.js
- - test/built-ins/Temporal/PlainDate/prototype/since/argument-propertybag-calendar-wrong-type.js
- - test/built-ins/Temporal/PlainDate/prototype/since/argument-string-calendar-annotation.js
- - test/built-ins/Temporal/PlainDate/prototype/since/argument-string-invalid.js
- - test/built-ins/Temporal/PlainDate/prototype/since/calendar-temporal-object.js
- - test/built-ins/Temporal/PlainDate/prototype/since/order-of-operations.js
- - test/built-ins/Temporal/PlainDate/prototype/subtract/balance-smaller-units.js
- - test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-object.js
- - test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-calendar-annotation.js
- - test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-time-designator-required-for-disambiguation.js
- - test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-time-zone-annotation.js
- - test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-string-unknown-annotation.js
- - test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/basic.js
- - test/built-ins/Temporal/PlainDate/prototype/toString/calendarname-always.js
- - test/built-ins/Temporal/PlainDate/prototype/toString/calendarname-auto.js
- - test/built-ins/Temporal/PlainDate/prototype/toString/calendarname-critical.js
- - test/built-ins/Temporal/PlainDate/prototype/toString/calendarname-invalid-string.js
- - test/built-ins/Temporal/PlainDate/prototype/toString/calendarname-undefined.js
- - test/built-ins/Temporal/PlainDate/prototype/toString/calendarname-wrong-type.js
- - test/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-case-insensitive.js
- - test/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-leap-second.js
- - test/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-number.js
- - test/built-ins/Temporal/PlainDate/prototype/until/argument-propertybag-calendar-wrong-type.js
- - test/built-ins/Temporal/PlainDate/prototype/until/argument-string-calendar-annotation.js
- - test/built-ins/Temporal/PlainDate/prototype/until/argument-string-invalid.js
- - test/built-ins/Temporal/PlainDate/prototype/until/calendar-temporal-object.js
- - test/built-ins/Temporal/PlainDate/prototype/until/order-of-operations.js
- - test/built-ins/Temporal/PlainDateTime/calendar-case-insensitive.js
- - test/built-ins/Temporal/PlainDateTime/calendar-invalid-iso-string.js
- - test/built-ins/Temporal/PlainDateTime/calendar-number.js
- - test/built-ins/Temporal/PlainDateTime/calendar-wrong-type.js
- - test/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-leap-second.js
- - test/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-number.js
- - test/built-ins/Temporal/PlainDateTime/compare/argument-propertybag-calendar-wrong-type.js
- - test/built-ins/Temporal/PlainDateTime/compare/calendar-temporal-object.js
- - test/built-ins/Temporal/PlainDateTime/constructor-full.js
- - test/built-ins/Temporal/PlainDateTime/from/argument-plaindate.js
- - test/built-ins/Temporal/PlainDateTime/from/argument-plaindatetime.js
- - test/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-case-insensitive.js
- - test/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-leap-second.js
- - test/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-number.js
- - test/built-ins/Temporal/PlainDateTime/from/argument-propertybag-calendar-wrong-type.js
- - test/built-ins/Temporal/PlainDateTime/from/argument-string-calendar-annotation.js
- - test/built-ins/Temporal/PlainDateTime/from/argument-string-invalid.js
- - test/built-ins/Temporal/PlainDateTime/from/argument-string-time-zone-annotation.js
- - test/built-ins/Temporal/PlainDateTime/from/argument-string-unknown-annotation.js
- - test/built-ins/Temporal/PlainDateTime/from/calendar-temporal-object.js
- - test/built-ins/Temporal/PlainDateTime/from/overflow-invalid-string.js
- - test/built-ins/Temporal/PlainDateTime/from/overflow-wrong-type.js
- - test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-case-insensitive.js
- - test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-leap-second.js
- - test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-number.js
- - test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-propertybag-calendar-wrong-type.js
- - test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-calendar-annotation.js
- - test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-time-zone-annotation.js
- - test/built-ins/Temporal/PlainDateTime/prototype/equals/calendar-temporal-object.js
- - test/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-always.js
- - test/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-auto.js
- - test/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-critical.js
- - test/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-invalid-string.js
- - test/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-never.js
- - test/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-undefined.js
- - test/built-ins/Temporal/PlainDateTime/prototype/toString/calendarname-wrong-type.js
- - test/built-ins/Temporal/PlainDateTime/prototype/with/calendar-temporal-object-throws.js
- - test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-calendar-annotation.js
- - test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-time-designator-required-for-disambiguation.js
- - test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-time-zone-annotation.js
- - test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-unknown-annotation.js
- - test/built-ins/Temporal/PlainMonthDay/calendar-case-insensitive.js
- - test/built-ins/Temporal/PlainMonthDay/calendar-invalid.js
- - test/built-ins/Temporal/PlainMonthDay/calendar-invalid-iso-string.js
- - test/built-ins/Temporal/PlainMonthDay/calendar-iso-string.js
- - test/built-ins/Temporal/PlainMonthDay/calendar-number.js
- - test/built-ins/Temporal/PlainMonthDay/calendar-wrong-type.js
- - test/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-case-insensitive.js
- - test/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-iso-string.js
- - test/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-leap-second.js
- - test/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-number.js
- - test/built-ins/Temporal/PlainMonthDay/from/argument-propertybag-calendar-wrong-type.js
- - test/built-ins/Temporal/PlainMonthDay/from/argument-string-calendar-annotation.js
- - test/built-ins/Temporal/PlainMonthDay/from/argument-string-unknown-annotation.js
- - test/built-ins/Temporal/PlainMonthDay/from/fields-string.js
- - test/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-case-insensitive.js
- - test/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-iso-string.js
- - test/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-leap-second.js
- - test/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-number.js
- - test/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-propertybag-calendar-wrong-type.js
- - test/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string.js
- - test/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-unknown-annotation.js
- - test/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-auto.js
- - test/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-critical.js
- - test/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-never.js
- - test/built-ins/Temporal/PlainMonthDay/prototype/toString/calendarname-wrong-type.js
- - test/built-ins/Temporal/PlainMonthDay/prototype/toString/order-of-operations.js
- - test/built-ins/Temporal/PlainMonthDay/refisoyear-undefined.js
- - test/built-ins/Temporal/PlainTime/compare/argument-string-time-designator-required-for-disambiguation.js
- - test/built-ins/Temporal/PlainTime/from/argument-string-time-designator-required-for-disambiguation.js
- - test/built-ins/Temporal/PlainTime/prototype/equals/argument-string-time-designator-required-for-disambiguation.js
- - test/built-ins/Temporal/PlainTime/prototype/since/argument-string-time-designator-required-for-disambiguation.js
- - test/built-ins/Temporal/PlainTime/prototype/until/argument-string-time-designator-required-for-disambiguation.js
- - test/built-ins/Temporal/PlainYearMonth/calendar-always.js
- - test/built-ins/Temporal/PlainYearMonth/calendar-case-insensitive.js
- - test/built-ins/Temporal/PlainYearMonth/calendar-invalid.js
- - test/built-ins/Temporal/PlainYearMonth/calendar-invalid-iso-string.js
- - test/built-ins/Temporal/PlainYearMonth/calendar-iso-string.js
- - test/built-ins/Temporal/PlainYearMonth/calendar-number.js
- - test/built-ins/Temporal/PlainYearMonth/calendar-string.js
- - test/built-ins/Temporal/PlainYearMonth/calendar-undefined.js
- - test/built-ins/Temporal/PlainYearMonth/calendar-wrong-type.js
- - test/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-case-insensitive.js
- - test/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-iso-string.js
- - test/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-leap-second.js
- - test/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-number.js
- - test/built-ins/Temporal/PlainYearMonth/compare/argument-propertybag-calendar-wrong-type.js
- - test/built-ins/Temporal/PlainYearMonth/compare/argument-string-critical-unknown-annotation.js
- - test/built-ins/Temporal/PlainYearMonth/compare/argument-string-unknown-annotation.js
- - test/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-case-insensitive.js
- - test/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-iso-string.js
- - test/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-leap-second.js
- - test/built-ins/Temporal/PlainYearMonth/from/argument-string-calendar-annotation.js
- - test/built-ins/Temporal/PlainYearMonth/limits.js
- - test/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-number.js
- - test/built-ins/Temporal/PlainYearMonth/from/argument-propertybag-calendar-wrong-type.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-case-insensitive.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-iso-string.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-leap-second.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-number.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-propertybag-calendar-wrong-type.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-unknown-annotation.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/era/branding.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/era/prop-desc.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/eraYear/branding.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/eraYear/prop-desc.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-case-insensitive.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-iso-string.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-leap-second.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-number.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/since/argument-propertybag-calendar-wrong-type.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-unknown-annotation.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-auto.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-critical.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-never.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/toString/calendarname-wrong-type.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/toString/order-of-operations.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-case-insensitive.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-iso-string.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-leap-second.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-number.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/until/argument-propertybag-calendar-wrong-type.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-unknown-annotation.js
- - test/built-ins/Temporal/PlainYearMonth/refisoday-undefined.js
-
- # Depends on annotations in datetime strings
- - test/built-ins/Temporal/PlainDate/compare/argument-string-time-zone-annotation.js
- - test/built-ins/Temporal/PlainDate/from/argument-string-time-zone-annotation.js
- - test/built-ins/Temporal/PlainDate/prototype/equals/argument-string-time-zone-annotation.js
- - test/built-ins/Temporal/PlainDate/prototype/since/argument-string-time-zone-annotation.js
- - test/built-ins/Temporal/PlainDate/prototype/until/argument-string-time-zone-annotation.js
- - test/built-ins/Temporal/PlainDate/prototype/until/argument-string-unknown-annotation.js
- - test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-calendar-annotation.js
- - test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-time-zone-annotation.js
- - test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-string-unknown-annotation.js
- - test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-calendar-annotation.js
- - test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-time-zone-annotation.js
- - test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-string-unknown-annotation.js
- - test/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-calendar-annotation.js
- - test/built-ins/Temporal/PlainMonthDay/prototype/equals/argument-string-date-with-utc-offset.js
- - test/built-ins/Temporal/PlainMonthDay/from/argument-string-date-with-utc-offset.js
- - test/built-ins/Temporal/PlainTime/compare/argument-string-calendar-annotation.js
- - test/built-ins/Temporal/PlainTime/compare/argument-string-time-zone-annotation.js
- - test/built-ins/Temporal/PlainTime/compare/argument-string-unknown-annotation.js
- - test/built-ins/Temporal/PlainTime/from/argument-string-calendar-annotation.js
- - test/built-ins/Temporal/PlainTime/from/argument-string-time-zone-annotation.js
- - test/built-ins/Temporal/PlainTime/from/argument-string-unknown-annotation.js
- - test/built-ins/Temporal/PlainTime/prototype/equals/argument-string-calendar-annotation.js
- - test/built-ins/Temporal/PlainTime/prototype/equals/argument-string-time-zone-annotation.js
- - test/built-ins/Temporal/PlainTime/prototype/equals/argument-string-unknown-annotation.js
- - test/built-ins/Temporal/PlainTime/prototype/since/argument-string-calendar-annotation.js
- - test/built-ins/Temporal/PlainTime/prototype/since/argument-string-time-zone-annotation.js
- - test/built-ins/Temporal/PlainTime/prototype/since/argument-string-unknown-annotation.js
- - test/built-ins/Temporal/PlainTime/prototype/until/argument-string-calendar-annotation.js
- - test/built-ins/Temporal/PlainTime/prototype/until/argument-string-time-zone-annotation.js
- - test/built-ins/Temporal/PlainTime/prototype/until/argument-string-unknown-annotation.js
- - test/built-ins/Temporal/PlainYearMonth/compare/argument-string-calendar-annotation.js
- - test/built-ins/Temporal/PlainYearMonth/compare/argument-string-date-with-utc-offset.js
- - test/built-ins/Temporal/PlainYearMonth/from/argument-string-unknown-annotation.js
- - test/built-ins/Temporal/PlainYearMonth/from/argument-string-date-with-utc-offset.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-date-with-utc-offset.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/equals/argument-string-calendar-annotation.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-calendar-annotation.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/since/argument-string-date-with-utc-offset.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-calendar-annotation.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/until/argument-string-date-with-utc-offset.js
-
- # Depends on Temporal.ZonedDateTime
- - test/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-negative-epochnanoseconds.js
- - test/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-negative-epochnanoseconds.js
- - test/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-slots.js
- - test/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-negative-epochnanoseconds.js
- - test/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days.js
- - test/built-ins/Temporal/Duration/prototype/total/unit-plurals-accepted-string.js
- - test/built-ins/Temporal/Duration/prototype/toString/smallestunit-plurals-accepted.js
- - test/built-ins/Temporal/Instant/compare/argument-zoneddatetime.js
- - test/built-ins/Temporal/Instant/from/argument-zoneddatetime.js
- - test/built-ins/Temporal/Instant/prototype/equals/argument-zoneddatetime.js
- - test/built-ins/Temporal/Instant/prototype/since/argument-zoneddatetime.js
- - test/built-ins/Temporal/Instant/prototype/toString/smallestunit-plurals-accepted.js
- - test/built-ins/Temporal/Instant/prototype/toString/timezone-offset.js
- - test/built-ins/Temporal/Instant/prototype/toString/timezone-string.js
- - test/built-ins/Temporal/Instant/prototype/toString/timezone-string-datetime.js
- - test/built-ins/Temporal/Instant/prototype/toString/timezone-string-leap-second.js
- - test/built-ins/Temporal/Instant/prototype/toString/timezone-string-multiple-offsets.js
- - test/built-ins/Temporal/Instant/prototype/until/argument-zoneddatetime.js
- - test/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime-slots.js
- - test/built-ins/Temporal/PlainDate/compare/argument-zoneddatetime.js
- - test/built-ins/Temporal/PlainDate/from/argument-zoneddatetime-slots.js
- - test/built-ins/Temporal/PlainDate/from/argument-zoneddatetime.js
- - test/built-ins/Temporal/PlainDate/from/overflow-invalid-string.js
- - test/built-ins/Temporal/PlainDate/prototype/equals/argument-zoneddatetime-slots.js
- - test/built-ins/Temporal/PlainDate/prototype/since/argument-zoneddatetime-slots.js
- - test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-balance-negative-time-units.js
- - test/built-ins/Temporal/PlainDate/prototype/toPlainDateTime/argument-zoneddatetime-negative-epochnanoseconds.js
- - test/built-ins/Temporal/PlainDate/prototype/until/argument-zoneddatetime-slots.js
- - test/built-ins/Temporal/PlainDate/prototype/with/plaindatelike-invalid.js
- - test/built-ins/Temporal/PlainDateTime/compare/argument-zoneddatetime-negative-epochnanoseconds.js
- - test/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-balance-negative-time-units.js
- - test/built-ins/Temporal/PlainDateTime/from/argument-zoneddatetime-negative-epochnanoseconds.js
- - test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-balance-negative-time-units.js
- - test/built-ins/Temporal/PlainDateTime/prototype/equals/argument-zoneddatetime-negative-epochnanoseconds.js
- - test/built-ins/Temporal/PlainDateTime/prototype/toString/smallestunit-plurals-accepted.js
- - test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-balance-negative-time-units.js
- - test/built-ins/Temporal/PlainDateTime/prototype/withPlainTime/argument-zoneddatetime-negative-epochnanoseconds.js
- - test/built-ins/Temporal/PlainMonthDay/from/calendar-temporal-object.js
- - test/built-ins/Temporal/PlainMonthDay/prototype/equals/calendar-temporal-object.js
- - test/built-ins/Temporal/PlainMonthDay/prototype/with/monthdaylike-invalid.js
- - test/built-ins/Temporal/PlainTime/compare/argument-zoneddatetime-negative-epochnanoseconds.js
- - test/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-balance-negative-time-units.js
- - test/built-ins/Temporal/PlainTime/from/argument-zoneddatetime-negative-epochnanoseconds.js
- - test/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-balance-negative-time-units.js
- - test/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-negative-epochnanoseconds.js
- - test/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-balance-negative-time-units.js
- - test/built-ins/Temporal/PlainTime/prototype/since/argument-zoneddatetime-negative-epochnanoseconds.js
- - test/built-ins/Temporal/PlainTime/prototype/toString/smallestunit-plurals-accepted.js
- - test/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-balance-negative-time-units.js
- - test/built-ins/Temporal/PlainTime/prototype/until/argument-zoneddatetime-negative-epochnanoseconds.js
- - test/built-ins/Temporal/PlainTime/prototype/with/plaintimelike-invalid.js
- - test/built-ins/Temporal/PlainYearMonth/compare/calendar-temporal-object.js
- - test/built-ins/Temporal/PlainYearMonth/from/calendar-temporal-object.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/equals/calendar-temporal-object.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/since/calendar-temporal-object.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/until/calendar-temporal-object.js
- - test/built-ins/Temporal/PlainYearMonth/prototype/with/yearmonthlike-invalid.js
- - test/intl402/Intl/DateTimeFormat/prototype/formatRange/fails-on-distinct-temporal-types.js
- - test/intl402/Intl/DateTimeFormat/prototype/formatRangeToParts/fails-on-distinct-temporal-types.js
- - test/intl402/Temporal/Instant/prototype/toString/timezone-offset.js
- - test/intl402/Temporal/Instant/prototype/toString/timezone-string-datetime.js
-
- # Depends on Temporal support in IntlDateTimeFormat::handleDateTimeValue()
- - test/intl402/DateTimeFormat/prototype/format/temporal-objects-resolved-time-zone.js
- - test/intl402/DateTimeFormat/prototype/formatRange/temporal-objects-resolved-time-zone.js
- - test/intl402/DateTimeFormat/prototype/formatRangeToParts/temporal-objects-resolved-time-zone.js
- - test/intl402/DateTimeFormat/prototype/formatToParts/temporal-objects-resolved-time-zone.js
- - test/intl402/DateTimeFormat/prototype/format/temporal-plainmonthday-formatting-datetime-style.js
- - test/intl402/DateTimeFormat/prototype/format/temporal-plainyearmonth-formatting-datetime-style.js
diff --git a/JSTests/test262/expectations.yaml b/JSTests/test262/expectations.yaml
index c439a288054b..9a0fd1a6d31c 100644
--- a/JSTests/test262/expectations.yaml
+++ b/JSTests/test262/expectations.yaml
@@ -37,141 +37,6 @@ test/built-ins/Proxy/construct/arguments-realm.js:
test/built-ins/Proxy/construct/trap-is-not-callable-realm.js:
default: 'Test262Error: Expected a TypeError but got a different error constructor with the same name'
strict mode: 'Test262Error: Expected a TypeError but got a different error constructor with the same name'
-test/built-ins/Temporal/Duration/compare/blank-duration.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(1n, \"UTC\")')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(1n, \"UTC\")')"
-test/built-ins/Temporal/Duration/compare/exhaustive.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, \"UTC\")')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, \"UTC\")')"
-test/built-ins/Temporal/Duration/compare/relativeto-propertybag-optional-properties.js:
- default: 'RangeError: relativeTo with timeZone (ZonedDateTime) is not yet implemented'
- strict mode: 'RangeError: relativeTo with timeZone (ZonedDateTime) is not yet implemented'
-test/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string.js:
- default: 'RangeError: relativeTo with timeZone (ZonedDateTime) is not yet implemented'
- strict mode: 'RangeError: relativeTo with timeZone (ZonedDateTime) is not yet implemented'
-test/built-ins/Temporal/Duration/compare/relativeto-string-limits.js:
- default: 'RangeError: relativeTo as ZonedDateTime string is not yet implemented'
- strict mode: 'RangeError: relativeTo as ZonedDateTime string is not yet implemented'
-test/built-ins/Temporal/Duration/compare/relativeto-string.js:
- default: 'RangeError: relativeTo as ZonedDateTime string is not yet implemented'
- strict mode: 'RangeError: relativeTo as ZonedDateTime string is not yet implemented'
-test/built-ins/Temporal/Duration/compare/throws-when-target-zoned-date-time-outside-valid-limits.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(864n * 10n**19n, \"UTC\")')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(864n * 10n**19n, \"UTC\")')"
-test/built-ins/Temporal/Duration/prototype/round/blank-duration.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(1n, \"UTC\")')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(1n, \"UTC\")')"
-test/built-ins/Temporal/Duration/prototype/round/case-where-relativeto-affects-rounding-mode-half-even.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, \"UTC\")')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, \"UTC\")')"
-test/built-ins/Temporal/Duration/prototype/round/exact-multiple-of-larger-unit-plaindate.js:
- default: 'Test262Error: P31D weeks..months ceil: weeks result: Expected SameValue(«1», «0») to be true'
- strict mode: 'Test262Error: P31D weeks..months ceil: weeks result: Expected SameValue(«1», «0») to be true'
-test/built-ins/Temporal/Duration/prototype/round/exact-multiple-of-larger-unit-zoned.js:
- default: "TypeError: undefined is not an object (evaluating 'Temporal.ZonedDateTime.from')"
- strict mode: "TypeError: undefined is not an object (evaluating 'Temporal.ZonedDateTime.from')"
-test/built-ins/Temporal/Duration/prototype/round/next-day-out-of-range.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(86400_0000_0000_000_000_000n, \"UTC\")')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(86400_0000_0000_000_000_000n, \"UTC\")')"
-test/built-ins/Temporal/Duration/prototype/round/relativeto-date-limits.js:
- default: 'RangeError: relativeTo as ZonedDateTime string is not yet implemented'
- strict mode: 'RangeError: relativeTo as ZonedDateTime string is not yet implemented'
-test/built-ins/Temporal/Duration/prototype/round/relativeto-days-24-hours-relative-to-zoned-date-time.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, \"+04:30\")')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, \"+04:30\")')"
-test/built-ins/Temporal/Duration/prototype/round/relativeto-largestunit-smallestunit-combinations.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(63072000_000_000_000n /* = 1972-01-01T00Z */, \"UTC\")')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(63072000_000_000_000n /* = 1972-01-01T00Z */, \"UTC\")')"
-test/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-optional-properties.js:
- default: 'RangeError: relativeTo with timeZone (ZonedDateTime) is not yet implemented'
- strict mode: 'RangeError: relativeTo with timeZone (ZonedDateTime) is not yet implemented'
-test/built-ins/Temporal/Duration/prototype/round/relativeto-string-limits.js:
- default: 'RangeError: relativeTo as ZonedDateTime string is not yet implemented'
- strict mode: 'RangeError: relativeTo as ZonedDateTime string is not yet implemented'
-test/built-ins/Temporal/Duration/prototype/round/relativeto-string.js:
- default: 'RangeError: relativeTo as ZonedDateTime string is not yet implemented'
- strict mode: 'RangeError: relativeTo as ZonedDateTime string is not yet implemented'
-test/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-large-time-component-out-of-range.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, \"UTC\")')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, \"UTC\")')"
-test/built-ins/Temporal/Duration/prototype/round/rounding-increment-relativeto.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, \"UTC\")')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, \"UTC\")')"
-test/built-ins/Temporal/Duration/prototype/round/rounding-increments.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, \"UTC\")')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, \"UTC\")')"
-test/built-ins/Temporal/Duration/prototype/round/rounding-is-noop.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, \"UTC\", \"iso8601\")')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, \"UTC\", \"iso8601\")')"
-test/built-ins/Temporal/Duration/prototype/round/roundingincrement-days-large.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, \"UTC\")')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, \"UTC\")')"
-test/built-ins/Temporal/Duration/prototype/round/zero-duration.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, 'UTC')')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, 'UTC')')"
-test/built-ins/Temporal/Duration/prototype/total/blank-duration.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(1n, \"UTC\")')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(1n, \"UTC\")')"
-test/built-ins/Temporal/Duration/prototype/total/no-dst-day-length.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, \"+04:30\")')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, \"+04:30\")')"
-test/built-ins/Temporal/Duration/prototype/total/relativeto-date-limits.js:
- default: 'RangeError: relativeTo as ZonedDateTime string is not yet implemented'
- strict mode: 'RangeError: relativeTo as ZonedDateTime string is not yet implemented'
-test/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-optional-properties.js:
- default: 'RangeError: relativeTo with timeZone (ZonedDateTime) is not yet implemented'
- strict mode: 'RangeError: relativeTo with timeZone (ZonedDateTime) is not yet implemented'
-test/built-ins/Temporal/Duration/prototype/total/relativeto-string-limits.js:
- default: 'RangeError: relativeTo as ZonedDateTime string is not yet implemented'
- strict mode: 'RangeError: relativeTo as ZonedDateTime string is not yet implemented'
-test/built-ins/Temporal/Duration/prototype/total/relativeto-string.js:
- default: 'RangeError: relativeTo as ZonedDateTime string is not yet implemented'
- strict mode: 'RangeError: relativeTo as ZonedDateTime string is not yet implemented'
-test/built-ins/Temporal/Duration/prototype/total/relativeto-total-of-each-unit.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(63072000_000_000_000n /* = 1972-01-01T00Z */, \"UTC\")')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(63072000_000_000_000n /* = 1972-01-01T00Z */, \"UTC\")')"
-test/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-large-time-component-out-of-range.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, \"UTC\")')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, \"UTC\")')"
-test/built-ins/Temporal/Duration/prototype/total/throws-if-date-time-invalid-with-zoneddatetime-relative.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(864n * 10n**19n - 1n, \"UTC\")')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(864n * 10n**19n - 1n, \"UTC\")')"
-test/built-ins/Temporal/Duration/prototype/total/throws-if-target-nanoseconds-outside-valid-limits.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(864n * 10n**19n, \"UTC\")')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(864n * 10n**19n, \"UTC\")')"
-test/built-ins/Temporal/Duration/prototype/total/zero-duration.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, 'UTC')')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, 'UTC')')"
-test/built-ins/Temporal/PlainDate/from/order-of-operations.js:
- default: 'Test262Error: Actual [get options.overflow, get options.overflow.toString, call options.overflow.toString, get fields.calendar, get fields.day, get fields.day.valueOf, call fields.day.valueOf, get fields.month, get fields.month.valueOf, call fields.month.valueOf, get fields.monthCode, get fields.monthCode.toString, call fields.monthCode.toString, get fields.year, get fields.year.valueOf, call fields.year.valueOf] and expected [get fields.calendar, get fields.day, get fields.day.valueOf, call fields.day.valueOf, get fields.month, get fields.month.valueOf, call fields.month.valueOf, get fields.monthCode, get fields.monthCode.toString, call fields.monthCode.toString, get fields.year, get fields.year.valueOf, call fields.year.valueOf, get options.overflow, get options.overflow.toString, call options.overflow.toString] should have the same contents. order of operations'
- strict mode: 'Test262Error: Actual [get options.overflow, get options.overflow.toString, call options.overflow.toString, get fields.calendar, get fields.day, get fields.day.valueOf, call fields.day.valueOf, get fields.month, get fields.month.valueOf, call fields.month.valueOf, get fields.monthCode, get fields.monthCode.toString, call fields.monthCode.toString, get fields.year, get fields.year.valueOf, call fields.year.valueOf] and expected [get fields.calendar, get fields.day, get fields.day.valueOf, call fields.day.valueOf, get fields.month, get fields.month.valueOf, call fields.month.valueOf, get fields.monthCode, get fields.monthCode.toString, call fields.monthCode.toString, get fields.year, get fields.year.valueOf, call fields.year.valueOf, get options.overflow, get options.overflow.toString, call options.overflow.toString] should have the same contents. order of operations'
-test/built-ins/Temporal/PlainDate/prototype/since/exact-multiple-of-larger-unit.js:
- default: 'Test262Error: P1M weeks..months ceil: weeks result: Expected SameValue(«1», «0») to be true'
- strict mode: 'Test262Error: P1M weeks..months ceil: weeks result: Expected SameValue(«1», «0») to be true'
-test/built-ins/Temporal/PlainDate/prototype/until/exact-multiple-of-larger-unit.js:
- default: 'Test262Error: P1M weeks..months ceil: weeks result: Expected SameValue(«1», «0») to be true'
- strict mode: 'Test262Error: P1M weeks..months ceil: weeks result: Expected SameValue(«1», «0») to be true'
-test/built-ins/Temporal/PlainDateTime/from/order-of-operations.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, \"UTC\")')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, \"UTC\")')"
-test/built-ins/Temporal/PlainTime/from/observable-get-overflow-argument-string-invalid.js:
- default: 'Test262Error: Actual [get overflow, get overflow.toString, call overflow.toString] and expected [] should have the same contents. options read after ISO string parsing'
- strict mode: 'Test262Error: Actual [get overflow, get overflow.toString, call overflow.toString] and expected [] should have the same contents. options read after ISO string parsing'
-test/built-ins/Temporal/PlainTime/from/options-wrong-type.js:
- default: 'Test262Error: Invalid string processed before throwing TypeError Expected a RangeError but got a TypeError'
- strict mode: 'Test262Error: Invalid string processed before throwing TypeError Expected a RangeError but got a TypeError'
-test/built-ins/Temporal/PlainTime/from/order-of-operations.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, \"UTC\")')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, \"UTC\")')"
-test/built-ins/Temporal/PlainYearMonth/prototype/since/exact-multiple-of-larger-unit.js:
- default: 'Test262Error: P1Y months..years ceil: months result: Expected SameValue(«2», «0») to be true'
- strict mode: 'Test262Error: P1Y months..years ceil: months result: Expected SameValue(«2», «0») to be true'
-test/built-ins/Temporal/PlainYearMonth/prototype/until/exact-multiple-of-larger-unit.js:
- default: 'Test262Error: P1Y months..years ceil: months result: Expected SameValue(«2», «0») to be true'
- strict mode: 'Test262Error: P1Y months..years ceil: months result: Expected SameValue(«2», «0») to be true'
-test/built-ins/Temporal/getOwnPropertyNames.js:
- default: 'Test262Error: ZonedDateTime'
- strict mode: 'Test262Error: ZonedDateTime'
test/built-ins/TypedArray/prototype/slice/speciesctor-return-same-buffer-with-offset.js:
default: 'Test262Error: Actual [20, 30, 40, 60] and expected [20, 20, 20, 60] should have the same contents. (Testing with Float64Array and makePassthrough.)'
strict mode: 'Test262Error: Actual [20, 30, 40, 60] and expected [20, 20, 20, 60] should have the same contents. (Testing with Float64Array and makePassthrough.)'
@@ -187,42 +52,9 @@ test/built-ins/Uint8Array/prototype/setFromBase64/trailing-garbage-empty.js:
test/built-ins/Uint8Array/prototype/setFromBase64/trailing-garbage.js:
default: 'SyntaxError: Uint8Array.prototype.setFromBase64 requires a valid base64 string'
strict mode: 'SyntaxError: Uint8Array.prototype.setFromBase64 requires a valid base64 string'
-test/intl402/DateTimeFormat/prototype/format/temporal-objects-format-with-era.js:
- default: 'Test262Error: toLocaleString on an Instant with era option should return the same results as toLocaleString on the same Date with the same options Expected SameValue(«"12/31/1969 A, 4:00:00 PM PST"», «"12/31/1969 A, 4:00:00 PM"») to be true'
- strict mode: 'Test262Error: toLocaleString on an Instant with era option should return the same results as toLocaleString on the same Date with the same options Expected SameValue(«"12/31/1969 A, 4:00:00 PM PST"», «"12/31/1969 A, 4:00:00 PM"») to be true'
-test/intl402/DateTimeFormat/prototype/format/temporal-plaintime-formatting-datetime-style.js:
- default: 'Test262Error: dateStyle = full, timeStyle = full Expected SameValue(«"12:34:56 AM"», «"12:34:56 AM Greenwich Mean Time"») to be true'
- strict mode: 'Test262Error: dateStyle = full, timeStyle = full Expected SameValue(«"12:34:56 AM"», «"12:34:56 AM Greenwich Mean Time"») to be true'
-test/intl402/DateTimeFormat/prototype/format/temporal-zoneddatetime-not-supported.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, timeZone)')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, timeZone)')"
-test/intl402/DateTimeFormat/prototype/formatRange/fails-on-distinct-temporal-types.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, 'America/Kentucky/Louisville')')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, 'America/Kentucky/Louisville')')"
-test/intl402/DateTimeFormat/prototype/formatRange/temporal-zoneddatetime-not-supported.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, timeZone)')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, timeZone)')"
-test/intl402/DateTimeFormat/prototype/formatRange/to-datetime-formattable-with-different-arg-kinds.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, \"UTC\")')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, \"UTC\")')"
-test/intl402/DateTimeFormat/prototype/formatRangeToParts/fails-on-distinct-temporal-types.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, 'America/Kentucky/Louisville')')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, 'America/Kentucky/Louisville')')"
-test/intl402/DateTimeFormat/prototype/formatRangeToParts/temporal-zoneddatetime-not-supported.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, timeZone)')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, timeZone)')"
-test/intl402/DateTimeFormat/prototype/formatRangeToParts/to-datetime-formattable-with-different-arg-kinds.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, \"UTC\")')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, \"UTC\")')"
-test/intl402/DateTimeFormat/prototype/formatToParts/temporal-zoneddatetime-not-supported.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, timeZone)')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, timeZone)')"
test/intl402/FallbackSymbol/per-realm.js:
default: 'Test262Error: %Intl%.[[FallbackSymbol]] should be different per-realm Expected SameValue(«Symbol(IntlLegacyConstructedSymbol)», «Symbol(IntlLegacyConstructedSymbol)») to be false'
strict mode: 'Test262Error: %Intl%.[[FallbackSymbol]] should be different per-realm Expected SameValue(«Symbol(IntlLegacyConstructedSymbol)», «Symbol(IntlLegacyConstructedSymbol)») to be false'
-test/intl402/Locale/extensions-grandfathered.js:
- default: 'Test262Error: Expected SameValue(«"fr-Cyrl-FR-gaulish-u-nu-latn"», «"fr-Cyrl-FR-u-nu-latn"») to be true'
- strict mode: 'Test262Error: Expected SameValue(«"fr-Cyrl-FR-gaulish-u-nu-latn"», «"fr-Cyrl-FR-u-nu-latn"») to be true'
test/intl402/NumberFormat/prototype/format/unit-ja-JP.js:
default: 'Test262Error: Expected SameValue(«"時速-987キロメートル"», «"時速 -987 キロメートル"») to be true'
strict mode: 'Test262Error: Expected SameValue(«"時速-987キロメートル"», «"時速 -987 キロメートル"») to be true'
@@ -244,60 +76,6 @@ test/intl402/PluralRules/constructor-option-read-order.js:
test/intl402/PluralRules/constructor-options-throwing-getters.js:
default: 'Test262Error: Exception from compactDisplay getter should be propagated Expected a CustomError to be thrown but no exception was thrown at all'
strict mode: 'Test262Error: Exception from compactDisplay getter should be propagated Expected a CustomError to be thrown but no exception was thrown at all'
-test/intl402/Temporal/Duration/compare/relativeto-sub-minute-offset.js:
- default: 'RangeError: relativeTo as ZonedDateTime string is not yet implemented'
- strict mode: 'RangeError: relativeTo as ZonedDateTime string is not yet implemented'
-test/intl402/Temporal/Duration/compare/twenty-five-hour-day.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(941184000_000_000_000n, \"America/Vancouver\")')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(941184000_000_000_000n, \"America/Vancouver\")')"
-test/intl402/Temporal/Duration/prototype/round/adjust-rounded-duration-days.js:
- default: "TypeError: undefined is not an object (evaluating 'Temporal.ZonedDateTime.from')"
- strict mode: "TypeError: undefined is not an object (evaluating 'Temporal.ZonedDateTime.from')"
-test/intl402/Temporal/Duration/prototype/round/dst-balancing-result.js:
- default: 'Error: Temporal.PlainDateTime.prototype.toZonedDateTime is not yet implemented'
- strict mode: 'Error: Temporal.PlainDateTime.prototype.toZonedDateTime is not yet implemented'
-test/intl402/Temporal/Duration/prototype/round/dst-rounding-result.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime("
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime("
-test/intl402/Temporal/Duration/prototype/round/relativeto-dst-back-transition.js:
- default: "TypeError: undefined is not an object (evaluating 'Temporal.ZonedDateTime.from')"
- strict mode: "TypeError: undefined is not an object (evaluating 'Temporal.ZonedDateTime.from')"
-test/intl402/Temporal/Duration/prototype/round/relativeto-sub-minute-offset.js:
- default: 'RangeError: relativeTo as ZonedDateTime string is not yet implemented'
- strict mode: 'RangeError: relativeTo as ZonedDateTime string is not yet implemented'
-test/intl402/Temporal/Duration/prototype/round/rounding-increment-relativeto.js:
- default: "TypeError: undefined is not an object (evaluating 'Temporal.ZonedDateTime.from')"
- strict mode: "TypeError: undefined is not an object (evaluating 'Temporal.ZonedDateTime.from')"
-test/intl402/Temporal/Duration/prototype/round/rounding-with-largestunit.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, \"UTC\")')"
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime(0n, \"UTC\")')"
-test/intl402/Temporal/Duration/prototype/total/dst-balancing-result.js:
- default: 'Error: Temporal.PlainDateTime.prototype.toZonedDateTime is not yet implemented'
- strict mode: 'Error: Temporal.PlainDateTime.prototype.toZonedDateTime is not yet implemented'
-test/intl402/Temporal/Duration/prototype/total/dst-day-length.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime("
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime("
-test/intl402/Temporal/Duration/prototype/total/dst-rounding-result.js:
- default: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime("
- strict mode: "TypeError: undefined is not a constructor (evaluating 'new Temporal.ZonedDateTime("
-test/intl402/Temporal/Duration/prototype/total/relativeto-dst-back-transition.js:
- default: "TypeError: undefined is not an object (evaluating 'Temporal.ZonedDateTime.from')"
- strict mode: "TypeError: undefined is not an object (evaluating 'Temporal.ZonedDateTime.from')"
-test/intl402/Temporal/Duration/prototype/total/relativeto-sub-minute-offset.js:
- default: 'RangeError: relativeTo as ZonedDateTime string is not yet implemented'
- strict mode: 'RangeError: relativeTo as ZonedDateTime string is not yet implemented'
-test/intl402/Temporal/Instant/prototype/toLocaleString/default-includes-time-not-time-zone-name.js:
- default: 'Test262Error: Instant formatted with no options 12/26/2024, 11:46:40 AM UTC should not include time zone name'
- strict mode: 'Test262Error: Instant formatted with no options 12/26/2024, 11:46:40 AM UTC should not include time zone name'
-test/intl402/Temporal/Instant/prototype/toLocaleString/era.js:
- default: 'Test262Error: toLocaleString on an Instant with era option should return the same results as toLocaleString on the same Date with the same options Expected SameValue(«"12/31/1969 A, 4:00:00 PM PST"», «"12/31/1969 A, 4:00:00 PM"») to be true'
- strict mode: 'Test262Error: toLocaleString on an Instant with era option should return the same results as toLocaleString on the same Date with the same options Expected SameValue(«"12/31/1969 A, 4:00:00 PM PST"», «"12/31/1969 A, 4:00:00 PM"») to be true'
-test/intl402/Temporal/Instant/prototype/toLocaleString/option-timezonename-short.js:
- default: 'Test262Error: expected "11/18/1976, 9:23:30 AM EST" to be shorter than "11/18/1976, 9:23:30 AM EST".'
- strict mode: 'Test262Error: expected "11/18/1976, 9:23:30 AM EST" to be shorter than "11/18/1976, 9:23:30 AM EST".'
-test/intl402/Temporal/Instant/prototype/toLocaleString/respect-timezone-after-formatting-plaindatetime.js:
- default: 'Test262Error: Expected SameValue(«"12/31/1969 A, 4:00:00 PM PST"», «"12/31/1969 A, 4:00:00 PM"») to be true'
- strict mode: 'Test262Error: Expected SameValue(«"12/31/1969 A, 4:00:00 PM PST"», «"12/31/1969 A, 4:00:00 PM"») to be true'
test/language/destructuring/binding/keyed-destructuring-property-reference-target-evaluation-order-with-bindings.js:
default: 'Test262Error: Actual [binding::source, binding::sourceKey, sourceKey, get source, binding::defaultValue, binding::varTarget] and expected [binding::source, binding::sourceKey, sourceKey, binding::varTarget, get source, binding::defaultValue] should have the same contents. '
test/language/eval-code/direct/arrow-fn-body-cntns-arguments-func-decl-arrow-func-declare-arguments-assign-incl-def-param-arrow-arguments.js:
@@ -372,12 +150,6 @@ test/language/import/import-attributes/text-string.js:
module: 'TypeError: Import attribute type "text" is not valid'
test/language/import/import-attributes/text-via-namespace.js:
module: 'TypeError: Import attribute type "text" is not valid'
-test/language/statements/async-generator/yield-star-promise-not-unwrapped.js:
- default: 'Test262:AsyncTestFailure:Test262Error: Test262Error: yield* should not unwrap promises from manually-implemented async iterators Expected SameValue(«"unwrapped value"», «[object Promise]») to be true'
- strict mode: 'Test262:AsyncTestFailure:Test262Error: Test262Error: yield* should not unwrap promises from manually-implemented async iterators Expected SameValue(«"unwrapped value"», «[object Promise]») to be true'
-test/language/statements/async-generator/yield-star-return-then-getter-ticks.js:
- default: 'Test262:AsyncTestFailure:Test262Error: Test262Error: Actual [start, tick 1, tick 2, get then, tick 3, get return, get then] and expected [start, tick 1, get then, tick 2, get return, get then, tick 3] should have the same contents. Ticks for return with thenable getter'
- strict mode: 'Test262:AsyncTestFailure:Test262Error: Test262Error: Actual [start, tick 1, tick 2, get then, tick 3, get return, get then] and expected [start, tick 1, get then, tick 2, get return, get then, tick 3] should have the same contents. Ticks for return with thenable getter'
test/language/statements/class/elements/private-class-field-on-nonextensible-objects.js:
strict mode: 'Test262Error: Expected a TypeError to be thrown but no exception was thrown at all'
test/language/statements/class/subclass/private-class-field-on-nonextensible-return-override.js:
@@ -402,9 +174,6 @@ test/language/statements/with/set-mutable-binding-idref-compound-assign-with-pro
test/staging/sm/ArrayBuffer/slice-species.js:
default: 'Test262Error: Expected SameValue(«function ArrayBuffer() {'
strict mode: 'Test262Error: Expected SameValue(«function ArrayBuffer() {'
-test/staging/sm/Date/to-temporal-instant.js:
- default: 'TypeError: Temporal.Instant.prototype.toZonedDateTimeISO is not yet implemented'
- strict mode: 'TypeError: Temporal.Instant.prototype.toZonedDateTimeISO is not yet implemented'
test/staging/sm/Date/two-digit-years.js:
default: 'Test262Error: Expected SameValue(«NaN», «957164400000») to be true'
strict mode: 'Test262Error: Expected SameValue(«NaN», «957164400000») to be true'
diff --git a/JSTests/test262/test/intl402/Temporal/PlainMonthDay/prototype/toLocaleString/basic.js b/JSTests/test262/test/intl402/Temporal/PlainMonthDay/prototype/toLocaleString/basic.js
index 26c47dddbe33..1104601610ef 100644
--- a/JSTests/test262/test/intl402/Temporal/PlainMonthDay/prototype/toLocaleString/basic.js
+++ b/JSTests/test262/test/intl402/Temporal/PlainMonthDay/prototype/toLocaleString/basic.js
@@ -30,6 +30,7 @@ info: |
5. Return result.
features: [Temporal]
locale: [en-US, de-AT]
+features: [Temporal]
---*/
function findPart(parts, expectedType) {
diff --git a/JSTests/wasm/libwabt.js b/JSTests/wasm/libwabt.js
index 15fb04eaa58b..dceb1f5d3ed3 100644
Binary files a/JSTests/wasm/libwabt.js and b/JSTests/wasm/libwabt.js differ
diff --git a/JSTests/wasm/stress/memory32-atomics-pointer-upper-bits.js b/JSTests/wasm/stress/memory32-atomics-pointer-upper-bits.js
new file mode 100644
index 000000000000..8c1ffd0351f9
--- /dev/null
+++ b/JSTests/wasm/stress/memory32-atomics-pointer-upper-bits.js
@@ -0,0 +1,48 @@
+//@ skip if $architecture != "arm64" && $architecture != "x86_64"
+//@ runDefaultWasm("-m")
+import { instantiate } from "../wabt-wrapper.js";
+import * as assert from "../assert.js";
+
+// In IPInt, i32.wrap_i64 is a no-op: the 64-bit stack slot keeps the upper
+// 32 bits of the original i64. memory.atomic.notify/wait32/wait64 must
+// normalize the i32 pointer operand to its low 32 bits before the bounds
+// check; otherwise an in-bounds address with dirty upper bits is wrongly
+// rejected as out-of-bounds.
+
+const wat = `
+(module
+ (memory (export "memory") 1 1 shared)
+ (func (export "notify") (param $dirty i64) (result i32)
+ (memory.atomic.notify (i32.wrap_i64 (local.get $dirty)) (i32.const 0))
+ )
+ (func (export "wait32") (param $dirty i64) (result i32)
+ (memory.atomic.wait32 (i32.wrap_i64 (local.get $dirty)) (i32.const 1) (i64.const 0))
+ )
+ (func (export "wait64") (param $dirty i64) (result i32)
+ (memory.atomic.wait64 (i32.wrap_i64 (local.get $dirty)) (i64.const 1) (i64.const 0))
+ )
+)
+`;
+
+const { exports } = await instantiate(wat, {}, { threads: true });
+
+for (let i = 0; i < wasmTestLoopCount; i++) {
+ // Address 16 is in bounds; the upper 32 bits must be ignored.
+ assert.eq(exports.notify(0x1_0000_0010n), 0);
+ assert.eq(exports.notify(0xffff_ffff_0000_0010n), 0);
+ // Memory contains 0, expected value is 1, so wait returns "not-equal" (1).
+ assert.eq(exports.wait32(0x1_0000_0010n), 1);
+ assert.eq(exports.wait32(0xdead_beef_0000_0010n), 1);
+ assert.eq(exports.wait64(0x1_0000_0010n), 1);
+ assert.eq(exports.wait64(0xdead_beef_0000_0010n), 1);
+
+ // Last in-bounds aligned addresses.
+ assert.eq(exports.notify(0x1_0000_fffcn), 0);
+ assert.eq(exports.wait32(0x1_0000_fffcn), 1);
+ assert.eq(exports.wait64(0x1_0000_fff8n), 1);
+
+ // Out-of-bounds after wrapping must still trap.
+ assert.throws(() => exports.notify(0x1_0001_0000n), WebAssembly.RuntimeError, "Out of bounds memory access");
+ assert.throws(() => exports.wait32(0x1_0001_0000n), WebAssembly.RuntimeError, "Out of bounds memory access");
+ assert.throws(() => exports.wait64(0x1_0001_0000n), WebAssembly.RuntimeError, "Out of bounds memory access");
+}
diff --git a/JSTests/wasm/stress/memory32-grow-upper-bits.js b/JSTests/wasm/stress/memory32-grow-upper-bits.js
new file mode 100644
index 000000000000..23906c2da238
--- /dev/null
+++ b/JSTests/wasm/stress/memory32-grow-upper-bits.js
@@ -0,0 +1,38 @@
+//@ runDefaultWasm("-m")
+import { instantiate } from "../wabt-wrapper.js";
+import * as assert from "../assert.js";
+
+// In IPInt, i32.wrap_i64 is a no-op: the 64-bit stack slot keeps the upper
+// 32 bits of the original i64. memory.grow must normalize the i32 delta to
+// its low 32 bits; otherwise a valid grow request with dirty upper bits is
+// wrongly rejected with -1.
+
+const wat = `
+(module
+ (memory (export "memory") 1 8)
+ (func (export "grow") (param $dirty i64) (result i32)
+ (memory.grow (i32.wrap_i64 (local.get $dirty)))
+ )
+ (func (export "size") (result i32)
+ (memory.size)
+ )
+)
+`;
+
+const { exports } = await instantiate(wat, {});
+
+// Delta 1 with dirty upper bits: must grow by exactly 1 page.
+assert.eq(exports.grow(0x1_0000_0001n), 1);
+assert.eq(exports.size(), 2);
+assert.eq(exports.grow(0xdead_beef_0000_0001n), 2);
+assert.eq(exports.size(), 3);
+
+for (let i = 0; i < wasmTestLoopCount; i++) {
+ // Delta 0 with dirty upper bits: no-op grow returning the current size.
+ assert.eq(exports.grow(0x1_0000_0000n), 3);
+ assert.eq(exports.grow(0xffff_ffff_0000_0000n), 3);
+ assert.eq(exports.size(), 3);
+
+ // A genuinely huge u32 delta must still fail.
+ assert.eq(exports.grow(0xffff_ffffn), -1);
+}
diff --git a/JSTests/wasm/stress/omg-indirect-tail-call-late-input-clobber.js b/JSTests/wasm/stress/omg-indirect-tail-call-late-input-clobber.js
new file mode 100644
index 000000000000..9fc5c5722e09
--- /dev/null
+++ b/JSTests/wasm/stress/omg-indirect-tail-call-late-input-clobber.js
@@ -0,0 +1,110 @@
+//@ runDefaultWasm("--useWasmTailCalls=1", "--useBBQJIT=1", "--useConcurrentJIT=0", "--thresholdForBBQOptimizeAfterWarmUp=0", "--thresholdForBBQOptimizeSoon=0", "--thresholdForOMGOptimizeAfterWarmUp=50", "--thresholdForOMGOptimizeSoon=50", "--wasmInliningMaximumWasmCalleeSize=0")
+
+load("../gc-spec-harness/wasm-module-builder.js", "caller relative");
+
+const callerArgumentCount = 12;
+const targetArgumentCount = 24;
+const argumentBase = 0x110000;
+const observedIndex = 22;
+
+function assertEqual(actual, expected)
+{
+ if (actual !== expected)
+ throw new Error(`expected ${expected}, got ${actual}`);
+}
+
+function makeTarget()
+{
+ const ownerBuilder = new WasmModuleBuilder();
+ ownerBuilder.addFunction("owned", makeSig([], []))
+ .addBody([])
+ .exportFunc();
+ const owner = ownerBuilder.instantiate();
+
+ const targetBuilder = new WasmModuleBuilder();
+ targetBuilder.addImport("x", "owned", makeSig([], []));
+ const targetType = targetBuilder.addType(makeSig(
+ Array(targetArgumentCount).fill(kWasmI32),
+ [kWasmI32]));
+ targetBuilder.addFunction("dump", targetType)
+ .addBody([kExprLocalGet, observedIndex])
+ .exportFunc();
+ return targetBuilder.instantiate({ x: { owned: owner.exports.owned } });
+}
+
+function makeRelay()
+{
+ const builder = new WasmModuleBuilder();
+ const targetType = builder.addType(makeSig(
+ Array(targetArgumentCount).fill(kWasmI32),
+ [kWasmI32]));
+ builder.addMemory(1);
+
+ const body = [kExprLocalGet, 0, kExprIf, kWasmI32];
+ for (let index = 0; index < callerArgumentCount; ++index)
+ body.push(kExprLocalGet, 1 + index);
+ for (let index = callerArgumentCount; index < targetArgumentCount; ++index)
+ body.push(...wasmI32Const(argumentBase + index));
+ body.push(
+ kExprLocalGet, 1 + callerArgumentCount,
+ kExprReturnCallRef, ...wasmUnsignedLeb(targetType),
+ kExprElse, ...wasmI32Const(31337),
+ kExprEnd);
+
+ builder.addFunction("entry", makeSig(
+ [
+ kWasmI32,
+ ...Array(callerArgumentCount).fill(kWasmI32),
+ wasmRefType(targetType),
+ ],
+ [kWasmI32]))
+ .addBody(body)
+ .exportFunc();
+ return builder.instantiate();
+}
+
+function makeOuter(relayFunction, targetFunction)
+{
+ const builder = new WasmModuleBuilder();
+ const targetType = builder.addType(makeSig(
+ Array(targetArgumentCount).fill(kWasmI32),
+ [kWasmI32]));
+ const relayType = builder.addType(makeSig(
+ [
+ kWasmI32,
+ ...Array(callerArgumentCount).fill(kWasmI32),
+ wasmRefType(targetType),
+ ],
+ [kWasmI32]));
+ const dump = builder.addImport("t", "dump", targetType);
+ const relay = builder.addImport("r", "entry", relayType);
+ builder.addDeclarativeElementSegment([dump]);
+
+ const body = [...wasmI32Const(1)];
+ for (let index = 0; index < callerArgumentCount; ++index)
+ body.push(...wasmI32Const(argumentBase + index));
+ body.push(
+ kExprRefFunc, ...wasmUnsignedLeb(dump),
+ kExprReturnCall, ...wasmUnsignedLeb(relay));
+
+ builder.addFunction("entry", makeSig([], [kWasmI32]))
+ .addBody(body)
+ .exportFunc();
+ return builder.instantiate({
+ t: { dump: targetFunction },
+ r: { entry: relayFunction },
+ });
+}
+
+const target = makeTarget();
+const relay = makeRelay();
+const outer = makeOuter(relay.exports.entry, target.exports.dump);
+const warmupArguments = [0];
+for (let index = 0; index < callerArgumentCount; ++index)
+ warmupArguments.push(argumentBase + index);
+warmupArguments.push(target.exports.dump);
+
+for (let index = 0; index < wasmTestLoopCount; ++index)
+ assertEqual(relay.exports.entry(...warmupArguments), 31337);
+
+assertEqual(outer.exports.entry(), argumentBase + observedIndex);
diff --git a/LayoutTests/TestExpectations b/LayoutTests/TestExpectations
index 186d20cc8610..bfaccac654ec 100644
--- a/LayoutTests/TestExpectations
+++ b/LayoutTests/TestExpectations
@@ -72,6 +72,7 @@ fast/zooming/ios [ Skip ]
fast/forms/ios [ Skip ]
http/tests/site-isolation/open-panel-in-cross-origin-iframe.html [ Skip ]
http/tests/site-isolation/select-in-cross-origin-iframe.html [ Skip ]
+http/tests/site-isolation/autofill-credentials-in-cross-origin-iframe.html [ Skip ]
fast/viewport/ios [ Skip ]
fast/visual-viewport/ios/ [ Skip ]
fast/device-orientation [ Skip ]
@@ -381,6 +382,9 @@ http/tests/security/strip-referrer-to-origin-for-third-party-redirects-in-privat
http/tests/security/strip-referrer-to-origin-for-third-party-requests-in-private-mode.html [ Skip ]
media/deactivate-audio-session.html [ Skip ]
+# Skip the test which hits a debug assertion for now.
+[ Debug ] media/track/webvtt-parser-does-not-leak.html [ Skip ]
+
# Skip isolated-tree specific tests.
accessibility/isolated-tree [ Skip ]
@@ -561,7 +565,7 @@ webkit.org/b/311501 imported/w3c/web-platform-tests/mediasession/media-session-a
# Media Session tests: rdar://178443322
imported/w3c/web-platform-tests/mediasession/idlharness.window.html [ Failure ]
imported/w3c/web-platform-tests/mediasession/mediametadata.html [ Failure ]
-imported/w3c/web-platform-tests/mediasession/setactionhandler.html [ Failure ]
+imported/w3c/web-platform-tests/mediasession/setactionhandler.html [ Pass ]
imported/w3c/web-platform-tests/mediasession/setcameraactive.html [ Pass Failure ]
imported/w3c/web-platform-tests/mediasession/setmicrophoneactive.html [ Pass Failure ]
@@ -1736,6 +1740,8 @@ imported/w3c/web-platform-tests/css/css-grid/abspos/grid-abspos-staticpos-align-
imported/w3c/web-platform-tests/css/css-grid/abspos/grid-abspos-staticpos-align-self-rtl-004.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-grid/abspos/grid-abspos-staticpos-align-self-rtl-last-baseline-003.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-grid/abspos/grid-abspos-staticpos-align-self-rtl-last-baseline-004.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/abspos/grid-abspos-staticpos-align-self-safe-outer-cb-001.tentative.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/abspos/grid-abspos-staticpos-align-self-safe-outer-cb-003.tentative.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-grid/abspos/grid-abspos-staticpos-align-self-vertWM-003.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-grid/abspos/grid-abspos-staticpos-align-self-vertWM-004.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-grid/abspos/grid-abspos-staticpos-align-self-vertWM-last-baseline-003.html [ ImageOnlyFailure ]
@@ -2074,6 +2080,47 @@ imported/w3c/web-platform-tests/css/css-grid/grid-lanes/track-sizing/grid-lanes-
imported/w3c/web-platform-tests/css/css-grid/grid-lanes/track-sizing/grid-lanes-percentage-rows-indefinite-height-003.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-grid/grid-lanes/track-sizing/grid-lanes-percentage-rows-indefinite-height-004.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-grid/grid-lanes/track-sizing/grid-lanes-within-flexbox-indefinite-001.html [ ImageOnlyFailure ]
+
+# Newly imported grid-lanes reftests failing as image diffs on mac and ios.
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/alignment/column-align-items-001.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/alignment/column-align-items-002.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/alignment/column-align-items-003.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/alignment/column-align-self-001.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/alignment/column-align-self-002.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/alignment/column-align-self-003.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/alignment/row-justify-items-001.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/alignment/row-justify-items-002.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/alignment/row-justify-items-003.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/alignment/row-justify-self-001.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/alignment/row-justify-self-002.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/alignment/row-justify-self-003.html [ ImageOnlyFailure ]
+
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/invalidation/grid-lanes-direction-dynamic-change-001.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/invalidation/grid-lanes-pack-dynamic-change.html [ ImageOnlyFailure ]
+
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/gap/column-subgrid-grid-gap-007.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/gap/column-subgrid-grid-gap-008.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/gap/column-subgrid-grid-gap-009.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/gap/column-subgrid-grid-gap-012.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/gap/column-subgrid-grid-gap-013.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/gap/column-subgrid-grid-gap-014.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/gap/row-subgrid-grid-gap-002.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/gap/row-subgrid-grid-gap-006.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/gap/row-subgrid-grid-gap-007.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/gap/row-subgrid-grid-gap-008.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/gap/row-subgrid-grid-gap-009.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/gap/row-subgrid-grid-gap-012.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/gap/row-subgrid-grid-gap-013.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/gap/row-subgrid-grid-gap-014.html [ ImageOnlyFailure ]
+
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/track-sizing/column-subgrid-extra-margin-006.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/track-sizing/column-subgrid-extra-margin-007.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/track-sizing/column-subgrid-extra-margin-008.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/track-sizing/column-subgrid-extra-margin-009.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/track-sizing/row-subgrid-extra-margin-007.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/track-sizing/row-subgrid-extra-margin-008.html [ ImageOnlyFailure ]
+webkit.org/b/316453 imported/w3c/web-platform-tests/css/css-grid/grid-lanes/subgrid/grid-subgridded-to-grid-lanes/track-sizing/row-subgrid-extra-margin-009.html [ ImageOnlyFailure ]
+
imported/w3c/web-platform-tests/css/css-grid/subgrid/subgrid-item-with-margin-left-auto.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-grid/parsing/grid-template-columns-crash.html [ Skip ]
@@ -2146,7 +2193,6 @@ imported/w3c/web-platform-tests/html/browsers/windows/nested-browsing-contexts/f
# These MathML WPT tests fail.
webkit.org/b/180013 mathml/non-core/lengths-2.html [ ImageOnlyFailure ]
-imported/w3c/web-platform-tests/mathml/presentation-markup/direction/direction-overall-003.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/mathml/presentation-markup/operators/embellished-op-1-2.html [ ImageOnlyFailure Pass ]
imported/w3c/web-platform-tests/mathml/presentation-markup/operators/embellished-op-1-3.html [ ImageOnlyFailure Pass ]
imported/w3c/web-platform-tests/mathml/presentation-markup/operators/embellished-op-1-4.html [ ImageOnlyFailure Pass ]
@@ -2159,17 +2205,14 @@ imported/w3c/web-platform-tests/mathml/presentation-markup/operators/embellished
imported/w3c/web-platform-tests/mathml/presentation-markup/operators/embellished-op-3-3.html [ ImageOnlyFailure Pass ]
imported/w3c/web-platform-tests/mathml/presentation-markup/operators/embellished-op-3-4.html [ ImageOnlyFailure Pass ]
imported/w3c/web-platform-tests/mathml/presentation-markup/operators/embellished-op-3-5.html [ ImageOnlyFailure Pass ]
-imported/w3c/web-platform-tests/mathml/presentation-markup/operators/embellished-op-5-1.html [ ImageOnlyFailure ]
webkit.org/b/308410 imported/w3c/web-platform-tests/mathml/presentation-markup/operators/operator-dictionary-arabic-002.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/mathml/presentation-markup/mrow/legacy-mrow-like-elements-002.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/mathml/presentation-markup/mrow/semantics-002.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/mathml/presentation-markup/mrow/semantics-003.html [ ImageOnlyFailure ]
-imported/w3c/web-platform-tests/mathml/presentation-markup/mrow/semantics-005.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/mathml/presentation-markup/scripts/munder-mover-align-accent-false.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/mathml/presentation-markup/scripts/munder-mover-align-accent-true.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/mathml/presentation-markup/scripts/munderover-align-accent-false.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/mathml/presentation-markup/scripts/munderover-align-accent-true.html [ ImageOnlyFailure ]
-imported/w3c/web-platform-tests/mathml/presentation-markup/scripts/stretchy-mover-2b.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/mathml/presentation-markup/scripts/stretchy-munderover-1e.html [ ImageOnlyFailure Pass ]
imported/w3c/web-platform-tests/mathml/presentation-markup/scripts/stretchy-munderover-2g.html [ ImageOnlyFailure Pass ]
imported/w3c/web-platform-tests/mathml/presentation-markup/tables/columnspan-rowspan-006.html [ ImageOnlyFailure ]
@@ -2181,9 +2224,7 @@ imported/w3c/web-platform-tests/mathml/relations/css-styling/presentational-hint
imported/w3c/web-platform-tests/mathml/relations/css-styling/transform.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/mathml/presentation-markup/operators/mo-lspace-rspace-2.html [ ImageOnlyFailure ]
-imported/w3c/web-platform-tests/mathml/presentation-markup/operators/mo-movablelimits-and-embellished-operator.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/mathml/presentation-markup/operators/painting-stretchy-operator-001.html [ ImageOnlyFailure ]
-imported/w3c/web-platform-tests/mathml/presentation-markup/scripts/underover-stretchy-002.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/mathml/relations/css-styling/display-with-overflow.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/mathml/relations/css-styling/overflow/computed-value-001.html [ Failure ]
imported/w3c/web-platform-tests/mathml/relations/css-styling/floats/floating-inside-mathml-with-block-display.html [ ImageOnlyFailure ]
@@ -2310,8 +2351,6 @@ imported/w3c/web-platform-tests/html/semantics/embedded-content/media-elements/t
imported/w3c/web-platform-tests/html/semantics/embedded-content/the-iframe-element/cross_origin_parentage.sub.html [ Skip ]
imported/w3c/web-platform-tests/html/semantics/embedded-content/the-iframe-element/src-repeated-in-ancestor.html [ Skip ]
imported/w3c/web-platform-tests/html/semantics/embedded-content/the-img-element/decode/image-decode-iframe.html [ Skip ]
-imported/w3c/web-platform-tests/html/semantics/embedded-content/the-img-element/decode/image-decode-path-changes.html [ Skip ]
-imported/w3c/web-platform-tests/html/semantics/embedded-content/the-img-element/decode/image-decode.html [ Skip ]
imported/w3c/web-platform-tests/html/semantics/embedded-content/the-img-element/invalid-src.html [ Skip ]
imported/w3c/web-platform-tests/html/semantics/embedded-content/the-img-element/srcset/srcset-w-reselect-on-resize.html [ Skip ]
imported/w3c/web-platform-tests/html/semantics/embedded-content/the-object-element/object-events.html [ Skip ]
@@ -4590,7 +4629,6 @@ imported/w3c/web-platform-tests/css/css-sizing/responsive-iframe/responsive-ifra
webkit.org/b/230041 imported/w3c/web-platform-tests/css/css-text-decor/text-decoration-color-selection-pseudo-01.html [ ImageOnlyFailure ]
webkit.org/b/203528 imported/w3c/web-platform-tests/css/css-text-decor/text-decoration-thickness-linethrough-001.html [ ImageOnlyFailure ]
webkit.org/b/203528 imported/w3c/web-platform-tests/css/css-text-decor/text-decoration-thickness-vertical-002.html [ ImageOnlyFailure ]
-webkit.org/b/233171 imported/w3c/web-platform-tests/css/css-text-decor/text-decoration-propagation-display-contents.html [ ImageOnlyFailure ]
webkit.org/b/230291 imported/w3c/web-platform-tests/css/css-text-decor/text-decoration-skip-ink-005.html [ ImageOnlyFailure ]
webkit.org/b/230291 imported/w3c/web-platform-tests/css/css-text-decor/text-decoration-skip-ink-upright-001.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-text-decor/text-emphasis-position-over-left-002.xht [ ImageOnlyFailure ]
@@ -4830,7 +4868,6 @@ webkit.org/b/206882 imported/w3c/web-platform-tests/css/css-fonts/variations/at-
webkit.org/b/214300 imported/w3c/web-platform-tests/css/css-fonts/font-language-override-01.html [ ImageOnlyFailure ]
webkit.org/b/214300 imported/w3c/web-platform-tests/css/css-fonts/test-synthetic-italic-2.html [ ImageOnlyFailure ]
webkit.org/b/214300 imported/w3c/web-platform-tests/css/css-fonts/test-synthetic-italic-3.html [ ImageOnlyFailure ]
-webkit.org/b/214300 imported/w3c/web-platform-tests/css/css-fonts/variations/font-slant-1.html [ ImageOnlyFailure ]
webkit.org/b/214300 imported/w3c/web-platform-tests/css/css-fonts/hiragana-katakana-kerning.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-fonts/font-face-local-not-family.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-fonts/font-family-name-000.xht [ ImageOnlyFailure ]
@@ -4847,10 +4884,6 @@ imported/w3c/web-platform-tests/css/css-fonts/size-adjust-unicode-range-system-f
imported/w3c/web-platform-tests/css/css-fonts/variation-sequences.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-fonts/downloadable-font-scoped-to-document.html
-# Untriaged font-style/font-face @font-face descriptor bugs
-webkit.org/b/206881 imported/w3c/web-platform-tests/css/css-fonts/font-face-weight-auto-variable.html [ ImageOnlyFailure ]
-webkit.org/b/206881 imported/w3c/web-platform-tests/css/css-fonts/font-face-weight-default-variable.html [ ImageOnlyFailure ]
-
# Triaged css-fonts failures
webkit.org/b/246564 imported/w3c/web-platform-tests/css/css-fonts/font-synthesis-small-caps-not-applied.html [ ImageOnlyFailure ]
webkit.org/b/150081 imported/w3c/web-platform-tests/css/css-fonts/font-language-override-02.html [ ImageOnlyFailure ]
@@ -5326,7 +5359,6 @@ imported/w3c/web-platform-tests/css/css-lists/marker-counter.html [ ImageOnlyFai
imported/w3c/web-platform-tests/css/css-lists/list-style-type-decimal-line-height.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-lists/list-style-type-decimal-vertical-lr.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-lists/list-style-type-decimal-vertical-rl.html [ ImageOnlyFailure ]
-imported/w3c/web-platform-tests/css/css-lists/marker-webkit-text-fill-color.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-lists/list-marker-with-lineheight-and-overflow-hidden-001.html [ ImageOnlyFailure ]
# list-style bidi support
@@ -5680,9 +5712,18 @@ imported/w3c/web-platform-tests/css/css-conditional/container-queries/custom-lay
imported/w3c/web-platform-tests/css/css-conditional/container-queries/inline-size-bfc-floats.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-conditional/container-queries/pseudo-elements-010.html [ ImageOnlyFailure ]
-# Scroll-state container queries (unsupported), enable parsing test only.
+# Scroll-state container queries (evaluation unsupported), enable parsing/serialization and computed-value tests only.
imported/w3c/web-platform-tests/css/css-conditional/container-queries/scroll-state [ Skip ]
imported/w3c/web-platform-tests/css/css-conditional/container-queries/scroll-state/container-type-scroll-state-parsing.html [ Pass ]
+imported/w3c/web-platform-tests/css/css-conditional/container-queries/scroll-state/container-type-scroll-state-computed.html [ Pass ]
+imported/w3c/web-platform-tests/css/css-conditional/container-queries/scroll-state/at-container-scrollable-parsing.html [ Pass ]
+imported/w3c/web-platform-tests/css/css-conditional/container-queries/scroll-state/at-container-scrollable-serialization.html [ Pass ]
+imported/w3c/web-platform-tests/css/css-conditional/container-queries/scroll-state/at-container-scrolled-parsing.html [ Pass ]
+imported/w3c/web-platform-tests/css/css-conditional/container-queries/scroll-state/at-container-scrolled-serialization.html [ Pass ]
+imported/w3c/web-platform-tests/css/css-conditional/container-queries/scroll-state/at-container-stuck-parsing.html [ Pass ]
+imported/w3c/web-platform-tests/css/css-conditional/container-queries/scroll-state/at-container-stuck-serialization.html [ Pass ]
+imported/w3c/web-platform-tests/css/css-conditional/container-queries/scroll-state/at-container-snapped-parsing.html [ Pass ]
+imported/w3c/web-platform-tests/css/css-conditional/container-queries/scroll-state/at-container-snapped-serialization.html [ Pass ]
webkit.org/b/279424 [ Debug ] imported/w3c/web-platform-tests/css/css-conditional/container-queries/svg-foreignobject-no-size-container.html [ Skip ] # Crash
@@ -6064,11 +6105,6 @@ imported/w3c/web-platform-tests/trusted-types/trusted-types-reporting-for-Shared
imported/w3c/web-platform-tests/trusted-types/should-sink-type-mismatch-violation-be-blocked-by-csp-002-worker.html [ Skip ]
imported/w3c/web-platform-tests/trusted-types/should-trusted-type-policy-creation-be-blocked-by-csp-004-worker.html [ Skip ]
-# Close watchers aren't finished yet
-webkit.org/b/315510 imported/w3c/web-platform-tests/close-watcher/iframes/dialog-same-origin-nn.html [ Skip ]
-webkit.org/b/315510 imported/w3c/web-platform-tests/close-watcher/iframes/dialog-same-origin-ynn.html [ Skip ]
-webkit.org/b/315510 imported/w3c/web-platform-tests/close-watcher/iframes/dialog-same-origin-ynyn.html [ Skip ]
-
# Needs moveBefore support
webkit.org/b/281223 imported/w3c/web-platform-tests/dom/nodes/moveBefore/focus-preserve-render.html [ Skip ]
webkit.org/b/281223 imported/w3c/web-platform-tests/dom/nodes/moveBefore/moveBefore-as-flex-item.html [ Skip ]
@@ -6076,9 +6112,6 @@ webkit.org/b/281223 imported/w3c/web-platform-tests/dom/nodes/moveBefore/moveBef
webkit.org/b/281223 imported/w3c/web-platform-tests/dom/nodes/moveBefore/select-option-optgroup.html [ Skip ]
webkit.org/b/281223 imported/w3c/web-platform-tests/dom/nodes/moveBefore/css-transition-trigger.html [ Skip ]
webkit.org/b/281223 imported/w3c/web-platform-tests/dom/nodes/moveBefore/relevant-mutations.html [ Skip ]
-webkit.org/b/281223 imported/w3c/web-platform-tests/dom/nodes/moveBefore/selection-preserve.html [ Skip ]
-webkit.org/b/281223 imported/w3c/web-platform-tests/dom/nodes/moveBefore/hover-style-update.html [ Skip ]
-webkit.org/b/281223 imported/w3c/web-platform-tests/dom/nodes/moveBefore/pointer-events.html [ Skip ]
# Flaky crash.
webkit.org/b/315031 imported/w3c/web-platform-tests/dom/nodes/moveBefore/throws-exception.html [ Skip ]
@@ -6597,7 +6630,70 @@ webkit.org/b/255101 imported/w3c/web-platform-tests/css/css-borders/border-radiu
webkit.org/b/255101 imported/w3c/web-platform-tests/css/css-borders/tentative/border-radius-side-shorthands/border-radius-side-shorthands-001.html [ ImageOnlyFailure ]
webkit.org/b/255101 imported/w3c/web-platform-tests/css/css-borders/tentative/border-radius-side-shorthands/border-radius-side-shorthands-002.html [ ImageOnlyFailure ]
-webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/tentative/corner-shape [ Skip ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/border-shape-absolute-coords-shape.tentative.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/border-shape/border-shape-background-origin-border-box.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/border-shape/border-shape-polygon-miter-limit.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/border-shape/border-shape-shadow-blur.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/border-shape/border-shape-shadow-circle.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/border-shape/border-shape-shadow-inset-blur.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/border-shape/border-shape-shadow-transparent.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/border-shape/border-shape-stroke-from-border.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/border-shape/border-shape-two-shapes-shadow.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/border-shape/border-shape-outline-double.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/corner-shape-backdrop-filter-overflow.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/corner-shape-backdrop-filter-video-overflow.html [ ImageOnlyFailure Pass ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/corner-shape-backdrop-filter.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/corner-shape-bevel-overflow-composite.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/corner-shape-bevel-overflow.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/corner-shape-computed.html [ Failure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/corner-shape-fill.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/border-shape/border-shape-clips-background.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/border-shape/border-shape-collapsed-shape-clips-background.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/border-shape/border-shape-geometry-box.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/border-shape/border-shape-ignore-radius.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/border-shape/border-shape-inner-outer.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/border-shape/border-shape-inset-shadow.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/border-shape/border-shape-outline-auto-forms.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/border-shape/border-shape-outline-auto-with-border.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/border-shape/border-shape-outline-auto.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/border-shape/border-shape-outline-double-path.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/corner-shorthand-rendering.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/border-shape/border-shape-outline-double-shape.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/border-shape/border-shape-outline-offset.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/border-shape/border-shape-outline-with-border-double-ellipse.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/border-shape/border-shape-outline-with-border-ellipse.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/border-shape/border-shape-outline-with-border.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/border-shape/border-shape-outline.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/border-shape/border-shape-overflow-solid-background.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/border-shape/border-shape-overflow.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/corner-shape-img-border.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/corner-shape-inset-shadow.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/corner-shape-interpolation.html [ Failure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/corner-shape-invalid.html [ Failure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/corner-shape-notch.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/corner-shape-overflow-clip-margin.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/corner-shape-square.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/corner-shape-svg-border.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/corner-shape-valid.html [ Failure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/corner-shape-video-border.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/corner-shape-iframe-border.html [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/render-corner-shape.html?border-radius=40&corner-shape=-1.5&shadow-spread=10 [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/render-corner-shape.html?border-radius=5&corner-top-left-shape=0.5&corner-bottom-right-shape=-0.5&shadow-spread=10 [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/render-corner-shape.html?corner-top-left-shape=2.5&border-radius=20%&border-width=10 [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/render-corner-shape.html?corner-shape=0.8&border-radius=40&border-width=10 [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/render-corner-shape.html?corner-shape=2.3&border-radius=40% [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/render-corner-shape.html?corner-bottom-right-shape=0.8&border-bottom-right-radius=50% [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/render-corner-shape.html?corner-top-left-shape=0.5&border-radius=40 [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/render-corner-shape.html?corner-top-left-shape=-0.5&border-radius=40 [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/render-corner-shape.html?border-top-left-radius=50%&corner-shape=0.7&border-left-width=30&border-top-width=30 [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/render-corner-shape.html?corner-shape=3&border-top-right-radius=33 [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/render-corner-shape.html?corner-shape=squircle&border-radius=50% [ ImageOnlyFailure ]
+webkit.org/b/277912 imported/w3c/web-platform-tests/css/css-borders/corner-shape/render-corner-shape.html?corner-top-left-shape=-4&border-radius=40 [ ImageOnlyFailure ]
+
+webkit.org/b/316474 imported/w3c/web-platform-tests/css/css-borders/border-width-rounding.tentative.html [ Pass Failure ]
+
+ipc/send-gpu-GetShareableBitmap-RemoteRenderingBackend.html [ ImageOnlyFailure ]
+media/media-source/media-source-real-play-after-eos.html [ ImageOnlyFailure ]
webkit.org/b/279222 imported/w3c/web-platform-tests/css/css-backgrounds/background-image-cors-no-reload.html [ Pass Failure ]
@@ -7016,6 +7112,9 @@ imported/w3c/web-platform-tests/navigation-api/navigation-methods/return-value/b
webkit.org/b/300006 imported/w3c/web-platform-tests/navigation-api/navigate-event/navigation-back-cross-document-preventDefault.html [ Skip ]
+# Crashing. rdar://179168475
+imported/w3c/web-platform-tests/navigation-api/navigate-event/dangling-navigate-event-after-bfcache-restore.html [ Skip ]
+
# Out of target of Interop 2025.
imported/w3c/web-platform-tests/navigation-api/navigate-event/replaceState-inside-back-handler-infinite.optional.html [ Skip ]
imported/w3c/web-platform-tests/navigation-api/navigate-event/navigate-multiple-history-pushState.html [ Skip ]
@@ -7914,7 +8013,7 @@ imported/w3c/web-platform-tests/svg/painting/reftests/text-shadow-02.html [ Imag
imported/w3c/web-platform-tests/svg/painting/reftests/text-shadow-03.html [ ImageOnlyFailure ]
# framework is missing for test to run
-webkit.org/b/299496 model-element/gpup-model-element.html [ Skip ]
+webkit.org/b/299496 model-element/gpup/gpup-model-element.html [ Skip ]
imported/w3c/web-platform-tests/css/css-pseudo/before-as-flex-container.html [ ImageOnlyFailure ]
imported/w3c/web-platform-tests/css/css-pseudo/svg-text-selection-fill-only.html [ ImageOnlyFailure ]
diff --git a/LayoutTests/accessibility/aria-grid-with-presentational-sections-expected.txt b/LayoutTests/accessibility/aria-grid-with-presentational-sections-expected.txt
new file mode 100644
index 000000000000..8932444cfd9c
--- /dev/null
+++ b/LayoutTests/accessibility/aria-grid-with-presentational-sections-expected.txt
@@ -0,0 +1,17 @@
+This test ensures an ARIA grid built from native table sections whose rows are wrapped in presentational scaffolding exposes its rows and cells.
+
+PASS: grid.role.toLowerCase().includes('table') === true
+PASS: grid.rowCount === 2
+PASS: grid.columnCount === 3
+#grid cellForColumnAndRow(0, 0).domIdentifier is sun
+#grid cellForColumnAndRow(1, 0).domIdentifier is mon
+#grid cellForColumnAndRow(2, 0).domIdentifier is tue
+#grid cellForColumnAndRow(0, 1).domIdentifier is day1
+#grid cellForColumnAndRow(1, 1).domIdentifier is day2
+#grid cellForColumnAndRow(2, 1).domIdentifier is day3
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+Sun Mon Tue
+1 2 3
diff --git a/LayoutTests/accessibility/aria-grid-with-presentational-sections.html b/LayoutTests/accessibility/aria-grid-with-presentational-sections.html
new file mode 100644
index 000000000000..871e32933121
--- /dev/null
+++ b/LayoutTests/accessibility/aria-grid-with-presentational-sections.html
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Sun
+ Mon
+ Tue
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/LayoutTests/accessibility/aria-owns-crash-after-subtree-update-expected.txt b/LayoutTests/accessibility/aria-owns-crash-after-subtree-update-expected.txt
new file mode 100644
index 000000000000..42ba4ba0476c
--- /dev/null
+++ b/LayoutTests/accessibility/aria-owns-crash-after-subtree-update-expected.txt
@@ -0,0 +1,20 @@
+This tests that mixed DOM + aria-owns cycles do not cause stack-overflow recursion in updateOwnedChildrenIfNecessary().
+
+PASS: typeof t1a.childrenCount === 'number'
+PASS: typeof t2a.childrenCount === 'number'
+PASS: typeof t3self.childrenCount === 'number'
+PASS: typeof t4a.childrenCount === 'number'
+PASS: did not stack overflow.
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+a
+b
+c
+child of t2-a
+b
+self
+a-mutated
+b
+c
diff --git a/LayoutTests/accessibility/aria-owns-crash-after-subtree-update.html b/LayoutTests/accessibility/aria-owns-crash-after-subtree-update.html
new file mode 100644
index 000000000000..65160952909a
--- /dev/null
+++ b/LayoutTests/accessibility/aria-owns-crash-after-subtree-update.html
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+a
+b
+c
+
+
+
+b
+
+
+self
+
+
+
+
+
+
+
diff --git a/LayoutTests/accessibility/iframe-content-remains-accessible-after-navigation-expected.txt b/LayoutTests/accessibility/iframe-content-remains-accessible-after-navigation-expected.txt
new file mode 100644
index 000000000000..66c485176f12
--- /dev/null
+++ b/LayoutTests/accessibility/iframe-content-remains-accessible-after-navigation-expected.txt
@@ -0,0 +1,9 @@
+This test ensures iframe content remains accessible across rapid iframe document replacements.
+
+PASS: yesButton && yesButton.title.includes('Yes') === true
+PASS: noButton && noButton.title.includes('No') === true
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/accessibility/iframe-content-remains-accessible-after-navigation.html b/LayoutTests/accessibility/iframe-content-remains-accessible-after-navigation.html
new file mode 100644
index 000000000000..bfe1c9aca6f4
--- /dev/null
+++ b/LayoutTests/accessibility/iframe-content-remains-accessible-after-navigation.html
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/LayoutTests/accessibility/mac/input-type-change-without-renderer-expected.txt b/LayoutTests/accessibility/mac/input-type-change-without-renderer-expected.txt
new file mode 100644
index 000000000000..713453f40e22
--- /dev/null
+++ b/LayoutTests/accessibility/mac/input-type-change-without-renderer-expected.txt
@@ -0,0 +1,9 @@
+This tests that changing the type attribute of a non-rendered input updates its role description.
+
+PASS: roleDescriptionIncludes('telephone') === true
+PASS: roleDescriptionIncludes('email') === true
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/accessibility/mac/input-type-change-without-renderer.html b/LayoutTests/accessibility/mac/input-type-change-without-renderer.html
new file mode 100644
index 000000000000..d8e4cf178fc2
--- /dev/null
+++ b/LayoutTests/accessibility/mac/input-type-change-without-renderer.html
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/LayoutTests/accessibility/mac/selection-element-tabbing-to-link.html b/LayoutTests/accessibility/mac/selection-element-tabbing-to-link.html
index 76e33f9fd4fb..7c96eee2b01a 100644
--- a/LayoutTests/accessibility/mac/selection-element-tabbing-to-link.html
+++ b/LayoutTests/accessibility/mac/selection-element-tabbing-to-link.html
@@ -63,7 +63,6 @@
if (window.accessibilityController) {
jsTestIsAsync = true;
- testRunner.waitUntilDone();
accessibilityController.enableEnhancedAccessibility(true);
webArea = accessibilityController.rootElement.childAtIndex(0);
diff --git a/LayoutTests/accessibility/resources/iframe-with-buttons.html b/LayoutTests/accessibility/resources/iframe-with-buttons.html
new file mode 100644
index 000000000000..3133777821b6
--- /dev/null
+++ b/LayoutTests/accessibility/resources/iframe-with-buttons.html
@@ -0,0 +1,8 @@
+
+
+
+Hello there!
+Yes
+No
+
+
diff --git a/LayoutTests/editing/input/delete-text-in-composition.html b/LayoutTests/editing/input/delete-text-in-composition.html
index faa822939d9d..fcadb3853acd 100644
--- a/LayoutTests/editing/input/delete-text-in-composition.html
+++ b/LayoutTests/editing/input/delete-text-in-composition.html
@@ -8,11 +8,8 @@
diff --git a/LayoutTests/editing/selection/5497643.html b/LayoutTests/editing/selection/5497643.html
index 7059a98b3507..f72f7d0bdbfe 100644
--- a/LayoutTests/editing/selection/5497643.html
+++ b/LayoutTests/editing/selection/5497643.html
@@ -5,10 +5,7 @@
+ Hello
+
+
diff --git a/LayoutTests/editing/selection/ios/select-text-by-long-press-with-focused-element-expected.txt b/LayoutTests/editing/selection/ios/select-text-by-long-press-with-focused-element-expected.txt
new file mode 100644
index 000000000000..2335e17a5084
--- /dev/null
+++ b/LayoutTests/editing/selection/ios/select-text-by-long-press-with-focused-element-expected.txt
@@ -0,0 +1,14 @@
+Verifies that long-pressing non-editable text while a separate editable element is focused starts a selection and dismisses the keyboard (mirrors UIKit TextField behavior: focus and non-editable selection are mutually exclusive). Requires WebKitTestRunner.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS keyboardWasShowing is true
+PASS focusedElementWasInput is true
+PASS getSelection().toString() became 'Hello'
+PASS keyboardDismissed is true
+PASS focusedElementBlurred is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+Hello
diff --git a/LayoutTests/editing/selection/ios/select-text-by-long-press-with-focused-element.html b/LayoutTests/editing/selection/ios/select-text-by-long-press-with-focused-element.html
new file mode 100644
index 000000000000..ff898b312fd5
--- /dev/null
+++ b/LayoutTests/editing/selection/ios/select-text-by-long-press-with-focused-element.html
@@ -0,0 +1,52 @@
+
+
+